使用 vips (ruby-vips8) 合并多个图像
Combine multiple images using vips (ruby-vips8)
如何将函数应用于相同分辨率的两个图像的相应像素?就像 Photoshop 在用另一层覆盖一层时所做的那样。如果超过两张图片呢?
如果它是 Wolfram Mathematica,我会获取这些图像的列表并将它们转置以获得单个 "image",其中每个 "pixel" 将是一个 N 像素的数组 - 我会应用Mean[]
函数给他们。
但是我该如何使用 vips 呢?有这么多 Vips::Image methods and only 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"
我希望它的意思是 "calculating an average image" 但我不确定我在这里做了什么。
ruby-vips8自带一个complete set of operator overloads,所以你可以只对图像做算术运算。它还会自动消除公共子表达式,因此您无需在排序或分组时过于小心,您只需编写一个等式就可以了。
在你的例子中:
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()
to sum the array, then a vips_linear()
to do the divide, and finally a vips_cast()
将其敲回 uchar。
当您调用 write_to_file
时,您机器上的每个核心都将获得管道的副本,并且它们将排队处理来自解压器的源图像中的图块。每次完成一行输出图块时,后台线程将使用选定的图像写入库(在我的示例中为 libtiff)将这些扫描线发送回磁盘。
您应该会看到低内存使用率和良好的 CPU 利用率。
如何将函数应用于相同分辨率的两个图像的相应像素?就像 Photoshop 在用另一层覆盖一层时所做的那样。如果超过两张图片呢?
如果它是 Wolfram Mathematica,我会获取这些图像的列表并将它们转置以获得单个 "image",其中每个 "pixel" 将是一个 N 像素的数组 - 我会应用Mean[]
函数给他们。
但是我该如何使用 vips 呢?有这么多 Vips::Image methods and only 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"
我希望它的意思是 "calculating an average image" 但我不确定我在这里做了什么。
ruby-vips8自带一个complete set of operator overloads,所以你可以只对图像做算术运算。它还会自动消除公共子表达式,因此您无需在排序或分组时过于小心,您只需编写一个等式就可以了。
在你的例子中:
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()
to sum the array, then a vips_linear()
to do the divide, and finally a vips_cast()
将其敲回 uchar。
当您调用 write_to_file
时,您机器上的每个核心都将获得管道的副本,并且它们将排队处理来自解压器的源图像中的图块。每次完成一行输出图块时,后台线程将使用选定的图像写入库(在我的示例中为 libtiff)将这些扫描线发送回磁盘。
您应该会看到低内存使用率和良好的 CPU 利用率。