Update to 2.0.0 tree from current Fremantle build
[opencv] / samples / octave / squares.m
1 #! /usr/bin/env octave
2 ##
3 ## The full "Square Detector" program.
4 ## It loads several images subsequentally and tries to find squares in
5 ## each image
6 ##
7
8 cv;
9 highgui;
10
11 global g;
12
13 g.thresh = 50;
14 g.img = [];
15 g.img0 = [];
16 g.storage = [];
17 g.wndname = "Square Detection Demo";
18
19 function ret = compute_angle( pt1, pt2, pt0 )
20   dx1 = pt1.x - pt0.x;
21   dy1 = pt1.y - pt0.y;
22   dx2 = pt2.x - pt0.x;
23   dy2 = pt2.y - pt0.y;
24   ret = (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
25 endfunction
26
27 function squares = findSquares4( img, storage )
28   global g;
29   global cv;
30
31   N = 11;
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 );
40
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 ));
44
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
50   for c=1:3,
51     ## extract the c-th color plane
52     channels = {[], [], []};
53     channels{c} = tgray;
54     cvSplit( subimage, channels{1}, channels{2}, channels{3}, [] ) ;
55     for l=1:N,
56       ## hack: use Canny instead of zero threshold level.
57       ## Canny helps to catch squares with gradient shading
58       if( l == 1 )
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 );
65       else
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 );
69       endif
70
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) );
73
74       if (!swig_this(contours))
75         continue;
76       endif
77      
78       ## test each contour
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)
85         ## and be convex.
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) )
92           s = 0;
93           for i=1:5,
94             ## find minimum angle between joint
95             ## edges (maximum of cosine)
96             if( i > 2 )
97               t = abs(compute_angle( result{i}, result{i-2}, result{i-1}));
98               if (s<t)
99                 s=t;
100               endif
101             endif
102           endfor
103           ## if cosines of all angles are small
104           ## (all angles are ~90 degree) then write quandrange
105           ## vertices to resultant sequence
106           if( s < 0.3 )
107             for i=1:4,
108               squares.append( result{i} )
109             endfor
110           endif
111         endif
112       endfor
113     endfor
114   endfor
115 endfunction
116
117 ## the function draws all the squares in the image
118 function drawSquares( img, squares )
119   global g;
120   global cv;
121
122   cpy = cvCloneImage( img );
123   ## read 4 sequence elements at a time (all vertices of a square)
124   i=0;
125   while (i<squares.total)
126     pt = { squares{i}, squares{i+1}, squares{i+2}, squares{i+3} };
127
128     ## draw the square as a closed polyline
129     cvPolyLine( cpy, {pt}, 1, CV_RGB(0,255,0), 3, cv.CV_AA, 0 );
130     i+=4;
131   endwhile
132
133   ## show the resultant image
134   cvShowImage( g.wndname, cpy );
135 endfunction
136
137 function on_trackbar( a )
138   global g;
139
140   if( swig_this(g.img) )
141     drawSquares( g.img, findSquares4( g.img, g.storage ) );
142   endif
143 endfunction
144
145 g.names =  {"../c/pic1.png", "../c/pic2.png", "../c/pic3.png", \
146             "../c/pic4.png", "../c/pic5.png", "../c/pic6.png" };
147
148 ## create memory storage that will contain all the dynamic data
149 g.storage = cvCreateMemStorage(0);
150 for name = g.names,
151   g.img0 = cvLoadImage( name, 1 );
152   if (!swig_this(g.img0))
153     printf("Couldn't load %s\n",name);
154     continue;
155   endif
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
162   on_trackbar(0);
163   ## wait for key.
164   ## Also the function cvWaitKey takes care of event processing
165   c = cvWaitKey(0);
166   ## clear memory storage - reset free space position
167   cvClearMemStorage( g.storage );
168   if( c == '\x1b' )
169     break;
170   endif
171 endfor
172 cvDestroyWindow( g.wndname );
173