从图像中删除透明水印 - python

U10*_*ard 1 python transparency opencv watermark image

我正在尝试从图像中删除透明水印。

这是我的示例图片:

我想从图像中删除文本“水印”。如您所见,文本是透明的。所以我想将该文本替换为原始背景。

像这样的东西将是我想要的输出:

我尝试了一些例子(我目前正在使用cv2,如果其他库可以解决问题也请推荐),但它们都没有成功。我知道要走的路是有一个面具(就像在这篇文章中),但他们都已经有蒙版图像,但我没有。

这是我尝试做的蒙版,我将饱和度调低为黑白,并创建了一个图像“imagemask.jpg”,然后尝试使用循环遍历像素for

mask = cv2.imread('imagemask.jpg')
new = []
rows, cols, _ = mask.shape
for i in range(rows):
    new.append([])
    #print(i)
    for j in range(cols):
        k = img[i, j]
        #print(k)
        if all(x in range(110, 130) for x in k):
            new[-1].append((255, 255, 255))
        else:
            new[-1].append((0, 0, 0))

cv2.imwrite('finalmask.jpg', np.array(new))
Run Code Online (Sandbox Code Playgroud)

然后想使用遮罩的代码,但我意识到“finalmask.jpg”完全是一团糟......所以我没有尝试使用遮罩的代码。

这实际上可能吗?我已经尝试了大约3个小时,但没有运气......

sta*_*ine 7

这不是小事,我的朋友。雪上加霜的是,你的图像分辨率非常低,经过压缩,并且有令人讨厌的眩光 - 这根本无助于处理。请查看您的输入并相应地设定您的期望。话虽如此,让我们尝试用我们所拥有的来获得最好的结果。这些是我建议的步骤:

  1. 尝试从图像中分割水印文本
  2. 过滤分段掩码并尝试获得尽可能干净的二进制掩码
  3. 使用文本蒙版以输入图像作为参考来修复有问题的区域

现在,正如您已经看到的,棘手的部分是对文本进行分段。在尝试了一些技术和色彩空间后,我发现CMYK色彩空间- 特别是K 通道- 提供了有希望的结果。文字相当清晰,我们可以尝试一下Adaptive Thresholding,让我们看一下:

# Imports
import cv2
import numpy as np

# Read image
imagePath = "D://opencvImages//"
img = cv2.imread(imagePath+"0f5zZm.jpg")

# Store a deep copy for the inpaint operation:
originalImg = img.copy()

# Convert to float and divide by 255:
imgFloat = img.astype(np.float) / 255.

# Calculate channel K:
kChannel = 1 - np.max(imgFloat, axis=2) 
Run Code Online (Sandbox Code Playgroud)

OpenCV不直接提供BGR转换CMYK,因此我必须K使用转换公式手动获取频道。这非常简单。K(或Key )通道表示最低强度(黑色)的像素,颜色为白色。这意味着几乎白色的文本将呈现为黑色......这是K输入的通道:

您看到这里输入上较暗的像素几乎是白色了吗?这很好,文本和其他内容之间似乎得到了清晰的区分。遗憾的是我们的右侧有一些令人讨厌的眩光。无论如何,转换涉及到float运算,所以要小心data types。也许我们可以通过稍微调整亮度/对比度来改善此图像。只是一点点,我只是想将更多的文字与令人讨厌的眩光分开:

# Apply a contrast/brightness adjustment on Channel K:
alpha = 0
beta = 1.2
adjustedK = cv2.normalize(kChannel, None, alpha, beta, cv2.NORM_MINMAX, cv2.CV_32F)

# Convert back to uint 8:
adjustedK = (255*adjustedK).astype(np.uint8)
Run Code Online (Sandbox Code Playgroud)

这是调整后的图像:

文字和眩光之间的分离似乎有点多。好吧,让我们Adaptive Thresholding对这个坏男孩应用一个来获得初始分割掩码:

# Adaptive Thresholding on adjusted Channel K:
windowSize = 21
windowConstant = 11
binaryImg = cv2.adaptiveThreshold(adjustedK, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, windowSize, windowConstant)
Run Code Online (Sandbox Code Playgroud)

你看到我在这里使用了一个不太大的windowSize阈值吗?如果您愿意,可以随意调整这些参数。这是我得到的二值图像:

是的,有很多噪音。这是我建议使用更干净的蒙版的方法:有一些明显的斑点比文本大。同样,还有其他比文本小的斑点。让我们找到大斑点和小斑点并减去它们。如果我们正确设置参数,结果图像应该包含文本。让我们来看看:

# Get the biggest blobs on the image:
minArea = 180
bigBlobs = areaFilter(minArea, binaryImg)

# Filter the smallest blobs on the image:
minArea = 20
smallBlobs = areaFilter(minArea, binaryImg)

# Let's try to isolate the text:
textMask = smallBlobs - bigBlobs
cv2.imshow("Text Mask", textMask)
cv2.waitKey(0)
Run Code Online (Sandbox Code Playgroud)

在这里,我使用了一个名为 的辅助函数areaFilter。此函数返回图像中高于最小面积阈值的所有斑点。我将在答案末尾发布该功能。与此同时,看看这些很酷的图片:

大斑点:

过滤小斑点:

它们之间的区别:

遗憾的是,似乎字符的某些部分未能通过过滤操作。这是因为眩光和文本的交集太多,算法无法获得清晰的分离。可能有利于修复结果的是这个蒙版上的微妙模糊,以消除压缩别名。让我们涂抹一些Gaussian Blur来使面膜变得光滑一些:

# Blur the mask a little bit to get a
# smoother inpanting result:
kernelSize = (3, 3)
textMask = cv2.GaussianBlur(textMask, kernelSize, cv2.BORDER_DEFAULT)
Run Code Online (Sandbox Code Playgroud)

内核没那么大,我只是想要一个微妙的效果。这是结果:

最后,让我们应用修复:

# Apply the inpaint method:
inpaintRadius = 10
inpaintMethod = cv2.INPAINT_TELEA
result = cv2.inpaint(originalImg, textMask, inpaintRadius, inpaintMethod)
cv2.imshow("Inpaint Result", result)
cv2.waitKey(0)
Run Code Online (Sandbox Code Playgroud)

这是最终结果:

好吧,考虑到输入图像,还不错。您可以尝试调整一些值来进一步改善结果,但我的兄弟,现实生活是输入图像一开始就不是那么好。这是areaFilter函数:

def areaFilter(minArea, inputImage):

    # Perform an area filter on the binary blobs:
    componentsNumber, labeledImage, componentStats, componentCentroids = \
    cv2.connectedComponentsWithStats(inputImage, connectivity=4)

    # Get the indices/labels of the remaining components based on the area stat
    # (skip the background component at index 0)
    remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]

    # Filter the labeled pixels based on the remaining labels,
    # assign pixel intensity to 255 (uint8) for the remaining pixels
    filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')

    return filteredImage
Run Code Online (Sandbox Code Playgroud)