如何去除文档图像中的水印?

kak*_*kak 9 python opencv image image-processing computer-vision

我有以下图片

以及具有完全相同徽标的另一个变体

我试图去掉徽标本身,同时保留底层文本。使用以下代码段

import skimage.filters as filters
import cv2

image = cv2.imread('ingrained.jpeg')

gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
smooth1 = cv2.GaussianBlur(gray, (5,5), 0)
division1 = cv2.divide(gray, smooth1, scale=255)

sharpened = filters.unsharp_mask(division1, radius=3, amount=7, preserve_range=False)
sharpened = (255*sharpened).clip(0,255).astype(np.uint8)

# line segments
components, output, stats, centroids = cv2.connectedComponentsWithStats(sharpened, connectivity=8)
sizes = stats[1:, -1]; components = components - 1
size = 100
result = np.zeros((output.shape))
for i in range(0, components):
    if sizes[i] >= size:
        result[output == i + 1] = 255

cv2.imwrite('image-after.jpeg',result)
Run Code Online (Sandbox Code Playgroud)

我得到了这些结果

但如图所示,所得到的图像在水印轮廓的残留和字母被洗掉方面分别不一致。有没有更好的解决方案可以补充?理想的解决方案是删除水印边框而不影响其下方的文本。

nat*_*ncy 9

由于我们知道水印是粉红色的,因此我们可以使用两遍 HSV 颜色阈值方法。第一步是删除大部分水印,同时保持字母完整,第二步是过滤掉更多的粉红色。这是一个潜在的解决方案:

  1. 第一遍 HSV 颜色阈值。 加载图像,转换为HSV格式,然后为二值图像设置HSV颜色阈值

  2. 扩张以修复轮廓。因为任何类型的阈值处理都会导致字母被冲掉,所以我们需要通过膨胀来修复轮廓以重建一些字符。

  3. 第二遍 HSV 颜色阈值。现在,我们使用第一遍HSV 掩模对原始图像进行按位运算,以获得中间结果,但仍然存在粉红色伪影。为了删除它们,我们执行第二遍 HSV 阈值,通过生成新的掩码来删除字符周围的粉红色。

  4. 将图像转换为灰度然后删除粉红色轮廓。我们将第一个 HSV 颜色阈值的结果转换为灰色,然后将背景从黑色切换为白色。最后我们应用第二遍 HSV 掩模的结果来得到我们的最终结果。


输入图像->第一个 HSV 掩模 + 膨胀->按位与

请注意,背景粉红色消失了,但字母周围仍然存在粉红色伪影。所以现在我们为剩余的粉红色生成第二个蒙版。

第二个掩模->转换为灰度+反转->应用第二个掩模以获得结果

放大结果

在此输入图像描述

代码

import numpy as np
import cv2

# Load image, convert to HSV, then HSV color threshold
image = cv2.imread('1.jpg')
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 0])
upper = np.array([179, 255, 163])
mask = cv2.inRange(hsv, lower, upper)

# Dilate to repair
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
dilate = cv2.dilate(mask, kernel, iterations=1)

# Second pass of HSV to remove pink
colored = cv2.bitwise_and(original, original, mask=dilate)
colored_hsv = cv2.cvtColor(colored, cv2.COLOR_BGR2HSV)
lower_two = np.array([96, 89, 161])
upper_two = np.array([179, 255, 255])
mask_two = cv2.inRange(colored_hsv, lower_two, upper_two)

# Convert to grayscale then remove pink contours
result = cv2.cvtColor(colored, cv2.COLOR_BGR2GRAY)
result[result <= 10] = 255
cv2.imshow('result before removal', result)
result[mask_two==255] = 255

cv2.imshow('dilate', dilate)
cv2.imshow('colored', colored)
cv2.imshow('mask_two', mask_two)
cv2.imshow('result after removal', result)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)

根据图像,您可能需要调整下/上 HSV 范围。要确定 HSV 下限/上限范围,您可以使用带有滑块的 HSV 阈值脚本,这样您就无需猜测和检查。只需更改图片路径即可

import cv2
import numpy as np

def nothing(x):
    pass

# Load image
image = cv2.imread('1.jpg')

# Create a window
cv2.namedWindow('image')

# Create trackbars for color change
# Hue is from 0-179 for Opencv
cv2.createTrackbar('HMin', 'image', 0, 179, nothing)
cv2.createTrackbar('SMin', 'image', 0, 255, nothing)
cv2.createTrackbar('VMin', 'image', 0, 255, nothing)
cv2.createTrackbar('HMax', 'image', 0, 179, nothing)
cv2.createTrackbar('SMax', 'image', 0, 255, nothing)
cv2.createTrackbar('VMax', 'image', 0, 255, nothing)

# Set default value for Max HSV trackbars
cv2.setTrackbarPos('HMax', 'image', 179)
cv2.setTrackbarPos('SMax', 'image', 255)
cv2.setTrackbarPos('VMax', 'image', 255)

# Initialize HSV min/max values
hMin = sMin = vMin = hMax = sMax = vMax = 0
phMin = psMin = pvMin = phMax = psMax = pvMax = 0

while(1):
    # Get current positions of all trackbars
    hMin = cv2.getTrackbarPos('HMin', 'image')
    sMin = cv2.getTrackbarPos('SMin', 'image')
    vMin = cv2.getTrackbarPos('VMin', 'image')
    hMax = cv2.getTrackbarPos('HMax', 'image')
    sMax = cv2.getTrackbarPos('SMax', 'image')
    vMax = cv2.getTrackbarPos('VMax', 'image')

    # Set minimum and maximum HSV values to display
    lower = np.array([hMin, sMin, vMin])
    upper = np.array([hMax, sMax, vMax])

    # Convert to HSV format and color threshold
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower, upper)
    result = cv2.bitwise_and(image, image, mask=mask)

    # Print if there is a change in HSV value
    if((phMin != hMin) | (psMin != sMin) | (pvMin != vMin) | (phMax != hMax) | (psMax != sMax) | (pvMax != vMax) ):
        print("(hMin = %d , sMin = %d, vMin = %d), (hMax = %d , sMax = %d, vMax = %d)" % (hMin , sMin , vMin, hMax, sMax , vMax))
        phMin = hMin
        psMin = sMin
        pvMin = vMin
        phMax = hMax
        psMax = sMax
        pvMax = vMax

    # Display result image
    cv2.imshow('image', result)
    if cv2.waitKey(10) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)


Ann*_*Zen 7

这个概念

为此,我使用了两个简单的 HSV 掩模;一种是淡出徽标(使用简单的公式),另一种是通过完全删除徽标来完成屏蔽。

这是原始图像预屏蔽图像完全屏蔽图像,按顺序:

这是两个面具的样子:

输出

在此输入图像描述

代码

import cv2
import numpy as np

def HSV_mask(img_hsv, lower):
    lower = np.array(lower)
    upper = np.array([255, 255, 255])
    return cv2.inRange(img_hsv, lower, upper)
    
img = cv2.imread("image.jpg")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray[img_gray >= 235] = 255
mask1 = HSV_mask(img_hsv, [0, 0, 155])[..., None].astype(np.float32)
mask2 = HSV_mask(img_hsv, [0, 20, 0])
masked = np.uint8((img + mask1) / (1 + mask1 / 255))
gray = cv2.cvtColor(masked, cv2.COLOR_BGR2GRAY)
gray[gray >= 180] = 255
gray[mask2 == 0] = img_gray[mask2 == 0]

cv2.imshow("result", gray)
cv2.waitKey(0)
Run Code Online (Sandbox Code Playgroud)

说明

  1. 导入必要的库:
import cv2
import numpy as np
Run Code Online (Sandbox Code Playgroud)
  1. 定义一个函数 ,HSV_mask它将接收图像(已转换为 HSV 颜色空间)和 HSV 掩码的下限范围(上限范围为255, 255, 255,并返回 HSV 掩码:
def HSV_mask(img_hsv, lower):
    lower = np.array(lower)
    upper = np.array([255, 255, 255])
    return cv2.inRange(img_hsv, lower, upper)
Run Code Online (Sandbox Code Playgroud)
  1. 读入图像 ,image.jpg并定义另外两个变量,用于将图像转换为 HSV 和灰度。对于灰度图像,将其所有大于或等于235的像素替换为255;这将消除图像白色部分的一些噪点:
img = cv2.imread("image.jpg")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray[img_gray >= 235] = 255
Run Code Online (Sandbox Code Playgroud)
  1. 使用之前定义的函数定义 2 个变量mask1和。将屏蔽除文本之外的所有内容,并将屏蔽除徽标之外的所有内容:mask2HSV_maskmask1mask2
mask1 = HSV_mask(img_hsv, [0, 0, 155])[..., None].astype(np.float32)
mask2 = HSV_mask(img_hsv, [0, 20, 0])
Run Code Online (Sandbox Code Playgroud)
  1. 使用公式遮盖原始图像mask1,该公式会淡出(但不会删除)徽标。这只是一个预处理步骤,以便我们稍后可以干净地删除徽标:
masked = np.uint8((img + mask1) / (1 + mask1 / 255))
Run Code Online (Sandbox Code Playgroud)
  1. 将带有褪色徽标的图像转换为灰度,并应用mask2,以便所有被蒙版遮盖的像素都将转换回原始图像:
gray = cv2.cvtColor(masked, cv2.COLOR_BGR2GRAY)
gray[gray >= 180] = 255
gray[mask2 == 0] = img_gray[mask2 == 0]
Run Code Online (Sandbox Code Playgroud)
  1. 最后展示一下结果:
cv2.imshow("result", gray)
cv2.waitKey(0)
Run Code Online (Sandbox Code Playgroud)