点击上方 小张Python,加为星标

第一时间收到 Python 技术干货!

参考连接:https://stackoverflow.com/questions/32125281/removing-watermark-out-of-an-image-using-opencv

好久不见,大家好啊,最近太忙了,搞得好久没更原创文了(说到底还是懒,),

这两天在 Stackoverflow 上面看到了一个有趣的案例,是关于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;效果如下


这个方法整体大概思想,先提取图像中圆环部分区域,对圆环内的文字做阈值分割进行提取,最后将提取到的图像区域在初始图像中进行替换

这里答主主要用到了三种重要算法:图像位运算(和、或)阈值分割霍夫圆检测

下面就是这个思路的代码部分,原答主用的是 C++ ,因为我做的是 Python 教程,就用 Python 转换了一下

import cv2import 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)

最终结果预览比对


上面是第一种实现方法,这种方法思路主要用到阈值分割,从最终结果来看确实去掉了水印,但还是有一定的瑕疵:

  • 比如圆内文字背景与圆外背景是不一样的,存在很大色差,并且圆内的文字提取结果来看是不完整的;
  • 此方法不具有普遍性,因为这类方法只能针对于圆形水印,假设水印是不规则多边形此方法可能就会失效

下面介绍第二种思路,与第一种有相似的地方,也用到了阈值分割、图像像素位运算 相关算法,但同却又有自己的独特地方,从客观角度分析来看,这种方法的最终结果会更好一点

作者: dhanushka

思路主要分为四部分

  • 1,源图像记为 A,用形态学滤波器删除图像中文字区域,得到的图像记为 B;

  • 2,获取A,B 图像的之差,用 A-B ,得到区别后再用阈值分割进行处理,得到 C;

  • 3,阈值分割背景图像,提取水印覆盖的黑色部分记为 D;

  • 4,从 A 中提取在区域 D 中的像素,再用阈值分割方法分割像素,最终将提取到的像素贴到 B 中,得到最终去除水印的图像

代码贴在下方

import cv2import 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 是完全可行的,若是再加上一个批量操作,变得更可了,大大解放我们的双手!

并且这两种思路中用到的的一些方法,是值得我们借鉴的,比如 图像像素或与和操作、形态学过滤、霍夫圆检测等技术,可借助于这些方法应用到其它场景,例如提取图像中圆形区域、行人路上斑马线检测、去除不规则图像连接区域等。

好了,以上就是本篇文章的全部内容了,如果觉得不错,请不要吝啬你的双手,点赞、转发、留言,感谢三连!

推荐阅读:

GitHub年度报告:C/C#/C++被后起之秀干翻了这 10 个 Python 可视化工具,你用过哪些?

opencv 阈值分割_用 OpenCV 去除图片中的水印,骚操作!相关推荐

  1. java opencv 阀值分割_利用OpenCV实现局部动态阈值分割

    利用OpenCV实现局部动态阈值分割,参考Halcon dyn_threshold算子的思路实现. #include "dialog.h" #include #include &q ...

  2. c++opencv汉字分割_基于OpenCV 的车牌识别

    车牌识别是一种图像处理技术,用于识别不同车辆.这项技术被广泛用于各种安全检测中.现在让我一起基于OpenCV编写Python代码来完成这一任务. 车牌识别的相关步骤 1.车牌检测:第一步是从汽车上检测 ...

  3. 用 OpenCV 去除图片中的水印,骚操作!

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|AI算法与图像处理 这两天在 Stackoverflow ...

  4. 使用opencv去除图片中的水印代码

    很抱歉,因为涉及代码编写,我无法通过文字详细的呈现.但是我可以给出一些概述性的信息. 使用OpenCV去除图片中的水印通常需要使用图像处理技术,如图像的二值化,膨胀和腐蚀,形态学处理等. 步骤: 读取 ...

  5. opencv 阈值分割 — threshold()

    OpenCV阈值分割函数:threshold() 函数原型: double threshold(InputArray src, OutputArray dst, double thresh, doub ...

  6. c++opencv汉字分割_机器学习小白,还不快pick一下——【视觉与图像:阈值分割】...

    " 前言:安利Python来开发OpenCV的原因其实细心的小伙伴早在?[视觉与图像]Python+OpenCV教程入门篇就找到了想要的答案.(点蓝字即可打开) " 今天周五了! ...

  7. OpenCV —— 阈值分割(直方图技术法,熵算法,Otsu,自适应阈值算法)

    阈值分割 1. 全局阈值分割 直方图技术法 熵算法 Otsu算法 2. 局部阈值分割 自适应阈值 阈值的分割的核心就是如何选取阈值,选取正确的阈值时分割成功的关键.可以使用手动设置阈值,也可以采用直方 ...

  8. opencv阈值分割类型

    threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type); src:输入图,只能输入单通道图 ...

  9. python opencv 图像切割_【OpenCV+Python】图像的基本操作与算术运算

    图像的基本操作 在上个教程中,我们介绍了使用鼠标画笔的功能.本次教程,我们将要谈及OpenCV图像处理的基本操作. 本次教程的所有操作基本上都和Numpy相关,而不是与OpenCV相关.要使用Open ...

最新文章

  1. 强悍!使用Flash和Silverlight制作控件
  2. if else流程判断
  3. C# Azure 存储-分布式缓存Redis的新建配置查看
  4. 打造杰出软件开发团队的12条指导建议
  5. LeetCode 221 最大正方形
  6. 虚拟机如何装linux6,如何在vmvare中安装redhat linux6虚拟机
  7. HCIE-RS面试--环路产生及防环机制
  8. Oracle RAC -常见CRS命令
  9. python安装笔记_Python学习笔记(一)python的安装和配置
  10. js实现中文简体繁体转换
  11. 批量计算机添加网络打印机方法,快速添加网络打印机的步骤详解
  12. html字体倾斜45度,CSS3+JS 很酷的45度角斜射式照片墙效果
  13. 使用cocoapods导入第三方后 报错_OBJC_CLASS_$_XXX
  14. 触屏计算机显示器CDU,如何在计算机上进行校准触摸屏?
  15. 家的温暖社会实践报告
  16. bigdecimal 平均数_用Java计算平均值
  17. 大学里的网络安全专业为什么没多少人就读?
  18. 广度优先搜索——动态类迷宫问题
  19. iOS Swift 将公历日期转换为中国农历
  20. Login.aspx

热门文章

  1. 啊u学科学计算机,动画 | 《阿U学科学》:开学啦!在游戏中学习科学知识吧
  2. 基于ADS的c语言程序设计实验,实验一:基于ADS软件传输线理论仿真设计与分析.docx...
  3. VB讲课笔记13:二级公共基础
  4. Java案例:HttpClient演示
  5. 17.立体匹配——匹配问题,好区域匹配 测验,窗口大小的影响,遮蔽(Occlusion),顺序约束_2
  6. 【英语学习】【English L06】U05 Appointments L2 I'd like to make an airport shuttle service reservation
  7. 【英语学习】【WOTD】tenacious 释义/词源/示例
  8. 【英语学习】【Daily English】U12 E-World L02 All you have to do is download this taxi app
  9. Pentium 4处理器架构/微架构/流水线 (9) - NetBurst执行核详解 - 执行单元与发射口
  10. bob战队 rust_c、rust、golang、swift性能比较