数米粒实验
一、实验步骤
变量定义:
cImg1:原图
cImg2:背景图
cImg3:去背景图
cutbackRImg:去背景图像,三通道
singleImg:存储转换后的单通道图像
dst_contours:原图,用于画米粒轮廓
1.去背景:
- 通过对原始图像进行5次腐蚀和5次膨胀(开操作)得到米粒图像的背景图
cvErode(cImg1,cImg2,element,5);// 腐蚀
cvDilate(cImg2,cImg2,element,5);// 膨胀
- 原图减去背景图得到去背景图
cvSub(cImg1,cImg2,cImg3,0);// 去背景图像
2.去背景图预处理:
- 将三通道图像转为单通道图像
CTestView::Three2one(cutbackRImg, singleImg); // 三通道转单通道,自己写的函数
- 对得到的单通道图像进行一次开操作(先腐蚀后膨胀),去掉噪声(小白点)
cvErode(singleImg, singleImg,element,1);// 腐蚀
cvDilate(singleImg, singleImg,element,1);// 膨胀
- 对去噪后的图像进行图像分割二值化,阈值取50
cvThreshold(singleImg, singleImg, 50, 255, CV_THRESH_BINARY); //对单通道数组应用固定阈值操作
3.数米粒
- 通过OpenCV函数,将图像中的米粒区域存入序列当中,并的到米粒区域的数量
CvMemStorage* stor=cvCreateMemStorage(0); // 创建内存存储器,0默认内存块大小为64k
CvSeq *cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),stor); // 创建序列
int numberOfObject = cvFindContours(singleImg,stor,&cont,sizeof(CvContour),CV_RETR_TREE);
- 遍历序列,得到每个区域的坐标、面积、周长,并用白色在原图像中画出米粒边框,同时保存最大米粒的面积、周长和坐标
float area=0;
float length=0;
int x1, y1;
CvSeq *cont1;
for (cont1 = cont; cont1 != NULL; cont1 = cont1->h_next)
{
CvRect rect = cvBoundingRect(cont1, 0);
double area1 = fabs(cvContourArea(cont1, CV_WHOLE_SEQ));
double length1 = cvArcLength(cont1);
if(area < area1)
{
area = area1;
length = length1;
x1 = rect.x;
y1 = rect.y;
}
cvDrawContours(dst_contours, cont1, CV_RGB(255,255,255), CV_RGB(255,0,0), 0, 1, 8, cvPoint(1,1));
}
- 再次遍历整个序列,用红色在原图像中画出最大米粒的边框
for (cont1 = cont; cont1 != NULL; cont1=cont1->h_next)
{
CvRect rect = cvBoundingRect(cont1, 0);
double area1 = fabs(cvContourArea(cont1, CV_WHOLE_SEQ));
if(rect.x==x1 && y1==rect.y && area1==area)
cvDrawContours(dst_contours, cont1, CV_RGB(255,0,0), CV_RGB(255,0,0), 0, 1 , 8, cvPoint(1,1));
}
二、完整实验代码:
// 去背景
void CTestView::OnSubtest()
{
// TODO: Add your command handler code here
// TODO: Add your command handler code here
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
/*
cImg1:原图
cImg2:背景图
cImg3:去背景图
*/
IplImage *cImg1 = pDoc->m_img.GetImage();
IplImage *cImg2 = cvCreateImage(cvSize(pDoc->m_img.Width(), pDoc->m_img.Height()), 8, 3);
IplImage *cImg3 = cvCreateImage(cvSize(pDoc->m_img.Width(), pDoc->m_img.Height()), 8, 3);
IplConvKernel* element = cvCreateStructuringElementEx(4,4,1,1,CV_SHAPE_ELLIPSE,0);
cvErode(cImg1,cImg2,element,5);// 腐蚀
cvDilate(cImg2,cImg2,element,5);// 膨胀
cvSub(cImg1,cImg2,cImg3,0);// 去背景图像
// 保存图片
cvSaveImage("D:\\学习\\图像处理与机器视觉\\报告三\\backImg.jpg", cImg2);
cvSaveImage("D:\\学习\\图像处理与机器视觉\\报告三\\backRImg.jpg", cImg3);
// 显示图片
pDoc->m_img.Load("D:\\学习\\图像处理与机器视觉\\报告三\\backRImg.jpg");
// 刷新窗口
Invalidate();
}
// 三通道转单通道
void CTestView::Three2one(IplImage *inputImg, IplImage *outputImg)
{
uchar *data = (uchar *)inputImg->imageData;
int wp = inputImg->widthStep;
uchar *data1 = (uchar *)outputImg->imageData;
int wp1 = outputImg->widthStep;
for(int i = 0; i < inputImg->height; i++)
{
for(int j = 0; j < inputImg->width; j++)
{
data1[i * wp1 + j] = data[i * wp + 3 * j];
}
}
}
void CTestView::OnCountmili()
{
// TODO: Add your command handler code here
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
/*
cutbackRImg:去背景图像,三通道
singleImg:存储转换后的单通道图像
dst_contours:原图,用于画米粒轮廓
*/
IplImage *cutbackRImg = pDoc->m_img.GetImage();
IplImage *singleImg = cvCreateImage(cvSize(pDoc->m_img.Width(), pDoc->m_img.Height()), 8, 1);
IplImage *dst_contours = cvLoadImage("rice.jpg");
// 预处理
CTestView::Three2one(cutbackRImg, singleImg); // 三通道转单通道
IplConvKernel* element=cvCreateStructuringElementEx(4,4,1,1,CV_SHAPE_ELLIPSE,0);// 用于腐蚀膨胀的结构元素
cvErode(singleImg, singleImg,element,1);// 腐蚀
cvDilate(singleImg, singleImg,element,1);// 膨胀
cvThreshold(singleImg, singleImg, 50, 255, CV_THRESH_BINARY); //对单通道数组应用固定阈值操作
// 数米粒
CvMemStorage* stor=cvCreateMemStorage(0); // 创建内存存储器,0默认内存块大小为64k
CvSeq *cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),stor); // 创建序列
int numberOfObject = cvFindContours(singleImg,stor,&cont,sizeof(CvContour),CV_RETR_TREE);
// 米粒面积,周长,位置
double area = 0;
double length = 0;
int x1, y1;
CvSeq *cont1;
for (cont1 = cont; cont1 != NULL; cont1 = cont1->h_next)
{
CvRect rect = cvBoundingRect(cont1, 0);
double area1 = fabs(cvContourArea(cont1, CV_WHOLE_SEQ));
double length1 = cvArcLength(cont1);
if(area < area1)
{
area = area1;
length = length1;
x1 = rect.x;
y1 = rect.y;
}
cvDrawContours(dst_contours, cont1, CV_RGB(255,255,255), CV_RGB(255,0,0), 0, 1, 8, cvPoint(1,1));
}
// 最大轮廓
for (cont1 = cont; cont1 != NULL; cont1=cont1->h_next)
{
CvRect rect = cvBoundingRect(cont1, 0);
double area1 = fabs(cvContourArea(cont1, CV_WHOLE_SEQ));
if(rect.x==x1 && y1==rect.y && area1==area)
cvDrawContours(dst_contours, cont1, CV_RGB(255,0,0), CV_RGB(255,0,0), 0, 1 , 8, cvPoint(1,1));
}
// 保存图片
cvSaveImage("D:\\学习\\图像处理与机器视觉\\报告三\\draw.jpg", dst_contours);
// 显示图片
pDoc->m_img.Load("D:\\学习\\图像处理与机器视觉\\报告三\\draw.jpg");
// 刷新窗口
Invalidate();
char buf[100];
sprintf(buf, "图中的米粒数为:%d\n最大米粒面积为:%f\n最大米粒周长为:%f\n最大米粒位置坐标为:%d %d\n", numberOfObject, area,length, x1, y1);
MessageBox(buf);
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%