【问题标题】:how to remove unwanted lines/noise in OpenCV?如何删除 OpenCV 中不需要的线条/噪音?
【发布时间】:2014-05-18 21:41:27
【问题描述】:

我正在为 android 开发 OCR 应用程序(构建为 java 应用程序)。我想从相机捕获的图像中检测文本并进行预处理,我正在使用 OpenCV,但是我得到了一些额外的行,这些行被读取为文本,我遵循了这种方法:

1-RGB 转灰度 2-阈值 3-高斯模糊 4-中值模糊 5-扩张 6-侵蚀

结果至少比以前好,但仍然没有得到正确的结果。 我怎样才能去除这种噪音,像这样的过滤器的一般序列是什么,可以应用于任何图像以改善 OCR 的结果。 我是 OpenCV 的新手,所以请指导我。 谢谢。

old Image


Updated Image


从上面的图像中,我能够找到第 3 项的结果,但只有在绘制所有轮廓时,这是我不想要的东西,因为也有噪音。我在这里缺少什么。不知道该怎么做。 修改后的代码:

package simple_contours;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

public class Main {

    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat src_img,src_grey,src_blur,src_thresh,src_dilate,dest_img; 
        src_img=Highgui.imread("n_num.jpg",Imgproc.COLOR_BGR2GRAY);


        src_grey=new Mat(src_img.size(), Core.DEPTH_MASK_8U);
        src_blur=new Mat(src_img.size(), Core.DEPTH_MASK_8U);
        src_thresh=new Mat(src_img.size(), Core.DEPTH_MASK_8U);
        src_dilate=new Mat(src_img.size(), Core.DEPTH_MASK_8U);
        dest_img=Mat.zeros(640,480, CvType.CV_8UC3);
        Core.bitwise_not(dest_img, dest_img);
        Highgui.imwrite("dest.jpg", dest_img);

        Imgproc.cvtColor(src_img, src_grey, Imgproc.COLOR_BGR2GRAY);
        Imgproc.GaussianBlur(src_grey, src_blur, new Size(3, 3), 0);
        Imgproc.threshold(src_blur, src_thresh, 80, 255, Imgproc.THRESH_BINARY_INV);
        Imgproc.dilate(src_thresh, src_dilate, Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2)));

        Highgui.imwrite("Threshold.jpg", src_thresh);
        Highgui.imwrite("Dilate.jpg", src_dilate);




          List<MatOfPoint> contours = new ArrayList<MatOfPoint>();  
          Mat heirarchy= new Mat();
          Point shift=new Point(150,0);
          Imgproc.findContours(src_dilate, contours,heirarchy, Imgproc.RETR_TREE,Imgproc.CHAIN_APPROX_SIMPLE,shift);
          double[] cont_area =new double[contours.size()]; 

             for(int i=0; i< contours.size();i++)
             { 
                Rect rect = Imgproc.boundingRect(contours.get(i));
                cont_area[i]=Imgproc.contourArea(contours.get(i));

                System.out.println("Hight: "+rect.height);
                System.out.println("WIDTH: "+rect.width);
                System.out.println("AREA: "+cont_area[i]);
              //System.out.println(rect.x +","+rect.y+","+rect.height+","+rect.width);

                  Core.rectangle(src_img, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height),new Scalar(0,0,255));
                  Imgproc.drawContours(dest_img, contours, i, new Scalar(0,0,0),-1,8,heirarchy,2,shift);
                  Core.rectangle(dest_img, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height),new Scalar(0,255,0));
         }

             Highgui.imwrite("Final.jpg", dest_img);
             Highgui.imwrite("Original.jpg", src_img);
    }

}

【问题讨论】:

  • 尝试腐蚀图像(实验内核大小)然后拨号

标签: java android c++ opencv


【解决方案1】:

我认为您只能使用侵蚀/扩张选项走这么远。问题在于,噪音不仅仅是噪音,而是包含与您尝试检测的某些字符不一定完全不同的伪影。

我建议解决方案需要检测图像中的轮廓。首先,您应该准备图像以使其更易于处理轮廓处理。

我过去使用过以下序列:

  • 高斯模糊
  • 自适应阈值
  • 负图像
  • 扩张

现在您可以找到轮廓。这应该挑选出字母和不受欢迎的人工制品。然后,您需要消除伪影,这可能需要一些策略,例如:

  • 计算出边界框的面积,并消除那些面积太小而不能成为字母的区域
  • 检查边界框的宽度与高度:消除高度太小的那些

可能有更复杂的方法,例如尝试找到穿过较大轮廓中间的轴,这将为您提供字符的位置和方向(例如旋转的矩形);您可以使用该信息来忽略不在区域中的所有其他像素。

抱歉,这不是一个简单的解决方案,而是一个复杂的问题,因此您可能需要进行一些试验并制定多种策略来移除不需要的人工制品。

【讨论】:

  • 感谢您的回复,我会按照您提到的内容进行操作,如有任何进展,我会尽快回复。
  • 我找到了轮廓,但似乎无法正确绘制它们。您能否查看更新的图像以了解我的问题。 @timegalore
  • 更新后的图片看起来不错;您已经用一个框正确地界定了每个字母,这样您就可以消除不需要的字母。当您说轮廓未正确绘制时-以什么方式?目前您将它们绘制为黑色,因此它们可能无法很好地显示,因为文本是黑色的。
  • 问题是 findcontour 将 O 和 4 检测为两个不同的轮廓而不是 1。您可以在图像中看到。这可能是因为模式为 CV_RETR_TREE、厚度 -1(对于 CV_FILLED)和 - 1(对于contourIdx),它绘制包括噪声在内的所有轮廓。如果我选​​择i而不是-1,它会绘制选定的轮廓但绘制0和4填充。可能是因为O有2个轮廓并且不绘制内部轮廓。我想绘制类似大小的轮廓,像 O 4 这样有孔的轮廓应该被读作单个轮廓。我希望你能得到我想说的
  • Imgproc.RETR_EXTERNAL 可能是最好的,因为它只检索外轮廓。我没有使用 CV_FILLED 选项——我总是指定一个厚度。更新图像中的数字 2 不是您想要的效果吗?您可以通过计算它们的面积来消除较小的噪声位。
【解决方案2】:

尝试侵蚀图像(实验内核大小),然后扩张恢复。

如果线条/像素噪点(剩余噪点或原始)非常小,请尝试中值模糊。 它应该可以工作,如果没有,请分享您的结果以进一步工作。

【讨论】:

  • 我已经这样做了一百次,但问题是我不是针对特定图像执行此操作,不同的图像在特定设置下会产生不同的结果。我必须为此找到其他解决方案。
【解决方案3】:

我通过使用层次结构中的信息解决了这个问题,第 4 个索引值包含有关父轮廓的信息,因此应该绘制那些具有 -1 值的轮廓。这解决了填充轮廓问题。 :)

【讨论】:

  • 嗨,Asher,我不太清楚你的意思。如果可能的话,你能详细解释一下吗
猜你喜欢
  • 1970-01-01
  • 2011-06-10
  • 2017-04-03
  • 2021-11-23
  • 2014-10-24
  • 2020-05-03
  • 2020-03-31
  • 1970-01-01
  • 2018-07-15
相关资源
最近更新 更多