了解如何编写python脚本以创建用于设置照片样式的基于四叉树的过滤器
所以最近,我发现了一个由 Michael Fogleman 完成的名为 四叉树艺术(QuadTree Art)的项目。它启发我尝试编写该项目的个人版本。这就是我将在本文中讨论的内容,如何实现你自己的四叉树艺术程序,正如我在这里所做的:github.com/ribab/quadart
上面是我用 kstudio 根据在 freepik.com 上找到的一张苹果图片生成的图像。原图如下:
只有当颜色的标准差太大时,我的算法才会继续将图像分成四分之一。
为了说明算法的工作原理,我为QuadArt实现了最大递归特性,使用这个shell命令创建了10个不同递归深度的图像:
for i in {1..10}; do ./quadart.py apple.jpg -o r-out/apple-r$i.jpg -m $i --thresh 25; done
然后我通过这个命令用ImageMagick生成了这个 PNG
convert -delay 40 -loop 0 *.jpg apple-r.gif
下面是GIF,动态展示了quadart魔法。
简单说说 QuadArt 算法
尽管我的程序 QuadArt 占用了181行代码,但用于生成Quadart的实际递归算法只需要8行就能描述。
上面的算法是直接从我的代码中提取出来的。class QuadArt 是包含 imageio 图像数据、 wand 绘制画布和标准差阈值的类。x, y, w, h被传递到函数中,以指定当前被分析子图像左上角的x,y位置,以及它的宽度和高度。
调试慢速 QuadArt 生成
最初,我使用 Python Wand 模块实现了整个Quadart程序,该模块在底层使用 ImageMagick。这个库把圆渲染得很漂亮。在通过实现基于四叉树的照片过滤器的第一个步骤进行编码之后,我遇到了一个问题,代码处理时间太长。事实证明,让 Wand 检查每个像素的颜色对于计算标准差来说需要很长的时间,而且 Wand 没有执行这种分析的内置特性。另外,当屏幕上没有显示任何内容时,很难判断代码是否被卡住。
为了判断我的代码是否有任何进展,我需要某种类型的加载条。但是,使用迭代算法加载条形图要容易得多,当中,你可以精确地知道算法需要多少次迭代才能完成。使用基于四叉树的递归算法,我知道递归深度1最多运行4次,深度2最多运行16次,依此类推。因此,考虑到这个想法,我实现了一个算法的补充,当程序执行时在终端上显示一个加载条。此加载条跟踪递归算法在深度3处执行的次数。
这个加载条GIF是由zanz在xwininfo的帮助下生成的。
为了让加载条函数跟踪 recursive_draw()的进度,我只需要跟踪它的退出点,并跟踪当前的递归深度。这两种退出点是指什么时候 recursive_draw()进一步递归或没有递归。下面是被修改为调用 loading_bar()的 recursive_draw() 函数:
loading_bar() 的逻辑是只能在depth<=3时计算进度, 但我仍然需要检查在 recursive_draw() 的第一个退出点中当前 self.recurse_depth 是否等于3,否则由于递归,将有对loading_bar()多余的调用。
这就是 loading_bar() 的样子
为了监视你自己的递归函数,你可以很容易地将它放在python代码的顶部,将recursion_spread 修改为函数每次递归时调用自身的次数,然后从递归函数的所有端点调用loading_bar() ,确保在每个递归分支它只被调用一次。
用 imageio 和 numpy 进行图像分析
对于是否拆分为更多四分分支的 recursive_draw() 阈值,函数too_many_colors() 计算红色、绿色和蓝色的标准差,如果标准差超过阈值,则返回True。对于 Quadart 的生成,我发现一个不错的阈值大约是 25 标准差,否则图像会变得太像素化或太细。python图像分析库imageio非常适合这种分析,因为它可以直接插入numpy进行快速统计计算。
我通过 imageio 和 numpy 进行图像分析的初始设置如下:
导入 imageio 和 numpy
用 imageio 读取图像 (filename 是我们要分析的图像的名字)
选择我们要分析的图像的一部分。有效地修剪img。left, right, up, and down 指定裁剪img的位置。
查找图像宽度和高度
通过将长边减去短边的差,确保 img 是方形的。
现在 imageio 对象img可用于计算标准差,如下所示:
选择颜色
根据颜色计算平均值
根据颜色计算标准差
这就是我的程序 QuadArt 如何计算 recursive_draw() 函数是否会因颜色偏差过大而进一步递归。看一下 too_many_colors()
上面的函数是这样做的:
选择颜色
根据颜色计算平均值
若平均值非常接近白色,则立即返回 False
根据颜色计算标准差
若标准偏差大于任何颜色的阈值,则返回 True (进一步递归)
否则返回 False
最后显示圆
现在简单的部分:在 wand中显示圆。我执行图像过滤器的策略是从空白画布构建生成的图像。这是一个如何用 Wand 画东西的模板
生成的QuadArt画布的纵横比始终是方形的,这样 QuadArt 的递归算法可以将图像均匀地分割为四分之一。默认情况下,我使用output_size=512,因为512是2的幂次,可以连续二分,分为更多四分分支,而不会损失分辨率。
然而,输入图像的大小可能会有所不同。为了解决这个问题,我将所需的输出图大小除以裁切的输入图像的宽度,如下所示:
我在 recursive_draw() 中使用的函数是 draw_avg()。这是一个简单的函数,它计算输入图像在边界内的平均颜色,然后在盒子中绘制一个圆(绘制一个正方形,如果用户愿意)。
函数 get_color() 首先获取输入图像的裁剪部分(以imageio格式),然后计算该裁剪部分中红色、绿色和蓝色的平均值,然后根据计算出的平均颜色创建wand.color.Color 对象。
在已定义的盒子中绘制一个圆或一个正方形,这是前面由 too_many_colors() 计算得出的具有足够低偏差的四分分支。在绘制到画布之前,坐标连同宽度和高度乘以 output_scale。将 wand.drawing 的填充颜色设置为之前计算的平均颜色。然后将圆或正方形绘制到画布上。
就是这样!这就是我如何实现四叉树照片样式器,以及你如何实现同样功能,或者是如何受到启发并创建自己的算法来样式化你的照片。
你可以在此处查看整个代码: github.com/ribab/quadart/blob/master/quadart.py
英文原文:http://www.codingwithricky.com/2019/08/03/1-quadtree-photo-stylizer-python/
译者:青书