我想根据图像形状绘制轮廓蒙版(形状)。我正在使用opencv-python 版本 4.9.0.80

所以当我们有这样的图像时。

我们想要这样的输出,里面不带图像。避免灰色背景(添加它是为了使基于图像形状的轮廓蒙版能够正确可见)。

我已经尝试过cv.convexHull(),但结果并没有达到预期

我正在尝试这段代码。

import cv2 as cv
import numpy as np

path = '/opt/boundary-remover/SW_test2-01.png'
# path = '/opt/boundary-remover/scaled_2x.png'
# Load image
src = cv.imread(cv.samples.findFile(path))
cv.imwrite('1_original.png', src)

# Convert image to gray and blur it
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
cv.imwrite('2_gray.png', src_gray)

# Detect edges using Canny
canny_output = cv.Canny(src_gray, 100, 200)
cv.imwrite('3_canny.png', canny_output)

# Find contours
contours, _ = cv.findContours(canny_output, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)

# Combine all contours into a single contour
all_contours = np.concatenate(contours)

# Find the convex hull object for the combined contour
hull = cv.convexHull(all_contours)

# Draw the combined contour and its convex hull
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
color = (0, 255, 0)  # Green color
# cv.drawContours(drawing, [all_contours], 0, color)
# cv.imwrite('4_all_contours.png', drawing)
cv.drawContours(drawing, [hull], 0, color)
cv.imwrite('5_hull.png', drawing)

cv.drawContours(drawing, [hull], -1, color, thickness=cv.FILLED)
cv.imwrite('7_filled.png', drawing)

2

  • 获取所有非灰色像素,您将获得所需的蒙版


    – 

  • 其实你的出发点是什么,你的愿望是什么?从图像中删除灰色并获取徽标的像素?


    – 


3 个回答
3

如果我理解正确的话,你想从白色背景中删除符号吗?灰色背景图像是您想要的示例吗?我可以帮助掩蔽部分。您可以尝试图像膨胀。您可以在各个步骤之后绘制图像,以直观地了解正在发生的情况。基本上,我对图像进行了二值化,并通过形态学开口去除了小的椒盐噪声。然后,用圆形核对图像进行膨胀。这种膨胀将图像“涂抹”成您想要的图案。然后,我填充放大图像中的空白。这可能不是您的通用解决方案,但它确实适用于提供的两种情况。

import cv2 as cv
import numpy as np


def get_circular_kernel(diameter):
    """
    Author: F.Wessels
    https://stackoverflow.com/a/70886011/15279460
    """
    mid = (diameter - 1) / 2
    distances = np.indices((diameter, diameter)) - np.array([mid, mid])[:,    None, None]
    kernel = ((np.linalg.norm(distances, axis=0) - mid) <= 0).astype(int)
    return kernel


src = cv.imread(path)

cv.imshow('Original', src)

# Convert image to gray and then binarize
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
th, im_th = cv.threshold(src_gray, 240, 255, cv.THRESH_BINARY)

# remove small objects to 'clean' image
src_gray_c = cv.morphologyEx(~im_th, cv.MORPH_OPEN, (3,3))

# dilate image with a circular kernel
dilated_image = cv.dilate(src_gray_c, kernel=get_circular_kernel(20).astype(np.uint8), iterations=1)

# Author: Abid Rahman K
# https://stackoverflow.com/a/10317883/15279460
des = dilated_image.copy()
contour,hier = cv.findContours(des,cv.RETR_CCOMP,cv.CHAIN_APPROX_SIMPLE)
for cnt in contour:
    cv.drawContours(des,[cnt],0,255,-1)
final = des.copy()

cv.imshow('Final', final)
cv.waitKey(0)
cv.destroyAllWindows()

图1


图2

1

  • 感谢你的回答。我尝试过这种方法,但方式不同。问题是我们传递的直径为 20 get_circular_kernel(20)。基本上,我们正在填补空白。它在这里工作,但如果我们传递高尺寸(2x 高度和宽度)的相同图像,它将不起作用。即使我们能够根据图像形状动态计算直径值,如果我们的图像是圆形(O),它也不起作用,因为间隙太大。


    – 

以下是 Python/OpenCV 方法的概述。不幸的是,我的 OpenCV 目前无法运行,所以我在 Imagemagick 中进行了处理。

read image
convert to gray
threshold on white to make a mask and invert so white is black and the rest is white
get the convex hull of the mask
get the contour of the convex hull
draw a white filled contour on a copy of the input (or a black background) for the convex hull
dilate the previous result (filled convex hull) about 10-20 pixels with a circular kernel. This will round the corners.
Put the dilated image into the alpha channel of the input

图像魔术代码:

Convert white to transparent and extract the alpha channel as a mask
Get the convex hull about the alpha channel and draw it filled with white over the input image.
Apply a circular morphology dilate kernel with size of 10 to the filled convex hull image
Add the result as the alpha channel to the image

magick image1.jpg -fuzz 15% -transparent white -alpha extract x.png
magick x.png -set option:hull "%[convex-hull]" -fill white -draw "polygon %[hull]" x2.png
magick x2.png -morphology dilate disk:10 x3.png
magick image1.jpg x3.png -compose copy_opacity -composite x4.png

阈值图像作为掩模

填充凸包

形态扩张凸包

结果 – 将膨胀的填充凸包放入输入的 Alpha 通道中

添加

这是第二张图像的结果。

阈值图像作为掩模

填充凸包

形态扩张凸包

结果 – 将膨胀的填充凸包放入输入的 Alpha 通道中

下载后看到有透明背景

附加2

如果删除凸包步骤,您将得到一个更好地遵循字母形状的白色边框。

  • 临界点
  • 扩张阈值
  • 将扩张的阈值图像放入输入的 Alpha 通道中

对于 Image1——Imagemagick 代码

magick image1.jpg -fuzz 15% -transparent white -alpha extract z.png
magick z.png -morphology dilate disk:10 z2.png
magick image1.jpg z2.png -compose copy_opacity -composite z3.png

临界点:

膨胀:

结果1:

对于图像2:

临界点:

膨胀:

结果2:

7

  • 凸包的问题是它没有圆形边缘,并且不适用于文本图像(查看第二个 Betset 文本图像;这是我分享的最终结果)。


    – 

  • 您忽略了这样一个事实:带有圆形内核的 10-20 像素的扩张会使角落变圆。我已经更正了我的图像以正确显示这一点。


    – 

  • @NiRmaL 请参阅我上面的评论以及我上面答案中的更改。


    – 

  • 感谢您提供更清晰的信息,但在预期的输出中,我们需要一个基于图像形状的掩模。例如,如果应用 10 像素的内边距,则距所有外边缘的距离应为 10 像素。请检查我共享的预期输出中带有文本“Betset”的图像。凸包提供类似框架的输出,因此在“Betset”中,我们在 B_t__t 附近得到更多间隙(e_se 形成 Betset)。


    – 


  • @NiRmaL 看看我的答案、那个例子和代码。我将图像处理到阈值以获得蒙版并将其放入图像的 Alpha 通道中。您必须选择正确的阈值。如果使用 OpenCV,上限范围为纯白色,下限范围低于纯白色约 15%,请尝试使用 cv2.inRange。如果您仍然得到其他结果,请显示您的代码和阈值结果。


    – 

如果我根据您在这里所说的话正确理解您的问题:避免灰色背景,那么这是一种方法:

def replaceGrayWithWhite(im):
    imGray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    mask = (imGray>250).astype(np.uint8)
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    maskFilled = cv2.drawContours(mask.copy(), contours, -1, 1, -1)
    maskFilledInv = cv2.bitwise_not(maskFilled) - 254
    '''
    Took this from here: https://stackoverflow.com/a/38493075/16815358
    '''
    foreground = cv2.bitwise_or(im, im, mask=maskFilled)
    background = 255*np.ones_like(im)
    background = cv2.bitwise_and(background, background, mask=maskFilledInv)
    final = cv2.bitwise_or(foreground, background)
    return final

导入的是 numpy 和 opencv,结果如下:

4

  • 不,我添加的灰色背景是为了使基于图像形状的轮廓蒙版能够正确可见。我已经更新了描述。


    – 

  • 所以你想从白色背景图像变成灰色背景图像吗?


    – 

  • 如果是这样,greenerpasture的答案应该适合你想要的


    – 

  • 谢谢。 Greenerpasture 的回答方法存在一些问题。我为此添加了评论。


    –