微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

OpenCV - 在视频和图像上找到黑板边缘

如何解决OpenCV - 在视频和图像上找到黑板边缘

更新

您可以在此处找到我在 GitHub 上用于测试的所有图像:

GitHub repository with sources

还有 2 个视频,检测也应该在其中进行

原始问题

我尝试使用 OpenCV 4.x.x 来查找黑板的边缘(下图),但不知何故我无法成功。我目前的代码如下所示:(带有 OpenCV 和实时摄像头源的 Android),其中 imgMat 是来自摄像头源的 Mat:

    Mat gray = new Mat();
    Imgproc.cvtColor(imgMat,gray,Imgproc.COLOR_RGB2BGR);

    Mat blurred = new Mat();
    Imgproc.blur(gray,blurred,new org.opencv.core.Size(3,3));

    Mat canny = new Mat();
    Imgproc.Canny(blurred,canny,80,230);

    Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new org.opencv.core.Size(2,2));
    Mat dilated = new Mat();
    Imgproc.morphologyEx(canny,dilated,Imgproc.MORPH_DILATE,kernel,new Point(0,0),10);
    Mat rectimage = new Mat();
    Imgproc.morphologyEx(dilated,rectimage,Imgproc.MORPH_CLOSE,5);
    Mat endproduct = new Mat();
    Imgproc.Canny(rectimage,endproduct,120,230);

    List<MatOfPoint> contours = new ArrayList<>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(endproduct,contours,hierarchy,Imgproc.RETR_LIST,Imgproc.CHAIN_APPROX_SIMPLE);

    double maxArea = 0;
    boolean hasContour = false;
    MatOfPoint2f biggestContour = new MatOfPoint2f();
    Iterator<MatOfPoint> each = contours.iterator();
    while (each.hasNext()) {
        MatOfPoint wrapper = each.next();
        double area = Imgproc.contourArea(wrapper);
        if (area > maxArea) {
            maxArea = area;
            biggestContour = new MatOfPoint2f(wrapper.toArray());
            hasContour = true;
        }
    }

    if (hasContour) {
        Mat output = imgMat.clone();

        MatOfPoint2f approx = new MatOfPoint2f();
        MatOfPoint poly = new MatOfPoint();

        Imgproc.approxpolyDP(biggestContour,approx,Imgproc.arcLength(biggestContour,true) * .02,true);
        approx.convertTo(poly,CvType.CV_32S);

        Rect rect = Imgproc.boundingRect(poly);

     }

不知何故,我无法让它工作,尽管相同的代码(用 python 编写)在我的计算机上运行并带有视频。我从矩形中获取输出并将其显示在我的移动屏幕上,它在那里闪烁很多并且无法正常工作。

这些是我在 python 程序上尝试过的图像,它们起作用了:

big blackboard

big blackboard2

我做错了什么?我无法不断检测黑板的边缘。

有关黑板的其他信息:

  • 总是矩形
  • 可能有不同的照明
  • 应该忽略文本,只检测主板
  • 外面的黑板也应该被忽略
  • 只应显示/返回主板的轮廓

感谢您的任何建议或代码

解决方法

我使用 HSV 是因为这是检测特定颜色的最简单方法。我使用了丰度测试来自动选择颜色阈值(因此这适用于绿色或蓝色板)。但是,此测试在白色或黑色板上会失败,因为根据色调,白色和黑色都算作所有颜色。相反,在 HSV 中,白色和黑色最容易被检测为非常低的饱和度(白色)或非常低的值(黑色)。

我对每个都进行了 3 向检查,并选择了其中像素最多的蒙版(我假设板是图像的主要部分)。我不确定这将如何在其他图像上工作,因为我们这里只有一个,所以这可能适用于其他主板,也可能不适用。

我使用 approxPolyDP 来减少轮廓中的点数,直到我有 4 个点并用它来绘制形状。

enter image description here

enter image description here

import cv2
import numpy as np

# get unique colors (to speed up search) and return the most abundant mask
def getAbundantColor(channel,margin):
    # get uniques
    unique_colors,counts = np.unique(channel,return_counts=True);

    # check for the most abundant color
    most = None;
    biggest_count = -1;
    for col in unique_colors:
        # count number of white pixels
        mask = cv2.inRange(channel,int(col - margin),int(col + margin));
        count = np.count_nonzero(mask);

        # if bigger,set new "most"
        if count > biggest_count:
            biggest_count = count;
            most = mask;
    return most,biggest_count;

# load image
img = cv2.imread("blackboard.jpg");

# it's huge,scale down so that we can see the whole thing
h,w = img.shape[:2];
scale = 0.25;
h = int(scale*h);
w = int(scale*w);
img = cv2.resize(img,(w,h));

# hsv
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV);
h,s,v = cv2.split(hsv);

# median blur to get rid of most of the text
h = cv2.medianBlur(h,5);
s = cv2.medianBlur(s,5);
v = cv2.medianBlur(v,5);

# get most abundant color
color_margin = 30;
hmask,hcount = getAbundantColor(h,color_margin);

# detect white and black separately
light_margin = 30;
# white
wmask = cv2.inRange(s,light_margin);
wcount = np.count_nonzero(wmask);

# black
bmask = cv2.inRange(v,light_margin);
bcount = np.count_nonzero(bmask);

# check which is biggest
sorter = [[hcount,hmask],[wcount,wmask],[bcount,bmask]];
sorter.sort();
mask = sorter[-1][1];

# dilate and erode to close holes
kernel = np.ones((3,3),np.uint8);
mask = cv2.dilate(mask,kernel,iterations = 2);
mask = cv2.erode(mask,iterations = 4);
mask = cv2.dilate(mask,iterations = 2);

# get contours # OpenCV 3.4,in OpenCV 2* or 4* it returns (contours,_)
_,contours,_ = cv2.findContours(mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE);

# for each contour,approximate a simpler shape until we have 4 points
simplified = [];
for con in contours:
    # go until we have 4 points
    num_points = 999999;
    step_size = 0.01;
    percent = step_size;
    while num_points >= 4:
        # get number of points
        epsilon = percent * cv2.arcLength(con,True);
        approx = cv2.approxPolyDP(con,epsilon,True);
        num_points = len(approx);

        # increment
        percent += step_size;

    # step back and get the points
    # there could be more than 4 points if our step size misses it
    percent -= step_size * 2;
    epsilon = percent * cv2.arcLength(con,True);
    approx = cv2.approxPolyDP(con,True);
    simplified.append(approx);
cv2.drawContours(img,simplified,-1,(0,200),2);

# print out the number of points
for points in simplified:
    print("Num Points: " + str(len(points)));

# show image
cv2.imshow("Image",img);
cv2.imshow("Hue",h);
cv2.imshow("Mask",mask);
cv2.waitKey(0);

编辑:为了适应电路板颜色和外观的不确定性,我假设电路板本身将是图片的主要部分。涉及分拣机的线正在寻找图像中最丰富的颜色。如果板子后面的白墙在图像中占据更多空间,那么这将是为遮罩选择的颜色。

还有其他方法可以尝试只选择棋盘,但是想出一个包罗万象的解决方案真的很困难。如果您能想出某种方法来屏蔽电路板,其余的代码应该做同样的工作。如果您愿意在未知颜色假设上让步并提供失败案例的原始图片,那么我可能会想出一个合适的掩码。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。