如何在OpenCV中提取具有特定颜色的图像片段?

时间:2018-08-20 09:38:44

标签: python numpy opencv image-processing

我使用徽标和其他简单的图形,其中没有渐变或复杂的图案。我的任务是从徽标段中提取字母和其他元素。

enter image description here

为此,我先定义背景色,然后遍历图片以对图像进行分割。这是我的代码,用于更多理解:

MAXIMUM_COLOR_TRANSITION_DELTA = 100  # 0 - 765


def expand_segment_recursive(image, unexplored_foreground, segment, point, color):
    height, width, _ = image.shape
    # Unpack coordinates from point
    py, px = point

    # Create list of pixels to check
    neighbourhood_pixels = [(py, px + 1), (py, px - 1), (py + 1, px), (py - 1, px)]

    allowed_zone = unexplored_foreground & np.invert(segment)

    for y, x in neighbourhood_pixels:
        # Add pixel to segment if its coordinates within the image shape and its color differs from segment color no
        # more than MAXIMUM_COLOR_TRANSITION_DELTA
        if y in range(height) and x in range(width) and allowed_zone[y, x]:
            color_delta = np.sum(np.abs(image[y, x].astype(np.int) - color.astype(np.int)))
            print(color_delta)
            if color_delta <= MAXIMUM_COLOR_TRANSITION_DELTA:
                segment[y, x] = True
                segment = expand_segment_recursive(image, unexplored_foreground, segment, (y, x), color)
                allowed_zone = unexplored_foreground & np.invert(segment)

    return segment


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Pass image as the argument to use the tool")
        exit(-1)

    IMAGE_FILENAME = sys.argv[1]
    print(IMAGE_FILENAME)

    image = cv.imread(IMAGE_FILENAME)
    height, width, _ = image.shape

    # To filter the background I use median value of the image, as background in most cases takes > 50% of image area.
    background_color = np.median(image, axis=(0, 1))
    print("Background color: ", background_color)

    # Create foreground mask to find segments in it (TODO: Optimize this part)
    foreground = np.zeros(shape=(height, width, 1), dtype=np.bool)
    for y in range(height):
        for x in range(width):
            if not np.array_equal(image[y, x], background_color):
                foreground[y, x] = True

    unexplored_foreground = foreground

    for y in range(height):
        for x in range(width):
            if unexplored_foreground[y, x]:
                segment = np.zeros(foreground.shape, foreground.dtype)
                segment[y, x] = True
                segment = expand_segment_recursive(image, unexplored_foreground, segment, (y, x), image[y, x])

                cv.imshow("segment", segment.astype(np.uint8) * 255)

                while cv.waitKey(0) != 27:
                    continue

这是所需的结果: enter image description here

在运行时结束时,我希望提取13个分离的段(针对此特定图像)。但是相反,我得到了RecursionError:超过了最大递归深度,这并不奇怪,因为可以为图像的每个像素调用expand_segment_recursive()。而且,即使使用600x500的小图像分辨率,我最多也可以获得30万次通话。

我的问题是在这种情况下如何摆脱递归并可能使用Numpy或OpenCV算法优化算法?

1 个答案:

答案 0 :(得分:0)

实际上,您可以分两步使用thresholded图像(二进制)和connectedComponents来完成此工作。另外,您可以使用findContours或其他方法。

代码如下:

import numpy as np
import cv2

# load image as greyscale
img = cv2.imread("hp.png", 0)

# puts 0 to the white (background) and 255 in other places (greyscale value < 250)
_, thresholded = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY_INV)

# gets the labels and the amount of labels, label 0 is the background
amount, labels = cv2.connectedComponents(thresholded)

# lets draw it for visualization purposes
preview = np.zeros((img.shape[0], img.shape[2], 3), dtype=np.uint8)

print (amount) #should be 3 -> two components + background

# draw label 1 blue and label 2 green
preview[labels == 1] = (255, 0, 0)
preview[labels == 2] = (0, 255, 0)

cv2.imshow("frame", preview)
cv2.waitKey(0)

最后,阈值图像将如下所示:

enter image description here

,预览图像(带有彩色分段的图像)将如下所示:

enter image description here

使用蒙版,您始终可以使用numpy函数获取所需的片段坐标或为片段着色(就像我在预览中所做的一样)

更新

要获得不同颜色的线段,您可以尝试在线段之间创建“边框”。由于它们是纯色而不是渐变,因此您可以尝试像canny这样的边缘检测器,然后将其放在图像中为黑色。...

import numpy as np
import cv2

img = cv2.imread("total.png", 0)

# background to black
img[img>=200] = 0
# get edges
canny = cv2.Canny(img, 60, 180)
# make them thicker
kernel = np.ones((3,3),np.uint8)
canny = cv2.morphologyEx(canny, cv2.MORPH_DILATE, kernel)
# apply edges as border in the image
img[canny==255] = 0

# same as before
amount, labels = cv2.connectedComponents(img)
preview = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
print (amount) #should be 14 -> 13 components + background

# color them randomly
for i in range(1, amount):
  preview[labels == i] = np.random.randint(0,255, size=3, dtype=np.uint8)

cv2.imshow("frame", preview )
cv2.waitKey(0)

结果是:

enter image description here