【问题标题】:Removing transparent watermark from an image - python从图像中删除透明水印 - python
【发布时间】:2021-06-20 20:07:21
【问题描述】:

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

这是我的示例图片:

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

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

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

这是我尝试做的蒙版,我将饱和度调低为黑白,并创建了一个图像“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))

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

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

【问题讨论】:

  • 除非图像属于您,否则您为什么要做属于“非法”领域的事情。正是出于这个原因才进行了水印,因此没有人可以窃取它们。
  • @KnightForked Well。我明白你的意思。但我只是想在特定图像上使用此代码。也不供公众使用。

标签: python image opencv transparency watermark


【解决方案1】:

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

  1. 尝试分割图像中的水印文本
  2. 过滤分割掩码并尝试使二进制掩码尽可能干净
  3. 使用文本掩码in-paint使用输入图像作为参考的违规区域

现在,正如您已经看到的,棘手的部分是分割文本。在尝试了一些技术和色彩空间后,我发现CMYK color space - 特别是 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) 

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

你看到输入上较暗的像素在这里几乎是白色的吗?很好,它似乎在文本和其他所有内容之间得到了明确的分离。很遗憾,我们在右侧有一些令人讨厌的眩光。无论如何,转换涉及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)

这是调整后的图像:

似乎文字和眩光之间的距离有点大。好的,让我们在这个 bad boy 上应用 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)

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

是的,有很多噪音。以下是我提出的获得更清洁面具的建议:有一些明显的斑点比文本更大。同样,还有其他比文本的 blob。让我们找到大斑点和小斑点并减去它们。如果我们正确设置参数,生成的图像应该包含文本。让我们看看:

# 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)

这里我使用了一个名为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)

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

最后,让我们应用内画:

# 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)

这是最终结果:

好吧,考虑到输入图像,还不错。您可以尝试调整一些值来进一步改善结果,但现实生活中,老兄,输入图像一开始并不是那么好。这是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

【讨论】:

  • 问题是我的输出是:i.stack.imgur.com/j2EXi.jpg,一些字符被删除了一些部分,但仍然无法正常工作
  • 非常感谢!我做到了。对于未来的读者,我成功的方法是用 numpy 切片从图像中裁剪出水印,然后设置一个非常高的最小 blob 值,然后显然代码会删除水印!之后我使用PIL.Image.paste 将裁剪后的图像粘贴回原始图像!哪个有效!非常感谢!
  • @U11-转发啊!由于您的输入比原始图像更大更清晰,因此必须修改一些阈值,例如两个区域过滤器的minArea。无论如何,很高兴我能帮上忙!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多