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 }