【问题标题】:Find contours of rectangular objects touching each other查找相互接触的矩形对象的轮廓
【发布时间】:2021-06-08 08:40:34
【问题描述】:

这是我正在使用的灰度 uint8 图像:source grayscale image。 该图像是将 6 个不同的彩色深度图像拼接成一个的结果。图像中有 3 个矩形对象,我的目标是找到这些对象的边缘。显然,我可以毫无问题地找到对象的外部边缘。但是,将对象彼此分开是一件很痛苦的事情。

图像中所需的矩形: 输入图像为 numpy 数组:https://drive.google.com/file/d/1uN9R4MgVQBzjJuMhcqWMUAhWDJCatHSf/view?usp=sharing

  • 首先我试图对图像进行阈值二值化,然后是一些 腐蚀+膨胀处理以区分所有三个对象 彼此。然后轮廓 + minAreaRect 会给我必要的 结果。这个选项不够健壮,因为场景中的对象 可以彼此如此接近,以至于它们之间的边缘具有相同的 深度作为物体表面的粗糙度。所以重要的边缘可以 与物体表面偏差“混合”。因此,有时, 我将两个对象合并为一个对象。
  • 使用带有自动计算系数的精明边缘检测 (来自图片中值)捕捉所有不必要的亮度变化和边缘。手动调整系数的 Canny 效果更好,但它不会给出封闭边缘结果 + 它不可靠(每次都必须手动调整)。
  • 我尝试的另一件事 - 非线性调整图像的亮度(幂律变换) - 以增加物体表面的亮度,使暗边空腔保持不变。

p = 0.2; c = (input_image.max()) / (input_image.max()**(p)); output_image = (c*blur_gray.astype(np.float)**(p)).astype(np.uint8)

这是一个结果:brightness adjusted image。 该图像的阈值二值化在边缘方面给出了更好的结果。我尝试了精明和拉普拉斯边缘检测,但得到的结果给出了轮廓的断开部分,在物体表面区域有一些噪声:binarized result of Laplacian filtering。在我看来,下一步必须是某种边缘估计/恢复算法。我尝试了霍夫变换来获得边缘线,但它没有给出任何可理解的结果。


在我看来,我只是在兜圈子,没有得到任何可理解的结果。所以我请求帮助。可能我的方法从根本上是错误的,或者由于我没有足够的知识而遗漏了一些东西。有什么想法或建议吗?

附:发完这个,我会继续,会尝试实现wateshed分割算法,可能会奏效。

【问题讨论】:

  • 你能发布一张带有预期的物体边界框的图片吗?我不确定图像中应该有多少不同的对象。
  • @IanChu 更新问题

标签: python opencv


【解决方案1】:

我试图想出一种方法来强调分隔形状的垂直线和水平线。

我首先对原始图像(来自 numpy)进行阈值处理,然后只使用了一个看起来合理的 [0, 10] 范围。

我在图像上运行了一个垂直和水平线内核以生成两个蒙版

垂直内核

水平内核

我将两个掩码组合在一起,这样我们就可以将两条线分开

现在我们可以使用 findContours 来查找框了。我过滤掉小轮廓,只得到 3 个矩形,并使用 4 边近似来尝试只得到它们的边。

import cv2
import numpy as np
import random

# approx n-sided shape
def approxSides(contour, numSides, step_size):
    # approx until numSides points
    num_points = 999999;
    percent = step_size;
    while num_points >= numSides:
        # get number of points
        epsilon = percent * cv2.arcLength(contour, True);
        approx = cv2.approxPolyDP(contour, epsilon, True);
        num_points = len(approx);

        # increment
        percent += step_size;

    # step back and get the points
    # there could be more than numSides points if our step size misses it
    percent -= step_size * 2;
    epsilon = percent * cv2.arcLength(contour, True);
    approx = cv2.approxPolyDP(contour, epsilon, True);
    return approx;

# convolve
def conv(mask, kernel, size, half):
    # get res
    h,w = mask.shape[:2];

    # loop
    nmask = np.zeros_like(mask);
    for y in range(half, h - half):
        print("Y: " + str(y) + " || " + str(h));
        for x in range(half, w - half):
            total = np.sum(np.multiply(mask[y-half:y+half+1, x-half:x+half+1], kernel));
            total /= 255;
            if total > half:
                nmask[y][x] = 255;
            else:
                nmask[y][x] = 0;
    return nmask;

# load numpy array
img = np.load("output_data.npy");
mask = cv2.inRange(img, 0, 10);

# resize
h,w = mask.shape[:2];
scale = 0.25;
h = int(h*scale);
w = int(w*scale);
mask = cv2.resize(mask, (w,h));

# use a line filter
size = 31; # size / 2 is max bridge size
half = int(size/2);
vKernel = np.zeros((size,size), np.float32);
for a in range(size):
    vKernel[a][half] = 1/size;
hKernel = np.zeros((size,size), np.float32);
for a in range(size):
    hKernel[half][a] = 1/size;

# run filters
vmask = cv2.filter2D(mask, -1, vKernel);
vmask = cv2.inRange(vmask, (half * 255 / size), 255);
hmask = cv2.filter2D(mask, -1, hKernel);
hmask = cv2.inRange(hmask, (half * 255 / size), 255);
combined = cv2.bitwise_or(vmask, hmask);

# contours OpenCV3.4, if you're using OpenCV 2 or 4, it returns (contours, _)
combined = cv2.bitwise_not(combined);
_, contours, _ = cv2.findContours(combined, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

# filter out small contours
cutoff_size = 1000;
big_cons = [];
for con in contours:
    area = cv2.contourArea(con);
    if area > cutoff_size:
        big_cons.append(con);

# do approx for 4-sided shape
colored = cv2.cvtColor(combined, cv2.COLOR_GRAY2BGR);
four_sides = [];
for con in big_cons:
    approx = approxSides(con, 4, 0.01);
    color = [random.randint(0,255) for a in range(3)];
    cv2.drawContours(colored, [approx], -1, color, 2);
    four_sides.append(approx); # not used for anything

# show
cv2.imshow("Image", img);
cv2.imshow("mask", mask);
cv2.imshow("vmask", vmask);
cv2.imshow("hmask", hmask);
cv2.imshow("combined", combined);
cv2.imshow("Color", colored);
cv2.waitKey(0);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 2021-09-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-24
    • 1970-01-01
    相关资源
    最近更新 更多