// check ratio requirement b:w:b:w:b = 1:1:3:1:1
bool qr_checkRatio()
{
totalFinderSize = 0;
for(int i =0;i<5; i++)
{
int count = stateCount[i];
totalFinderSize += count;
if(count == 0)
return false;
}
if(totalFinderSize<7)
return false;
int moduleSize = ceil(totalFinderSize / 7.0); // scale factor of the finder
// tolerate some "slop" of the ratio
double maxVariance = moduleSize*tol_factor;
bool retVal = ((abs(moduleSize - (stateCount[0]))< maxVariance) &&
(abs(moduleSize - (stateCount[1]))< maxVariance) &&
(abs(3*moduleSize - (stateCount[2]))< 3*maxVariance) &&
(abs(moduleSize - (stateCount[3]))< maxVariance) &&
(abs(moduleSize - (stateCount[4]))< maxVariance));
return retVal;
}
以上代码就是判断是否为一个块。这个探测图像的比例为(1:1:3:1:1),计算出每个模块的平均宽度。差值不能超过0.5,这个tol_factor=0.5。如果满足这个条件那么就是我们所需找的一个定位点。
/* group possible finder locations, that is, each location vote in an array, so that
we can find three largest votes, calculate the mean location of these three groups and
finally draw them on the image
*/
void group_points(vector<Point>& points)
{
CvScalar red = CV_RGB(255,0,0);
/* if the size of vector, number of possible finder locations is greater than 3,
we need to group them. if not, then just draw them on the image
*/
if (points.size()>= 3)
{
double distance;
vector<vector<Point>> group(points.size());// every vector stores the finder locations which belong to one group
vector<int> score(points.size());// store the number of votes
vector<int> score_index(points.size());// store the index of score when we sort the score
int temp1;
int temp2;
// set values for score_index
for(size_t k=0; k < points.size();++k)
{
score_index[k] = k;
}
/* group the points by distance
check whether point i is near enough to point j (j<i), if so, then vote for j.
No matter whether i is near to j or not, it will vote for itself
*/
for(size_t i = 0; i < points.size(); ++i)
{
for (size_t j=0; j < i; ++j)
{
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)));
if (distance < tol_distance)
{
score[j] += 1;
group[j].push_back(points[i]);
break;
}
}
score[i] += 1;
group[i].push_back(points[i]);
}
// sort the score and write new index into score_index
for(size_t m = 0; m < points.size()-1; ++m)
{
for(size_t n = m; n < points.size(); ++n)
{
if (score[m]<=score[n])
{
temp1 = score_index[m];
score_index[m] = score_index[n];
score_index[n] = temp1;
temp2 = score[m];
score[m] = score[n];
score[n] = temp2;
}
}
}
// calculate the mean location of three groups with largest votes
vector<Point>::iterator it;
for (it = group[score_index[0]].begin(); it != group[score_index[0]].end(); ++it)
{
qr_point1 += (*it);
}
qr_point1.x = qr_point1.x/score[0];
qr_point1.y = qr_point1.y/score[0];
for (it = group[score_index[1]].begin(); it != group[score_index[1]].end(); ++it)
{
qr_point2 += (*it);
}
qr_point2.x = qr_point2.x/score[1];
qr_point2.y = qr_point2.y/score[1];
for (it = group[score_index[2]].begin(); it != group[score_index[2]].end(); ++it)
{
qr_point3 += (*it);
}
qr_point3.x = qr_point3.x/score[2];
qr_point3.y = qr_point3.y/score[2];
// output the final finder center location
cout<<qr_point1<<endl;
cout<<qr_point2<<endl;
cout<<qr_point3<<endl;
draw_crosshair(qr_point1,src,red);
draw_crosshair(qr_point2,src,red);
draw_crosshair(qr_point3,src,red);
}
else
{
for(int v = 0; v < points.size(); ++v)
{
draw_crosshair(points[v],src,red);
}
}
}
使用这种算法可能会找到很多这样的点,那么需要把一堆的点进行一个均值化处理。这样得到的结果更加精确!其实这个已经写得比较复杂,其实做的事情就是把一些点根据它的范围把它归类再求均值!
//draw a red crosshair on some point of the image
void draw_crosshair(Point qr_point,Mat src,CvScalar color)
{
Point up1,up2;
up1.x = qr_point.x;
up1.y = qr_point.y -2;
up2.x = qr_point.x;
up2.y = qr_point.y +2;
Point down1,down2;
down1.x = qr_point.x -2;
down1.y = qr_point.y;
down2.x = qr_point.x + 2;
down2.y = qr_point.y;
// draw two lines that intersects on qr_point
line(src,up1,up2,color,1,8);
line(src,down1,down2,color,1,8);
}
这个函数是用于找到QR码三个点之后,把它在图片上标记出来。
line( CvArr* img,CvPoint pt1, CvPoint pt2, CvScalar color,int thickness=1, int line_type=8, int shift=0 );
第一个参数img:要划的线所在的图像;第二个参数pt1:直线起点;第二个参数pt2:直线终点;第三个参数color:直线的颜色 e.g:Scalor(0,0,255);第四个参数thickness=1:线条粗细;第五个参数line_type=8, 8 (or 0) - 8-connected line(8邻接)连接 线。4 - 4-connected line(4邻接)连接线。CV_AA - antialiased 线条。第六个参数:坐标点的小数点位数。
至于怎么找这些点计算其个数,请看我下一篇BLOG!
注意点:
release():这个函数是用于自己定义的指针,分配了内存,那么要自己释放。
cvDestroyWindow,该函数为开放计算机视觉(OpenCV)库库函数之一,用来销毁一个窗口。
waitKey():waitKey仅对窗口机制起作用,即namedWindow产生的窗口。若在此之前没有产生窗口,则waitKey无用。waitKey(10)是等待10ms,在此期间按下按钮则会返回按下的键值。waitKey(0)就是无限等待,直到有按键。另外,在imshow之后如果没有waitKey语句则不会正常显示图像。