Python+OpenCV圖片去水印的多種方案實現(xiàn)
1. 前言
本文為作者學習記錄,使用Python結合OpenCV,總結了幾種常見的水印去除方式,簡單圖片去水印效果良好,但是復雜圖片有點一言難盡,本文部分代碼僅供參考,并不能針對所有水印通用,需要根據(jù)具體水印顏色、位置等情況進行分析調(diào)整代碼。
2. 顏色介紹
本文總共使用了兩種格式的顏色,一種是BGR,另一種是HSV。
關于如何獲取BGR的顏色,可以直接使用截圖或者吸取顏色的工具吸取即可。如下圖:
有一點需要注意:Python里面用的是BGR,截圖工具給的是RGB,這個使用的使用需要調(diào)整下順序

HSV是由色調(diào)(H),飽和度(S),亮度(V)組成,如何獲取HSV的值,這里提供一段Python的代碼獲取。其中'image/3_water.jpg'替換成你的圖片路徑。詳細如下:
import cv2
import numpy as np
from matplotlib import pyplot as plt
image=cv2.imread('image/3_water.jpg')
HSV=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)
def getpos(event,x,y,flags,param):
if event==cv2.EVENT_LBUTTONDOWN: #定義一個鼠標左鍵按下去的事件
print(HSV[y,x])
cv2.imshow("imageHSV",HSV)
cv2.imshow('image',image)
cv2.setMouseCallback("imageHSV",getpos)
cv2.waitKey(0)
效果圖如下,左邊的為原圖,右邊的為轉(zhuǎn)為HSV的圖片,點擊你需要獲取顏色的位置,會在控制臺打印對應的顏色值。

3. 添加水印
先準備幾張測試圖片,給這些圖片打上水印。這里以下面的圖片經(jīng)行演示。

添加水印代碼如下:
import cv2
import numpy as np
def create_watermark(image_path):
# 讀取圖像
image = cv2.imread(image_path)
# 設置水印文本
watermark_text = 'www.xiaoxiaofeng.com'
# 設置字體
font = cv2.FONT_HERSHEY_SIMPLEX
# 設置字體大小
font_scale = 0.5
# 注意,這里是BGR 藍色(B)、綠色(G)和紅色(R)
font_color = (62, 62, 187)
# 設置字體粗細
thickness = 1
# 獲取圖像的高度和寬度
height, width = image.shape[:2]
# 設置文本位置(右下角)
text_position = ( width - 200, height - 20 ) # 例如,在右下角
# 添加水印文本
cv2.putText(image, watermark_text, text_position, font, font_scale, font_color, thickness, cv2.LINE_AA)
# 顯示圖像
cv2.imshow('Watermarked Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 保存圖像
cv2.imwrite('image/2_water.jpg', image)
create_watermark('image/2.jpg')
添加完水印的效果如下圖:

4. 去除水印
關于去除水印,效果最好的應該還是訓練模型,用模型去除效果肯定比較好,但是對于簡單的水印,也沒必要去訓練模型,而且訓練模型的門檻比較高,自己的"超配00年代"的電腦就別想了。
這里針對幾種常見的水印,簡單的描述下去水印的思想,以及實現(xiàn)代碼。
4.1 文檔類圖片去水印
關于圖片準備,這里直接用word創(chuàng)建了一個帶水印的文檔,然后截圖,圖片如下:

實現(xiàn)目標:去除中間背景的中的【笑小楓】的水印。
實現(xiàn)思想:背景為白色,字體顏色為黑色,水印顏色為灰色,因此可以將水印的灰色替換為白色即可
方案一:
通過觀測,水印的顏色大多為(214, 214, 214),但是邊緣的鋸齒處有部分顏色為214-245之間,這里定義3個色彩相加之和位于 640-740之間的,全部替換為白色,當然這樣可能會誤傷一部分顏色,具體需要針對多種方案比較選用。
import numpy as np
import cv2
def remote_water_mark_1(image):
# 讀取圖像
image = cv2.imread(image)
# 顯示原始圖像
cv2.imshow('Original Image', image)
# 設置替換顏色為白色
replace_color = (255, 255, 255)
# 獲取圖片大小
height, width = image.shape[:2]
for i in range(height):
for j in range(width):
# 獲取當前像素點的顏色值
varP = image[i, j]
# 如果當前像素點的顏色值總和在640到760之間,則替換為白色
if sum(varP) > 640 and sum(varP) < 740:
image[i, j] = replace_color
# 顯示處理后的圖像
cv2.imshow('Result Image', image)
cv2.waitKey(0)
remote_water_mark_1('image/3.jpg')
去水印后的效果圖如下:

可以看到,圖片底部的水印去掉了,仔細看圖片中的文字顏色有點細微變化,這就是因為部分像素顏色被誤傷了導致的。下面看下方案二,可以解決這個問題。
方案二:
和方案一的思想一致,但是不再使用BGR的顏色處理圖片,而是使用HSV對圖片進行處理。通過創(chuàng)建顏色范圍的掩碼,替換掉對應像素的顏色,具體代碼如下:
import numpy as np
import cv2
def remote_water_mark_2(image):
# 讀取圖像
image = cv2.imread(image)
# 顯示原始圖像
cv2.imshow('Original Image', image)
# 將圖像從BGR顏色空間轉(zhuǎn)換為HSV顏色空間
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 定義要替換的顏色范圍(在 HSV 空間中)
lower_blue = np.array([0, 0, 214])
upper_blue = np.array([0, 0, 245])
# 在HSV圖像中,根據(jù)定義的顏色范圍創(chuàng)建掩碼
mask = cv2.inRange(hsv_image, lower_blue, upper_blue)
# 顯示掩碼圖像
cv2.imshow('mask Image', mask)
# 將掩碼中對應位置的顏色替換為紅色(在 HSV 空間中),白底在HSV空間就是紅色。
hsv_image[mask > 0] = [0, 0, 255]
# 如果需要,將圖片轉(zhuǎn)換回 BGR 顏色空間
result_image = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
# 顯示結果圖像
cv2.imshow('Result Image', result_image)
cv2.waitKey(0)
remote_water_mark_2('image/3.jpg')
去水印后的效果圖如下,可以通過mask掩碼圖像(黑色背景,白色水印圖)清晰的看到水印的形狀。這里可以根據(jù)mask掩碼調(diào)整顏色區(qū)間的參數(shù)。可以看到右邊圖片成功去除水印,并且文字顏色一致。

4.2 固定位置水印去除方式
上文提到了,水印背景顏色如果和圖片顏色類型,可能會誤傷,出現(xiàn)意向不到問題。所有針對可以確認固定位置的水印,我們盡量處理固定位置,減少對圖片的損傷。
如下圖:水印固定在右下角,顏色和字體顏色基本一致,這樣如果全圖處理,整張圖片就面目全非了。所以我們可以針對右下角固定的位置,特殊處理。

代碼如下:
import numpy as np
import cv2
def remote_water_mark_3(image):
# 讀取圖像
image = cv2.imread(image)
# 顯示原始圖像
cv2.imshow('Original Image', image)
# 獲取圖像的高度和寬度
height, width = image.shape[:2]
# 定義 ROI 的坐標和大小
x, y, w, h = width-280, height-40, 280, 40
roi = image[y:y+h, x:x+w]
# 將 ROI 從 BGR 顏色空間轉(zhuǎn)換為 HSV 顏色空間
hsv_image = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
# 定義要替換的顏色范圍,范圍盡量越小越好(在 HSV 空間中)
lower_blue = np.array([0, 0, 0])
upper_blue = np.array([0, 0, 240])
# 創(chuàng)建掩碼
mask = cv2.inRange(hsv_image, lower_blue, upper_blue)
# 替換顏色
hsv_image[mask > 0] = [0, 0, 255] # 將匹配的顏色替換為紅色(在 HSV 空間中)
# 如果需要,將圖片轉(zhuǎn)換回 BGR 顏色空間
roi = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
# 將處理后的 ROI 放回原圖
image[y:y+h, x:x+w] = roi
# 顯示處理后的圖像
cv2.imshow('Result Image', image)
cv2.waitKey(0)
remote_water_mark_2('image/1_water.jpg')
處理后的效果如下圖所示

可以看見水印已經(jīng)去掉了。因為這里背景是白色,所以很好處理,但如果背景不是白色,而是一些其他顏色改怎么處理呢?接下來看下面
4.3 復雜背景色的水印處理
針對復雜背景色的圖片,處理時肯定會一定程度的損傷圖片了,如果需要相對精準,可以訓練模型處理,單純使用代碼,還是有一定程序限制。如果水印位置不固定,數(shù)量不固定,大小不固定等等,代碼局限性就很大了,純openCV代碼暫還沒找到方案,這里還是以簡單的示例。
先看下面這張相對簡單的圖片

方案一:
使用inpaint函數(shù)修復圖片,代碼如下:
import numpy as np
import cv2
def remote_water_mark_6(image):
# 讀取圖像
image = cv2.imread(image)
# 顯示原始圖像
cv2.imshow('Original Image', image)
height, width = image.shape[:2]
# 創(chuàng)建一個掩碼,標記水印區(qū)域
mask = np.zeros(image.shape[:2], np.uint8)
height, width = image.shape[:2]
# 假設水印在這個區(qū)域,繪制矩形掩碼
cv2.rectangle(mask, (width-220, height-50), (width, height), 255, -1)
# 使用inpaint函數(shù)修復圖像
denoised_image = cv2.inpaint(image, mask, 1, cv2.INPAINT_TELEA)
# 顯示結果圖像
cv2.imshow('Result Image', denoised_image)
cv2.waitKey(0)
remote_water_mark_6('image/2_water.jpg')
處理后的效果圖如下,可以看到水印處理掉了,但是圖片水印處有略微變形。

針對下面這張圖片進行測試:

可以看到水印雖然去掉了,但是水印出糊的很厲害

方案二:
此方案為自己寫的,暫未經(jīng)大量圖片測試,但測試了部分圖片,效果還可,因此放在這里做個比較
實現(xiàn)思想:實現(xiàn)步驟基本于上面一致,針對水印顏色區(qū)間像素進行顏色替換,替換的顏色值,這里取周邊像素顏色出現(xiàn)次數(shù)最多的顏色做替換。然后對區(qū)域圖片進行噪點處理,盡量中和處理部分于原圖像的匹配度,如果水印下方是文字類型,不可以進行噪點處理。
具體代碼如下:
具體使用時需要根據(jù)水印位置和顏色調(diào)整對應的參數(shù)
import numpy as np
import cv2
from collections import Counter
def find_most_frequent_color(color_list):
"""
找到顏色列表中出現(xiàn)頻率最高的顏色。
Args:
color_list (list): 顏色列表,每個顏色可以是一個包含三個整數(shù)的列表,分別表示RGB值。
Returns:
tuple: 出現(xiàn)頻率最高的顏色,以元組形式返回,包含三個整數(shù),分別表示RGB值。
"""
# 將顏色列表中的每個顏色轉(zhuǎn)換為元組形式
color_list = [tuple(color) for color in color_list]
# 使用 Counter 統(tǒng)計每種顏色出現(xiàn)的次數(shù)
color_counts = Counter(color_list)
# 獲取出現(xiàn)次數(shù)最多的顏色,并返回該顏色
return color_counts.most_common(1)[0][0]
def remote_water_mark_5(image):
# 讀取圖像
image = cv2.imread(image)
# 顯示原始圖像
cv2.imshow('Original Image', image)
height, width = image.shape[:2]
# 定義 ROI(水印的位置,需要根據(jù)實際情況調(diào)整)
x, y, w, h = width-200, height-35, 200, 35 # ROI 的坐標和大小
roi = image[y:y+h, x:x+w]
for yy in range(25, -1, -1):
for xx in range(195):
# 獲取當前像素的顏色
pixel_color = roi[yy, xx]
pixel_color_sum = sum(pixel_color[:3])
# 檢查當前像素的顏色是否與要替換的顏色匹配,需要根據(jù)實際情況調(diào)整
if (pixel_color_sum > 300 and pixel_color_sum < 650):
if sum(roi[yy, xx]) == sum(roi[yy, xx-1]) and sum(roi[yy, xx]) == sum(roi[yy+1, xx]):
continue
is_fix = False
color_set = [roi[yy, xx]]
for i in range(5, -1, -1):
if is_fix:
break
temp = roi[yy + i, xx + i]
temp1 = roi[yy - i, xx - i]
color_set.append(temp)
color_set.append(temp1)
color_set.append( roi[yy + i, xx + 2])
color_set.append( roi[yy + 2, xx + i])
color_set.append( roi[yy - i, xx - 2])
color_set.append( roi[yy - 2, xx - i])
color_set.append( roi[yy + i, xx - i])
color_set.append( roi[yy - i, xx + i])
if sum(temp) == sum(temp1):
roi[yy, xx] = temp
is_fix = True
elif i == 1:
most_frequent_color = find_most_frequent_color(color_set)
roi[yy, xx] = most_frequent_color
# 濾波窗口大小,如果背景處是文字,不可以進行噪點處理,不然會糊掉
kernel_size = 3
# 對 ROI 進行中值濾波
roi = cv2.medianBlur(roi, kernel_size)
# 將處理后的 ROI 放回原圖
image[y:y+h, x:x+w] = roi
# 顯示處理后的圖像
cv2.imshow('Result Image', image)
cv2.waitKey(0)
remote_water_mark_5('image/4_water.jpg')
測試效果如下圖所示:


下面這個圖,去除了噪點處理的代碼。

4.4 訓練模型(未完成)
作者嘗試過進行訓練模型處理,但在訓練數(shù)據(jù)時,電腦CPU咔咔100%,因此放棄。這里就不附代碼供大家參考,因為沒有訓練出模型,不知道效果如何,也不知道對不對。
5. 本文總結
本文寫了幾種去水印的方案,具體如何選擇,小伙伴們可以根據(jù)實際情況去選擇,本文也是我使用python去水印一路研究過來的總結。
因為剛剛接觸python不多,如有錯誤之處,大家可以幫忙指出來,感謝!
以上就是Python+OpenCV圖片去水印的多種方案實現(xiàn)的詳細內(nèi)容,更多關于Python OpenCV圖片去水印的資料請關注腳本之家其它相關文章!
相關文章
python中實現(xiàn)數(shù)組和列表讀取一列的方法
下面小編就為大家分享一篇python中實現(xiàn)數(shù)組和列表讀取一列的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04
Python?JSON數(shù)據(jù)解析過程(最新推薦)
json模塊提供了將JSON格式的數(shù)據(jù)轉(zhuǎn)換為Python對象(如列表、字典等)以及將Python對象轉(zhuǎn)換為JSON格式的數(shù)據(jù)的方法,下面給大家分享使用json模塊解析JSON數(shù)據(jù)的常見方法,感興趣的朋友一起看看吧2024-02-02
使用Pandas實現(xiàn)MySQL窗口函數(shù)的解決方法
本文主要介紹 MySQL 中的窗口函數(shù)row_number()、lead()/lag()、rank()/dense_rank()、first_value()、count()、sum()如何使用pandas實現(xiàn),同時二者又有什么區(qū)別,感興趣的朋友一起看看吧2023-02-02

