使用Depix进行马赛克的消除测试
0. 前言
最近看到各种公众号都在推一个叫Depix的Github项目,用途是能够消除文字马赛克,抱着试试看的态度测试了一下这个项目。
太长不看版:公众号对该项目的效果有一定程度的夸大,但是还是要注意使用各种方法对个人隐私进行保护
项目地址:https://github.com/beurtschipper/Depix
项目自带的Example如下:
这个项目的文档上说,只需要马赛克后的图像,马赛克图像上包含的字符的De Bruijn序列,就可以生成去马赛克的图像。(测试效果如上图所示)
接下来就测试一下。
1. 准备工作
1.1 环境搭建
首先,安装Python3和PIP3。
我这里在linux云端进行的测试,测试的Python环境是Python3,安装过程这里不再赘述。
运行项目需要环境pillow和image,输入命令使用pip进行安装:
pip3 install pillow
pip3 install image
如果下载速度过慢,则需要更改为国内源再测试。
1.2 例程运行
为了检测运行效果,先执行软件自带的例程进行测试:
python3 depix.py -p images/testimages/testimage3_pixels.png -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png -o output.png
测试成功的话,同目录下会出现output.png,如果能正常打开并看到如上图所示的Hello from the other side,说明测试通过。
如果报错No Module named xxx,则说明运行环境没搭建好,需要先搜索缺少的module在哪个包里,再使用pip下载缺少的包。
2. 实际测试
按照项目网站上的说明,要去除文字上的马赛克,需要做如下准备:
- 将待解码图片中的马赛克部分单独截取下来作为一个单独的矩形图片。
- 生成一个De Bruijn sequence (德布鲁因序列),并使用该序列生成一张和待解码图片中的马赛克部分完全一致的字体、大小的图像。(这里后续详细说明)
- 调用脚本解马赛克。
详细说明如下。
2.1 待解码图片准备
在这里我们使用记事本截图+某聊天软件自带的马赛克功能。
马赛克的模糊度调低点,保证正好把文字全抹掉。
效果如下所示:
马赛克前:
马赛克后:
文字内容是998877665544。
2.2 De Bruijn sequence (德布鲁因序列)
2.2.1 德布鲁因序列介绍
德布鲁因序列,一般有两个属性:元素和阶。
元素:就是这个序列中有x个不同的字符。
阶:在x个不同的字符中抽出y个进行组合,这个y就是阶
如果某个x元素y阶的德布鲁因序列,把这个序列头尾相接。那么,在x中抽出y个字符组合出来的字符串都能在这个序列里找到,且只能找到一次。
Example:
只有3个元素(abc)2阶的德布鲁因序列为:a a b a c b b c c (用下文的在线生成器生成的序列,会在序列尾部增加一个和序列头部相同的字符)
把序列首尾相接组成一个环,在abc中任意抽出2个字符组成一个字符串,都能在这个序列环里找到,且只能找到一次。
2.2.2 德布鲁因序列生成
生成网站:https://damip.net/article-de-bruijn-sequence
只要输入我们要生成的序列中包含的字符和长度,就可以生成对应的序列。
在这里按照公众号介绍的算法原理,只需要生成2阶(Code length: 2)的序列就可以了。
(在这里生成的是3阶)
由于我们测试的马赛克字符中只有数字,那么Alphabet一栏只需要输入所有数字 1234567890
就好。
生成的序列如下:
注:
x字符y阶的德布鲁因序列长度为:len=xylen=x^ylen=xy。
即:10字符的3阶德布鲁因序列长度为1000,这里在线生成器为了实现和头尾相接相同的效果增加了y-1字符的长度
2.2.3 德布鲁因序列图像制作
得到了德布鲁因序列之后,就需要将序列转换成和待解码文字相同样式(字体大小颜色等,甚至最好使用一样的编辑器和截图工具)的文字图片。
由于待解码图像是记事本上的文字,因此我们只需要在同一个记事本上粘贴上述序列并截图即可。
生成的图像如下:
2.3 实际解码
2.3.1 解码命令
假设保存的德布鲁因序列图像名字为search.png,待解码图像名字toFind.png。
那么解码命令如下所示:
python3 depix.py -p toFind.png -s search.png -o op.png
2.3.2 解码效果分析
解码效果如下:
效果没公众号吹得那么厉害,只看到了第一个字符的一部分。
但是经过测试,多次解码后可以看到更多的数据。
将第一次生成的文件再次进行解码后效果如下:
写一个脚本进行循环解码:
#/bin/bash
for I in {1..50};dolet op=$I+1python3 depix.py -p op$I.png -s search.png -o op$op.png
done
echo done.
解码51次后的效果:
看来算法还是有一定效果的。
但是多次解码后,输出的图像和这次的图像相差无几,说明这就是算法的极限了。
2.4 简单分析
首先我们打开主要的脚本文件depix.py。从代码上看,函数需要三个参数:待解码图片、德布鲁因序列图片和输出图片,并把待解码图片、德布鲁因序列图片的路径分别赋给pixelatedImagePath、searchImagePath 两个变量:
parser = argparse.ArgumentParser(description = usage)
parser.add_argument('-p', '--pixelimage', help = 'Path to image with pixelated rectangle', required=True) #待解码图片,必须
parser.add_argument('-s', '--searchimage', help = 'Path to image with patterns to search', required=True) #德布鲁因序列图片,必须
parser.add_argument('-o', '--outputimage', help = 'Path to output image', nargs='?', default='output.png') #输出图片,非必须,默认为output.png
args = parser.parse_args()
pixelatedImagePath = args.pixelimage
searchImagePath = args.searchimage
由于已经知道了该方法的原理是通过将德布鲁因序列图片用相同的形式打码,之后再和原图进行比对,找出相同的图块,那么只要我们跟踪这个序列图片做了什么操作,就可以知道这个算法的适用范围。
跟踪searchImagePath这个变量,发现其在下文中作为LoadedImage函数的参数,将图片载入给了变量searchImage,而searchImage这个变量的首次调用是在findRectangleMatches函数中:
logging.info("Loading search image from %s" % searchImagePath)
searchImage = LoadedImage(searchImagePath) # 载入德布鲁因序列图像
# 省略
logging.info("Finding matches in search image")
rectangleMatches = findRectangleMatches(rectangeSizeOccurences, pixelatedSubRectanges, searchImage) #首次调用
这个调用它的函数在depixlib文件夹下的functions.py,定义如下:
def findRectangleMatches(rectangeSizeOccurences, pixelatedSubRectanges, searchImage):rectangleMatches = {}for rectangeSizeOccurence in rectangeSizeOccurences:rectangleSize = rectangeSizeOccurencerectangleWidth = rectangleSize[0]rectangleHeight = rectangleSize[1]pixelsInRectangle = rectangleWidth*rectangleHeight# logging.info('For rectangle size {}x{}'.format(rectangleWidth, rectangleHeight))# filter out the desired rectangle sizematchingRectangles = []for colorRectange in pixelatedSubRectanges:if (colorRectange.width, colorRectange.height) == rectangleSize:matchingRectangles.append(colorRectange)for x in range(searchImage.width - rectangleWidth):for y in range(searchImage.height - rectangleHeight):r = g = b = 0matchData = []for xx in range(rectangleWidth):for yy in range(rectangleHeight):newPixel = searchImage.imageData[x+xx][y+yy]rr,gg,bb = newPixelmatchData.append(newPixel)r += rrg += ggb += bbaverageColor = (int(r / pixelsInRectangle), int(g / pixelsInRectangle), int(b / pixelsInRectangle))for matchingRectangle in matchingRectangles:if (matchingRectangle.x,matchingRectangle.y) not in rectangleMatches:rectangleMatches[(matchingRectangle.x,matchingRectangle.y)] = []if matchingRectangle.color == averageColor:newRectangleMatch = RectangleMatch(x, y, matchData)rectangleMatches[(matchingRectangle.x,matchingRectangle.y)].append(newRectangleMatch)# if x % 64 == 0:# logging.info('Scanning in searchImage: {}/{}'.format(x, searchImage.width - rectangleWidth))return rectangleMatches
从代码中的xy循环中,我们可以看出该函数将德布鲁因序列图像划分成了rectangleWidth * rectangleHeight 大小的小方块,并对小方块中的颜色取平均值。这就是项目中提到的linear box filter马赛克。
简单说明一下,linear box filter马赛克的打码方式是:
- 将待打码的图像按照像素分割成一系列的正方形小区域
- 将每块区域的所有像素点的颜色取平均值,得出每个区域的平均颜色
- 使用每块区域的平均颜色填充这个区域,实现马赛克效果
因此,如果每一块区域的面积越大,图像就越模糊,反之则越清晰
接下来我们来看这个rectangleWidth * rectangleHeight是怎么得出的。
取消下面代码的注释。
logging.info('For rectangle size {}x{}'.format(rectangleWidth, rectangleHeight))
再次运行后,发现这里输出了待解码图片中的每一个马赛克方格的大小,说明在解马赛克的过程中,该脚本会计算出待解码图片中每个马赛克方格的大小,再根据这个大小对德布鲁因序列图片进行马赛克处理。
输出结果:
使用画图计算出的马赛克大小:
至于这个像素大小的计算方式,需要向上追溯到对待解码图片的处理逻辑。
脚本读取了待解码图片后,首先会先查找所有的相同颜色的矩形方块,并将找到的同色矩形方块存入list:
pixelatedImage = LoadedImage(pixelatedImagePath) # 待解码图片读取
#...
pixelatedSubRectanges = findSameColorSubRectangles(pixelatedImage, pixelatedRectange) #同色矩形区域寻找
# ...
def findSameColorSubRectangles(pixelatedImage, rectangle): #函数定义,在functions.py中sameColorRectanges = []x = rectangle.xmaxx = rectangle.x + rectangle.width + 1maxy = rectangle.y + rectangle.height + 1while x < maxx:y = rectangle.ywhile y < maxy:sameColorRectange = findSameColorRectangle(pixelatedImage, (x, y), (maxx, maxy))if sameColorRectange == False:continue# logging.info("Found rectangle at (%s, %s) with size (%s,%s) and color %s" % (x, y, sameColorRectange.width,sameColorRectange.height,sameColorRectange.color))sameColorRectanges.append(sameColorRectange)y += sameColorRectange.heightx += sameColorRectange.widthreturn sameColorRectanges
接下来,程序会移除掉待解码图片里的无用色块:
pixelatedSubRectanges = removeMootColorRectangles(pixelatedSubRectanges) #移除无用色块def removeMootColorRectangles(colorRectanges): #函数定义pixelatedSubRectanges = []for colorRectange in colorRectanges:if colorRectange.color in [(0,0,0),(255,255,255)]: #如果颜色是纯白/纯黑,跳过该色块continuepixelatedSubRectanges.append(colorRectange) #将色块附加到pixelatedSubRectanges这个list中return pixelatedSubRectanges
从这里看出,程序对无用色块(背景色)的判断逻辑是,只要是纯白/纯黑,就会跳过。这里得出两个推论:
- 该算法在纯色的背景下表现较好
- 如果需要在其他背景色下解马赛克,需要在这里修改对应背景色的RGB值
接下来,从色块中找出每个方块的大小的出现次数:
def findRectangleSizeOccurences(colorRectanges):rectangeSizeOccurences = {}for colorRectange in colorRectanges:size = (colorRectange.width, colorRectange.height)if size in rectangeSizeOccurences:rectangeSizeOccurences[size] += 1else:rectangeSizeOccurences[size] = 1return rectangeSizeOccurences
这里的方块大小,就是前面findRectangleMatches中对德布鲁因序列处理的方块大小。
接下来的处理逻辑就是对德布鲁因序列图片打码,再对各种色块进行匹配的流程,后续再进一步分析。
3. 总结
后续再次对去马赛克效果进行多次测试,发现该脚本的适用范围是有限的。
从测试结果和算法上来看,这个算法有如下的局限性。
- 这个算法的原理是将德布鲁因序列图用相同的马赛克形式进行打码,之后再将打码的序列图像和待解码图像进行对比,查找可能的文字序列。这个原理在很多公众号上都有描述,这里就不再展开。从这里能推断出,如果马赛克形式和算法中的马赛克形式不相同,或是马赛克模糊度提高,就会增大解码的难度。
- 马赛克形式和算法中的马赛克算法不相同
- 马赛克模糊度提高(即取平均值的色块大小增加)
- 马赛克文字的背景颜色尽量不是纯色
- 对马赛克的文字进行多次打码,也会增加破解的难度。
- 从解码过程中可知,解码必须要生成一个包含待解码文字中所有字符的德布鲁因序列。因此,我们必须要了解待解码文字中包含了什么字符。这也限制了该方法更适用于英文、数字等字符序列的解码。像中文就……
事实上,在多次尝试后发现成功解码的仅有此文中这一次,且文中的例子也只能解出其中的几个数字。
但是不能因此就放松对个人隐私的保护。在发布图像时,建议使用多重马赛克/马赛克+涂抹等方式保护个人信息,进一步增加安全性。
使用Depix进行马赛克的消除测试相关推荐
- Depix:还原马赛克工具的试用及总结
背景 一周前发现git上有个叫Depix的项目非常火,可以用来去除马赛克. 好奇之下准备下来试用一下这个工具 参考: https://github.com/beurtschipper/Depix 算法 ...
- python 马赛克还原_马赛克消除还原工具Depix测试
项目地址: https://github.com/beurtschipper/Depix python depix.py -p [pixelated rectangle image] -s [sear ...
- python去除视频马赛克_DeepMosaics
DeepMosaics 这是一个通过深度学习自动的为图片/视频添加马赛克,或消除马赛克的项目. 它基于"语义分割"以及"图像翻译". 更多例子 原始 自动打码 ...
- junit:junit_简而言之,JUnit:测试结构
junit:junit 尽管存在有关JUnit测试的书籍和文章,但我仍然经常遇到程序员,他们至多对这个工具及其正确用法都不甚了解. 因此,我想到了编写多部分教程的想法,从我的角度解释了要点. 也许在本 ...
- c++返回指针时候注意提防_编写干净的测试–提防魔术
c++返回指针时候注意提防 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 简洁的代码易于阅读. 这可能会让您感到有些惊讶,但我认为该 ...
- 简而言之,JUnit:测试结构
尽管存在关于JUnit测试的书籍和文章,但我仍然经常遇到程序员,他们至多对这个工具及其正确用法都不甚了解. 因此,我想到了编写多部分教程的想法,从我的角度解释了要点. 也许在这个小型系列中采用的动手方 ...
- 编写干净的测试–提防魔术
很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义. 但是,有一个似乎是通用的定义: 干净的代码易于阅读. 这可能会让您感到有些惊讶,但是我认为该定义也适用于测试代码. 使 ...
- 第一百一十四期:盘点十大最新Web UI测试工具
本文为您盘点目前十大最新Web UI测试工具的各自优缺点,以方便您根据实际情况进行选择. 作者:陈峻 在过去的几年中,业界至少出现了十二种全新的UI测试自动化工具.虽然每一种工具都有各自的侧重点,但是 ...
- 2020年你不可不知的自动化框架,可替代Selenuim的测试框架Top10
Selenium是一种开源自动测试工具.它可以跨不同的浏览器和平台在Web应用程序上执行功能,回归,负载测试.Slenium是最好的工具之一,但确实有一些缺点. 业界有一些强大的工具可以替代Selen ...
- sinon spy_Sinon教程:使用嘲弄,间谍和存根进行JavaScript测试
sinon spy 本文由Mark Brown和MarcTowler进行了同行评审. 感谢所有SitePoint的同行评审员使SitePoint内容达到最佳状态! 编写单元测试时,最大的绊脚石之一就是 ...
最新文章
- 系统升级到10.13之后cocoapods安装失败问题解决办法
- ActiveMQ学习总结(10)——ActiveMQ采用Spring注解方式发送和监听
- STM32 内存管理实验
- 一文学会Maven的版本发布
- Ubuntu16.04如何换pip源
- OPPO R11发布盛典昨晚举行,精彩程度不亚于大型晚会!
- mysql 讲义_MySQL 讲义
- #创建记事本程序在哪打开_微软Windows 10记事本商店版归来?系统可选,能卸载...
- cannot find -lunwind-x86_64
- hive分区用2个字段有何限制_Hive分区表和桶表的使用
- nginx 没有cookie_Nginx入门学习(1):一些概念
- Ubuntu 配置环境变量
- 电脑操作精典密笈60式
- oracle闪回scn,Oracle闪回查询及scn_to_timestamp
- K-近邻算法(KNN)
- 计算机机房搬迁预算,信息中心机房整体搬迁方案.doc
- centos7用html5播放器,centos7安装多媒体播放器SMPlayer
- 友情链接对于网站来说有什么作用?
- 前端 Vue 浏览器调试工具 Vue.js devtools 安装
- C++面经与嵌入式软件面经(蒋豆芽专栏总结)完成了!
热门文章
- 局域网联通公网小部分知识点
- 非常好用的模糊pid温度控制算法_PID参数调试“口诀”,总结的真好!
- looking for domain authoritative name server and domain name location
- 易支付源码 28k支付第四方支付源码-Oreo支付系统
- 链脉名片谈互联网经典营销理论“鱼塘理论”
- provisional headers are shown问题排查
- 去除趋势杀软的退出密码
- 区块链开发语言python_区块链开发语言有哪些?哪种语言更适合区块链开发?
- 戴尔服务器u盘装系统看不见磁盘,戴尔电脑u盘装系统找不到硬盘怎么解决
- 详述GPS原理及RTK技术应用