


















下面的函数用来找到第一个凸点。
Point findFisrtWhitePoint(Mat& edgeImg,int blSize,int blNumOfPix) { Point vertexWhite; bool firstWhitePointFindOrnot=0; for(int i=0;i+blSize<edgeImg.rows;i+=blSize) { for(int j=0;j+blSize<edgeImg.cols;j+=blSize) { Rect rect(j,i,blSize,blSize); Mat block=edgeImg(rect); int blValueSum=sum(block)[0]; if(blValueSum/blNumOfPix==255) { vertexWhite=(Point(j,i)); firstWhitePointFindOrnot=1; break; } if(firstWhitePointFindOrnot) break; } } return vertexWhite; } //定义角点类型, struct Corner { Corner():concave(0),convex(0),isCorner(0),direct(0) {ptCorner=Point(0,0);} Corner(Point pt) {ptCorner=pt; concave=0; convex=0; isCorner=0;direct=0; } Corner(Point pt,bool concave_,bool convex_) {ptCorner=pt;concave=concave_;convex=convex_;} Point ptCorner;//角点坐标 bool concave; bool convex; bool isCorner; int direct; double theta; enum directOfStep{left=1,down=2,right=3,up=4}; void getTypeOfvertex() { if(concave) cout<<"concave"<< endl; if(convex) cout<<"convex"<< endl; } void decideStepTo(Mat&img) { uchar p0=img.ptr<uchar>(ptCorner.y)[ptCorner.x];//角点坐标灰度值 uchar p1=img.ptr<uchar>(ptCorner.y-1)[ptCorner.x-1]; uchar p2=img.ptr<uchar>(ptCorner.y-1)[ptCorner.x]; uchar p3=img.ptr<uchar>(ptCorner.y-1)[ptCorner.x+1]; uchar p4=img.ptr<uchar>(ptCorner.y)[ptCorner.x+1]; uchar p5=img.ptr<uchar>(ptCorner.y+1)[ptCorner.x+1]; uchar p6=img.ptr<uchar>(ptCorner.y+1)[ptCorner.x]; uchar p7=img.ptr<uchar>(ptCorner.y+1)[ptCorner.x-1]; uchar p8=img.ptr<uchar>(ptCorner.y)[ptCorner.x-1]; int sumOfBlock=p0+p1+p2+p3+p4+p5+p6+p7+p8; int Gx=p3+p4+p5-p1-p7-p8; int Gy=p5+p6+p7-p1-p2-p3; theta=cvRound(atan2(Gy,Gx)*180/3.14159); if(sumOfBlock==255*4)//凸点 { isCorner=1; convex=1; if(theta==45) direct=down; else if(theta==-135) direct=up; else if(theta==135) direct=left; else if(theta==-45) direct=right; }else if(sumOfBlock==255*8)//凹点 { isCorner=1; concave=1; if(theta==45) direct=left; else if(theta==-135) direct=right; else if(theta==135) direct=up; else if(theta==-45) direct=down; } else if((p1+p2+p4+p5==0)&&(p3+p6+p7+p8+p0==255*5))//两个矩形区域对角连接点1 { isCorner=1; convex=1; direct=left; }else if(p1+p5+p6+p8==0&&p2+p3+p4+p7+p0==255*5)//两个矩形区域对角连接点2 { isCorner=1; convex=1; direct=right; }else if(p3+p4+p6+p7==0&&p8+p1+p2+p5+p0==255*5) { isCorner=1; convex=1; direct=up; }else if(p2+p3+p7+p8==0&&p1+p4+p5+p6+p0==255*5) { isCorner=1; convex=1; direct=down; }else isCorner=0; } }; 获取墙壁的内部区域,后面将会对这个区域查找凹凸角点,然后在这些角点里挑选符合MPP的点 void getInsideRegion(Mat&img,Mat&edgeImg,int blSize) { int blNumOfPix=blSize*blSize; for(int i=0;i+blSize<edgeImg.rows;i+=blSize) { for(int j=0;j+blSize<edgeImg.cols;j+=blSize) { Rect rect(j,i,blSize,blSize); Mat block=edgeImg(rect); int blValueSum=sum(block)[0]; if(blValueSum>0) block =0;//(block+255)/2; Mat block2=img(rect);//在原图像img上,与block对应区域截取block2 int blValueSum2=sum(block2)[0]; if(blValueSum2/(blNumOfPix)==255)//如果block2位于枫叶图形的内部区域,设定edgeImg对应区域为255 block=255; } } //去除edgeImg中的小的闭合区域,保留最大的闭合区域 vector<vector<Point> > contours; vector<Vec4i> hierarchy; findContours(edgeImg,contours,hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); //找到contours中的最大闭合区域 int maxAreaIndex=-1; double maxArea=0; int ctSize=contours.size(); if(ctSize>1) { for(int i=0;i<ctSize;i++) { double area=contourArea(contours[i]); if(area>maxArea) { maxArea=area; maxAreaIndex=i; } } } //保留最大区域,其他全部抹除 Mat mask=Mat::zeros(edgeImg.size(),CV_8UC1); if(maxAreaIndex!=-1) { drawContours(mask,contours,maxAreaIndex,Scalar(255),-1); edgeImg=mask.clone(); } } vector<Corner> findAllCorners(Mat&edgeImg,int blSize) { int blNumOfPix=blSize*blSize; int numOfPixImg=edgeImg.rows*edgeImg.cols; Point firstPoint=findFisrtWhitePoint(edgeImg,blSize,blNumOfPix); Point nextpoint=firstPoint; vector<Corner> cornerTrain; int direct=2; for(int i=1;i<numOfPixImg;i++) { Corner cn(nextpoint); cn.decideStepTo(edgeImg);//当前角点的下一步搜索方向 if(cn.isCorner) { direct=cn.direct; cornerTrain.push_back(cn); } switch (direct) { case 1: { nextpoint +=Point(-1,0); break;} case 2: { nextpoint +=Point(0,1); break; } case 3: {nextpoint +=Point(1,0); break; } case 4: { nextpoint +=Point(0,-1); break; } } if(nextpoint==firstPoint) break; } //将凹角点转成镜像点 for(size_t i=0;i<cornerTrain.size();i++)//对每个凹点取镜像 { if(cornerTrain[i].theta==45&&cornerTrain[i].concave) cornerTrain[i].ptCorner+=Point(-blSize,-blSize); else if(cornerTrain[i].theta==135&&cornerTrain[i].concave) cornerTrain[i].ptCorner+=Point(blSize,-blSize); else if(cornerTrain[i].theta==-45&&cornerTrain[i].concave) cornerTrain[i].ptCorner+=Point(-blSize,blSize); else if(cornerTrain[i].theta==-135&&cornerTrain[i].concave) cornerTrain[i].ptCorner+=Point(blSize,blSize); } cornerTrain.push_back(cornerTrain[0]);//最后将第一个角点压入栈,形成闭合角点连环。 return cornerTrain; }
下面的函数可以将找到的角点绘制到edgeImg上,winposition用来定位显示窗口的屏幕坐标。 void drawCorners(vector<Corner> cnTrain,Mat& edgeImg,Point winposition) { int numOfcorners=cnTrain.size(); Mat outimage; cvtColor(edgeImg,outimage,COLOR_GRAY2BGR); for(int i=0;i<numOfcorners;i++) { if(cnTrain[i].convex) circle(outimage,cnTrain[i].ptCorner,5,Scalar(255,255,255),-1); else if(cnTrain[i].concave) { circle(outimage,cnTrain[i].ptCorner,5,Scalar(255,0,0),-1); } } string winx="the original image of maple leaf "; imshow(winx,outimage); moveWindow(winx,winposition.x,winposition.y); }
符号函数sign。 double sign(Point VL,Point WBc,Point Vk) { double ax=VL.x,ay=VL.y; double bx=WBc.x,by=WBc.y; double cx=Vk.x,cy=Vk.y; Mat A=(Mat_<double>(3,3)<<ay,ax,1, by,bx,1, cy,cx,1); double detvalue=determinant(A); return detvalue; }
从已知的所有凹凸点,挑选出MPP点。 vector<Point> getMpp(vector<Corner> cnTrain) { //1.初始化VL、Wc、Bc Point VL,Wc,Bc,Vk; //2.考察Vk点 vector<Point> vecMpp;//将MPP上的点保存到vecMpp VL=cnTrain[0].ptCorner;//第一个MPP点 Wc=VL; Bc=VL; vecMpp.push_back(VL); for(size_t i=1;i<cnTrain.size();i++) { Vk=cnTrain[i].ptCorner; double signWhite=sign(VL,Wc,Vk);//验证Wc是否是MPP点 double signBlue=sign(VL,Bc,Vk);//验证Bc是否是MPP点 if(signWhite>0)//条件a,判断白色凸点为MPP点 { vecMpp.push_back(Wc);//第i个MPP点 VL=Wc;// Bc=VL; i--; }else if(signBlue>0||signBlue==0)//条件b, { if(cnTrain[i].convex) Wc=Vk; else if(cnTrain[i].concave) Bc=Vk; }else//条件c,判断蓝色凹点为MPP点 { vecMpp.push_back(Bc); VL=Bc; Wc=VL; i--; } } vecMpp.push_back(vecMpp[0]);//加入起始点,形成闭环 return vecMpp; }
下面的函数是用于显示MPP的所有点。 void displayMpp(vector<Point> vecmpp) { for(size_t i=0;i<vecmpp.size();i++) { cout<<"the "<<i<<"point: "<<vecmpp[i]<<endl; } }
//下面的函数时用于测试sign函数的角点 vector<Corner> getTestTrain() { Point V0(2,4),V1(2,3),V2(3,3),V3(3,2),V4(4,1),V5(7,1),V6(8,2),V7(9,2), V8(10,1),V9(14,1), V10(14,3),V11(12,4),V12(12,5),V13(14,6),V14(14,9); vector<Corner> cornerTrain={Corner(V0,0,1),Corner(V1,1,0),Corner(V2,0,1),Corner(V3,1,0),Corner(V4,0,1), Corner(V5,0,1),Corner(V6,0,1),Corner(V7,1,0),Corner(V8,0,1),Corner(V9,0,1),Corner(V10,0,1), Corner(V11,1,0),Corner(V12,1,0),Corner(V13,0,1),Corner(V14,0,1)}; return cornerTrain; }
int main()
{
// vector<Corner> cornerTrain=getTestTrain();
//cout<<sign(Point(1,4),Point(2,3),Point(3,4))<<endl;
int blSize=10;//细胞尺寸
Mat img=imread("D:/Qt/MyImage/CH11/Fig1108(a).tif",0);
Mat edgeImg=my::traceEdge(img);//获取图像的边缘,这是我自己的定义的函数,大家也可以用getContours。
string win1="edge image";
string win0="original image";
namedWindow(win0);
imshow(win0,img);moveWindow(win0,700,50);
imshow(win1,edgeImg);moveWindow(win1,1300,50);
getInsideRegion(img,edgeImg,blSize);//将edgeImg的边界墙删除,保留原图像的内部区域
vector<Corner> cornerTrain= findAllCorners(edgeImg,blSize);
drawCorners(cornerTrain,edgeImg,Point(700,730));
vector<Point> vecMpp=getMpp(cornerTrain);
// cout<<"vecmpp size="<<vecMpp.size()<<endl;
// displayMpp(vecMpp);
Mat colorImg;
cvtColor(edgeImg,colorImg,COLOR_GRAY2BGR);
string win2="color image";
namedWindow(win2);moveWindow(win2,1300,730);
for(size_t i=0;i+1<vecMpp.size();i++)
{
circle(colorImg,vecMpp[i],1,Scalar(255,0,0));
line(colorImg,vecMpp[i],vecMpp[i+1],Scalar(0,255),3);
imshow(win2,colorImg);
waitKey(300);
}
waitKey();
return 0;
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。