1. 前言
本文为作者学习记录,使用python结合opencv,总结了几种常见的水印去除方式,简单图片去水印效果良好,但是复杂图片有点一言难尽,本文部分代码仅供参考,并不能针对所有水印通用,需要根据具体水印颜色、位置等情况进行分析调整代码。
2. 颜色介绍
本文总共使用了两种格式的颜色,一种是bgr
,另一种是hsv
。
关于如何获取bgr
的颜色,可以直接使用截图或者吸取颜色的工具吸取即可。如下图:
有一点需要注意:python里面用的是bgr
,截图工具给的是rgb
,这个使用的使用需要调整下顺序
hsv
是由色调(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)
效果图如下,左边的为原图,右边的为转为hsv
的图片,点击你需要获取颜色的位置,会在控制台打印对应的颜色值。
3. 添加水印
先准备几张测试图片,给这些图片打上水印。这里以下面的图片经行演示。
添加水印代码如下:
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年代"的电脑就别想了。
这里针对几种常见的水印,简单的描述下去水印的思想,以及实现代码。
4.1 文档类图片去水印
关于图片准备,这里直接用word创建了一个带水印的文档,然后截图,图片如下:
实现目标:去除中间背景的中的【笑小枫】的水印。
实现思想:背景为白色,字体颜色为黑色,水印颜色为灰色,因此可以将水印的灰色替换为白色即可
方案一:
通过观测,水印的颜色大多为(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
对图片进行处理。通过创建颜色范围的掩码,替换掉对应像素的颜色,具体代码如下:
import numpy as np import cv2 def remote_water_mark_2(image): # 读取图像 image = cv2.imread(image) # 显示原始图像 cv2.imshow('original image', image) # 将图像从bgr颜色空间转换为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图像中,根据定义的颜色范围创建掩码 mask = cv2.inrange(hsv_image, lower_blue, upper_blue) # 显示掩码图像 cv2.imshow('mask image', mask) # 将掩码中对应位置的颜色替换为红色(在 hsv 空间中),白底在hsv空间就是红色。 hsv_image[mask > 0] = [0, 0, 255] # 如果需要,将图片转换回 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
掩码图像(黑色背景,白色水印图)清晰的看到水印的形状。这里可以根据mask
掩码调整颜色区间的参数。可以看到右边图片成功去除水印,并且文字颜色一致。
4.2 固定位置水印去除方式
上文提到了,水印背景颜色如果和图片颜色类型,可能会误伤,出现意向不到问题。所有针对可以确认固定位置的水印,我们尽量处理固定位置,减少对图片的损伤。
如下图:水印固定在右下角,颜色和字体颜色基本一致,这样如果全图处理,整张图片就面目全非了。所以我们可以针对右下角固定的位置,特殊处理。
代码如下:
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 颜色空间转换为 hsv 颜色空间 hsv_image = cv2.cvtcolor(roi, cv2.color_bgr2hsv) # 定义要替换的颜色范围,范围尽量越小越好(在 hsv 空间中) lower_blue = np.array([0, 0, 0]) upper_blue = np.array([0, 0, 240]) # 创建掩码 mask = cv2.inrange(hsv_image, lower_blue, upper_blue) # 替换颜色 hsv_image[mask > 0] = [0, 0, 255] # 将匹配的颜色替换为红色(在 hsv 空间中) # 如果需要,将图片转换回 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')
处理后的效果如下图所示
可以看见水印已经去掉了。因为这里背景是白色,所以很好处理,但如果背景不是白色,而是一些其他颜色改怎么处理呢?接下来看下面
4.3 复杂背景色的水印处理
针对复杂背景色的图片,处理时肯定会一定程度的损伤图片了,如果需要相对精准,可以训练模型处理,单纯使用代码,还是有一定程序限制。如果水印位置不固定,数量不固定,大小不固定等等,代码局限性就很大了,纯opencv代码暂还没找到方案,这里还是以简单的示例。
先看下面这张相对简单的图片
方案一:
使用inpaint
函数修复图片,代码如下:
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] # 创建一个掩码,标记水印区域 mask = np.zeros(image.shape[:2], np.uint8) height, width = image.shape[:2] # 假设水印在这个区域,绘制矩形掩码 cv2.rectangle(mask, (width-220, height-50), (width, height), 255, -1) # 使用inpaint函数修复图像 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')
处理后的效果图如下,可以看到水印处理掉了,但是图片水印处有略微变形。
针对下面这张图片进行测试:
可以看到水印虽然去掉了,但是水印出糊的很厉害
方案二:
此方案为自己写的,暂未经大量图片测试,但测试了部分图片,效果还可,因此放在这里做个比较
实现思想:实现步骤基本于上面一致,针对水印颜色区间像素进行颜色替换,替换的颜色值,这里取周边像素颜色出现次数最多的颜色做替换。然后对区域图片进行噪点处理,尽量中和处理部分于原图像的匹配度,如果水印下方是文字类型,不可以进行噪点处理。
具体代码如下:
具体使用时需要根据水印位置和颜色调整对应的参数
import numpy as np import cv2 from collections import counter def find_most_frequent_color(color_list): """ 找到颜色列表中出现频率最高的颜色。 args: color_list (list): 颜色列表,每个颜色可以是一个包含三个整数的列表,分别表示rgb值。 returns: tuple: 出现频率最高的颜色,以元组形式返回,包含三个整数,分别表示rgb值。 """ # 将颜色列表中的每个颜色转换为元组形式 color_list = [tuple(color) for color in color_list] # 使用 counter 统计每种颜色出现的次数 color_counts = counter(color_list) # 获取出现次数最多的颜色,并返回该颜色 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(水印的位置,需要根据实际情况调整) 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]) # 检查当前像素的颜色是否与要替换的颜色匹配,需要根据实际情况调整 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 训练模型(未完成)
作者尝试过进行训练模型处理,但在训练数据时,电脑cpu咔咔100%,因此放弃。这里就不附代码供大家参考,因为没有训练出模型,不知道效果如何,也不知道对不对。
5. 本文总结
本文写了几种去水印的方案,具体如何选择,小伙伴们可以根据实际情况去选择,本文也是我使用python去水印一路研究过来的总结。
因为刚刚接触python不多,如有错误之处,大家可以帮忙指出来,感谢!
以上就是python+opencv图片去水印的多种方案实现的详细内容,更多关于python opencv图片去水印的资料请关注代码网其它相关文章!
发表评论