使用Depix进行马赛克的消除测试

使用Depix进行马赛克的消除测试0.前言最近看到各种公众号都在推一个叫Depix的Github项目,用途是能够消除文字马赛克,抱着试试看的态度测试了一下这个项目。太长不看版:公众号对该项目的效果有一定程度的夸大,但是还是要注意使用各种方法对个人隐私进行保护项目地址:https://github.com/beurtschipper/Depix项目自带的Example如下:这个项目的文档上说,只需要马赛克后的图像,马赛克图像上包含的字符的DeBruijn序列,就可以生成去马赛克的图像。(测试效果如上图所示)接下来就测试一

大家好,又见面了,我是你们的朋友全栈君。

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. 实际测试

按照项目网站上的说明,要去除文字上的马赛克,需要做如下准备:

  1. 将待解码图片中的马赛克部分单独截取下来作为一个单独的矩形图片。
  2. 生成一个De Bruijn sequence (德布鲁因序列),并使用该序列生成一张和待解码图片中的马赛克部分完全一致的字体、大小的图像。(这里后续详细说明)
  3. 调用脚本解马赛克。

详细说明如下。

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阶的德布鲁因序列长度为: l e n = x y len=x^y len=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};do
	let op=$I+1
  python3 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 = rectangeSizeOccurence
		rectangleWidth = rectangleSize[0]
		rectangleHeight = rectangleSize[1]
		pixelsInRectangle = rectangleWidth*rectangleHeight
		# logging.info('For rectangle size {}x{}'.format(rectangleWidth, rectangleHeight))
		# filter out the desired rectangle size
		matchingRectangles = []
		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 = 0
				matchData = []
				for xx in range(rectangleWidth):
					for yy in range(rectangleHeight):
						newPixel = searchImage.imageData[x+xx][y+yy]
						rr,gg,bb = newPixel
						matchData.append(newPixel)
						r += rr
						g += gg
						b += bb
				averageColor = (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马赛克的打码方式是:

  1. 将待打码的图像按照像素分割成一系列的正方形小区域
  2. 将每块区域的所有像素点的颜色取平均值,得出每个区域的平均颜色
  3. 使用每块区域的平均颜色填充这个区域,实现马赛克效果
    因此,如果每一块区域的面积越大,图像就越模糊,反之则越清晰

接下来我们来看这个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.x
	maxx = rectangle.x + rectangle.width + 1
	maxy = rectangle.y + rectangle.height + 1
	while x < maxx:
		y = rectangle.y
		while 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.height
		x += sameColorRectange.width
	return sameColorRectanges

接下来,程序会移除掉待解码图片里的无用色块:

pixelatedSubRectanges = removeMootColorRectangles(pixelatedSubRectanges)  #移除无用色块

def removeMootColorRectangles(colorRectanges):   #函数定义
	pixelatedSubRectanges = []
	for colorRectange in colorRectanges:
			if colorRectange.color in [(0,0,0),(255,255,255)]:  #如果颜色是纯白/纯黑,跳过该色块
				continue
			pixelatedSubRectanges.append(colorRectange)   #将色块附加到pixelatedSubRectanges这个list中
	return pixelatedSubRectanges

从这里看出,程序对无用色块(背景色)的判断逻辑是,只要是纯白/纯黑,就会跳过。这里得出两个推论:

  1. 该算法在纯色的背景下表现较好
  2. 如果需要在其他背景色下解马赛克,需要在这里修改对应背景色的RGB值

接下来,从色块中找出每个方块的大小的出现次数:

def findRectangleSizeOccurences(colorRectanges):
	rectangeSizeOccurences = { 
   }
	for colorRectange in colorRectanges:
		size = (colorRectange.width, colorRectange.height)
		if size in rectangeSizeOccurences:
			rectangeSizeOccurences[size] += 1
		else:
			rectangeSizeOccurences[size] = 1
	return rectangeSizeOccurences

这里的方块大小,就是前面findRectangleMatches中对德布鲁因序列处理的方块大小。

接下来的处理逻辑就是对德布鲁因序列图片打码,再对各种色块进行匹配的流程,后续再进一步分析。

3. 总结

后续再次对去马赛克效果进行多次测试,发现该脚本的适用范围是有限的。

从测试结果和算法上来看,这个算法有如下的局限性。

  1. 这个算法的原理是将德布鲁因序列图用相同的马赛克形式进行打码,之后再将打码的序列图像和待解码图像进行对比,查找可能的文字序列。这个原理在很多公众号上都有描述,这里就不再展开。从这里能推断出,如果马赛克形式和算法中的马赛克形式不相同,或是马赛克模糊度提高,就会增大解码的难度。
    1. 马赛克形式和算法中的马赛克算法不相同
    2. 马赛克模糊度提高(即取平均值的色块大小增加)
    3. 马赛克文字的背景颜色尽量不是纯色
  2. 对马赛克的文字进行多次打码,也会增加激活成功教程的难度。
  3. 从解码过程中可知,解码必须要生成一个包含待解码文字中所有字符的德布鲁因序列。因此,我们必须要了解待解码文字中包含了什么字符。这也限制了该方法更适用于英文、数字等字符序列的解码。像中文就……

事实上,在多次尝试后发现成功解码的仅有此文中这一次,且文中的例子也只能解出其中的几个数字。

但是不能因此就放松对个人隐私的保护。在发布图像时,建议使用多重马赛克/马赛克+涂抹等方式保护个人信息,进一步增加安全性。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149162.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月24日 下午10:00
下一篇 2022年6月24日 下午10:00


相关推荐

  • DirectX修复工具下载(exagear模拟器数据包在哪里)

    DirectX修复工具通用数据包2019Q3版大小:135MB/7z格式压缩下载地址1:https://download.csdn.net/download/vbcom/11692840说明:本资源中包含DirectX修复工具数据包2019Q3版,适合已经有主程序而缺少数据包的用户使用。本数据包为通用数据包,适合各种版本的Direc…

    2022年4月13日
    468
  • 运营 | 小白学数据分析之DNU/DAU

    运营 | 小白学数据分析之DNU/DAUhttp www sykong com 2014 05 19081http www sykong com tag E5 B0 8F E7 99 BD E5 AD A6 E8 BF 90 E8 90 A5 运营 小白学数据分析之 DNU DAU2014 05 0615 45 手游运营 19 100 文 jetp

    2026年3月17日
    2
  • 右下面弹出框实现代码

    右下面弹出框实现代码

    2021年9月13日
    48
  • Linux renice_free linux

    Linux renice_free linuxLinuxnice和renice命令教程(7个示例)Linux命令行的威力可以从以下事实来衡量:您甚至可以使用命令行工具轻松调整进程的调度优先级。是的,这是可能的,在本教程中,我们将讨论如何使用nice和renice实用程序来实现这一点。但在这之前,值得一提的是本文中的所有示例都已在Ubuntu16.04LTS系统上进行了测试。Linuxnice和renice命令尽管nice命令允许您以修…

    2025年6月8日
    5
  • visual studio2019的安装以及使用

    visual studio2019的安装以及使用一 下载安装包下载地址选择 visualstudio 的 community 版本二 下载好后运行三 组件的选择如果是用来学 C C 的话 选择以下两个就够了之后如果还需要其他一些功能的话 可以后续在进行添加 打开 visualstudio 进入修改四 进行安装如果不需要修改安装位置的话点击安装就可以了 不过由于 vs2019 占用的空间较大最好不要装在

    2025年7月4日
    6
  • blender模型(sklearn模型融合)

    前言机器学习中很多训练模型通过融合方式都有可能使得准确率等评估指标有所提高,这一块有很多问题想学习,于是写篇博客来介绍,主要想解决:什么是融合?几种方式融合基本的模型融合组合及适用场景、优缺点等什么是融合?构建并结合多个学习器来完成学习任务,我们把它称为模型融合或者集成学习。不同的模型有各自的长处,具有差异性,而模型融合可以使得发挥出各个模型的优势,让这些相对较弱的模型(学习器)通…

    2022年4月16日
    126

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号