基于opencv的叶片锯齿和叶裂检测

来源:互联网 发布:asp.net和java的区别 编辑:程序博客网 时间:2024/04/29 00:54

植物叶片的锯齿和叶裂的数量、深度等参数是研究叶子形态特征的重要部分,为了实现叶裂检测,我查找论文一些论文,选择了一篇自己认为简单的论文进行了实现,其效果非还不错,下面给出图例:

输入图:


检测结果图:



算法思想来源于论文:王晓洁, 于浩杰, 郑小东. 凸包在植物叶锯齿与叶裂位置识别中的应用[J]. 农机化研究, 2013, 35(3): 214-217.
在根据opencv的基础上实现此论文也相对简单,下面是代码实现部分。项目工程的下载地址:http://download.csdn.net/detail/boy313918205/9707587

#include "stdafx.h"
#include<opencv2/opencv.hpp>
using namespace std;

//锯齿结构体
typedef struct Stoothdata
{
int toothorder;     //锯齿序号
CvPoint toothlocation;//锯齿位置
float toothdeeppix;//锯齿深度
float toothwidthpix;//锯齿宽度
float   w1d; //锯齿宽深比
};
int getsawtoothdata(IplImage *inputshow , IplImage *binarybig,std::vector<Stoothdata> *onetoothdata);
int _tmain(int argc, _TCHAR* argv[])
{
IplImage * image = cvLoadImage("LeafBinary.jpg",1); //显示图片

IplImage *binary= cvCreateImage(cvSize(image->width, image->height), IPL_DEPTH_8U, 1);;

cvCvtColor(image,binary, CV_BGR2GRAY);
cvThreshold(binary,binary,125,255,CV_THRESH_BINARY); //分割域值

std::vector<Stoothdata> onetoothdata;
getsawtoothdata(image , binary,&onetoothdata);
cvNamedWindow("binary",1); 
cvShowImage("binary",binary);
cvNamedWindow("image",1); 
cvShowImage("image",image);
cvSaveImage("image_save.jpg",image);
cvWaitKey(0);

return 0;
}
/************************************************************************/
/*
功能:得到锯齿数据
输入:二值图,用于展示图
输出:叶子展示图和锯齿数据
*/
/************************************************************************/
int getsawtoothdata(IplImage *inputshow , IplImage *binarybig,std::vector<Stoothdata> *onetoothdata)
{


CvMemStorage * storage = cvCreateMemStorage(0);
CvSeq * pcontour = 0;
IplImage * pfind = cvCloneImage(binarybig);
cvFindContours(pfind,storage,&pcontour,sizeof(CvContour),CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

/////////////////////////////////////////////////////////////
//找出最大外轮廓作为叶子轮廓
CvSeq * pcontmax = 0;
CvSeq * contur = pcontour;
float area,maxPixArea = 10;//设面积最小大于10
for(;contur;contur = contur->h_next)
{
area = fabs(cvContourArea( contur, CV_WHOLE_SEQ )); //计算面积
if(area > maxPixArea)
{
pcontmax = contur;
maxPixArea = area;
}
}
CvSeq *hull = 0;
CvSeq * papproach1wai = 0;
CvMemStorage * mstorage = cvCreateMemStorage(0);
//多边形逼近
papproach1wai = cvApproxPoly(pcontmax,sizeof(CvContour),mstorage,CV_POLY_APPROX_DP,3,0);
//cvDrawContours(inputshow,papproach1wai,CV_RGB(255,0,0), CV_RGB(255, 0, 0),0,1,8,cvPoint(0,0));


//找出凹点
hull = cvConvexHull2(papproach1wai,0,CV_CLOCKWISE,0);
//提出凹点数据
CvSeq *defect = cvConvexityDefects(papproach1wai,hull);
//锯齿序号
int num=0;
//存放锯齿
for (int i = 0;i<defect->total;i++)
{
CvConvexityDefect cdi =*(CvConvexityDefect*)cvGetSeqElem(defect, i);
CvPoint startp = *cdi.start;
CvPoint endp = *cdi.end;


int sign=0;
CvPoint pt1 = startp,pt2=endp;
float distanc1=0,distanc2=0,distanc3=0;
CvPoint dispt1 = cvPoint(0,0),dispt2 = cvPoint(0,0),dispt3 = cvPoint(0,0);
vector<CvPoint> vmaopt;
vector<CvPoint> vmgaopt;
for (int j = 0;j<papproach1wai->total;j++)

CvPoint * pt0 = (CvPoint*)cvGetSeqElem(papproach1wai, j); // 读出第j个点
if ((pt0->x == startp.x)&&(pt0->y == startp.y))
{
sign = 1;
}
if (sign)
{
//cvCircle(src1,*pt0,2,cvScalar((i%2)*255,255,0),2,8,0);
dispt3 = dispt2;
dispt2 = dispt1;
dispt1 = *pt0;


distanc3 = distanc2;
distanc2 = distanc1;
//点到直线的距离
distanc1 = fabs((float)(pt2.y - pt1.y)*pt0->x + (pt1.x - pt2.x)*pt0->y + pt2.x*pt1.y - pt1.x*pt2.y)
/sqrt((float)(pt2.y - pt1.y)*(pt2.y - pt1.y) + (pt1.x - pt2.x)*(pt1.x - pt2.x));
//记录凹点
if ((distanc1 < distanc2)&&(distanc2 > distanc3))
{
//cvCircle(src1,dispt2,3,cvScalar(0,255,255),3,8,0);
vmaopt.push_back(dispt2);
}//记录高点
else if((distanc1 > distanc2)&&(distanc2 < distanc3))
{
//cvCircle(src1,dispt2,3,cvScalar(255,0,255),3,8,0);
vmgaopt.push_back(dispt2);
}
}
if ((pt0->x == endp.x)&&(pt0->y == endp.y))
{
sign = 0;
//break;
}
}
Stoothdata mtooth;
if(1==vmaopt.size())
{
mtooth.toothlocation = vmaopt.at(0);
mtooth.toothdeeppix = cdi.depth;
mtooth.toothwidthpix = sqrt((float)(startp.x-endp.x)*(startp.x-endp.x) + (startp.y-endp.y)*(startp.y-endp.y));
mtooth.w1d = mtooth.toothwidthpix/mtooth.toothdeeppix;


if ((mtooth.w1d < 11)&&(mtooth.toothdeeppix>3)&&(mtooth.toothwidthpix>5))
{
mtooth.toothorder = ++num;
onetoothdata->push_back(mtooth);

cvCircle(inputshow,mtooth.toothlocation,2,cvScalar(255,255,0),8,8,0);

cvLine(inputshow,startp,endp,CV_RGB(255,0,255),2);

cvCircle(inputshow,startp,2,cvScalar(0,255,0),2,8,0);
cvCircle(inputshow,endp,2,cvScalar(0,255,255),8,8,0);
}
}
else if(vmaopt.size()>1)
{
CvPoint gao1 = startp;
CvPoint gao2 = cvPoint(0,0);
for (int k=0;k< vmaopt.size();k++)
{
CvPoint aopt = vmaopt.at(k);
if (k < vmaopt.size()-1)
gao2 = vmgaopt.at(k);
else
gao2 = endp;

int mdepth = fabs((float)(gao2.y - gao1.y)*aopt.x + (gao1.x - gao2.x)*aopt.y + gao2.x*gao1.y - gao1.x*gao2.y)
/sqrt((float)(gao2.y - gao1.y)*(gao2.y - gao1.y) + (gao1.x - gao2.x)*(gao1.x - gao2.x));
mtooth.toothwidthpix = sqrt((float)(gao1.x-gao2.x)*(gao1.x-gao2.x) + (gao1.y-gao2.y)*(gao1.y-gao2.y));


mtooth.toothlocation = aopt;
mtooth.toothdeeppix = mdepth;
mtooth.w1d = mtooth.toothwidthpix/mtooth.toothdeeppix;


if ((mtooth.w1d < 11)&&(mtooth.toothdeeppix>3)&&(mtooth.toothwidthpix>5))
{
mtooth.toothorder = ++num;
onetoothdata->push_back(mtooth);

cvCircle(inputshow,mtooth.toothlocation,2,cvScalar(255,255,0),8,8,0);

cvLine(inputshow,gao1,gao2,CV_RGB(255,0,255),2);

cvCircle(inputshow,gao1,2,cvScalar(0,255,0),2,8,0);
cvCircle(inputshow,gao2,2,cvScalar(0,255,255),8,8,0);
}
gao1 = gao2;
}
}
}
return 0;
}


0 0
原创粉丝点击