Problem using distance transform with new OpenCV C++ interface / How to ensure that Mat is a binary mask?
I'm porting my code to use the new OpenCV C++ interface. I like the possibility to strictly type everything, so derive all my images and matrices from the template class:
Mat_<type> var;
Now I have problems using the distanceTransform function. My code does something like this:
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 fails. It gives the following error:
Unsupported format or combination of formats (source image must be 8uC1 and the distance map must be 32fC1 (or 8uC1 in case of simple L1 distance transform)) in unknown function, file ........\src\cv\cvdistransform.cpp, line 730
I figured out that the problem is the source param开发者_StackOverflow社区eter. It doesn't pass the CV_IS_MASK_ARR(src) test.
From what I understand, threshold calls create on imgBinary to allocate the memory. So I don't have the possibility to control the exact type (should be CV_8UC1) of the imgBinary matrix, do I? How can I make sure that imgBinary is a proper binary mask to make distanceTransform happy?
Thanks for your help!
Cheers, Robert
If you wish, you can check the code below. It's a new version of DT with OpenCV C++ API
//#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;
}
精彩评论