Intel® Integrated Performance Primitives
Deliberate problems developing high-performance vision, signal, security, and storage applications.

ippiCanny does not work?

eren
Beginner
1,414 Views
Hi all,

I am using the IPP 5.0 for Linux on a 32-bit Dual-Xeon @ 2.8 GHz with 2 GB of RAM running SuSe 9.3. I am trying to perform edge detection inside a function called from my main(), but the outcome of ippiCanny is pretty much rubbish. The code segment I use is the following:

// Perform Canny edge detection on the grayscale frame
// Step 1: Get the buffer sizes for the Sobel operations and setup the corresponding buffers
ippiFilterSobelHorizGetBufferSize_32f_C1R(frameSizeDec, ippMskSize5x5, &horizBufferSize);
Ipp8u *horizBuffer = ippsMalloc_8u(horizBufferSize);
ippiFilterSobelVertGetBufferSize_32f_C1R(frameSizeDec, ippMskSize5x5, &vertBufferSize);
Ipp8u *vertBuffer = ippsMalloc_8u(vertBufferSize);
	
// Step 2: Perform the Sobel filtering in the horizontal and vertical dimensions
Ipp32f *horizSobel = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f);
ippiFilterSobelHorizBorder_32f_C1R(grayFrameFloat, step1_32f, horizSobel, step1_32f, frameSizeDec, ippMskSize5x5, ippBorderRepl, 0, horizBuffer);
// 	ippiMulC_16s_C1IRSfs(-1, horizSobel, step1_16s, frameSizeDec, 0);
Ipp32f *vertSobel = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f);
ippiFilterSobelVertBorder_32f_C1R(grayFrameFloat, step1_32f, vertSobel, step1_32f, frameSizeDec, ippMskSize5x5, ippBorderRepl, 0, vertBuffer);
// 	ippiMulC_16s_C1IRSfs(-1, vertSobel, step1_16s, frameSizeDec, 0);
	
// Step 3: Before running the actual edge detector, create a color histogram of the image to decide on Canny's thresholds
Ipp32f *magnitude = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f);
ippsMagnitude_32f(horizSobel, vertSobel, magnitude, step1_8u * frameSizeDec.height);
	
ippiMax_32f_C1R(magnitude, step1_32f, frameSizeDec, &max);
if (max) ippiDivC_32f_C1IR(max, magnitude, step1_32f, frameSizeDec);
	
Ipp32s *histogram = ippsMalloc_32s(HIST_BINS);
ippsSet_32s(0, histogram, HIST_BINS);
Ipp32f *binEdges = ippsMalloc_32f(HIST_BINS + 1);
ippsVectorRamp_32f(binEdges, HIST_BINS + 1, 0.0, 1.0 / (Ipp32f)HIST_BINS);
ippiHistogramRange_32f_C1R(magnitude, step1_32f, frameSizeDec, histogram, binEdges, HIST_BINS + 1);
cumSum = 0;
	

for (i = 0; i < HIST_BINS; i++)
{
	cumSum += (*(histogram + i));
	if (cumSum > (Ipp32s)(0.7 * frameSizeDec.width * frameSizeDec.height)) break;
}
	
highThresh = (Ipp32f)(i + 2) / (Ipp32f)HIST_BINS;
lowThresh = 0.7 * highThresh;
cout << "Canny: lowThresh = " << lowThresh << ", highThresh = " << highThresh << endl;
	
// Step 4: Now call the actual Canny function
ippiCannyGetSize(frameSizeDec, &cannyBufferSize);
Ipp8u *cannyBuffer = ippsMalloc_8u(cannyBufferSize);
Ipp8u *buff = ippiMalloc_8u_C1(frameSizeDec.width, frameSizeDec.height, &step1_8u);
ippiCanny_32f8u_C1R(horizSobel, step1_32f, vertSobel, step1_32f, edges, step1_8u, frameSizeDec, lowThresh, highThresh, cannyBuffer);

My input is the grayFrameFloat buffer and the edges buffer is globally defined outside of this function. I have tried using the ippiCanny_32f8u and the ippiCanny_16s8u versions (with corresponding changes in the code of course) and I get the same result, which is a whole lot of edges where there should be none. Specifically, I get edges in the areas where the magnitude of horizSobel and vertSobel (viewed together as a complex buffer) is more than highThresh (as I should), but also many many more edges where the magnitude is less than lowThresh (which is ridiculous as the Canny algorithm should discard such regions immediately). I would like to show you an example outcome of the above code to further illustrate my problem but I don't know how to post an image (if that's even possible in this forum).

Can anybody help me?

Andreas.



0 Kudos
6 Replies
Chao_Y_Intel
Moderator
1,414 Views

Hi Andreas,

Could you provide one runnable test code for us? That will help us to investigate the problem more quickly.

Thanks,

Chao

0 Kudos
eren
Beginner
1,414 Views
Um, are you talking about an executable? Or do you need some values for the parameters I use, such as frameSizeDec etc?

Andreas.

P.S. I forgot to mention in my original post that the values of lowThresh and highThresh that I give to ippiCanny don't seem to have any effect (which I think they should), except for when lowThresh > highThresh (in which case I get no edges, and rightfully so).
0 Kudos
eren
Beginner
1,414 Views
I have written a simple program that reads an image from file, resizes it by a given factor (default is 2), converts it to grayscale and then feeds it to the Canny edge detector (after computing the Sobel horizontal and vertical derivatives of course). I am using an image size of 640x480 (prior to resizing) because that is what the camera I am working with here returns, but I don't think changing it will affect things. Apart from IPP functions, I am also using OpenCV 0.96 for loading and displaying images (using the auxilliary functions openWindow_8u and openWindow_32f). Here is the code:

#ifdef HAVE_CONFIG_H
#include 
#endif

#include "ipp.h"
#include "cv.h"
#include "highgui.h"

#include 
#include 

using namespace std;

// Frame decimation factor
#define FRAME_DEC 2
// Number of histogram bins
#define HIST_BINS 64

// Default and decinated camera frame size
IppiSize frameSize = {640, 480};
IppiSize frameSizeDec = {640 / FRAME_DEC, 480 / FRAME_DEC};

// Global memory structures
Ipp8u *grayFrame;
Ipp8u *edges;

// Auxilliary functions
void openWindow_8u(Ipp8u *img, IppiSize *size, int nChannels, char *name, int wait);
void openWindow_32f(Ipp32f *img, IppiSize *size, int nChannels, char *name, int wait);

int main(int argc, char *argv[])
{
	int step1_8u, step1_8u_dec, step3_8u, step3_8u_dec, step1_32f_dec, horizBufferSize, vertBufferSize, i, cannyBufferSize;
	Ipp32s cumSum;
	Ipp32f max, highThresh, lowThresh;
	
	IplImage *cvImg = cvLoadImage("seq_000000.jpg", -1);
	
	Ipp8u *frame = ippiMalloc_8u_C3(frameSize.width, frameSize.height, &step3_8u);
	Ipp8u *frameDec = ippiMalloc_8u_C3(frameSizeDec.width, frameSizeDec.height, &step3_8u_dec);
	grayFrame = ippiMalloc_8u_C1(frameSizeDec.width, frameSizeDec.height, &step1_8u_dec);
	edges = ippiMalloc_8u_C1(frameSizeDec.width, frameSizeDec.height, &step1_8u_dec);
	
	
IppiRect ROI = {0, 0, frameSize.width, frameSize.height};
	step1_8u = step3_8u / 3;
	ippiCopy_8u_C3R((Ipp8u *)(cvImg -> imageData), step3_8u, frame, step3_8u, frameSize);
	ippiResize_8u_C3R(frame, frameSize, step3_8u, ROI, frameDec, step3_8u_dec, frameSizeDec, 1.0F / (Ipp32f)FRAME_DEC, 1.0F / (Ipp32f)FRAME_DEC, IPPI_INTER_NN);
	ippiRGBToGray_8u_C3C1R(frameDec, step3_8u_dec, grayFrame, step1_8u_dec, frameSizeDec);
	
	// Perform Canny edge detection on the grayscale frame
	// Step 1: Get the buffer sizes for the Sobel operations and setup the corresponding buffers
	ippiFilterSobelHorizGetBufferSize_32f_C1R(frameSizeDec, ippMskSize5x5, &horizBufferSize);
	Ipp8u *horizBuffer = ippsMalloc_8u(horizBufferSize);
	ippiFilterSobelVertGetBufferSize_32f_C1R(frameSizeDec, ippMskSize5x5, &vertBufferSize);
	Ipp8u *vertBuffer = ippsMalloc_8u(vertBufferSize);
	
	// Step 2: Perform the Sobel filtering in the horizontal and vertical dimensions
	Ipp32f *grayFrameFloat = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f_dec);
	ippiConvert_8u32f_C1R(grayFrame, step1_8u_dec, grayFrameFloat, step1_32f_dec, frameSizeDec);
	ippiDivC_32f_C1IR((Ipp32f)IPP_MAX_8U, grayFrameFloat, step1_32f_dec, frameSizeDec);
	Ipp32f *horizSobel = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f_dec);
	ippiFilterSobelHorizBorder_32f_C1R(grayFrameFloat, step1_32f_dec, horizSobel, step1_32f_dec, frameSizeDec, ippMskSize5x5, ippBorderRepl, 0, horizBuffer);
// 	ippiMulC_32f_C1IR(-1.0, horizSobel, step1_32f, frameSizeDec);
	Ipp32f *vertSobel = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f_dec);
	ippiFilterSobelVertBorder_32f_C1R(grayFrameFloat, step1_32f_dec, vertSobel, step1_32f_dec, frameSizeDec, ippMskSize5x5, ippBorderRepl, 0, vertBuffer);
// 	ippiMulC_32f_C1IR(-1.0, vertSobel, step1_32f, frameSizeDec);
	
	// Step 3: Before running the actual edge detector, create a color histogram of the image to decide on Canny's thresholds
	Ipp32f *magnitude = ippiMalloc_32f_C1(frameSizeDec.width, frameSizeDec.height, &step1_32f_dec);
	ippsMagnitude_32f(horizSobel, vertSobel, magnitude, step1_8u_dec * frameSizeDec.height);
	
	ippiMax_32f_C1R(magnitude, step1_32f_dec, frameSizeDec, &max);
	if (max) ippiDivC_32f_C1IR(max, magnitude, step1_32f_dec, frameSizeDec);
	
	Ipp32s *histogram = ippsMalloc_32s(HIST_BINS);
	ippsSet_32s(0, histogram, HIST_BINS);
	Ipp32f *binEdges = ippsMalloc_32f(HIST_BINS + 1);
	ippsVectorRamp_32f(binEdges, HIST_BINS + 1, 0.0, 1.0 / (Ipp32f)HIST_BINS);
	ippiHistogramRange_32f_C1R(magnitude, s
tep1_32f_dec, frameSizeDec, histogram, binEdges, HIST_BINS + 1);
	cumSum = 0;
	
	for (i = 0; i < HIST_BINS; i++)
	{
		cumSum += (*(histogram + i));
		if (cumSum > (Ipp32s)(0.7 * frameSizeDec.width * frameSizeDec.height)) break;
	}
	
	highThresh = (Ipp32f)(i + 2) / (Ipp32f)HIST_BINS;
	lowThresh = 0.7 * highThresh;
	cout << "Canny: lowThresh = " << lowThresh << ", highThresh = " << highThresh << endl;
	openWindow_8u(grayFrame, &frameSizeDec, 1, "Grayscale Frame", 10);
	openWindow_32f(grayFrameFloat, &frameSizeDec, 1, "Grayscale Frame (Float)", 10);
	openWindow_32f(magnitude, &frameSizeDec, 1, "Magnitude", 10);
	
	// Step 4: Now call the actual Canny function
	ippiCannyGetSize(frameSizeDec, &cannyBufferSize);
	Ipp8u *cannyBuffer = ippsMalloc_8u(cannyBufferSize);
	
	Ipp8u *buff = ippiMalloc_8u_C1(frameSizeDec.width, frameSizeDec.height, &step1_8u_dec);
	ippiCompareC_32f_C1R(magnitude, step1_32f_dec, highThresh, edges, step1_8u_dec, frameSizeDec, ippCmpGreater);
	openWindow_8u(edges, &frameSizeDec, 1, "Magnitude more than highThresh", 10);
	ippiNot_8u_C1R(edges, step1_8u_dec, buff, step1_8u_dec, frameSizeDec);
	
	ippiCompareC_32f_C1R(magnitude, step1_32f_dec, lowThresh, edges, step1_8u_dec, frameSizeDec, ippCmpLess);
	openWindow_8u(edges, &frameSizeDec, 1, "Magnitude less than lowThresh", 10);
	ippiNot_8u_C1IR(edges, step1_8u_dec, frameSizeDec);
	
	ippiAnd_8u_C1IR(edges, step1_8u_dec, buff, step1_8u_dec, frameSizeDec);
	openWindow_8u(buff, &frameSizeDec, 1, "Magnitude between lowThresh and highThresh", 10);
	
	IppStatus status = ippiCanny_32f8u_C1R(vertSobel, step1_32f_dec, horizSobel, step1_32f_dec, edges, step1_8u_dec, frameSizeDec, lowThresh, highThresh, cannyBuffer);
	cout << "Canny status: " << status << endl;
	openWindow_8u(edges, &frameSizeDec, 1, "IPP Canny Edge Detector", 0);
	
	ippiFree(frame);
	ippiFree(frameDec);
	ippiFree(grayFrame);
	ippiFree(edges);
	ippiFree(grayFrameFloat);
	ippiFree(horizSobel);
	ippiFree(vertSobel);
	ip
piFree(magnitude);
	ippiFree(buff);
	
	ippsFree(horizBuffer);
	ippsFree(vertBuffer);
	ippsFree(histogram);
	ippsFree(binEdges);
	ippsFree(cannyBuffer);
	
	cvReleaseImage(&cvImg);

return 0;
} void openWindow_8u(Ipp8u *img, IppiSize *size, int nChannels, char *name, int wait) { IplImage *cvImg; // cv image used to display CvSize sizeCv; // cv size structure Ipp8u *tmp; // IPP temporary image sizeCv.width = size->width; // define size sizeCv.height = size->height; // of the displayed image int tmpStep; // create a cv image to be displayed cvImg = cvCreateImage(sizeCv, IPL_DEPTH_8U, nChannels); // create temporary image so that img is unchanged if (nChannels == 1) tmp = ippiMalloc_8u_C1(size -> width, size -> height, &tmpStep); else if (nChannels == 3) tmp = ippiMalloc_8u_C3(size -> width, size -> height, &tmpStep); // copy img into tmp if (nChannels == 1) ippiCopy_8u_C1R(img, tmpStep, tmp, tmpStep, *size); else if (nChannels == 3) ippiCopy_8u_C3R(img, tmpStep, tmp, tmpStep, *size); // set cv image to contain tmp cvSetData(cvImg, (void *)tmp, tmpStep); cvNamedWindow(name, 1); // create window cvShowImage(name, cvImg); // displays cv image cvWaitKey(wait); // wait for key (==0) or wait milliseconds ippiFree(tmp); cvReleaseImageHeader(&cvImg); } void openWindow_32f(Ipp32f *img, IppiSize *size, int nChannels, char *name, int wait) { IplImage *cvImg; // cv image used to display CvSize sizeCv; // cv size structure Ipp32f *tmp; // IPP temporary image sizeCv.width = size->width; // define size sizeCv.height = size->height; // of the displayed image int tmpStep; // create a cv image to be displayed cvImg = cvCreateImage(sizeCv, IPL_DEPTH_32F, nChannels); // create temporary image so that img is unchanged if (nChannels == 1) tmp = ippiMalloc_32f_C1(size -> width, size -> height, &tmpStep); else if (nChannels == 3) tmp = ippiMalloc_32f_C3(size -> width, size -> height, &tmpStep); // copy img into tmp if (nChannels == 1) ippiCopy_32f_C1R(img, tmpStep, tmp, tmpStep, *size); else if (nChannels == 3) ippiCopy_32f_C3R(img, tmpStep, tmp, tmpStep, *size); // set cv image to contain tmp cvSetData(cvImg, (void *)tmp, tmpStep); cvNamedWindow(name, 1); // create window cvShowImage(name, cvImg); // displays cv image cvWaitKey(wait); // wait for key (==0) or wait milliseconds ippiFree(tmp); cvReleaseImageHeader(&cvImg); }
I guess this code can run as is by just linking with the appropriate IPP and OpenCV libraries (you will need ippi, ipps, ippcv, ippcc, cv, cvaux, cxcore and highgui). Just replace "seq_000000.jpg" with an image of your own and you should be set...

Apart from the final output of ippiCanny, I also display the pixels whose magnitude is higher than highThresh (which Canny should immediately accept as edges and as you can see are indeed such), the pixels whose magnitude is less than lowThresh (which Canny should immediately reject as edges and as you can see are not edges) and also those between the two thresholds (which Canny should accept as edges only if they are connected to a strong edge, i.e. of the first set of pixels). The end result however considers as edges many of the pixels whose magnitude is less than lowThresh, and that gives me problems.

So, any ideas?

Andreas.

0 Kudos
mlotkov
Beginner
1,414 Views

Hi Andreas,

in this sample is betterto use ippiFilterSobelNegVertBorder_32f_C1R instead of ippiFilterSobelVertBorder_32f_C1R.

Thanks,

Michael

0 Kudos
eren
Beginner
1,414 Views
Hi Michael,

I tried out your suggestion, as well as mupltiplied the output of the Sobel filtering by -1, as was pointed out in another thread, both to no avail. So I am now using a Laplacian of Gaussian (LoG) filter for the edge detector based on MATLAB's correspondning function, and everything works out fine. I will try it out again when I install IPP 5.1 in case there was some kind of bug in version 5.0.

Thanks for your reply anyways,
Andreas.
0 Kudos
Intel_C_Intel
Employee
1,414 Views

Hi, Andreas,

Have you negated Vert Sobel only or both Sobels? May be the reason is in thresholds. Could you send the final example with the image where Canny does not work? It could help us to enhance the description of this function and provide some recommendations.

Thanks,

Alexander

0 Kudos
Reply