3 ## The full "Square Detector" program.
4 ## It loads several images subsequentally and tries to find squares in
17 g.wndname = "Square Detection Demo";
19 function ret = compute_angle( pt1, pt2, pt0 )
24 ret = (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
27 function squares = findSquares4( img, storage )
32 sz = cvSize( img.width, img.height );
33 timg = cvCloneImage( img ); # make a copy of input image
34 gray = cvCreateImage( sz, 8, 1 );
35 pyr = cvCreateImage( cvSize(int32(sz.width/2), int32(sz.height/2)), 8, 3 );
36 ## create empty sequence that will contain points -
37 ## 4 points per square (the square's vertices)
38 squares = cvCreateSeq( 0, cv.sizeof_CvSeq, cv.sizeof_CvPoint, storage );
39 squares = cv.CvSeq_CvPoint.cast( squares );
41 ## select the maximum ROI in the image
42 ## with the width and height divisible by 2
43 subimage = cvGetSubRect( timg, cvRect( 0, 0, sz.width, sz.height ));
45 ## down-scale and upscale the image to filter out the noise
46 cvPyrDown( subimage, pyr, 7 );
47 cvPyrUp( pyr, subimage, 7 );
48 tgray = cvCreateImage( sz, 8, 1 );
49 ## find squares in every color plane of the image
51 ## extract the c-th color plane
52 channels = {[], [], []};
54 cvSplit( subimage, channels{1}, channels{2}, channels{3}, [] ) ;
56 ## hack: use Canny instead of zero threshold level.
57 ## Canny helps to catch squares with gradient shading
59 ## apply Canny. Take the upper threshold from slider
60 ## and set the lower to 0 (which forces edges merging)
61 cvCanny( tgray, gray, 0, g.thresh, 5 );
62 ## dilate canny output to remove potential
63 ## holes between edge segments
64 cvDilate( gray, gray, [], 1 );
66 ## apply threshold if l!=0
67 ## tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
68 cvThreshold( tgray, gray, l*255/N, 255, cv.CV_THRESH_BINARY );
71 ## find contours and store them all as a list
72 [count, contours] = cvFindContours( gray, storage, cv.sizeof_CvContour, cv.CV_RETR_LIST, cv.CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
74 if (!swig_this(contours))
79 for contour = CvSeq_hrange(contours),
80 ## approximate contour with accuracy proportional
81 ## to the contour perimeter
82 result = cvApproxPoly( contour, cv.sizeof_CvContour, storage, cv.CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
83 ## square contours should have 4 vertices after approximation
84 ## relatively large area (to filter out noisy contours)
86 ## Note: absolute value of an area is used because
87 ## area may be positive or negative - in accordance with the
88 ## contour orientation
89 if( result.total == 4 &&
90 abs(cvContourArea(result)) > 1000 &&
91 cvCheckContourConvexity(result) )
94 ## find minimum angle between joint
95 ## edges (maximum of cosine)
97 t = abs(compute_angle( result{i}, result{i-2}, result{i-1}));
103 ## if cosines of all angles are small
104 ## (all angles are ~90 degree) then write quandrange
105 ## vertices to resultant sequence
108 squares.append( result{i} )
117 ## the function draws all the squares in the image
118 function drawSquares( img, squares )
122 cpy = cvCloneImage( img );
123 ## read 4 sequence elements at a time (all vertices of a square)
125 while (i<squares.total)
126 pt = { squares{i}, squares{i+1}, squares{i+2}, squares{i+3} };
128 ## draw the square as a closed polyline
129 cvPolyLine( cpy, {pt}, 1, CV_RGB(0,255,0), 3, cv.CV_AA, 0 );
133 ## show the resultant image
134 cvShowImage( g.wndname, cpy );
137 function on_trackbar( a )
140 if( swig_this(g.img) )
141 drawSquares( g.img, findSquares4( g.img, g.storage ) );
145 g.names = {"../c/pic1.png", "../c/pic2.png", "../c/pic3.png", \
146 "../c/pic4.png", "../c/pic5.png", "../c/pic6.png" };
148 ## create memory storage that will contain all the dynamic data
149 g.storage = cvCreateMemStorage(0);
151 g.img0 = cvLoadImage( name, 1 );
152 if (!swig_this(g.img0))
153 printf("Couldn't load %s\n",name);
156 g.img = cvCloneImage( g.img0 );
157 ## create window and a trackbar (slider) with parent "image" and set callback
158 ## (the slider regulates upper threshold, passed to Canny edge detector)
159 cvNamedWindow( g.wndname, 1 );
160 cvCreateTrackbar( "canny thresh", g.wndname, g.thresh, 1000, @on_trackbar );
161 ## force the image processing
164 ## Also the function cvWaitKey takes care of event processing
166 ## clear memory storage - reset free space position
167 cvClearMemStorage( g.storage );
172 cvDestroyWindow( g.wndname );