參考連線:

https://

stackoverflow。com/quest

ions/32125281/removing-watermark-out-of-an-image-using-opencv

好久不見,大家好啊,最近太忙了,搞得好久沒更原創文了(說到底還是懶,),

這兩天在 Stackoverflow 上面看到了一個有趣的案例,是關於OpenCV 的一個討論,討論的主題就是如何用 OpenCV 來去除下面圖片中的水印,原圖如下;

用 OpenCV 去除圖片中的水印,騷操作!

題主想把紙張中的 黑色圓環去掉只留下背景,因此一些感興趣的 CV 愛好者在下面寫上自己的想法、並貼上自己的解決程式碼

看到關於這個主題的答案後,只能感嘆真正的大佬,都是從實踐場景出發來解決問題,

因為篇幅有限,在文章中只貼上得票最高的兩個問答思路及程式碼, 讓我們感受下他們思路的巧妙之處!

作者:Joel G

這老哥的思路,總體為五部分

1,首先將影象轉化為灰度圖記為 A;

2,利用霍夫圓在 A 中檢測最大的橢圓,然後在新的影象中建立相同半徑的圓得到 B;

3,對灰度圖和繪製圓的影象,應用OpenCV 的

bitwise_and

與運算,在原灰度影象 A 中提取只包含橢圓影象區域記為 C;

4,對影象 C 設定合適的閾值進行文字提取最終得到 D;

5, 對 影象 A 和 D 做

bitwise_or

操作,即能夠得到最終影象 E;

以下是在自己機子上跑出來的結果,從左到右依次對應上面的 A,C,D,E;效果如下

用 OpenCV 去除圖片中的水印,騷操作!

這個方法整體大概思想,

先提取影象中圓環部分割槽域,對圓環內的文字做閾值分割進行提取,最後將提取到的影象區域在初始影象中進行替換

這裡答主主要用到了三種重要演算法:

影象位運算(和、或)

閾值分割

霍夫圓檢測

下面就是這個思路的程式碼部分,原答主用的是

C++

,因為我做的是 Python 教程,就用 Python 轉換了一下

import cv2

import numpy as np

if __name__ ==‘__main__’:

img_path = “F:/Data/Ceshi1/shuiyin。jpg”

img1 = cv2。imread(img_path)

cv2。namedWindow(‘img1’,cv2。WINDOW_FREERATIO)

cv2。imshow(‘img1’,img1)

# 轉化為 灰度圖

gray = cv2。cvtColor(img1,cv2。COLOR_BGR2GRAY)

# 建立一個白畫布

ellipse_img = np。full((img1。shape[0],img1。shape[1],3),0,dtype = np。uint8)

print(ellipse_img。shape,ellipse_img[0][0])

gray = cv2。GaussianBlur(gray,(5,5),0) # 高斯處理

# 應用霍夫圓檢測,檢測出所有圓

circles = cv2。HoughCircles(gray,cv2。HOUGH_GRADIENT,1,gray。shape[0]/8,100,100,100,0)

# 找到最大的圓

measure = 0。0

x = 0。0

y = 0。0

for circle in (circles[0]):

if circle[2] > measure:

measure = circle[2]

x = circle[0]

y = circle[1]

# 繪製圓

cv2。circle(img1,(x,y),3,(0,255,0),-1,8,0)

cv2。circle(img1,(x,y),int(measure),(0,255,0),2,8,0)

# 繪製相同大小的圓

ellipse_img = cv2。ellipse(ellipse_img,(x,y),(int(measure),int(measure)),0,0,360,(255,255,255),-1,8)

print(f‘center x is {x} ,y is {y}, radius is {measure}’)

ellipse_img = cv2。cvtColor(ellipse_img,cv2。COLOR_BGR2GRAY)

result = cv2。bitwise_and(gray,ellipse_img)

cv2。namedWindow(‘bitwise and’,cv2。WINDOW_FREERATIO)

cv2。imshow(‘bitwise and’,result)

# 估計圓影象畫素強度

x = result[int(x+30)][int(y)]

print(f‘intensity is {x}’)

# 閾值分割

_,ellipse_img = cv2。threshold(result,int(x) - 10,250,cv2。THRESH_BINARY)

# print(‘ellipse_img shape is {}’。format(ellipse_img。shape))

cv2。namedWindow(‘threshold’,cv2。WINDOW_FREERATIO)

cv2。imshow(‘threshold’,ellipse_img)

# 使用 bitwise_or 方法

print(‘shape ——————\n’)

print(ellipse_img。shape,gray。shape)

res = cv2。bitwise_or(gray,ellipse_img)

cv2。namedWindow(‘bitwise_or’,cv2。WINDOW_FREERATIO)

cv2。imshow(‘bitwise_or’,res)

cv2。waitKey(0)

最終結果預覽比對

用 OpenCV 去除圖片中的水印,騷操作!

上面是第一種實現方法,這種方法思路主要用到閾值分割,從最終結果來看確實去掉了水印,但還是有一定的瑕疵:

比如圓內文字背景與圓外背景是不一樣的,存在很大色差,並且圓內的文字提取結果來看是不完整的;

此方法不具有普遍性,因為這類方法只能針對於圓形水印,假設水印是

不規則多邊形

此方法可能就會失效

下面介紹第二種思路,與第一種有相似的地方,也用到了

閾值分割、影象畫素位運算

相關演算法,但同卻又有自己的獨特地方,從客觀角度分析來看,這種方法的最終結果會更好一點

作者:dhanushka

思路主要分為四部分

1,源影象記為 A,用形態學濾波器刪除影象中文字區域,得到的影象記為 B;

用 OpenCV 去除圖片中的水印,騷操作!

2,獲取A,B 影象的之差,用 A-B ,得到區別後再用閾值分割進行處理,得到 C;

用 OpenCV 去除圖片中的水印,騷操作!

3,閾值分割背景影象,提取水印覆蓋黑色部分記為 D,

用 OpenCV 去除圖片中的水印,騷操作!

4,從 A 中提取在區域 D 中的畫素,再用閾值分割方法分割畫素,最終將提取到的畫素貼到 B 中,得到最終去除水印的影象

用 OpenCV 去除圖片中的水印,騷操作!

程式碼貼在下方

import cv2

import numpy as np

if __name__ ==‘__main__’:

img_path = “F:/Data/Ceshi1/shuiyin。jpg”

im = cv2。imread(img_path)

gray = cv2。cvtColor(im,cv2。COLOR_BGR2GRAY)

background = gray。copy()

for i in range(1,5):

kernel = cv2。getStructuringElement(cv2。MORPH_ELLIPSE,(2*i+1,2*i+1))

# print(‘kernel size is ’,kernel)

background = cv2。morphologyEx(background,cv2。MORPH_CLOSE,kernel)

background = cv2。morphologyEx(background,cv2。MORPH_CLOSE,kernel)

diff = background - gray # 計算差距

cv2。namedWindow(‘diff’,cv2。WINDOW_FREERATIO) # 獲取影象中前景背景之差

cv2。imshow(‘diff’,background)

# 閾值分割獲取黑色字型

_,bw = cv2。threshold(diff,0,255,cv2。THRESH_BINARY_INV | cv2。THRESH_OTSU)

# 閾值分割獲取黑色區域

cv2。namedWindow(‘bw_before’, cv2。WINDOW_FREERATIO)

cv2。imshow(‘bw_before’, bw)

_,dark = cv2。threshold(background,0,255,cv2。THRESH_BINARY_INV|cv2。THRESH_OTSU)

darkpix = cv2。countNonZero(dark)# 獲取 dark非0d影象畫素個數

darkpix = [0]*darkpix

index = 0

cv2。namedWindow(‘gray’, cv2。WINDOW_FREERATIO)

cv2。imshow(‘gray’, gray)

for r in range(dark。shape[0]):

for c in range(dark。shape[1]):

if(dark[r][c]):

darkpix[index] = gray[r][c]

index = index +1

# 閾值分割 dark 區域 因此我們在裡面得到更深的畫素

darkpix = np。array(darkpix)

_,darkpix = cv2。threshold(darkpix,0,255,cv2。THRESH_BINARY | cv2。THRESH_OTSU)

cv2。namedWindow(‘darkpix’, cv2。WINDOW_FREERATIO)

cv2。imshow(‘darkpix’, darkpix)

# 把 取到的畫素貼上到 其渠道的 darker pixels

cv2。namedWindow(‘dark’,cv2。WINDOW_FREERATIO)

cv2。imshow(‘dark’,dark)

index = 0

for r in range(dark。shape[0]):

for c in range(dark。shape[1]):

if (dark[r][c]):

bw[r][c] = darkpix[index]

index = index +1

cv2。namedWindow(‘bw’,cv2。WINDOW_FREERATIO)

cv2。imshow(‘bw’,bw)

cv2。waitKey(0)

效果預覽對比

用 OpenCV 去除圖片中的水印,騷操作!

相對第一種方法,第二種方法實用性更強一點,無論影象前景水印為什麼形狀的,這種方法都可適用(水印區域與其他背景畫素強度差別大,且水印區域是連線在一起的),

如果考慮到商用途徑,只用 OpenCV 來解決複雜場景的圖片水印問題,是不現實的,還需人工的干涉;但不現實並不代表它沒有用,對於前後畫素值較大、簡單場景的水印,OpenCV 是完全可行的,若是再加上一個批次操作,變得更可了,大大解放我們的雙手!

並且這兩種思路中用到的的一些方法,是值得我們借鑑的,比如

影象畫素或與和操作、形態學過濾、霍夫圓檢測

等技術,可藉助於這些方法應用到其它場景,例如提取影象中圓形區域、行人路上斑馬線檢測、去除不規則影象連線區域等。

好了,以上就是本篇文章的全部內容了,如果覺得不錯,請不要吝嗇你的雙手,點贊、轉發、留言,感謝三連!