OpenCV实战4 玉米粒计数

原文在这里,这里是自己的笔记。

效果:

 

代码思路:

还是用联通区域数量(轮廓数量)来实现玉米粒计数,但是这里很值得学习的是怎么把存在重叠区域的玉米粒分离开。

  1. 使用单峰阈值处理获得二值化图像(比自适应阈值的效果还有好)
  2. 进行腐蚀操作,稍微断开一些联通区域,初步将玉米粒分类
  3. 进行距离变换,查找出亮包,找出物体的中心;使用自适应阈值操作得到完全分开的玉米粒
  4. 膨胀操作将一些断开的的轮廓进行联通
  5. 通过计算有效联通区域进行玉米粒计数

findContours函数

代码有很多注释,很容易看懂:

#include <iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
    Mat src, gray;

    src = imread("/home/jason/work/01-img/yumi.png");
    cvtColor(src, gray,  COLOR_BGR2GRAY);
    imshow("gray", gray);

    //单峰图像使用THRESH_TRIANGLE做阈值,比THRESH_OTSU效果还好
    // 这样就不用调阈值了,效果也很好
    Mat thresh;
    threshold(gray, thresh, 0, 255, THRESH_BINARY_INV |  THRESH_TRIANGLE);
    imshow("thresh",thresh);

    // 腐蚀操作,稍微断开一些联通区域,将玉米粒分离
    Mat kernel = getStructuringElement(MORPH_RECT, Size(15,15));/*kernelSize大一点效果比较好,5就效果很不好*/
    Mat erosion;
    erode(thresh, erosion, kernel);
    imshow("erosi", erosion);

    // 上一步操作还没有完全分离
    // 所以进行距离变换,查找出亮包,找出物体的中心
    Mat dist;
    distanceTransform(erosion, dist, DIST_L2, 3);
    normalize(dist,dist,1,0,NORM_MINMAX);
    imshow("dist", dist);
    cout<<"channel:" << dist.channels();

    // 然后对距离变换结果,使用自适应阈值
    // 会得到完全分开的玉米粒
    Mat dist_8U;
    dist.convertTo(dist_8U, CV_8UC1);/*本身就是单通道,可以不运行*/
    adaptiveThreshold(dist_8U, dist_8U, 255, ADAPTIVE_THRESH_GAUSSIAN_C,
                      THRESH_BINARY, 81, 0);
    imshow("dist_8U",dist_8U);

    // 但是有些轮廓是断开的的 ,所以还得通过膨胀操作将有效轮廓联通
    Mat dilation;
    dilate(dist_8U, dilation, kernel);
    imshow("dilation", dilation);

    // 现在就可以通过计算有效联通区域来计数了
    vector<vector<Point>> contours;
    vector<vector<Point>> EffectiveContours;
    findContours(dilation, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    for(size_t i=0; i<contours.size(); i++)
    {
        double area = contourArea(contours[i]);
        if (area>500)
        {
            EffectiveContours.push_back(contours[i]);
        }
    }

    // 展示
    for(size_t i=0; i<EffectiveContours.size(); i++)
    {
        Rect rect = boundingRect(EffectiveContours[i]);
        putText(src, to_string(i+1), Point(rect.x+10, rect.y+30), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0,0),2);

    }
    char text[10];
    sprintf(text, "%s%d", "Toal:", EffectiveContours.size());/*  %s%d  整型化为字符串  */
    putText(src, text, Point(10, 30), FONT_HERSHEY_SIMPLEX,1,Scalar(255,0,0),2);
    imshow("result", src);




    waitKey();
    cout << "Hello World!" << endl;
    return 0;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值