如何在OpenCV中移除不需要的线条/噪声?

3
我正在开发一个OCR应用程序,用Java构建为Android应用。 我想从相机拍摄的图像中检测文本,并使用OpenCV进行预处理,但我得到了一些额外的线条被识别成文本,我已经遵循了以下方法:
1-将RGB转换为灰度 2-阈值 3-高斯模糊 4-中值模糊 5-膨胀 6-腐蚀
结果至少比以前好,但仍然没有得到正确的结果。如何去除这种噪声,有哪些通用的滤镜序列可以应用于任何图像以改善OCR结果。我对OpenCV是新手,请指导我。谢谢。 旧图像

更新的图片


从上面的图像中,我能够找到第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);
    }

}

尝试侵蚀图像(调整内核大小进行实验),然后再进行膨胀。 - Pervez Alam
3个回答

8
我认为在侵蚀/膨胀选项方面,你只能做到这一步。问题在于,噪声不仅仅是噪声,还包含着一些与你试图检测的字符并没有太大差异的工件。
我建议解决方案需要涉及到检测图像中的轮廓。首先,您应该准备好图像,使其更易于进行轮廓处理。
我过去使用了以下序列:
高斯模糊 自适应阈值 负像 膨胀
现在可以使用findContours。这应该会挑选出字母和不良工件。然后,您需要消除工件,可能需要多种策略,例如:
计算边界框的面积,并消除那些面积过小的字母。 检查边界框宽度与高度:消除高度太小的那些物品。
也可以采用更复杂的方法,例如尝试找到通过较大轮廓中心的轴线,这将为您提供字符的位置和方向(例如作为旋转矩形);您可以使用这些信息忽略不在该区域内的所有其他像素。
很抱歉这不是一个简单的解决方案,但这是一个复杂的问题,因此您可能需要尝试一下并建立多种策略来消除不需要的工件。

谢谢您的回复,我会按照您所提到的进行操作,并在有进展时与您联系。 - Asher Ali
问题是findcontour将O和4检测为两个不同的轮廓而不是一个。您可以在图像中看到。这可能是因为模式为CV_RETR_TREE,厚度为-1(用于CV_FILLED)和-1(用于轮廓索引),它绘制包括噪声在内的所有轮廓。如果我选择i而不是-1,则会绘制所选轮廓,但会填充0和4.可能是因为O有2个轮廓,而不绘制内部轮廓。我希望绘制相似大小的轮廓,并将具有孔的轮廓(例如O 4)视为单个轮廓。我希望您能理解我的意思。 - Asher Ali
Imgproc.RETR_EXTERNAL 可能是最好的选择,因为它仅检索外部轮廓。我从未使用过 CV_FILLED 选项 - 我总是指定厚度。更新后的图像中的编号2不是您想要的效果吗?通过计算噪声比较小的位的面积,你可以消除它们。 - timegalore
cv_filled 可以让我填充轮廓的外形。而 external 只会给我外部轮廓,保留 4 的内部形状,这不是我想要的。必须有其他方法解决它。数字 2 只给出 0 和 4 的外部形状,所以我希望得到像数字 3 一样的形状,但不要内部轮廓和噪音。 - Asher Ali
你需要找到所有字母和数字的轮廓吗?如果你有一个字母的边界框(如2中所示),你可以将这些区域复制到一个空白图像中,从而删除围绕字母的框之外的任何内容。 - timegalore
显示剩余3条评论

0

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


嗨,Asher,我不太确定你在这里的意思。如果可能的话,你能详细解释一下吗? - Vivekanand

0

尝试侵蚀图像(尝试不同大小的核),然后膨胀以恢复。

如果线条/像素噪声(剩余噪声或原始噪声)非常小,请尝试中值模糊

如果还不行,请分享您的结果以进一步处理。


我已经做了这个很多次,但问题是我没有为特定的图像进行操作,不同的图像在特定的设置下会产生不同的结果。我必须找到其他的解决方案。 - Asher Ali

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