在数字图像处理领域,去除图片上的水印是一个常见的需求。无论是个人用户还是企业,在使用或处理图片时,都可能遇到需要去除水印的情况。然而,由于水印的形式多样,去除水印的方法也各有不同。本文将详细介绍几种使用 Python 和 OpenCV 实现图片去水印的方法,帮助读者根据不同的场景选择合适的去水印技术。
1. 前言
本文为作者学习记录,使用Python结合OpenCV,总结了几种常见的水印去除方式,简单图片去水印效果良好,但是复杂图片有点一言难尽,本文部分代码仅供参考,并不能针对所有水印通用,需要根据具体水印颜色、位置等情况进行分析调整代码。
2. 颜色介绍
本文总共使用了两种格式的颜色,一种是BGR
,另一种是HSV
。
关于如何获取BGR
的颜色,可以直接使用截图或者吸取颜色的工具吸取即可。如下图:
有一点需要注意:Python里面用的是BGR
,截图工具给的是RGB
,这个使用的使用需要调整下顺序
HSV
是由色调(H),饱和度(S),亮度(V)组成,如何获取HSV
的值,这里提供一段Python的代码获取。其中\'image/3_water.jpg\'
替换成你的图片路径。详细如下:
importcv2importnumpyasnpfrommatplotlibimportpyplotaspltimage=cv2.imread(\'image/3_water.jpg\')HSV=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)defgetpos(event,x,y,flags,param):ifevent==cv2.EVENT_LBUTTONDOWN:#定义一个鼠标左键按下去的事件print(HSV[y,x])cv2.imshow(\"imageHSV\",HSV)cv2.imshow(\'image\',image)cv2.setMouseCallback(\"imageHSV\",getpos)cv2.waitKey(0)
效果图如下,左边的为原图,右边的为转为HSV
的图片,点击你需要获取颜色的位置,会在控制台打印对应的颜色值。
3. 添加水印
先准备几张测试图片,给这些图片打上水印。这里以下面的图片经行演示。
添加水印代码如下:
importcv2importnumpyasnpdefcreate_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(\'WatermarkedImage\',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
之间的,全部替换为白色,当然这样可能会误伤一部分颜色,具体需要针对多种方案比较选用。
importnumpyasnpimportcv2defremote_water_mark_1(image):#读取图像image=cv2.imread(image)#显示原始图像cv2.imshow(\'OriginalImage\',image)#设置替换颜色为白色replace_color=(255,255,255)#获取图片大小height,width=image.shape[:2]foriinrange(height):forjinrange(width):#获取当前像素点的颜色值varP=image[i,j]#如果当前像素点的颜色值总和在640到760之间,则替换为白色ifsum(varP)>640andsum(varP)<740:image[i,j]=replace_color#显示处理后的图像cv2.imshow(\'ResultImage\',image)cv2.waitKey(0)remote_water_mark_1(\'image/3.jpg\')
去水印后的效果图如下:
可以看到,图片底部的水印去掉了,仔细看图片中的文字颜色有点细微变化,这就是因为部分像素颜色被误伤了导致的。下面看下方案二,可以解决这个问题。
方案二:
和方案一的思想一致,但是不再使用BGR
的颜色处理图片,而是使用HSV
对图片进行处理。通过创建颜色范围的掩码,替换掉对应像素的颜色,具体代码如下:
importnumpyasnpimportcv2defremote_water_mark_2(image):#读取图像image=cv2.imread(image)#显示原始图像cv2.imshow(\'OriginalImage\',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(\'maskImage\',mask)#将掩码中对应位置的颜色替换为红色(在HSV空间中),白底在HSV空间就是红色。hsv_image[mask>0]=[0,0,255]#如果需要,将图片转换回BGR颜色空间result_image=cv2.cvtColor(hsv_image,cv2.COLOR_HSV2BGR)#显示结果图像cv2.imshow(\'ResultImage\',result_image)cv2.waitKey(0)remote_water_mark_2(\'image/3.jpg\')
去水印后的效果图如下,可以通过mask
掩码图像(黑色背景,白色水印图)清晰的看到水印的形状。这里可以根据mask
掩码调整颜色区间的参数。可以看到右边图片成功去除水印,并且文字颜色一致。
4.2 固定位置水印去除方式
上文提到了,水印背景颜色如果和图片颜色类型,可能会误伤,出现意向不到问题。所有针对可以确认固定位置的水印,我们尽量处理固定位置,减少对图片的损伤。
如下图:水印固定在右下角,颜色和字体颜色基本一致,这样如果全图处理,整张图片就面目全非了。所以我们可以针对右下角固定的位置,特殊处理。
代码如下:
importnumpyasnpimportcv2defremote_water_mark_3(image):#读取图像image=cv2.imread(image)#显示原始图像cv2.imshow(\'OriginalImage\',image)#获取图像的高度和宽度height,width=image.shape[:2]#定义ROI的坐标和大小x,y,w,h=width-280,height-40,280,40roi=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(\'ResultImage\',image)cv2.waitKey(0)remote_water_mark_2(\'image/1_water.jpg\')
处理后的效果如下图所示
可以看见水印已经去掉了。因为这里背景是白色,所以很好处理,但如果背景不是白色,而是一些其他颜色改怎么处理呢?接下来看下面
4.3 复杂背景色的水印处理
针对复杂背景色的图片,处理时肯定会一定程度的损伤图片了,如果需要相对精准,可以训练模型处理,单纯使用代码,还是有一定程序限制。如果水印位置不固定,数量不固定,大小不固定等等,代码局限性就很大了,纯openCV代码暂还没找到方案,这里还是以简单的示例。
先看下面这张相对简单的图片
方案一:
使用inpaint
函数修复图片,代码如下:
importnumpyasnpimportcv2defremote_water_mark_6(image):#读取图像image=cv2.imread(image)#显示原始图像cv2.imshow(\'OriginalImage\',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(\'ResultImage\',denoised_image)cv2.waitKey(0)remote_water_mark_6(\'image/2_water.jpg\')
处理后的效果图如下,可以看到水印处理掉了,但是图片水印处有略微变形。
针对下面这张图片进行测试:
可以看到水印虽然去掉了,但是水印出糊的很厉害
方案二:
此方案为自己写的,暂未经大量图片测试,但测试了部分图片,效果还可,因此放在这里做个比较
实现思想:实现步骤基本于上面一致,针对水印颜色区间像素进行颜色替换,替换的颜色值,这里取周边像素颜色出现次数最多的颜色做替换。然后对区域图片进行噪点处理,尽量中和处理部分于原图像的匹配度,如果水印下方是文字类型,不可以进行噪点处理。
具体代码如下:
具体使用时需要根据水印位置和颜色调整对应的参数
importnumpyasnpimportcv2fromcollectionsimportCounterdeffind_most_frequent_color(color_list):\"\"\"找到颜色列表中出现频率最高的颜色。Args:color_list(list):颜色列表,每个颜色可以是一个包含三个整数的列表,分别表示RGB值。Returns:tuple:出现频率最高的颜色,以元组形式返回,包含三个整数,分别表示RGB值。\"\"\"#将颜色列表中的每个颜色转换为元组形式color_list=[tuple(color)forcolorincolor_list]#使用Counter统计每种颜色出现的次数color_counts=Counter(color_list)#获取出现次数最多的颜色,并返回该颜色returncolor_counts.most_common(1)[0][0]defremote_water_mark_5(image):#读取图像image=cv2.imread(image)#显示原始图像cv2.imshow(\'OriginalImage\',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]foryyinrange(25,-1,-1):forxxinrange(195):#获取当前像素的颜色pixel_color=roi[yy,xx]pixel_color_sum=sum(pixel_color[:3])#检查当前像素的颜色是否与要替换的颜色匹配,需要根据实际情况调整if(pixel_color_sum>300andpixel_color_sum<650):ifsum(roi[yy,xx])==sum(roi[yy,xx-1])andsum(roi[yy,xx])==sum(roi[yy+1,xx]):continueis_fix=Falsecolor_set=[roi[yy,xx]]foriinrange(5,-1,-1):ifis_fix:breaktemp=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])ifsum(temp)==sum(temp1):roi[yy,xx]=tempis_fix=Trueelifi==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(\'ResultImage\',image)cv2.waitKey(0)remote_water_mark_5(\'image/4_water.jpg\')
测试效果如下图所示:
下面这个图,去除了噪点处理的代码。
4.4 训练模型(未完成)
作者尝试过进行训练模型处理,但在训练数据时,电脑CPU咔咔100%,因此放弃。这里就不附代码供大家参考,因为没有训练出模型,不知道效果如何,也不知道对不对。
总结
通过本文的介绍,我们了解了使用 Python 和 OpenCV 实现图片去水印的多种方法。每种方法都有其适用的场景和优缺点,通过这些方法的对比和实际应用,读者可以根据具体的需求和条件选择最合适的去水印方案。希望本文的内容能够为读者提供有价值的参考和帮助。