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

2024-10-02 20:41:13 发布

您现在位置:Python中文网/ 问答频道 /正文

更新

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

GitHub repository with sources

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

原始问题

我尝试使用OpenCV 4.x.x查找黑板的边缘(下图),但不知何故,我无法成功。目前我的代码是这样的:(带有OpenCV和live camera feed的Android),其中imgMat是camera feed中的一个垫子:

    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, kernel, new Point(0, 0), 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

我做错了什么?我不能经常检测黑板的边缘

关于黑板的其他信息:

  • 始终为矩形
  • 可能有不同的照明
  • 应忽略文本,仅检测主板
  • 外部黑板也应该被忽略
  • 仅显示/返回主板的轮廓

谢谢你的建议或代码


Tags: 代码newkernel黑板matmorphgraycanny
1条回答
网友
1楼 · 发布于 2024-10-02 20:41:13

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

我对每一个都做了三向检查,并选择了像素最多的遮罩(我假设电路板是图像的大部分)。我不确定这将如何在其他图像上工作,因为我们这里只有一个,所以这可能适用于或不适用于其他板

我使用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, 0, light_margin);
wcount = np.count_nonzero(wmask);

# black
bmask = cv2.inRange(v, 0, 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, kernel, iterations = 4);
mask = cv2.dilate(mask, kernel, 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, epsilon, True);
    simplified.append(approx);
cv2.drawContours(img, simplified, -1, (0,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);

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

还有其他方法可以尝试选择董事会,但很难找到一个全面解决方案。如果你能想出一些屏蔽电路板的方法,代码的其余部分也应该做同样的工作。如果你愿意改变未知颜色的假设,并提供失败案例的原始图片,那么我可能会想出一个合适的面具

相关问题 更多 >