【问题标题】:OpenCV Java removing objects from sceneOpenCV Java 从场景中删除对象
【发布时间】:2017-10-13 01:20:59
【问题描述】:

我已成功检测到边界框,现在我想删除图像中的所有其他内容,只保留边界框的内容以提高 tesseract 精度,下面是我所做和需要的图​​像表示(我希望二进制图像只包含字母,所有其他要删除的对象):

和我的代码:

 public static ProcessedFrame preProcessImage(Mat image){
    originalFrame = image.clone();
    roiColor = image.clone();
    Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2GRAY, 0);
    originalFrameGrayScale = image.clone();
    Mat morph = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(9, 9));
    Imgproc.morphologyEx(image, image, Imgproc.MORPH_TOPHAT, morph);
    Imgproc.Sobel(image, image, -1, 2, 0);
    Imgproc.GaussianBlur(image, image, new Size(5,5), 3,3);
    Imgproc.morphologyEx(image, image, Imgproc.MORPH_CLOSE, morph);
    Imgproc.threshold(image, image, 200, 255, Imgproc.THRESH_OTSU);
    Vector<Rect> rectangles = detectionContour(image);
    Mat roi = originalFrameGrayScale.clone();
    if(!rectangles.isEmpty()){
    roi = originalFrameGrayScale.submat(rectangles.get(0));
    roiBlack = roi.clone();
    roiColor = roiColor.submat(rectangles.get(0));
      Imgproc.rectangle(originalFrame, rectangles.get(0).br(), rectangles.get(0).tl(), new Scalar(0,0,255), 2);
    }
   Imgproc.medianBlur(roi, roi, 3); 
   Imgproc.adaptiveThreshold(roi, roi, 225, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 15, 3);
   Imgproc.medianBlur(roi, roi, 3);
   Imgproc.medianBlur(roi, roi, 3); 
   Imgproc.adaptiveThreshold(roi, roi, 225, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 15, 3);
   Imgproc.medianBlur(roi, roi, 3);
   roiBinarize = roi.clone();
   Mat erode = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
   Mat dilate = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(3, 3));
   Imgproc.morphologyEx(roi, roi, Imgproc.MORPH_CLOSE, dilate);
   Imgproc.morphologyEx(roi, roi, Imgproc.MORPH_CLOSE, erode);
   Vector<Rect> letters = detectionPlateCharacterContour(roi);
   doTesseractOCR(letters, roiBinarize);
    return  new ProcessedFrame(originalFrame, roiColor, roiBinarize, roi);

}






private static void doTesseractOCR(Vector<Rect> letters, Mat plate){
    Tesseract instance = new Tesseract(); //
    instance.setLanguage(LANGUAGE);
    String resultPlate = "AAA0000";
    for(int i= 0; i < letters.size(); i++){

     BufferedImage letter = OpenCvUtils.Mat2bufferedImage(plate.submat(letters.get(i)));
        try {
        String result = instance.doOCR(letter);
        String character = result.replace("\n", "");
        resultPlate = new StringBuilder(resultPlate).replace(i ,i+1, character).toString();
        } catch (TesseractException e) {
        System.err.println(e.getMessage());
        }

        System.out.println("Tesseract output: "+resultPlate);
    }

     try {
        String result = instance.doOCR(OpenCvUtils.Mat2bufferedImage(roiBinarize));
        System.out.println("Tesseract output2: "+result.replace("\n", ""));
         } catch (TesseractException e) {
        System.err.println(e.getMessage());
        }
}





 private static Vector<Rect> detectionPlateCharacterContour(Mat roi) {
    Mat contHierarchy = new Mat();
    Mat imageMat = roi.clone();
    Rect rect = null;
    List<MatOfPoint> contours = new ArrayList<>();
    Imgproc.findContours(imageMat, contours, contHierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
    Vector<Rect> rect_array = new Vector<>();

    for (int i = 0; i < contours.size(); i++) {
        rect = Imgproc.boundingRect(contours.get(i));
        double ratio = 0;

               if(rect.height > rect.width){
            ratio = rect.height/rect.width;

            }else{
                ratio = rect.width/rect.height;

            }
         Logger.printMessage("Ratio of letter: "+ratio);
      double contourarea = Imgproc.contourArea(contours.get(i));
         if (contourarea >= 160 && contourarea <= 1000 && ( ratio >= 1 && ratio <= 2)) {
         Imgproc.rectangle(roiColor, rect.br(), rect.tl(), new Scalar(10,50,255));
           rect_array.add(rect);
         }
    }


    contHierarchy.release();
    return rect_array;
}




 private static Vector<Rect> detectionContour(Mat outmat) {
    Mat contHierarchy = new Mat();
    Mat imageMat = outmat.clone();

    List<MatOfPoint> contours = new ArrayList<>();

    Imgproc.findContours(imageMat, contours, contHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);

    Vector<Rect> rect_array = new Vector<>();
    for (int i = 0; i < contours.size(); i++) {
        Rect rect = Imgproc.boundingRect(contours.get(i));

        Mat contour = contours.get(i);
        double contourarea = Imgproc.contourArea(contour);

            double ratio = 0;
            int radius = 0;
            if(rect.height > rect.width){
            ratio = rect.height/rect.width;
            radius = rect.height/2;
            }else{
                ratio = rect.width/rect.height;
                radius = rect.width/2;
            }
        if (contourarea >= 2000 && contourarea <= 10000 && ( ratio == 1 || ratio == 2)) {   
            Logger.printMessage("Rectangle ratio: "+ratio);
            MatOfPoint2f mat2f = new MatOfPoint2f();
            contours.get(i).convertTo(mat2f, CvType.CV_32FC2);
            RotatedRect rotatedRect = Imgproc.minAreaRect( mat2f );
            double rotationAngle = rotatedRect.angle;
            if(rotatedRect.angle > 0)
                rotationAngle = 90 - rotatedRect.angle;
            else
               rotationAngle = rotatedRect.angle; 
            Logger.printMessage("Rotation is: "+(rotationAngle));
            rect = enlargeROI(originalFrame, rect, 10);
            rect_array.add(rect);
        }
    }


    contHierarchy.release();
    return rect_array;
}





private Vector<Rect> detectionContours(Mat outmat) {
    Mat contHierarchy = new Mat();
    Mat imageMat = outmat.clone();
    Rect contourRect = null;
    List<MatOfPoint> contours = new ArrayList<>();
    Imgproc.findContours(imageMat, contours, contHierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
    Vector<Rect> rect_array = new Vector<>();
    for (int i = 0; i < contours.size(); i++) {
        Mat contour = contours.get(i);
        double contourarea = Imgproc.contourArea(contour);
        if (contourarea > minBlob && contourarea < maxBlob) {
            contourRect = Imgproc.boundingRect(contours.get(i));
            rect_array.add(contourRect);
        }
    }


    contHierarchy.release();
    return rect_array;
}

当我运行这段代码打印出每个矩形的质心时,我发现 2 x: 18.0 y: 111.0 2 x: 42.0 y:109.0 7 x:65.0 y:108.0 0x:89.0 y: 108.0 一个 x: 29.0 y: 61.0 C x: 52.0 y: 58.0 P x: 77.0 y: 58.0

【问题讨论】:

  • OpenCv enhancing image for OCR 的可能重复项 - 请不要发布另一个关于明显相同问题的问题,而是适当地增强原始问题。
  • 我把帖子删了,问错了。
  • OK :) |你想用什么来替换你“删除”的图像部分? (我假设我们正在讨论与附加图像右下角的图像有关的一些进一步步骤。)由于您向 tesseract 提供了一个指定单个检测到的字母的边界框向量,我认为这只是一些外观要求用于展示?
  • 不,这不是为了美观,逐个处理字符的问题是我无法定位它们,因此最后我得到一个纠结的字符串,有一种方法可以知道哪个边界框 js我可以从第一行从左到右开始第二个,但我不知道
  • 你确定你得到的结果没有可预测的顺序吗?您肯定会从已经分割的符号中受益。你能发布一个你的结果的例子吗?所有的车牌都保证有两条线吗?您可以根据 y 坐标将符号分成两组,然后在每组中按 x 排序。 |为了摆脱其他东西,我只需制作一个相同大小的新图像,填充白色,然后从源中复制每个符号 ROI。

标签: java opencv ocr tesseract


【解决方案1】:

您似乎只需要对您的字母边界框进行一些预处理,并且在将它们提供给 Tesseract 之前这样做可能是有意义的,尽管这并不是绝对必要的。让我们首先用轮廓索引注释我们的示例图像,然后将它们的属性制成表格。

contour | bbox centroid | recognised
index   |   x   |   y   | symbol
--------+-------+-------+-----------
0       |  18.0 | 111.0 |   2
1       |  42.0 | 109.0 |   2
2       |  65.0 | 108.0 |   7
3       |  89.0 | 108.0 |   0
4       |  29.0 |  61.0 |   A
5       |  52.0 |  58.0 |   C
6       |  77.0 |  58.0 |   P

当一个人阅读这个车牌时,他们会从最上面的一行符号开始,从左到右阅读这些符号。在他们到达最右边的符号后,他们移动到下一行,然后再次从左到右。因此,我们应该将我们的符号(或代表它们的边界框)组织在一个结构中,以便我们轻松地做到这一点。

我们可以使用Vector&lt;Rect&gt; 来表示每一行符号。因为总是有两行符号,所以我们需要这个向量的两个实例——让我们将它们称为 row0row1


第一个任务是将符号的边界框分为两组,并在此基础上将它们添加到适当的行向量中。一个简单的方法可能是这样的:

  • 计算符号质心y 坐标的平均值。在我们的示例中,它大约是 mean_y=87.5
  • 对于每个检测到的符号:
    • 如果符号质心y坐标小于mean_y,则将该符号添加到row0
    • 否则将符号添加到 row1

当我们对我们的样本符号集执行此算法时,我们看到

     | contained
row  | symbol ids
-----+--------------
row0 |  4, 5, 6
row1 |  0, 1, 2, 3

如果这种简单的方法不够用,你可以尝试一些更高级的means of clustering


第二个任务是对每一行进行排序,使符号从左到右:

  • 对于每一行:
    • 按其质心的x 坐标对行元素进行升序排序

我的 Java 相当生锈,但您可以使用 comparator 来完成此操作。有关如何在 Java 中对集合进行排序的更多详细信息,您可以参考this SO answer

在我们的示例中,每一行中的元素已经有序,所以没有什么可做的......

     | contained
row  | symbol ids
-----+--------------
row0 |  4, 5, 6
row1 |  0, 1, 2, 3

现在我们可以

  • 对于每一行:
    • 对于行中的每个符号
      • 使用 Tesseract 处理
      • 根据需要处理结果(例如打印)

在我们的示例中,我们会得到

ACP
2270

【讨论】:

    猜你喜欢
    • 2013-08-23
    • 2015-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多