移动均值二值化

2021-06-28  本文已影响0人  寽虎非虫003

零、前言与参考

本文主要是针对光照环境有变化的时候的二值化的,来源于冈萨雷斯的书,并在此基础上进行了一些更改。
更改的主要效果是在有噪声,行列间变化更大的情况下,会更稳定;

主要来源

《数字图像处理》(第三版,中文),冈萨雷斯,P491~492:《使用移动平均》


说明
效果

一、原理

以m_{k}表示像素k处的移动平均值;\\ 以I_k表示k处的像素值\\ 以n个像素值俩计算平均;\\ b表示分割比例;\\ 则\\ \begin{equation} m_{k+1} = \left\{ \begin{aligned} {\frac{1}{n}\sum_{i=k+2-n}^{k+1}, k\geq n-1\\ m_k+\frac{1}{n}(z_{k+1})-z_{k-n}, k\geq n+1} \end{aligned} \right. \end{equation}\\ n取兴趣区域大小的5倍;

然后再进行二值化。

\begin{equation} g_{(x,y)} = \left\{ \begin{aligned} {1, f_{(x,y)}\geq bm_{(x,y)}\\ 0, f_{(x,y)}\leq bm_{(x,y)}} \end{aligned} \right. \end{equation}\\ 一般 b 取0.5;\\

改进的版本和原本的版本在二值化一步完全相同,不同的是,取得m_k的方式,改进版本的取得m_k是由滑窗均值滤波取得,而滤波卷积核的半窗值为感兴趣区域大小的2倍或更多;

二、基本实现

此部分代码来源于移动平均的阈值处理 opencv实现,但是这份代码只实现了按行进行移动平均的处理。

void ThresholdMeanMove(const Mat &src, Mat &dst, int n = 20, float b = 0.5f)
{
    int arrayNum = src.rows*src.cols;
    uchar *temp = new uchar[arrayNum];
    for (int y = 0; y < src.rows; y++) {//二维转换为一维Z字扫描
        for (int x = 0; x < src.cols; x++) {
            if (y % 2 == 0)
                temp[y*src.cols + x] = src.at<uchar>(y, x);
            else
                temp[y*src.cols + x] = src.at<uchar>(y, src.cols - 1 - x);
        }
    }

    dst.create(src.size(), CV_8U);
    float m_now = 0, m_pre = 0, dif = 0;
    int index = 0;
    for (int y = 0; y < src.rows; y++)
        for (int x = 0; x < src.cols; x++) {
            index = y * src.cols + x;
            if (index < n)//当前点总数不足时
                dif = temp[index];
            else
                dif = temp[index] - temp[index - n];

            dif *= (float)1 / n;
            m_now = m_pre + dif;//得出阈值
            m_pre = m_now;
            if (src.at<uchar>(y, x) > b*m_now)
                dst.at<uchar>(y, x) = 255;
            else
                dst.at<uchar>(y, x) = 0;
        }
    delete[] temp;
}

三、改进实现

使用均值滤波来得到均值的,而不是只是用逐行z形求均值。

void ThresholdMeanMoveWindow(const Mat &src, Mat &dst, int nWindowHalf = 10, float b = 0.5f)
{
    Mat m;//计算均值
    blur(src, m, Size(2*nWindowHalf+1, 2*nWindowHalf+1), Point(-1, -1), 4);
    dst = src > (b*m);
}

cuda加速

void ThresholdMeanMoveWindow_gpu(const Mat &src, Mat &dst, int nWindowHalf, float b)
{
    Mat m;//计算均值

    cv::cuda::GpuMat d_m, d_Src, d_Dst;
    d_Src.upload(src);
    //d_Dst.upload(src);
    //d_m.upload(src);

    cv::Ptr<cv::cuda::Filter> median = cv::cuda::createMedianFilter(d_Src.type(), nWindowHalf);
    median->apply(d_Src, d_m);
    cv::cuda::multiply(d_m, b, d_m);//<d_m与比例相乘
    cv::cuda::compare(d_Src, d_m, d_Dst, CMP_GE);//<比较
    d_Dst.download(dst);//<回传
}

四、结果

可自行运行对比。

五、一个点子

既然可以用均值滤波进行二值化,那么是不是也可以用高斯滤波等其它滤波进行二值化呢?

上一篇下一篇

猜你喜欢

热点阅读