使用带有新 OpenCV C++ 接口的距离变换出现问题/如何确保 Mat 是二进制掩码?

Posted

技术标签:

【中文标题】使用带有新 OpenCV C++ 接口的距离变换出现问题/如何确保 Mat 是二进制掩码?【英文标题】:Problem using distance transform with new OpenCV C++ interface / How to ensure that Mat is a binary mask? 【发布时间】:2010-10-26 09:03:00 【问题描述】:

我正在移植我的代码以使用新的 OpenCV C++ 接口。我喜欢严格键入所有内容的可能性,因此从模板类中派生我所有的图像和矩阵:

Mat_<type> var;

现在我在使用 distanceTransform 函数时遇到了问题。我的代码是这样的:

Mat_<float> imgGray;
Mat_<unsigned char> imgBinary;
Mat_<float> imgDistance;

// ... fill imgGray with data ...

threshold(imgGray, imgBinary, 0.25, 255, CV_THRESH_BINARY);
distanceTransform(imgBinary, imgDistance, CV_DIST_L2, CV_DIST_MASK_PRECISE);

distanceTransform 失败。它给出了以下错误:

未知函数中不支持的格式或格式组合(源图像必须为 8uC1,距离图必须为 32fC1(或 8uC1,如果是简单的 L1 距离变换)),文件........\src\ cv\cvdistransform.cpp,第 730 行

我发现问题出在源参数上。它没有通过 CV_IS_MASK_ARR(src) 测试。

据我了解,阈值调用在 imgBinary 上创建以分配内存。所以我无法控制 imgBinary 矩阵的确切类型(应该是 CV_8UC1),对吗?如何确保 imgBinary 是正确的二进制掩码以使 distanceTransform 满意?

感谢您的帮助!

干杯,罗伯特

【问题讨论】:

ok 问题是threshold得到float数据时,输出也是float。当使用比较(或更短:imgBinary = imgGray > 0.25)时,代码可以正常工作。 但是:当使用严格类型的矩阵时,我会在尝试使用另一种数据类型时出现异常(这就是我使用严格类型矩阵的原因......)。阈值能够将 Mat_ 的类型更改为浮动并不是很好。有没有这方面的cmets? 【参考方案1】:

如果您愿意,可以查看下面的代码。这是带有 OpenCV C++ API 的新版本 DT

//#define CV_NO_BACKWARD_COMPATIBILITY

// if you compile the program under Windows and MSVC2008/2005
#if defined WIN32 || defined _MSC_VER
 #pragma warning(disable:4786)
 #pragma warning(disable:4996)

 #define _CRT_SECURE_NO_WARNINGS 
 #define _CRT_SECURE_NO_DEPRECATE
 #define _SCL_SECURE_NO_WARNINGS
#endif

#include <iostream>
#include "cv.h"
#include "cvaux.h"
#include "highgui.h"
#define DEMO_MIXED_API_USE 1

using namespace cv;
using namespace std;

char wndname[] = "Distance transform";
char tbarname[] = "Threshold";
int mask_size = CV_DIST_MASK_5;
int build_voronoi = 0;
int edge_thresh = 100;
int dist_type = CV_DIST_L1;

// The output and temporary images
Mat dist;
Mat dist8u1;
Mat dist8u2;
Mat dist8u;
Mat dist32s;

Mat gray;
Mat edge;
Mat labels;

// threshold trackbar callback
void on_trackbar( int dummy, void *)

    static const uchar colors[][3] =
    
        0,0,0,
        255,0,0,
        255,128,0,
        255,255,0,
        0,255,0,
        0,128,255,
        0,255,255,
        0,0,255,
        255,0,255
    ;

    int msize = mask_size;
    int _dist_type = build_voronoi ? CV_DIST_L2 : dist_type;

    threshold( gray, edge, (double)edge_thresh, (double)edge_thresh, CV_THRESH_BINARY );

    if( build_voronoi )
        msize = CV_DIST_MASK_5;

    if( _dist_type == CV_DIST_L1 )
    
        distanceTransform( edge, dist, _dist_type, msize );
    
    else 
    
        build_voronoi ? distanceTransform( edge, dist, labels, _dist_type, msize ) : distanceTransform( edge, dist, _dist_type, msize ) ;
    

    if( !build_voronoi )
    
        // begin "painting" the distance transform result
        dist.convertTo( dist, dist.type(), 5000.0, 0 );             //before--> cvConvertScale( dist, dist, 5000.0, 0 );
        pow( dist, 0.5, dist );                                     //before--> cvPow(dist, dist, 0.5);

        dist.convertTo( dist32s, dist32s.type(), 1.0, 0.5 );        //before--> cvConvertScale( dist, dist32s, 1.0, 0.5 );
        bitwise_and( dist32s, Scalar::all(255), dist32s, Mat() );   //before--> cvAndS( dist32s, ScalarAll(255), dist32s, 0 );
        dist32s.convertTo(dist8u1, dist8u1.type(), 1, 0 );          //before--> cvConvertScale( dist32s, dist8u1, 1, 0 );
        dist32s.convertTo( dist32s, dist32s.type(), -1, 0 );        //before--> cvConvertScale( dist32s, dist32s, -1, 0 );
        add( dist32s, Scalar::all(255), dist32s, Mat() );           //before--> cvAddS( dist32s, cvScalarAll(255), dist32s, 0 );
        dist32s.convertTo( dist8u2, dist8u2.type(), 1, 0 );         //before--> cvConvertScale( dist32s, dist8u2, 1, 0 );

        vector<Mat> Out(3);
        Out[0] = dist8u1;
        Out[1] = dist8u2;
        Out[2] = dist8u2;
        merge( Out, dist8u );           //before--> cvMerge( dist8u1, dist8u2, dist8u2, 0, dist8u );

        // end "painting" the distance transform result
    
    else
    
        int i, j;
        for( i = 0; i < labels.rows; i++ )
        
            int* ll = labels.ptr<int>(i);       //before--> (int*)(labels->imageData + i*labels->widthStep)
            float* dd = dist.ptr<float>(i);     //before--> (float*)(dist->imageData + i*dist->widthStep)
            uchar* d = dist8u.ptr<uchar>(i);    //before--> (uchar*)(dist8u->imageData + i*dist8u->widthStep)

            for( j = 0; j < labels.cols; j++ )
            
                int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1;
                int b = cvRound(colors[idx][0]);        // if there is an option to cvRound in the new OpenCV C++ API, tell me please
                int g = cvRound(colors[idx][1]);
                int r = cvRound(colors[idx][2]);
                d[j*3] = saturate_cast<uchar>(b);       //before--> (uchar)b;
                d[j*3+1] = saturate_cast<uchar>(g);     //before--> (uchar)g; 
                d[j*3+2] = saturate_cast<uchar>(r);     //before--> (uchar)r;
            
        
    

    imshow( wndname, dist8u );  //before--> cvShowImage( wndname, dist8u );


int main( int argc, char* argv[] )

    const char* filename = (argc == 2 ? argv[1] : "Debug/stuff.jpg");//lena.jpg

    gray = imread( filename, 0 );   // -1 loads image full(3 channels + alpha) , 0 only in grayscale

    if( gray.empty() )
        return -1;

    cout << "Hot keys: \n"
         << "\tESC - quit the program\n"
         << "\tC - use C/Inf metric\n"
         << "\tL1 - use L1 metric\n"
         << "\tL2 - use L2 metric\n"
         << "\t3 - use 3x3 mask\n"
         << "\t5 - use 5x5 mask\n"
         << "\t0 - use precise distance transform\n"
         << "\tv - switch Voronoi diagram mode on/off\n"
         << "\tSPACE - loop through all the modes\n" << endl;

    dist = Mat( gray.size(), CV_32FC1 );    //dist = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32F, 1 );
    dist8u1 = gray.clone();                 //dist8u1 = cvCloneImage( gray );
    dist8u2 = gray.clone();                 //dist8u2 = cvCloneImage( gray );
    dist8u = Mat( gray.size(), CV_8UC3 );   //dist8u = cvCreateImage( cvGetSize(gray), IPL_DEPTH_8U, 3 );
    dist32s = Mat( gray.size(), CV_32SC1 ); //dist32s = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
    edge = gray.clone();                    //edge = cvCloneImage( gray );
    labels = Mat( gray.size(), CV_32SC1 );  //labels = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );

    namedWindow( wndname, 1 );

    createTrackbar( tbarname, wndname, &edge_thresh, 255, on_trackbar );

    for(;;)
    
        int c;

        // Call to update the view
        on_trackbar(0, 0);

        c = waitKey(0);

        if( (char)c == 27 )
            break;

        if( (char)c == 'c' || (char)c == 'C' )
            dist_type = CV_DIST_C;
        else if( (char)c == '1' )
            dist_type = CV_DIST_L1;
        else if( (char)c == '2' )
            dist_type = CV_DIST_L2;
        else if( (char)c == '3' )
            mask_size = CV_DIST_MASK_3;
        else if( (char)c == '5' )
            mask_size = CV_DIST_MASK_5;
        else if( (char)c == '0' )
            mask_size = CV_DIST_MASK_PRECISE;
        else if( (char)c == 'v' )
            build_voronoi ^= 1;
        else if( (char)c == ' ' )
        
            if( build_voronoi )
            
                build_voronoi = 0;
                mask_size = CV_DIST_MASK_3;
                dist_type = CV_DIST_C;
            
            else if( dist_type == CV_DIST_C )
                dist_type = CV_DIST_L1;
            else if( dist_type == CV_DIST_L1 )
                dist_type = CV_DIST_L2;
            else if( mask_size == CV_DIST_MASK_3 )
                mask_size = CV_DIST_MASK_5;
            else if( mask_size == CV_DIST_MASK_5 )
                mask_size = CV_DIST_MASK_PRECISE;
            else if( mask_size == CV_DIST_MASK_PRECISE )
                build_voronoi = 1;
        
    

    return 0;

【讨论】:

以上是关于使用带有新 OpenCV C++ 接口的距离变换出现问题/如何确保 Mat 是二进制掩码?的主要内容,如果未能解决你的问题,请参考以下文章

C++ OpenCV基于距离变换与分水岭的图像分割

OpenCV 距离变换的错误输出

从 OpenCV 距离变换中得到奇怪的结果

OpenCV 距离变换中的像素索引

奇怪的 OpenCV 距离变换结果

C++ OpenCV透视变换改进---直线拟合的应用