首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么我不能正确选择使用java opencv的矩形区域?

为什么我不能正确选择使用java opencv的矩形区域?
EN

Stack Overflow用户
提问于 2022-04-16 10:26:26
回答 1查看 227关注 0票数 1

通过opencv,我想从以下图片中选择收据的区域:

https://i.imgur.com/BnXzWPe.jpg

然后将它们裁剪并保存在单独的文件中。

这张照片是在1200 Dpi扫描的,在收据上加上一块黑色纸板,以便更好地识别边缘。

到目前为止,我得出的结果如下:

https://i.imgur.com/tRaPocd.jpg

在这里,您可以看到红色的边框、包含收据的矩形和带最小面积的矩形。

尽管正确地识别了Canny的边缘:

https://i.imgur.com/O5o2DWr.jpg

中央形象的绿地是不正确的,我不明白为什么。

这是源代码的摘录,您可以在这里找到完整的源代码:https://pastebin.com/NNc18pzA

代码语言:javascript
运行
复制
Imgproc.resize (srcMat, srcMat, new Size (0,0), 0.5, 0.5, Imgproc.INTER_AREA);
Imgproc.cvtColor (srcMat, grayMat, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold (grayMat, grayMat, 177, 200, Imgproc.THRESH_BINARY);
Imgproc.GaussianBlur (grayMat, blurredMat, new Size (21,21), 0, 0, Core.BORDER_DEFAULT); // 3,3, 9,9 15,15, ....

Mat rectKernel = Imgproc.getStructuringElement (Imgproc.MORPH_RECT, new Size (21,21));

Imgproc.dilate (blurredMat, dilatedMat, rectKernel, new Point (0,0), 1);
Imgproc.Canny (dilatedMat, cannyMat, 100,200.3);

List <MatOfPoint> contours = new ArrayList <MatOfPoint> ();
final Mat hierarchy = new Mat ();

Imgproc.findContours (cannyMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

这些是在中间阶段处理的图像:

灰色:https://i.imgur.com/ufeIMQT.jpg

模糊:https://i.imgur.com/lQQER6T.jpg

扩张:https://i.imgur.com/sHmfZyI.jpg

这是使用的opencv版本:

代码语言:javascript
运行
复制
<dependency>
    <groupId>org.openpnp</groupId>
    <artifactId>opencv</artifactId>
    <version>4.3.0-2</version>
</dependency> 

请帮帮我。

谢谢你们所有人。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-16 22:35:30

主要问题是在Canny边缘检测后应用findContours

Canny算子在外部轮廓中创建了很小的间隙:

结合approxPolyDP,我们得到了奇怪的结果。

简单的解决方案是跳过Canny运算符并将findContours应用于dilatedMat

Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);替换为:

代码语言:javascript
运行
复制
Imgproc.findContours(dilatedMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

注意:建议使用RETR_EXTERNAL,但在这种情况下不是必须的。

一个小的问题是,有一些小的轮廓,是由噪音造成的。

我们可以使用打开形态学手术去除小(噪声)轮廓(更好地应用于扩张前)。

另一个简单的解决方案是跳过小面积的等高线。

我发现在10000像素以下的区域可能被认为是“小”的。

在for循环中添加一个if语句:

代码语言:javascript
运行
复制
double area = Imgproc.contourArea(cnt);
if (area > 10000) { // Exclude small contours (noise)...

输出:

我将代码转换为Python ( JAVA代码保存在注释中)。

完整代码示例:

代码语言:javascript
运行
复制
import cv2
import numpy as np

srcMat = cv2.imread('receipts.jpg')  # Read input image
cv2.resize(srcMat, (0, 0), srcMat, 0.1, 0.1, cv2.INTER_AREA)  # Imgproc.resize (srcMat, srcMat, new Size (0,0), 0.5, 0.5, Imgproc.INTER_AREA);
grayMat = cv2.cvtColor(srcMat, cv2.COLOR_BGR2GRAY)  # grayMat = Imgproc.cvtColor (srcMat, grayMat, Imgproc.COLOR_BGR2GRAY);
grayMat = cv2.threshold(grayMat, 177, 200, cv2.THRESH_BINARY)[1]  # Imgproc.threshold (grayMat, grayMat, 177, 200, Imgproc.THRESH_BINARY);
blurredMat = cv2.GaussianBlur(grayMat, ksize=(21, 21), sigmaX=0, sigmaY=0)  # Imgproc.GaussianBlur (grayMat, blurredMat, new Size (21,21), 0, 0, Core.BORDER_DEFAULT); // 3,3, 9,9 15,15, ....

rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 21))  # Mat rectKernel = Imgproc.getStructuringElement (Imgproc.MORPH_RECT, new Size (21,21));

dilatedMat = cv2.dilate(blurredMat, rectKernel)  # Imgproc.dilate (blurredMat, dilatedMat, rectKernel, new Point (0,0), 1);
cannyMat = cv2.Canny(dilatedMat, 100, 200.3)  # Imgproc.Canny (dilatedMat, cannyMat, 100,200.3);

# List <MatOfPoint> contours = new ArrayList <MatOfPoint> ();
# final Mat hierarchy = new Mat ();
#contours, hierarchy = cv2.findContours(cannyMat, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

# Find contours over dilatedMat (the Canny operator creates gaps in the external contour).
contours, hierarchy = cv2.findContours(dilatedMat, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # Imgproc.findContours(dilatedMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);


# Use cv2.RETR_EXTERNAL instead of cv2.RETR_TREE
#contours, hierarchy = cv2.findContours(cannyMat, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

cntImg = srcMat.copy()
# Mark best_cnt with green line - used for testing
cv2.drawContours(cntImg, contours, -1, (0, 255, 0), 20)

# for(MatOfPoint cnt : contours) {
for cnt  in contours:
    area = cv2.contourArea(cnt)

    # Exclude small contours (noise)
    if area > 10000:
        mop2f = cv2.approxPolyDP(cnt, 0.02 * cv2.arcLength(cnt, True), True)  # Imgproc.approxPolyDP(mop2f, mop2f, 0.02*Imgproc.arcLength(mop2f, true), true);
        rr = cv2.minAreaRect(mop2f) #  RotatedRect rr = Imgproc.minAreaRect(mop2f);
        m = cv2.boxPoints(rr) # Imgproc.boxPoints(rr, m);  

        # Point[] rectPoints = new Point[4];
        # rr.points(rectPoints);
        rectPoints = np.int0(m)  # Convert all coordinates floating point values to int

        # for (int j = 0; j < 4; ++j) {
        #     Imgproc.line(srcMat, rectPoints[j], rectPoints[(j + 1) % 4], new Scalar(0,255,0), 20); }    
        #for j in range(4):
        #    cv2.line(srcMat, tuple(rectPoints[j]), tuple(rectPoints[(j + 1) % 4]), (0, 255, 0), 20)  # Imgproc.line(srcMat, rectPoints[j], rectPoints[(j + 1) % 4], new Scalar(0,255,0), 20);

        # Deaw the rectangles using drawContours instead of drawing lines
        # https://stackoverflow.com/questions/18207181/opencv-python-draw-minarearect-rotatedrect-not-implemented
        cv2.drawContours(srcMat, [rectPoints], 0, (0, 255, 0), 20)

        boundingRect = cv2.boundingRect(cnt)  # Rect boundingRect = Imgproc.boundingRect(cnt);
        cv2.rectangle(srcMat, boundingRect, (0, 0, 255), 20)  # Imgproc.rectangle(srcMat, boundingRect, new Scalar(0,0,255),20); //scalar not is RGB but BGR !

简化执行(建议):

  • 使用Imgproc.THRESH_OTSU进行自动阈值选择。
  • 没有必要应用GaussianBlur
  • 使用关闭而不是扩张。
  • 没有必要应用Canny

建议的JAVA代码:

代码语言:javascript
运行
复制
package myproject; //package it.neo7bf;

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

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

//import nu.pattern.OpenCV;

public class SeparationTest3 {
    
    static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }
    
    static class I {
        public String name;
        public int v;
        I(String name, int v) {
            this.name = name;
            this.v = v;
        }
    }
    
    public static void cannyTest() {

        List<I> images = List.of(
            new I("2022-04-16_085329",3)
        );

        for(I image : images) {

            Mat srcMat = Imgcodecs.imread("C:\\ProgettoScontrino\\scontrini\\campioni-test\\test-separazione\\"+image.name+".jpg");
            
            Mat grayMat = new Mat();
            //Mat blurredMat = new Mat();
            Mat dilatedMat = new Mat();
            //Mat cannyMat = new Mat();
            

            Imgproc.resize(srcMat, srcMat, new Size(0,0), 0.5, 0.5, Imgproc.INTER_AREA);
            Imgproc.cvtColor(srcMat, grayMat, Imgproc.COLOR_BGR2GRAY);
            //Imgproc.threshold(grayMat, grayMat, 177, 200, Imgproc.THRESH_BINARY);
            Imgproc.threshold(grayMat, grayMat, 0, 255, Imgproc.THRESH_OTSU);  //Use automatic threshold
            
            //There is no need to blur the image after threshold
            //Imgproc.GaussianBlur(grayMat, blurredMat, new Size(21,21),0, 0,Core.BORDER_DEFAULT); //3,3, 9,9 15,15,....
                
            Mat rectKernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(21,21));
            
            //Imgproc.dilate(blurredMat, dilatedMat, rectKernel, new Point(0,0),1);
            Imgproc.morphologyEx(grayMat, dilatedMat, Imgproc.MORPH_CLOSE, rectKernel);  // Use closing instead of dilate
            //Imgproc.Canny(dilatedMat,cannyMat,100,200,3); //No need for Canny
            
            List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
            final Mat hierarchy = new Mat();
    
            //Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
            
            Imgproc.findContours(dilatedMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
            
            //contours = getMaxContours(contours,image.v);
            
            for(MatOfPoint cnt : contours) {
                double area = Imgproc.contourArea(cnt);
                if (area > 10000) //Ignore small contours
                {
                    MatOfPoint2f mop2f = new MatOfPoint2f(cnt.toArray());
                    Imgproc.approxPolyDP(mop2f, mop2f, 0.02*Imgproc.arcLength(mop2f, true), true);
                    RotatedRect rr = Imgproc.minAreaRect(mop2f);
                    MatOfPoint m = new MatOfPoint();                    
                    Imgproc.boxPoints(rr, m);
                    Point[] rectPoints = new Point[4];          
                    rr.points(rectPoints);
                    for (int j = 0; j < 4; ++j) {
                      Imgproc.line(srcMat, rectPoints[j], rectPoints[(j + 1) % 4], new Scalar(0,255,0), 20); 
                    }

                    //BoundingBox
                    Rect boundingRect = Imgproc.boundingRect(cnt);
                    Imgproc.rectangle(srcMat, boundingRect, new Scalar(0,0,255),20); //scalar not is RGB but BGR !
                }
            }
                    
            //C:\ProgettoScontrino\scontrini\campioni-test\test-separazione\output\
            Imgcodecs.imwrite("C:\\ProgettoScontrino\\scontrini\\campioni-test\\test-separazione\\output\\"+image.name+"gray.jpg", grayMat);
            //Imgcodecs.imwrite("C:\\ProgettoScontrino\\scontrini\\campioni-test\\test-separazione\\output\\"+image.name+"blurred.jpg", blurredMat);
            Imgcodecs.imwrite("C:\\ProgettoScontrino\\scontrini\\campioni-test\\test-separazione\\output\\"+image.name+"dilated.jpg", dilatedMat);
            //Imgcodecs.imwrite("C:\\ProgettoScontrino\\scontrini\\campioni-test\\test-separazione\\output\\"+image.name+"canny.jpg", cannyMat);
            Imgcodecs.imwrite("C:\\ProgettoScontrino\\scontrini\\campioni-test\\test-separazione\\output\\"+image.name+"contours.jpg", srcMat);
        }
    }
    
    public static void main(String[] args) {
        cannyTest();
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71893073

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档