我想提出一种不同的方法。我的基本思路是将图像从BGR转换为
LAB颜色空间,并确定是否可以分离出蓝色区域。这可以通过专注于LAB中的b分量来完成,因为它代表从黄色到蓝色的颜色。
代码
img = cv2.imread('image_path', cv2.IMREAD_UNCHANGED)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
b_component = lab[:,:,2]
注意:蓝色区域实际上相当暗,因此可以轻松地隔离出来。
![enter image description here](https://istack.dev59.com/oxokj.webp)
th = cv2.threshold(b_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
但是在应用阈值后,图像中包含一些不想考虑的数字文本区域周围的不必要的白色像素。
![enter image description here](https://istack.dev59.com/0y3Be.webp)
为了避免不需要的区域,我尝试了以下方法:
- 查找面积大于某个值的轮廓,并将它们绘制在2通道掩模上
- 对每个轮廓的矩形边界框区域进行掩蔽
- 定位该边界框区域内阈值图像上值为255(白色)的像素
- 将这些像素值在原始PNG图像上改变为白色。
代码如下:
contours = cv2.findContours(th, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
black = np.zeros((img.shape[0], img.shape[1]), np.uint8)
res = img.copy()
for c in contours:
area = cv2.contourArea(c)
if int(area) > 200:
cv2.drawContours(black, [c], 0, 255, -1)
如果您看到以下掩码,则已将轮廓内的所有像素都用白色包围起来。但是,单词“bar”内的像素不应被考虑在内。
![enter image description here](https://istack.dev59.com/vkrFx.webp)
只隔离蓝色像素区域,我们使用阈值图像“AND”运算。
mask = cv2.bitwise_and(th, th, mask = black)
![enter image description here](https://istack.dev59.com/4AnXs.webp)
我们得到了实际想要的口罩。在mask中是白色的区域,在原始图像的副本res中也被变成了白色。
res[mask == 255] = (255, 255, 255, 255)
![enter image description here](https://istack.dev59.com/bw9ny.webp)
但上述图像并不完美。单词“foo”的边缘周围仍有一些区域可见。
接下来,我们会膨胀“mask”并重复操作。
res = img.copy()
kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
dilate = cv2.dilate(mask, kernel_ellipse, iterations=1)
res[dilate == 255] = (255, 255, 255, 255)
![enter image description here](https://istack.dev59.com/VPqso.webp)
注意:使用LAB颜色空间的A和B分量,您可以轻松地分离不同的颜色,而无需花费时间寻找范围。还可以分割具有相邻阴影和饱和度的颜色。
cv.inRange
? - Christoph Rackwitz