CvSeq* findSquares4(IplImage* img, CvMemStorage* storage)
{
CvSeq* contours;
int i, c, l, N = 11;
CvSize sz = cvSize(img->width & -2, img->height & -2);
IplImage* timg = cvCloneImage(img);
IplImage* gray = cvCreateImage(sz, 8, 1);
IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3);
IplImage* tgray;
CvSeq* result;
double s, t;
// ŽŽœšÒ»žö¿ÕÐòÁÐÓÃÓڎ探ÂÖÀªœÇµã
CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage);
cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height));
// ¹ýÂËÔëÒô
cvPyrDown(timg, pyr, 7);
cvPyrUp(pyr, timg, 7);
tgray = cvCreateImage(sz, 8, 1);
// ºìÂÌÀ¶3É«·Ö±ð³¢ÊÔÌáÈ¡
for (c = 0; c < 3; c++)
{
// ÌáÈ¡ the c-th color plane
cvSetImageCOI(timg, c + 1);
cvCopy(timg, tgray, 0);
/*if (c == 0)
cvShowImage("gray0", tgray);
if (c == 1)
cvShowImage("gray1", tgray);
if (c == 2)
cvShowImage("gray2", tgray);*/
// ³¢ÊÔž÷ÖÖãÐÖµÌáÈ¡µÃµœµÄ£šN=11£©
for (l = 0; l < N; l++)
{
// apply Canny. Take the upper threshold from slider
// Canny helps to catch squares with gradient shading
/*if (l == 0)
{
cvCanny(tgray, gray, 0, thresh, 5);
//ʹÓÃÈÎÒâœá¹¹ÔªËØÅòÕÍÍŒÏñ
cvDilate(gray, gray, 0, 1);
}
else
{
// apply threshold if l!=0:
cvThreshold(tgray, gray, (l + 1) * 255 / N, 255, CV_THRESH_BINARY);
}*/
cvThreshold(tgray, gray, (l + 1) * 255 / N, 255, CV_THRESH_BINARY);
//cvCvtColor(tgray, gray, CV_BGR2GRAY);
// ÕÒµœËùÓÐÂÖÀª²¢ÇҎ探ÔÚÐòÁÐÖÐ
cvFindContours(gray, storage, &contours, sizeof(CvContour),
CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
// ±éÀúÕÒµœµÄÿžöÂÖÀªcontours
int index = 0;
while (contours)
{
//ÓÃÖž¶šŸ«¶È±Æœü¶à±ßÐÎÇúÏß
result = cvApproxPoly(contours, sizeof(CvContour), storage,
CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
//printf("betsy: total=%d , index=%d\n",result->total ,index);
index ++;
if (result->total == 4 &&
fabs(cvContourArea(result, CV_WHOLE_SEQ)) > 100 &&
fabs(cvContourArea(result, CV_WHOLE_SEQ)) < 2000000 &&
cvCheckContourConvexity(result))
{
s = 0;
for (i = 0; i < 5; i++)
{
// find minimum angle between joint edges (maximum of cosine)
if (i >= 2)
{
t = fabs(angle(
(CvPoint*)cvGetSeqElem(result, i),
(CvPoint*)cvGetSeqElem(result, i - 2),
(CvPoint*)cvGetSeqElem(result, i - 1)));
s = s > t ? s : t;
}
}
// if ÓàÏÒÖµ ×㹻С£¬¿ÉÒÔÈ϶šœÇ¶ÈΪ90¶ÈÖ±œÇ
//cos0.1=83¶È£¬ÄܜϺõÄÇ÷œüÖ±œÇ
if (s > 88 && s < 92)
for (i = 0; i < 4; i++)
cvSeqPush(squares,
(CvPoint*)cvGetSeqElem(result, i));
}
// ŒÌÐø²éÕÒÏÂÒ»žöÂÖÀª
contours = contours->h_next;
}
}
}
cvReleaseImage(&gray);
cvReleaseImage(&pyr);
cvReleaseImage(&tgray);
cvReleaseImage(&timg);
return squares;
}
1. CvSeq* contours 创建了一个序列
2. 定义了CvSize的矩阵框大小,以像素为精度,(&-2 什么意思?)
3.IplImage* timg = cvCloneImage ,拷贝了原图用来处理(函数内部会额外分配内存,将源数据全部复制过来,包括ROI区域,使用前无需申请,在for循环中使用会消耗内存,每次用完需要用cvRelease来释放)
4.IplImage* gray = cvCreateImage(sz, 8, 1); 创建灰度图, 创建头并分配数据,宽高,位深度,通道
5.IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); 创建1/4图大小的三通道图像
6.CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage) 创建序列
cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height)); 设定了ROI
创建一个空序列用于存储轮廓角点
typedef struct CvPoint
{
int x;//图像中点的x坐标
int y;//图像中点的y坐标
}CvPoint;
7. cvPyrDown(timg, pyr, 7);
cvPyrUp(pyr, timg, 7);
cvPyrDown/Up 使用Gaussian金字塔分解对输入图像向下/上采样 ,为了过滤噪音
8. for (c = 0; c < 3; c++) 红绿蓝3色分别尝试提取
9.cvSetImageCOI : 设置图像的COI的函数,coi为0时表示选择所有通道,1第一通道以此类推
10.cvThreshold(tgray, gray, (l + 1) * 255 / N, 255, CV_THRESH_BINARY);
对单通道数组应用固定阈值操作。该函数的典型应用是对灰度图像进行阈值操作得到二值图像。(cvCmpS 也可以达到此目的) 或者是去掉噪声,例如过滤很小或很大象素值的图像
11.cvFindContours(gray, storage, &contours, sizeof(CvContour),
CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
找到所有轮廓并存储在序列中
12.cvApproxPoly(contours, sizeof(CvContour), storage,
CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
使用多边形去逼近轮廓,使顶点数目变少
13. cvContourArea(result, CV_WHOLE_SEQ) 计算轮廓面积
14.
cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );
角点检测算法可归纳为三类:
- 基于灰度图像的角点检测
- 基于二值图像的角点检测
- 基于轮廓曲线的角点检测
normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
该函数归一化输入数组使它的范数或者数值范围在一定的范围内。
convertScaleAbs( dst_norm, dst_norm_scaled );