如何在图像处理中增加像素强度差异?

4

我有一张如附件所示的图片。 有时候,数字的黑色强度与邻近像素没有太大差别,我很难提取这些数字(例如,设置阈值不是很有效,因为黑色的强度与灰色的强度非常接近,可能是由于反射或图像拍摄时对焦不良所致)。我希望使黑色和背景灰色之间的差异更大,这样我就可以提取数字而减少噪声。我的方法是使用OpenCVaddWeighted函数来增加差异。 color是原始RGB图像。这种处理方式合理吗?还有更有效的方法吗?

Mat blur_img = new Mat(color.size(), color.type());
org.opencv.core.Size size = new Size(9,9);
Imgproc.GaussianBlur(color, blur_img, size, 2);
Mat sharpened = new Mat(color.size(), CvType.CV_32FC3);
Core.addWeighted(color, 1.5, blur_img, -0.5, 0, sharpened);

看起来不错。你也可以考虑使用拉普拉斯滤波器 - herohuyongtao
我觉得它不是很可靠,对于一些捕获的图像,它能够正常工作。但是对于另一些图像,它并不能真正发挥作用。 - batuman
3个回答

5
您需要进行本地二值化(Berensen、Sauvola、局部Otsu等),而OpenCV恰好有adaptiveThreshold函数。这里是一个例子,请确保尝试不同的参数。 adaptiveThreshold adaptiveThreshold Berensen bernsen 代码
#include <opencv2/opencv.hpp>
using namespace cv;

Mat thresh_bernsen(Mat& gray,int ksize,int contrast_limit)
{
    Mat ret = Mat::zeros(gray.size(),gray.type());
    for(int i=0;i<gray.cols;i++ )
    {
        for(int j=0;j<gray.rows;j++ )
        {
            double mn=999,mx=0;
            int ti=0,tj=0;
            int tlx=i-ksize/2;
            int tly=j-ksize/2;
            int brx=i+ksize/2;
            int bry=j+ksize/2;
            if(tlx<0) tlx=0;
            if(tly<0) tly=0;
            if(brx>=gray.cols) brx=gray.cols-1;
            if(bry>=gray.rows) bry=gray.rows-1;

            minMaxIdx(gray(Rect(Point(tlx,tly),Point(brx,bry))),&mn,&mx,0,0);
            /* this does the above
            for(int ik=-ksize/2;ik<=ksize/2;ik++)
            {
                for(int jk=-ksize/2;jk<=ksize/2;jk++)
                {
                    ti=i+ik;
                    tj=j+jk;
                    if(ti>0 && ti<gray.cols && tj>0 && tj<gray.rows)
                    {
                        uchar pix = gray.at<uchar>(tj,ti);
                        if(pix<mn) mn=pix;
                        if(pix>mx) mx=pix;
                    }
                }
            }*/
            int median = 0.5 * (mn+mx);
            if(median<contrast_limit)
            {
                ret.at<uchar>(j,i)=0;
            }else
            {
                uchar pix = gray.at<uchar>(j,i);
                ret.at<uchar>(j,i) = pix>median?255:0;
            }
        }
    }
    return ret;
}
int main()
{
    Mat gray = imread("c:/data/number.jpg",0);
    gray=255-gray;
    Mat adaptthresh,bernsen;
    adaptiveThreshold(gray,adaptthresh,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,41,1);
    bernsen=thresh_bernsen(gray,25,40);
    imshow("gray",gray);
    imshow("adaptthresh",adaptthresh);
    imshow("bernsen",bernsen);
    waitKey(0);
}

Bernsen算法的结果更好,但对于我的移动应用程序来说速度非常慢。 - batuman
呀..实现需要加速 :(. 或许有一个neon实现...如果失败了,你可以尝试使用imagej。它实现了一堆本地阈值方法。也许其中一个足够快且好用。你还可以尝试调整adaptivethreshold的参数,比如增加块大小或在adaptivethreshold之前对图像进行中值模糊处理。或者你可以在传递给bernsen之前缩小图像(需要减小较小图像的ksize)以使其更快。 - Zaw Lin

3
简单阈值处理不能处理图像中的光照变化,而自适应阈值处理不能利用连通区域。目前在像这样提取片段的领域中的当前领导者是MSER。它通过所有可能的阈值,并找到连接最稳定的区域(在所有阈值下)。不要重新发明轮子,使用被证明为最佳特征和开源的东西,例如openCV MSER。有更多的链接在stack overflow上。

但是OpenCV中的MSER代码不是用于检测关键点吗?您能否提供参考文章/代码,以展示如何在这种情况下使用它进行分割,因为您提供的那些是用于检测关键点而不是分割本身,而且修改方式并不明显。我认为看一下这个会很有趣。谢谢。 - lightalchemist
这是用于检测像CC(连通组件)之类的区域,但更加稳定的算法。请点击上面的链接查找代码,这些是我能找到的最好的。MSER现在被广泛用于文本检测。还可以在samples src的c文件夹中查看mser_sample.cpp。 - Vlad
mser 可能无法处理不均匀的光照...vlfeat 也有一个 mser 实现,我认为它们会给出不同的结果(vlfeat 看起来更好)...最好尝试两种方法。 - Zaw Lin

1

编辑:根据 Vlad 的评论,我在 OpenCV 的 3.0 dev 分支中找到了以下链接:http://docs.opencv.org/trunk/modules/objdetect/doc/erfilter.html。 这似乎是使用修改后的原始 MSER 算法来检测关键点的技术,用于检测文本。看起来 OpenCV 很快就会拥有它了。该技术的详细信息在上面链接中的 2 篇论文中描述。谢谢 Vlad。

如果在使用 Zaw 提到的算法对图像进行阈值处理之前使用 Non-local means 去噪,可以减少一些噪声:

fastNlMeansDenoising(gray, gray, 3, 5, 15);

根据Zaw的描述,自适应阈值(使用相同参数):

adaptive_threshold

Bernsen阈值如Zaw所述(使用相同的参数): bern_threshold 调整阈值和去噪参数可能会给您带来更好的结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接