看到有人用opencv的模板匹配,其实更多的是相关系数匹配!
从前讲了很多相关系数,就是没去做!其实就是计算复杂度太大,不值得,那是刚好实现了基于形状轮廓的匹配,心思不在opencv上,而且一直从底层写起,故而,只学习opencv,从来很少调用opencv,到目前为止,从2011接触opencv算起,屈指可数用过的opencv调用,1,人脸识别,这个自己实在写不出来,或许不是写不出,只是一门心思在机器视觉上,机器视觉都全部完成了,所以,不是写不出来的问题,是学习舍得之道!
2,自己的机器视觉中用到canny,写了很多版本,接近opencv canny,所以,偶尔做对比时,把玩其精髓为何?
3,就是昨天今天用的这个cvtemplateMatch,即就是ncc相关系数匹配!
opencv像一个老朋友,陪伴15年了!虽然opencv版本已经足够好,足够多,我还是用这个十几兆的opencv1.0!
好,回归正题:
匹配,关键在速度和稳定性以及精确度,这是工业机器视觉的硬指标!
我们的兴趣在于,最初的理想,先使用笨方法(不拐弯的直觉)实现,然后一步一步达成上面的三个硬指标!
好,我们先达成!
第一,虽然我们说的是ncc,但一写起代码,我就惯性思维到轮廓匹配实现,走了弯路,实际opencv很直接,简单!人家是模板匹配,你非要把自己的轮廓再还原成图像模板再匹配,这是退步!
第二,原图不容易旋转,只旋转roi图像(模板)中提炼的轮廓点,这个当量极其小!而ncc是旋转原图像(假定中心旋转),roi图像(模板)不旋转!今天想明白这个道理,roi图像旋转,会出现不必要的补足像素,而这些补足像素不在原图中,所以匹配失败是必然的!
好,纠正了这两步,这就回归了opencv模板匹配正道!
1,我的相机是5472*3648:(如果在这样的图像里取模板匹配,工业现场就得挂机!)

2,我们高斯平滑后,每4行4列取一个像素,图像变成1368*912,这就是他们说的金字塔图像!
3,最关键,我们就在这个1368*912中取模板,并完成匹配!为什么这个可以,他的原理是:
高斯核是构建尺度(缩放)空间变换的唯一线性核(尺度空间 图像G(x,y,σ) 定义为原始图像 I(x,y) 与高斯核 σ 的卷积)筹备和证明整理-CSDN博客
好,opencv代码很简单:
a,把1368*912处理成canny图像:(这里截图出来的模板就是canny图像),道理很简单,我们用二值特征图像,获取模板,然后在回二值特征图像匹配!

b,我们完成的是+-45度的匹配,二值特征图像很方便旋转处理!

注意2436,844这个绿框的起始点,我们用来验证匹配结果:


610,211;你乘以4等于2440,844
他返回最大值:
以上是0度匹配,我们看看1度,2度,3度结果最大值以及对应的坐标:


这幅图像静止,还没动,可以知道,旋转45度,只有0度最好,因为opencv给出最大值为最佳匹配结果!
显然,图像动一动,比如10度,那么,再做一次0-45度旋转,可以预见10最好!
这样,我们就简单粗暴的实现了opencv的ncc相关系数匹配!

opencv 相关系数匹配源代码如下:
IplImage* pTest8byte=cvCreateImage(cvSize(localww1per4,localhh1per4 ),8,1);//使用了8位灰度图像
cvSetData(pTest8byte,temp_buffer256192,localww1per4);//测试成功ok201911101905
IplImage* cannyimg=cvCreateImage(cvSize(pTest8byte->width,pTest8byte->height),8,1);
cvCanny(pTest8byte,cannyimg,50,150,3);
//cvSaveImage("c:\\opencvcannyimg202407251047.bmp",cannyimg);
memcpy(globgaospydoutimgN精细for90,cannyimg->imageDataOrigin,localww1per4*localhh1per4);//canny图像也是255及0二值图像吗?
// //范例opencv
// //读取图像和模板
// IplImage* img = cvLoadImage("input.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//IplImage* templ = cvLoadImage("template.jpg", CV_LOAD_IMAGE_GRAYSCALE);
IplImage* templ;
IplImage* result;
//for(int cv=0;cv<91;cv++)//先完成一个0度匹配,试一试opencv的函数
// {
//IplImage*
templ=cvCreateImage(cvSize(globroiww, globroihh ),8,1);//使用了8位灰度图像,模板图像
cvSetData(templ, globroiImg,globroiww);
////使用 matchTemplate 进行匹配
list<CvPoint> lp ;
list<double> lpValue ;
for(int m_baoguang=0;m_baoguang<=45;m_baoguang++)
{
int 输出w=0;int 输出h=0;
byte * 输出图像;
double 弧度=m_baoguang*3.1415926/180;
//旋转0-45度//请确认是角度弧度?//弧度
RotateNormal((BYTE *)cannyimg->imageDataOrigin,输出图像, localww1per4, localhh1per4,//1368*912图像
输出w,输出h,弧度);
//IplImage*
result = cvCreateImage(cvSize(输出w - templ->width + 1, 输出h - templ->height + 1), IPL_DEPTH_32F, 1);
IplImage* pTest8byteM=cvCreateImage(cvSize(输出w,输出h ),8,1);//使用了8位灰度图像
cvSetData(pTest8byteM,输出图像,输出w);//测试成功ok201911101905
//ncc匹配//这里的cannyimg要旋转,并记录每个角度的成绩和位置
cvMatchTemplate(pTest8byteM, templ, result, CV_TM_CCOEFF);
cvReleaseImage(&pTest8byteM);
CvPoint minLoc, maxLoc;
double minVal, maxVal;
cvMinMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, 0);
lpValue.push_back(maxVal);
lp.push_back(maxLoc);
}
for(int i=315;i<=359;i++)
{}
opencv模板学习代码如下:
IplImage* pTest8byte=cvCreateImage(cvSize(localww1per4,localhh1per4 ),8,1);//使用了8位灰度图像
cvSetData(pTest8byte,temp_buffer256192,localww1per4);//测试成功ok201911101905
IplImage* cannyimg=cvCreateImage(cvSize(pTest8byte->width,pTest8byte->height),8,1);
cvCanny(pTest8byte,cannyimg,50,150,3);
//cvSaveImage("c:\\opencvcannyimg202407251047.bmp",cannyimg);
memcpy(globgaospydoutimgN精细for45,cannyimg->imageDataOrigin,localww1per4*localhh1per4);//cannyimg->imageData好于imageDataOrigin,要做测试,是否要改?
byte* roiImage = new byteroiww \* roihh;
this->globroiImg = new byteroiww \* roihh;globroiww=roiww;globroihh=roihh;
int te= roitop * localww1per4 +roileft;
for (int i = 0; i < roihh; i++)
for (int j = 0; j < roiww; j++)
{
globroiImgi \* roiww + j = roiImagei \* roiww + j = globgaospydoutimgN精细for45i \* localww1per4 + j +te ;
}
图像旋转源码: