血阳 发表于 2015-10-23 12:47:40

瓶子液体装满与否判断opencv3.0

本帖最后由 血阳 于 2019-8-15 20:54 编辑

处理一个题目,先把题目描述一遍吧:
       一家使用瓶子盛装各种工业化学药品的公司雇佣你来设计一种检测瓶子未装满的方法。瓶子在传送带上移动并通过自动装填和封盖机时的情形如图所示。当液位低于瓶颈底部和盘子肩部的中间点时,则认为瓶子未装满。瓶子横断面的侧面与倾斜面的区域定义为瓶子的肩部。瓶子在不断移动,但该公司有一个成像系统,该系统装备了一个前端照明闪光灯,可有效地停止瓶子的移动,所以你可以得到非常接近于下图显示的样例图像。基于上述资料,请你提出一个检测未完全装满瓶子的解决方案,编程实现该算法。

最后结果图:
http://image.geek-workshop.com/album/201510/23/124923pejtperoafp4oggs.png
个人思路:思路比较简单,就是找出瓶子上方的白色区域,然后再找到他们轮廓,找到连通区域,对区域面积进行判断,对于满足要求的地方(瓶未满)上色,其他地方删除,最后与原图叠加就行。
不多说,上程序:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
int main()
{
        Mat img, grayimg,dstimg;
        Mat cannyimg,openimg1;
        vector<vector< Point>> contours;
        vector<Vec4i> hierarchy;
        int h, w;
        int a = { 0 }, b = 0;
        img= imread("1.jpg");                     //读取图片
        imshow("原图",img);                        //显示原图
        cvtColor(img, grayimg, COLOR_BGR2GRAY);    //将图片转化为灰度图
        imshow("灰度图",grayimg);                  //显示灰度图
        blur(grayimg, grayimg, Size(9, 9));        //对图片进行平滑处理
        imshow("平滑处理后的灰度图",grayimg);      //显示平滑处理后的灰度图
        Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
        morphologyEx(grayimg, openimg1, MORPH_OPEN, element); // 对图片进行开运算
        imshow("开运算图",openimg1);///显示开运算后的图
        dstimg.create(openimg1.size(),openimg1.type());//创建一个和开运算图片一样大小,类型的矩阵

    /////下面是对图片进行二值化处理(二值化就是把有灰度的图直接转换为黑白图)
        uchar *p = openimg1.data;  //定义一个指针,指向图片矩阵第一个元素
        h = openimg1.rows;        //赋值图片的高给h
        w = openimg1.cols;        //赋值图片的宽给w
        for (unsigned int i = 0; i < h*w; i++)//遍历图片所有元素
        {
                if (*p >170)
                {
                        *p = 255;             //对于元素值大于170的赋值给元素255,即为白色
                }
                else *p = 0;             //其他情况,设为黑色
                *p++;
        }
        imshow("二值图",openimg1);
        ////////////////////////////

        /////设置一个相框
        uchar *q = dstimg.data;
        for (unsigned int i=0; i < h; i++)
        {
                if (i < 5||i>h-5)
                {
                        for (unsigned int j = 0; j < w; j++)
                        {
                                *q = 0;
                                *q++;
                        }
                }
                else
                {
                        for (unsigned int n = 0; n < w; n++)
                        {
                                if (n<5 || n>w - 5) *q = 0;
                                else *q = 1;
                                *q++;
                        }
                }
        }
        //imshow("creat",dstimg);
        /////用相框与处理的开运算图进行叠加
        uchar *pa = openimg1.data;
        uchar *qa = dstimg.data;
        for (unsigned int i = 0; i < w*h; i++)
        {
                *pa = (*pa)*(*qa);
                *pa++;
                *qa++;
        }
        imshow("加相框图",openimg1);
        Canny(openimg1,cannyimg,3,9,3);   ///对图片进行边缘检测
        imshow("边缘检测", cannyimg);   //显示边缘检测的图结果

        findContours(cannyimg, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE,Point(0, 0));  //提取图片轮廓
        Mat drawing = Mat::zeros(cannyimg.size(),CV_8UC3);//定义一个三通道的和原图尺寸一样大小的矩阵,方便上色

        printf("图片尺寸为高h=%d    宽w=%d    \n", h, w);
        for (unsigned int i = 0; i < contours.size(); i++)//遍历连通区域的个数
        {
                if (contourArea(contours[i] ) > 10000)      // 判断连通区域的面积是否大于10000
                {
                        Scalar color = Scalar(255, 255, 0);     //定义一个颜色
                        drawContours(drawing, contours, i, color, FILLED, 8, hierarchy, 0,  Point()); //对于满足要求的区域进行填充颜色
                }
                else
                {
                        Scalar color=Scalar(0,0,0);            //定义黑色
                        drawContours(drawing, contours, i, color,2, 8, hierarchy, 0,  Point());  //对不满足要求的区域删除(元素值全部为零,黑色)
                }
        }   //最后会得到一个只有不满足要求区域的图
        imshow("提取后的图",drawing); //显示上面操作所得到的图
        drawing = img - drawing;    //用原图减去处理说得的图即可标记处相应的区域
        imshow("最终结果", drawing);//显示最终结果

        waitKey(0);
}



最后,给上整个过程中出现的图吧:
这是灰度图:

这是灰度图开运算后的图:

这是二值图:

这是加了一个边界相框后的图:

这是边缘检测图:

这是提取轮廓,并排除其他,给特征轮廓上色的图:

最后这是结果图:
http://image.geek-workshop.com/album/201510/23/124923pejtperoafp4oggs.png

davidce 发表于 2015-10-23 13:07:54

赞,我也想学opencv

血阳 发表于 2015-10-23 13:14:52

davidce 发表于 2015-10-23 13:07 static/image/common/back.gif
赞,我也想学opencv

opencv不难,就是一个函数库。有编程基础的学一学就会,主要是知道里面的函数,然后会一些方法就可以入门了。

davidce 发表于 2015-10-23 17:43:04

这个图的背景是怎么处理掉的?

hp198969 发表于 2015-10-23 18:22:25

用一排光敏电阻,对面发出均匀强光。瓶子从光和电阻中间穿过。不满的阻值和满的阻值相差较大,一下子就分辨出来了……

血阳 发表于 2015-10-23 22:05:53

hp198969 发表于 2015-10-23 18:22 static/image/common/back.gif
用一排光敏电阻,对面发出均匀强光。瓶子从光和电阻中间穿过。不满的阻值和满的阻值相差较大,一下子就分辨 ...

呃……我写这个的主要目的不是在通过什么方法能更好地解决这个问题。
最主要的目的是练习opencv。学习opencv。
你说的方法当然好啦,只是我们的目的不一样罢啦。

血阳 发表于 2015-10-23 22:10:00

davidce 发表于 2015-10-23 17:43 static/image/common/back.gif
这个图的背景是怎么处理掉的?

什么意思?图的背景?不就是黑色的吗?

迷你强 发表于 2015-10-24 10:06:47

哇~~~~有高手在写OPENCV耶。。。本菜最近也是刚刚接触,好强大的说

血阳 发表于 2015-10-24 10:49:05

迷你强 发表于 2015-10-24 10:06 static/image/common/back.gif
哇~~~~有高手在写OPENCV耶。。。本菜最近也是刚刚接触,好强大的说

{:2_39:}我也是菜鸟一只,只是花了点时间了解了几个函数的意思并且应用而已o(╯□╰)o。。。opencv学习之路还很漫长啊。。。。

darkorigin 发表于 2015-10-24 13:43:21

是用PROCESSING来做的么

mikeliujia 发表于 2015-10-24 17:00:23

这个比较牛!:lol

血阳 发表于 2015-10-24 22:27:21

mikeliujia 发表于 2015-10-24 17:00 static/image/common/back.gif
这个比较牛!

我也还只是个菜鸟。还需要学习。

血阳 发表于 2015-10-24 22:30:40

darkorigin 发表于 2015-10-24 13:43 static/image/common/back.gif
是用PROCESSING来做的么

用的是opencv3.0做的,编译器的VS2013.
页: [1]
查看完整版本: 瓶子液体装满与否判断opencv3.0