在ActiveStorage自定义分析器中获取"Vips::Error Exception: VipsJpeg: out of order read at line 1440"



我的自定义分析器

class BlurhashAnalyzer < ActiveStorage::Analyzer::ImageAnalyzer::Vips
def metadata
read_image do |image|
if rotated_image?(image)
{ width: image.height, height: image.width }
else
{ width: image.width, height: image.height }
end.merge blurhash(image)
end
end
private
def blurhash(vips_image)
# Create a thumbnail first, otherwise the Blurhash encoding is very slow
byebug
processed_image = ImageProcessing::Vips.source(vips_image).resize_and_pad(200, 200).call
thumbnail = ::Vips::Image.new_from_file processed_image.path
{
blurhash: Blurhash.encode(
thumbnail.width,
thumbnail.height,
::Vips::Region.new(thumbnail).fetch(0, 0, thumbnail.width, thumbnail.height).unpack('C*')
)
}
rescue StandardError => e
raise e if Rails.env.development?
Rails.logger.error "Error while encoding Blurhash: #{e}"
{}
end
end

blurhash method中的异常

(process:29640): VIPS-WARNING **: 17:25:01.323: error in tile 0 x 120
*** Vips::Error Exception: VipsJpeg: out of order read at line 1440

但如果我用同一个文件创建一个新的Vips::Image,它会起作用:

(byebug) ImageProcessing::Vips.source(::Vips::Image.new_from_file vips_image.filename).resize_and_pad(200, 200).call
#<Tempfile:/var/folders/_j/m395qb5d2yscnx89dswmrxgm0000gn/T/image_processing20220624-29640-aflgbs.jpg>
(byebug) ImageProcessing::Vips.source(::Vips::Image.new_from_file vips_image.filename, access: :sequential).resize_and_pad(200, 200).call
#<Tempfile:/var/folders/_j/m395qb5d2yscnx89dswmrxgm0000gn/T/image_processing20220624-29640-eflx0v.jpg>

我检查了Rails 7.0.3Analyzer::ImageAnalyzer::Vips源代码:

...
def read_image
download_blob_to_tempfile do |file|
require "ruby-vips"
image = instrument("vips") do
::Vips::Image.new_from_file(file.path, access: :sequential)
end

创建Vips::Image的方法与我上面所做的相同,但如果我直接使用它,就会出现异常。

我知道这个问题与https://github.com/libvips/pyvips/issues/96,但我没有在这里旋转。

当您在流模式下打开图像,但尝试多次读取时,就会发生这种情况。

文档中有一章有一些背景:

https://www.libvips.org/API/current/How-it-opens-files.md.html

例如,如果您处理这样的文件:

image = Vips::Image.new_from_file "something.jpg", access: :sequential
image = 255 - image
image.write_to_file "something-inverted.jpg"

libvips会将所有计算延迟到最终的write_to_file,然后流式传输图像。解码、处理和重新编码将同时并行执行,并且只将图像的一小部分保存在内存中。

缺点是您只能进行一次快照处理。这将失败,例如:

image = Vips::Image.new_from_file "something.jpg", access: :sequential
image = 255 - image
image.write_to_file "something-inverted.jpg"
avg = image.avg()

由于无法计算管道执行后的平均值,因为图像已被读取、处理和处置,并且没有剩余像素。

如果你使用默认的随机访问模式,它工作得很好:

image = Vips::Image.new_from_file "something.jpg"
image = 255 - image
image.write_to_file "something-inverted.jpg"
avg = image.avg()

现在,JPG文件将被解码为一个内存阵列,只有处理和保存将并行运行,像素稍后仍将在那里计算平均值。

在您的情况下,图像已在顺序模式下打开,但您正在尝试读取像素两次。你需要在随机访问模式下打开原件,或者你需要执行image = image.copy_memory()在内存中制作一个可以重复使用的副本。

最新更新