Py学习  »  Python

使用四叉树算法在Python中实现照片样式器

Python程序员 • 4 年前 • 382 次点击  

了解如何编写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 进行图像分析的初始设置如下:

  1. 导入 imageio 和 numpy

  2. 用 imageio 读取图像 (filename 是我们要分析的图像的名字)

  3. 选择我们要分析的图像的一部分。有效地修剪img。left, right, up, and down 指定裁剪img的位置。

  4. 查找图像宽度和高度

  5. 通过将长边减去短边的差,确保 img 是方形的。

  6. 现在 imageio 对象img可用于计算标准差,如下所示:

    1. 选择颜色

    2. 根据颜色计算平均值

    3. 根据颜色计算标准差

这就是我的程序 QuadArt 如何计算 recursive_draw() 函数是否会因颜色偏差过大而进一步递归。看一下 too_many_colors()

上面的函数是这样做的:

  1. 选择颜色

  2. 根据颜色计算平均值

  3. 若平均值非常接近白色,则立即返回 False

  4. 根据颜色计算标准差

  5. 若标准偏差大于任何颜色的阈值,则返回 True (进一步递归)

  6. 否则返回 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/ 
译者:青书
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/38583
 
382 次点击