从具有 Python 的图像中删除特定颜色(具有颜色变化容差)的所有内容

Remove everything of a specific color (with a color variation tolerance) from an image with Python

我在 PNG 图像(白色背景)上有一些蓝色文本 #00a2e8 和一些黑色文本。

如何使用 Python PIL 或 OpenCV 去除图像上的所有蓝色(包括蓝色文本),并对颜色变化有一定的容忍度?

确实,文本的每个像素都不是完全相同的颜色,有变化,蓝色阴影。

这是我的想法:

在编码之前,是否有更标准的方法来使用 PIL 或 OpenCV Python?

示例 PNG file:应删除 foobar

我认为您正在寻找函数 inRange:

thresh = 5
bgr = [255 - thresh, thresh , thresh ]
minBGR = np.array([bgr[0] - thresh, bgr[1] - thresh, bgr[2] - thresh])
maxBGR = np.array([bgr[0] + thresh, bgr[1] + thresh, bgr[2] + thresh])
maskBGR = cv2.inRange(image, minBGR, maxBGR)
resultBGR = cv2.bitwise_or(image, maskBGR)

您的图片有一些问题。首先,它有一个完全多余的 alpha 通道,可以忽略不计。其次,你的蓝色周围的颜色与蓝色相去甚远!

我使用了您计划的方法,发现删除效果很差:

#!/usr/bin/env python3

import cv2
import numpy as np

# Load image
im = cv2.imread('nwP8M.png')

# Define lower and upper limits of our blue
BlueMin = np.array([90,  200, 200],np.uint8)
BlueMax = np.array([100, 255, 255],np.uint8)

# Go to HSV colourspace and get mask of blue pixels
HSV  = cv2.cvtColor(im,cv2.COLOR_BGR2HSV)
mask = cv2.inRange(HSV, BlueMin, BlueMax)

# Make all pixels in mask white
im[mask>0] = [255,255,255]
cv2.imwrite('DEBUG-plainMask.png', im)

这给出了这个:

如果你扩大范围,得到粗糙的边缘,你会开始影响绿色字母,所以我扩大了蒙版,使蓝色附近的像素 spatially白色以及接近蓝色的彩色像素:

# Try dilating (enlarging) mask with 3x3 structuring element
SE   = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
mask = cv2.dilate(mask, kernel, iterations=1)

# Make all pixels in mask white
im[mask>0] = [255,255,255]
cv2.imwrite('result.png', im)

这让你:

您可能希望用其他图像的实际值来欺骗,但原理是一样的。

我想用不同的方法插话。我的基本想法是将图像从 BGR 转换为 LAB color space 并弄清楚我是否可以将这些区域隔离为蓝色。这可以通过关注 LAB 的 b-component 来完成,因为它代表从黄色到蓝色的颜色。

代码

img = cv2.imread('image_path', cv2.IMREAD_UNCHANGED)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
b_component = lab[:,:,2]

(注:蓝色区域其实颜色比较深,容易隔离。)

th = cv2.threshold(b_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]

但是在应用阈值之后,图像在包含数字文本的区域周围包含一些不需要的白色像素,我们不想考虑这些。

为了避免不需要的区域,我尝试了以下方法:

  • 找到特定区域上方的轮廓并将它们中的每一个绘制在 2 通道蒙版上
  • 为每个轮廓屏蔽矩形边界框区域。
  • 在边界框区域内找到阈值图像上 255(白色)的像素
  • 将原始 PNG 图像上的这些像素值更改为白色。

在下面的代码中:

# finding contours
contours = cv2.findContours(th, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

# initialize a mask of image shape and make copy of original image
black = np.zeros((img.shape[0], img.shape[1]), np.uint8)
res = img.copy()

# draw only contours above certain area on the mask
for c in contours:
    area = cv2.contourArea(c)
    if int(area) > 200:
            cv2.drawContours(black, [c], 0, 255, -1)

如果您看到以下遮罩,它已将所有像素包围在白色轮廓内。然而,单词“bar”中的像素应该被考虑。

为了只隔离蓝色像素的区域,我们对阈值图像进行“与”运算th

mask = cv2.bitwise_and(th, th, mask = black)

我们得到了我们真正想要的面具。 mask 中的白色区域在原始图像的副本中变为白色 res:

res[mask == 255] = (255, 255, 255, 255)

但上图并不完美。在单词 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)

Note: Using the A and B components of LAB color space you can isolate different colors quite easily, without having to spend time searching for the range. Colors with nearby shading and saturation can also be segmented.