使用vips (ruby-vips8)合并多个图像

3
如何将函数应用于两个分辨率相同的图像的对应像素?就像Photoshop在覆盖一个图层时所做的那样。如果有多于两个图像呢?
如果使用Wolfram Mathematica,我会取这些图像的列表并将它们转置以获得一个单独的“图像”,其中每个“像素”都是N个像素的数组--然后我会对它们应用Mean[]函数。
但是如何在vips中实现这一点?有很多Vips :: Image方法,而且只有here能找到一些关于它们的最小描述。例如:
images = Dir["shots/*"].map{ |i| Vips::Image.new_from_file(i) }
ims = images.map(&:bandmean)
(ims.inject(:+) / ims.size).write_to_file "temp.png"

我希望它的意思是“计算平均图像”,但我不确定我在这里做了什么。

你能解释一下你想要实现什么吗?你是想加载一组图像并找到每个像素的平均值吗? - jcupitt
@user894763,确实是这样。想象一下它们是同一对象的嘈杂照片,我想要清理它。但总的来说,我想知道如何应用任何 my_custom_function(pixel1, pixel2, ..., pixelN) - Nakilon
2个回答

3

ruby-vips8带有完整的运算符重载集合,因此您可以直接对图像进行算术运算。它还可以自动消除常见子表达式,因此您不需要过于关注顺序或分组,只需编写一个方程即可正常工作。

在您的示例中:

require 'vips8'

images = Dir["shots/*"].map{ |i| Vips::Image.new_from_file(i) }
sum = images.reduce (:+)
avg = sum / images.length
avg.write_to_file "out.tif"

在使用常数进行加减乘除运算时,产生的图像类型总是浮点型。因此,在保存之前你可能需要将其强制转换为uchar(或者ushort?)以避免生成巨大的输出tiff。你可以这样写:

avg = sum / images.length
avg.cast("uchar").write_to_file "out.tif"

默认情况下,new_from_file会以随机访问的方式打开图像。如果源图像是JPG或PNG格式,这将涉及在处理开始之前将其完全解压缩到内存中(或者如果它们非常大,则解压缩到磁盘临时文件中)。

在此情况下,您只需要在编写结果时从上到下扫描输入图像,因此可以通过系统流式传输图像。将new_from_file更改为:

images = Dir["shots/*"].map { |i| Vips::Image.new_from_file(i,  :access => "sequential") }

为了表明您只会按顺序使用图像像素,并且您应该看到内存和CPU使用量的显着下降。

PNG是一种非常慢的格式,如果可能的话,我建议使用TIFF。

您可以尝试使用bandrank。它类似于对一组图像进行中值滤波:您提供一组图像,在每个像素位置上,它按像素值对图像进行排序并选择第N个图像。这是一种非常有效的方法来消除短暂的伪影。

您可以使用condition.ifthenelse(then, else)来计算更复杂的函数。例如,要将所有大于其局部平均值的像素设置为局部平均值,您可以编写以下代码:

(image > image.gaussblur(1)).ifthenelse(image.gaussblur(1), image)

你可能会好奇vips如何执行上面的程序。代码如下:
(images.reduce(:+) / images.length).cast("uchar")

将构建一个图像处理操作的管道:一系列vips_add()对数组求和,然后使用vips_linear()进行除法运算,最后使用vips_cast()将其转换回uchar。

当调用write_to_file时,您的计算机上的每个核心都将获得管道的副本,并排队处理来自解压器的源图像瓦片。每次完成一行输出瓦片时,后台线程将使用所选的图像写入库(在我的示例中为libtiff)将这些扫描线发送回磁盘。

您应该看到低内存使用和良好的CPU利用率。


你提供的关于现有 +-*/ 运算符的提示非常有用,谢谢。但是如果我的函数不像 .reduce(:+) / .size 那么简单,而是有 if-branches 等等,那该怎么办呢?在 Ruby 中是否足够容易实现,还是我需要跳到 C 语言中去实现? - Nakilon
1
您可以使用.ifthenelse添加分支,还有一系列操作符来构建各种本地过滤器。大多数事情都应该很简单,如果遇到困难,可以提出另一个问题。此外,还有vips跟踪器,它非常活跃: https://github.com/jcupitt/libvips/issues - jcupitt

-1

合并多张图片的Cli命令: vips arrayjoin '8900.jpg 8901.jpg 8902.jpg' out.jpg

如果需要垂直合并,请添加选项--across 1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接