代码改变世界

OpenCV第二个assignment:检测QR code的3个 finder centers

2012-10-02 08:00  美女驴  阅读(1240)  评论(0编辑  收藏  举报

这是我第二个加第三个OpenCV assignment的代码,贴出来和大家共享,我用的是visual studio 2008 pro+OpenCV,如果大家有更好的建议可以跟我讨论~

 代码实现功能:

(1). detect qr finder locations and mark them as red crosshairs
(2). determine an affine mapping from the detected mark locations to predefined output locations and create a normalized version of each image.
(3). manually mark the finder locations as green crosshairs

  1 #include "stdafx.h"
  2 #include <opencv2/opencv.hpp>
  3 #include <stdio.h>
  4 #include <vector>
  5 #include <iostream>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <math.h>
  9 
 10 // we can adjust these parameters for different qr code images
 11 #define tol_factor 0.5
 12 #define tol_distance 8
 13 
 14 using namespace std;
 15 using namespace cv;
 16 
 17 
 18 Mat gray_img,bw_img;
 19 Mat src;
 20 int stateCount[5] = {0};// store the number of pixels in each state of the state machine
 21 int totalFinderSize;// store the size of the Finder
 22 vector<Point> points_row;// store the possible locations of finders
 23 Point qr_point1; // final finder center locations
 24 Point qr_point2;
 25 Point qr_point3;
 26 int thresholdValue = 0;//the threshold for each image
 27 //for on_mouse() function 
 28 char chek_cut_state = 0;
 29 Point origin;
 30 
 31 void threshold_selection(int,void*);// by sliding the trackbar to select a good threshold
 32 void qr_find_row();// search finder locations row by row
 33 void qr_find_col();// search finder locations column by column
 34 bool qr_checkRatio();// judge whether a similar pattern is a possible finder or not
 35 void group_points(vector<Point>& points);// find the three largest vote locations in the accumulator array
 36 void draw_crosshair(Point qr_point,Mat src,CvScalar color);// draw a red crosshair on some point of the image
 37 void affine_trans(); // through affine transformation to create a normalized version of each image
 38 void on_mouse( int event, int x, int y, int flags, void* param); 
 39 
 40 //-----------------------------------------Main Function------------------------------------------------------------------------------
 41 int main (int argc, char** argv)
 42 {
 43 
 44     // read a color qr code image which is used to display the red crosshairs(detected finder locations) and green crosshairs(manually marked finder locations)
 45     const char* imagename = "1.jpg";
 46     src = imread(imagename,1);
 47     namedWindow("original image", CV_WINDOW_AUTOSIZE);
 48     imshow("original image",src);
 49 
 50     // convert the color image into gray image
 51     cvtColor(src,gray_img,CV_BGR2GRAY);
 52 
 53     // slide the threshold value trackbar to observe the binary image until the finder locations in the binary image are clear, then we get a good threshold value
 54     namedWindow("binary image",CV_WINDOW_AUTOSIZE);
 55     createTrackbar("threshold value","binary image",&thresholdValue,255, threshold_selection);
 56     threshold_selection(0,0);
 57     bw_img = gray_img > thresholdValue; //bw_img is our final binary image
 58 
 59     cout<<"press c to continue after choosing a threshold"<<endl;
 60     char c;
 61     while(1)
 62     {
 63         c = cvWaitKey(0);
 64         if(c =='c') break;
 65     }
 66         
 67     // detect qr code finders and mark finder centers as red crosshairs
 68     qr_find_row();
 69     //qr_find_col();
 70     group_points(points_row);
 71     imshow("original image",src); // display the color qr code image with red crosshairs
 72 
 73     //determine an affine mapping from the detected mark locations to predefined locations and use this mapping to create a normalized image
 74     affine_trans();
 75 
 76     //manually mark the finder locations as green crosshairs
 77     cout<<"manually marked finder locations"<<endl;
 78     cvSetMouseCallback("original image", on_mouse,0);
 79 
 80 
 81     // wait for any key to quit
 82     waitKey(0);
 83 
 84     // release memory for images and destroy windows
 85     src.release();
 86     gray_img.release();
 87     bw_img.release();
 88     cvDestroyWindow("original image");
 89     cvDestroyWindow("binary image");
 90  
 91     return 0;
 92 
 93 }
 94 //-----------------------------------------------------------------------------------------------------------------------------------
 95 
 96 // use trackbar to get the threshold value
 97 void threshold_selection(int,void*)
 98 {
 99     threshold(gray_img, bw_img, thresholdValue, 255 ,0);
100     imshow("binary image", bw_img);
101 }
102 
103 
104 
105 /* search possible qr finder locations row by row
106 This function goes through every row and keeps a track of the number of white or black
107 pixels it encounters. It also keeps a track of the order in which they're found. whenever
108 it founds something like b:w:b:w:b = 1:1:3:1:1, it will put the location of this possible
109 finder into vector<Point> points_row
110 */
111 
112 void qr_find_row()
113 {
114     int skipRows = 1;
115     int currentState=0;
116     int x,y;
117     for (int row = skipRows-1; row< bw_img.rows; row += skipRows)
118     {
119         stateCount[0] = 0; // inside black pixels
120         stateCount[1] = 0; // inside white pixels
121         stateCount[2] = 0; // inside black pixels
122         stateCount[3] = 0; // inside white pixels
123         stateCount[4] = 0; // inside black pixels
124         currentState = 0; // record the current state, b,w,b,w,b
125         
126         uchar* ptr_row = bw_img.ptr<uchar>(row); // get a pointer to the current row
127         for(int col = 0; col< bw_img.cols; col++)
128         {
129             if(ptr_row[col]<128)
130             {
131                 // we are at a black pixel
132 
133                 if((currentState & 0x1) == 1)
134                 {
135                     // W->B transition
136                     currentState++;
137                 }
138 
139                 // works for W->B and B->B
140                 stateCount[currentState]++;
141             }
142             else
143             {
144                 // we are at a white pixel
145 
146                 if((currentState & 0x1) == 1)
147                 {
148                     // the current state is state 1 or state 3 (white pixel state)
149                     // W->W
150                     stateCount[currentState]++;
151                 }
152                 else
153                 {   
154                     // the current state is state 0, state 2 or state 4 (black pixel state)
155 
156                     if(currentState == 4)
157                     {
158                         // we found the "white" area after one finder pattern
159                         // use ratio requirement to check whether it is a possible finder or not
160                         if(qr_checkRatio())
161                         {
162                             // ratio is correct, push this possible location into vector
163                             y = row;
164                             x = col-totalFinderSize/2;
165                             points_row.push_back(Point(x,y));
166                             currentState = 0;
167                             stateCount[0] = 0;
168                             stateCount[1] = 0;
169                             stateCount[2] = 0;
170                             stateCount[3] = 0;
171                             stateCount[4] = 0;
172                             // for debug
173                             //cout<<Point(x,y)<<endl;
174                             //draw_crosshair(Point(x,y),src);
175                         }
176                         else
177                         {
178                             // ratio is not correct, do the switch
179                             currentState = 3;
180                             stateCount[0] = stateCount[2];
181                             stateCount[1] = stateCount[3];
182                             stateCount[2] = stateCount[4];
183                             stateCount[3] = 1;
184                             stateCount[4] = 0;
185                         }
186 
187                     }
188                     else
189                     {
190                         // the current state is state 0 or state 2
191                         // B->W transition
192                         currentState++;
193                         stateCount[currentState]++;
194                     }
195                 }
196             }
197         }
198     }
199 }
200 
201 
202 /* search possible qr finder locations column by column
203 This function is similar to qr_find_row(). And the possible finder locations are
204 still pushed into vector points_row
205 */
206 void qr_find_col()
207 {
208     int skipCols = 1;
209     int currentState=0;
210     int x,y;
211     for (int col = skipCols-1; col< bw_img.cols; col += skipCols)
212     {
213         stateCount[0] = 0;
214         stateCount[1] = 0; 
215         stateCount[2] = 0; 
216         stateCount[3] = 0; 
217         stateCount[4] = 0; 
218         currentState = 0; 
219         
220         uchar* ptr_col = bw_img.ptr<uchar>(col);
221         for(int row = 0; row< bw_img.rows; row++)
222         {
223             if(ptr_col[row]<128)
224             {
225                 if((currentState & 0x1) == 1)
226                 {
227                     currentState++;
228                 }
229                 stateCount[currentState]++;
230             }
231             else
232             {
233                 if((currentState & 0x1) == 1)
234                 {
235                     stateCount[currentState]++;
236                 }
237                 else
238                 {
239                     if(currentState == 4)
240                     {
241                         if(qr_checkRatio())
242                         {
243                             y = row-totalFinderSize/2;;
244                             x = col;
245                             points_row.push_back(Point(x,y));
246                             currentState = 0;
247                             stateCount[0] = 0;
248                             stateCount[1] = 0;
249                             stateCount[2] = 0;
250                             stateCount[3] = 0;
251                             stateCount[4] = 0;
252                            //cout<<Point(x,y)<<endl;
253                            //draw_crosshair(Point(x,y),src);
254                         }
255                         else
256                         {
257                             currentState = 3;
258                             stateCount[0] = stateCount[2];
259                             stateCount[1] = stateCount[3];
260                             stateCount[2] = stateCount[4];
261                             stateCount[3] = 1;
262                             stateCount[4] = 0;
263                         }
264                         
265                     }
266                     else
267                     {
268                         currentState++;
269                         stateCount[currentState]++;
270                     }
271                 }
272             }
273         }
274     }
275 }
276 
277 
278 // check ratio requirement b:w:b:w:b = 1:1:3:1:1
279 bool qr_checkRatio()
280 {
281     totalFinderSize = 0;    
282     for(int i =0;i<5; i++)
283     {
284         int count = stateCount[i];
285         totalFinderSize += count;
286         if(count == 0)
287             return false;
288     }
289     if(totalFinderSize<7)
290         return false;
291 
292     int moduleSize = ceil(totalFinderSize / 7.0); // scale factor of the finder
293     
294     // tolerate some "slop" of the ratio
295     double maxVariance = moduleSize*tol_factor;
296     bool retVal = ((abs(moduleSize - (stateCount[0]))< maxVariance) &&
297     (abs(moduleSize - (stateCount[1]))< maxVariance) &&
298     (abs(3*moduleSize - (stateCount[2]))< 3*maxVariance) &&
299     (abs(moduleSize - (stateCount[3]))< maxVariance) &&
300     (abs(moduleSize - (stateCount[4]))< maxVariance));
301 
302     return retVal;
303 }
304 
305 /* group possible finder locations, that is, each location vote in an array, so that 
306 we can find three largest votes, calculate the mean location of these three groups and
307 finally draw them on the image
308 */
309 void group_points(vector<Point>& points)
310 {
311     
312    CvScalar red = CV_RGB(255,0,0);
313     /* if the size of vector, number of possible finder locations is greater than 3,
314     we need to group them. if not, then just draw them on the image
315     */
316     if (points.size()>= 3)
317     {
318         double distance;
319         vector<vector<Point>> group(points.size());// every vector stores the finder locations which belong to one group
320         vector<int> score(points.size());// store the number of votes
321         vector<int> score_index(points.size());// store the index of score when we sort the score
322         int temp1;
323         int temp2;
324 
325         // set values for score_index
326         for(size_t k=0; k < points.size();++k)
327         {
328             score_index[k] = k;
329         }
330 
331         /* group the points by distance
332         check whether point i is near enough to point j (j<i), if so, then vote for j.
333         No matter whether i is near to j or not, it will vote for itself
334         */
335         for(size_t i = 0; i < points.size(); ++i)
336         {
337             for (size_t j=0; j < i; ++j)
338             {
339                 distance = sqrt(double((points[i].x-points[j].x)*(points[i].x-points[j].x)+(points[i].y-points[j].y)*(points[i].y-points[j].y)));
340                 if (distance < tol_distance)
341                 {
342                     score[j] += 1;
343                     group[j].push_back(points[i]);
344                     break;
345                  }
346              }
347              score[i] += 1;
348              group[i].push_back(points[i]);
349          }
350 
351 
352         // sort the score and write new index into score_index
353          for(size_t m = 0; m < points.size()-1; ++m)
354          {
355             for(size_t n = m; n < points.size(); ++n)
356             {
357                if (score[m]<=score[n])
358                {
359                 temp1 = score_index[m];
360                 score_index[m] = score_index[n];
361                 score_index[n] = temp1;
362                 temp2 = score[m];
363                 score[m] = score[n];
364                 score[n] = temp2;
365                 }
366             }
367          }
368 
369          // calculate the mean location of three groups with largest votes
370          vector<Point>::iterator it;
371          for (it = group[score_index[0]].begin(); it != group[score_index[0]].end(); ++it)
372          {
373              qr_point1 += (*it);
374          }
375          qr_point1.x = qr_point1.x/score[0];
376          qr_point1.y = qr_point1.y/score[0];
377 
378          for (it = group[score_index[1]].begin(); it != group[score_index[1]].end(); ++it)
379          {
380              qr_point2 += (*it);
381          }
382          qr_point2.x = qr_point2.x/score[1];
383          qr_point2.y = qr_point2.y/score[1];
384 
385          for (it = group[score_index[2]].begin(); it != group[score_index[2]].end(); ++it)
386          {
387             qr_point3 += (*it);
388          }
389          qr_point3.x = qr_point3.x/score[2];
390          qr_point3.y = qr_point3.y/score[2];
391 
392          // output the final finder center location and its corresponding score
393          //cout<<score[0]<<endl;
394          //cout<<score[1]<<endl;
395          //cout<<score[2]<<endl;
396          //cout<<qr_point1<<endl;
397          //cout<<qr_point2<<endl;
398          //cout<<qr_point3<<endl;
399          draw_crosshair(qr_point1,src,red);
400          draw_crosshair(qr_point2,src,red);
401          draw_crosshair(qr_point3,src,red);
402     }
403     else
404     {
405         for(int v = 0; v < points.size(); ++v)
406         {
407             draw_crosshair(points[v],src,red);
408         }
409     }
410 
411 }
412 
413 
414 //draw a red crosshair on some point of the image
415 void draw_crosshair(Point qr_point,Mat src,CvScalar color)
416 {
417     Point up1,up2;
418     up1.x = qr_point.x;
419     up1.y = qr_point.y -2;
420     up2.x = qr_point.x;
421     up2.y = qr_point.y +2;
422     Point down1,down2;
423     down1.x = qr_point.x -2;
424     down1.y = qr_point.y;
425     down2.x = qr_point.x + 2;
426     down2.y = qr_point.y;
427     // draw two lines that intersects on qr_point
428     line(src,up1,up2,color,1,8);
429     line(src,down1,down2,color,1,8);
430 }
431 
432 
433 void affine_trans()
434 {
435   Point2f srcPoints[3];
436   Point2f dstPoints[3];
437 
438   Mat mapping(2,3,CV_32FC1);
439   Mat src_img,wrap_dst_img;
440   src_img = imread("1.jpg",1);//for final affine transform result because src has red crosshairs now
441   wrap_dst_img = Mat::zeros(700,700,CV_8UC3); // create an output image of size 700*700 and 24-bit color
442 
443   //set detected mark locations 
444   srcPoints[0] = qr_point1;
445   srcPoints[1] = qr_point2;;
446   srcPoints[2] = qr_point3;
447   cout<<"original finder locations"<<endl;
448   cout<<srcPoints[0]<<'\n'<<srcPoints[1]<<'\n'<<srcPoints[2]<<endl;
449 
450   //decide which one is finder mark location 1
451   double diffx0,diffy0,diffx1,diffy1,diffx2,diffy2;
452   diffx0 = srcPoints[0].x-srcPoints[1].x;
453   diffy0 = srcPoints[0].y-srcPoints[1].y;
454   diffx1 = srcPoints[1].x-srcPoints[2].x;
455   diffy1 = srcPoints[1].y-srcPoints[2].y;
456   diffx2 = srcPoints[2].x-srcPoints[0].x;
457   diffy2 = srcPoints[2].y-srcPoints[0].y;
458 
459   //use cos(theta)=<x,y>/(|x||y|) to calculate angle
460   double norm[3];
461   norm[0] = sqrt(diffx0*diffx0+diffy0*diffy0);
462   norm[1] = sqrt(diffx1*diffx1+diffy1*diffy1);
463   norm[2] = sqrt(diffx2*diffx2+diffy2*diffy2);
464 
465   double angle[3];
466   angle[0] = acos((-diffx0*diffx2-diffy0*diffy2)/(norm[0]*norm[2]));
467   angle[1] = acos((-diffx0*diffx1-diffy0*diffy1)/(norm[0]*norm[1]));
468   angle[2] = acos((-diffx2*diffx1-diffy2*diffy1)/(norm[2]*norm[1]));
469   cout<<"angles between any two of the three edges"<<endl;
470   cout<<angle[0]<<'\n'<<angle[1]<<'\n'<<angle[2]<<endl;
471   
472 
473   Point2f temp;
474   if((angle[1]>angle[0]) && (angle[1]>angle[2]))
475   {
476       temp = srcPoints[0];
477       srcPoints[0] = srcPoints[1];
478       srcPoints[1] = temp;
479   }
480   else if ((angle[2]>angle[1]) && (angle[2]>angle[0]))
481   {
482       temp = srcPoints[0];
483       srcPoints[0] = srcPoints[2];
484       srcPoints[2] = temp;
485   }
486   
487   //decide which one is finder mark location 2 and finder mark location 3
488   //note in the example of assignment 3, direction from vector 12 to vector 13 is clockwise
489   diffx0 = srcPoints[1].x-srcPoints[0].x;
490   diffy0 = srcPoints[1].y-srcPoints[0].y;
491   diffx2 = srcPoints[2].x-srcPoints[0].x;
492   diffy2 = srcPoints[2].y-srcPoints[0].y;
493 
494   double det = diffx0*diffy2-diffy0*diffx2;
495   if(det<0)
496   {
497       temp = srcPoints[1];
498       srcPoints[1] = srcPoints[2];
499       srcPoints[2] = temp;
500   }
501 
502   cout<<"final ordered finder locations"<<endl;
503   cout<<srcPoints[0]<<'\n'<<srcPoints[1]<<'\n'<<srcPoints[2]<<endl;
504 
505   //set 3 predefined output locations
506   dstPoints[0] = Point2f(400,50);
507   dstPoints[1] = Point2f(600,50);
508   dstPoints[2] = Point2f(400,250);
509   
510   //get the Affine Transform
511   mapping = getAffineTransform(srcPoints, dstPoints);
512   cout<<"mapping = "<<mapping<<endl;
513  
514   //check whether the mapping is correct or not
515   cout<<"test the mapping is correct or not"<<endl;
516   Point2f test;
517   for (int i =0; i < 3; i++)
518   {
519     double* ptr_row = mapping.ptr<double>(0);
520     //cout<<ptr_row[0]<<endl;
521     test.x = (ptr_row[0]*srcPoints[i].x + ptr_row[1]*srcPoints[i].y + ptr_row[2]*1);
522     ptr_row = mapping.ptr<double>(1);
523     test.y = (ptr_row[0]*srcPoints[i].x + ptr_row[1]*srcPoints[i].y + ptr_row[2]*1);
524     cout<<test<<endl;
525   }
526 
527   //apply the Affine Transform just found to the src_img
528   warpAffine(src_img, wrap_dst_img, mapping, wrap_dst_img.size());
529   imwrite("1.normalized.jpg",wrap_dst_img);
530 
531   //display the normalized version of each image
532   namedWindow("transform image", CV_WINDOW_AUTOSIZE);
533   imshow("transform image",wrap_dst_img);
534 
535   wrap_dst_img.release();
536   src_img.release();
537 }
538 
539 
540 //manually mark points on images
541 void on_mouse( int event, int x, int y, int flags, void* param )
542 
543 { 
544     CvScalar green = CV_RGB(0,255,0); //green
545     if( (event==CV_EVENT_LBUTTONDOWN) )
546     {
547         origin = Point(x,y);;
548         chek_cut_state=1; 
549 
550     }
551 
552     if( chek_cut_state && event==CV_EVENT_LBUTTONUP )
553     {
554         chek_cut_state=0;
555         draw_crosshair(origin,src,green);
556         imshow("original image",src);
557         cout<<origin<<endl;
558         imwrite("1_finder.jpg",src);
559     }
560 
561 }