图片能够拼接的前提是:两幅图片的边缘有重叠的部分
在CSDN上看到一篇关于《图像拼接原理》的博客,讲的不错。
拼接思路简述:
使用Hu矩匹配两幅图片的指定区域,符合条件则记录该像素或模板的位置,供拼接时使用。
程序:
#include "stdafx.h" #include <iostream> #include <highgui.h> #include "cv.h" #include <vector> #include <functional> using namespace std; using namespace cv; struct MatchR { int PositionX; //存储位置的X坐标 double MResult; //存储轮廓匹配结果 bool operator < (const MatchR& rhs) const //升序排序时必须写的函数 { return MResult < rhs.MResult; } }; vector<MatchR> vec; //获取输入图像的轮廓 CvSeq* getImageContour(IplImage* srcIn); int main(int argc, char* argv[]) { const char * LeftPath="leftpic.bmp"; const char * RightPath="rightpic.bmp"; IplImage * Lpic=cvLoadImage(LeftPath,CV_LOAD_IMAGE_GRAYSCALE); IplImage * Rpic=cvLoadImage(RightPath,CV_LOAD_IMAGE_GRAYSCALE); assert(Lpic && Rpic); //选取左图的一部分作为兴趣区域 const int TemplWi=(int)(Lpic->width)/10; CvRect RectROI=cvRect((Lpic->width-TemplWi),0,TemplWi,Lpic->height); //准备模板图像ImgTemplt cvSetImageROI(Lpic,RectROI); IplImage * ImgTemplt=cvCreateImage(cvSize(TemplWi,Lpic->height),Lpic->depth,1); cvCopy(Lpic,ImgTemplt,NULL); cvResetImageROI(Lpic); //获取图像1的轮廓 CvSeq* contour1; contour1 = getImageContour(ImgTemplt); //循环计算右图特定区域的轮廓与模板图像contour1对比 IplImage * TempImg=cvCreateImage(cvSize(TemplWi,Rpic->height),Rpic->depth,1);//临时图像 for(int i=0;i<(int)(Rpic->width-TemplWi)/10;++i) { CvRect rROI =cvRect(i,0,TemplWi,Rpic->height); cvSetImageROI(Rpic,rROI); cvCopy(Rpic,TempImg,NULL); cvResetImageROI(Rpic); //获取图像2的轮廓 CvSeq* contour2; contour2 = getImageContour(TempImg); MatchR resultS; resultS.PositionX=i; //计算匹配程度 resultS.MResult=cvMatchShapes(contour1,contour2,1); vec.push_back(resultS); //释放轮廓存储空间 cvReleaseMemStorage(&contour2->storage); } CvPoint StartPt; //排序找出vector中MResult最小的元素 sort(vec.begin(),vec.end()); StartPt.x=vec[0].PositionX+TemplWi; StartPt.y=0; //加载原图 IplImage * LpicColor=cvLoadImage(LeftPath,CV_LOAD_IMAGE_COLOR); IplImage * RpicColor=cvLoadImage(RightPath,CV_LOAD_IMAGE_COLOR); //创建长度宽度合适的图像ResukltImage作拼接用 int StitchImgWidth=LpicColor->width+(RpicColor->width-StartPt.x); IplImage * ResukltImage=cvCreateImage(cvSize(StitchImgWidth,LpicColor->height),LpicColor->depth,LpicColor->nChannels); //将左图复制到ResukltImage中 CvRect ROI1=cvRect(0,0,LpicColor->width,LpicColor->height); cvSetImageROI(ResukltImage,ROI1); cvCopy(LpicColor,ResukltImage); cvResetImageROI(ResukltImage); //将右图的以StartPt为左上角的区域复制到ResukltImage的右侧剩余部分 ROI1=cvRect(LpicColor->width,0,(ResukltImage->width-LpicColor->width),RpicColor->height); cvSetImageROI(ResukltImage,ROI1); CvRect ROI2=cvRect(StartPt.x,0,RpicColor->width-StartPt.x,RpicColor->height); cvSetImageROI(RpicColor,ROI2); cvCopy(RpicColor,ResukltImage,NULL); cvResetImageROI(RpicColor); cvResetImageROI(ResukltImage); //创建窗口显示拼接结果 cvNamedWindow("拼接后图像",0); cvShowImage("拼接后图像",ResukltImage); cvWaitKey(0); //释放轮廓存储空间 cvReleaseMemStorage(&contour1->storage); //释放图像 cvReleaseImage(&Lpic); cvReleaseImage(&Rpic); cvReleaseImage(&ImgTemplt); cvReleaseImage(&TempImg); cvReleaseImage(&ResukltImage); cvReleaseImage(&LpicColor); cvReleaseImage(&RpicColor); //销毁窗口 cvDestroyWindow("拼接后图像"); return 0; } /// <summary> /// 函数功能:获取输入图像的轮廓 /// </summary> /// <param name="srcIn"></param> /// <returns>CvSeq* : 存储轮廓信息</returns> CvSeq* getImageContour(IplImage* srcIn) { IplImage* src; src = cvCreateImage(cvGetSize(srcIn),8,1); //拷贝图像 cvCopy(srcIn,src); //创建空间 CvMemStorage* mem = cvCreateMemStorage(0); CvSeq* seq; if(!mem) { printf("mem is null"); } //二值化图像 cvThreshold(src,src,100,255,0); //cvCanny(src,src,15,30,3); //计算图像轮廓 cvFindContours(src,mem,&seq,sizeof(CvContour),CV_RETR_CCOMP); //释放图像空间 cvReleaseImage(&src); //返回轮廓信息 return seq; }
待拼接的图像左图-->leftpic:
待拼接的图像右图-->rightpic:
结果图片: