关于盒子的二值化算法

最近做一个工业盒子自动抓取的项目,涉及一项重要的算法:二值化。

要求盒子的抓取准确率 99.9% 以上,坐标精度在0.01-0.05毫米,所以难度颇大。

稳定的二值化,稳定的轮廓提取,稳定线条提取,稳定的亚像素逼近,以及高精度的相机标定都是这个项目最核心的地方。

关于二值化算法,我了解的就有很多:全局二值化,局部二值化等。

otsu, nat, niblack, nick,sauvola, wolf, adaptive threshold, ……还有最大熵,三角阈值,P分位数 ...

图像处理有一个重要的思想:基于场景最优化,才能真正解决应用问题。

由于纸盒有一定的随机性:旋转,视野忽大忽小,以及光照不均……

用 著名的 二值化真解决不了真正问题,比如 OSTU,达不到 每次 边缘分割很好。

扫描二维码关注公众号,回复: 3121511 查看本文章

考虑前景、背景特点,设计 基于最大块、最小块 的均值作为阈值,进行全局二值化算法。

边缘明显稳定多了,效果如下:

搞工业图像处理,就是要不断深入理解场景,找寻最合适的优化。

算法源代码如下:

COMMON_LIB_DECL void binary_min_max_block(const Mat& src, Mat& dst, 
				const Size& minBlock = Size(5, 5), const Size& maxBlock = Size(5, 5))
//搜索最小灰度块/最大灰度块,取均值
{
    //积分图,用于加速
    Mat intMat;
    integral_transform(src, intMat);
    //搜索的块尺寸
    Rect minRect, maxRect;
    double maxV = (std::numeric_limits<int>::min)();
    double minV = (std::numeric_limits<int>::max)();

    for (int i = 0; i < src.rows; ++i)
    {
        for (int j = 0; j < src.cols; ++j)
        {
            if (i + minBlock.height < src.rows && j + minBlock.width < src.cols)//越界判断
            {
                Rect _rmin = Rect(j, i, minBlock.width, minBlock.height); //求最小块
                double _sumMinBlockV = intergal_of_sum(intMat, _rmin);
                if (_sumMinBlockV < minV)
                {
                    minRect = _rmin;
                    minV = _sumMinBlockV;
                }
            }
            
            if (i + maxBlock.height < src.rows && j + maxBlock.width < src.cols)//越界判断
            {
                Rect _rmax = Rect(j, i, maxBlock.width, maxBlock.height);//求最大块
                double _sumMaxBlockV = intergal_of_sum(intMat, _rmax);
                if (_sumMaxBlockV > maxV)
                {
                    maxRect = _rmax;
                    maxV = _sumMaxBlockV;
                }
            }
        }
    }

#if 0  //调试开关
    Mat colorImg;
    cv::cvtColor(src, colorImg, CV_GRAY2BGR);
    cv::rectangle(colorImg, minRect, Scalar(255,0,0));
    cv::rectangle(colorImg, maxRect, Scalar(0,0,255));
    cv::imshow("colorImg", colorImg);
    cv::waitKey();
#endif

    double avgMax = maxV/maxBlock.area();
    double avgMin = minV/minBlock.area();

    double thres = (avgMax + avgMin)/2;
    cv::threshold(src, dst, thres, 255, CV_THRESH_BINARY);
}

测试:

有时候看不懂 opencv的函数命名,我就重定义一个封装API,做到一目了然,整理成模块。

intergal_transform 封装了 积分图调用。

intergal_sum 封装了 快速求取 左上角->右下角 的子块面积。

read_gray 就是 封装了代码,避免 每次去查文档,找枚举变量。

get_under_images 就是封装了 boost::filesystem::recursive_directory_iterator 读取所有 opencv支持的图片格式。

resize_to_width 封装了按照 固定宽度的比例尺 resize图片。

binary_min_max_block就是调用我设计的 二值化API.

show_img 代码有点多,主要是 避免了一些矩阵显示问题,imshow显示的特点:

    8-bit unsigned直接显示
    16-bit unsigned or 32-bit integer 像素点除以 256 ,把  [0,255*256] --> [0,255]
    32-bit floating 像素点 x 255, 即把 [0,1] --> [0,255]

小结:

目前这个二值化算法,已经作为这个项目的标准的自动二值化算法, 当然也保留了手动设定阈值的二值化。

搞图像处理,最重要的一点,就是算法可控,完备性。越是高级算法,可控性就越小,想做稳定就越困难。

也许,我们可能会用 预处理:伽马矫正, 直方图均衡,直方图规定化,然后二值化,或者边缘提取: Canny 等等其他算法,但是完备性不足,也许解决了 98%的场景,但是我们的要求是 99.9%.

猜你喜欢

转载自blog.csdn.net/laoki/article/details/82056761
今日推荐