使用Ruby高效地从远程图像中读取EXIF元数据

4
我有几千张高分辨率的JPEG照片存储在旅行博客网站上,并希望编写一些Ruby代码,从这些图像中提取一些关键的EXIF元数据值,而无需下载每个图像文件的全部内容(它们很大,我有很多这样的图片)。
我正在使用'exifr' gem来读取EXIF数据,它被设计为可以与任何类型的IO对象一起使用,而不仅仅是本地文件。但是,'Net :: HTTPResponse'对象实际上并不是一个IO对象,尽管如果您通过将' read_body '方法传递给块,则允许进行增量读取。然而,我已经阅读了关于这种增量读取是否真的允许您仅下载文件的一部分的争论,或者它是否只是允许您以块的方式读取内容以提高效率(即整个内容仍然被下载)。
那么,我想做的事情可行吗?我应该寻找代替品来替换'Net :: HTTP',还是有什么办法可以让我访问低级别的TCP套接字(它应该是一个IO对象),将其传递给'exifr'代码,仅读取足够的图像以获取EXIF数据?还有其他解决方案吗?

我快速浏览了一下维基百科关于JPEG压缩的文章,并且我想知道EXIF数据包是否位于文件中一个“可靠”的位置?一个特定的相机可能是可预测的,但我认为你不能可靠地期望EXIF数据在文件的末尾或开头...如果您可以找到文件中EXIF数据的字节偏移量以查看它是否足够类似于某些启发式算法,那将会很有帮助... - sarnold
1个回答

2

我快速生成了一张表格,列出了在我的照片堆中EXIF数据存储的位置:

$ find . -type f -exec grep -a -bo Exif {} \; > /tmp/exif
$ sort /tmp/exif  | uniq -c | sort -n
      1 12306:Exif
      1 3271386:Exif
      1 8210:Exif
      1 8234:Exif
      1 9234:Exif
      2 10258:Exif
     24 449:Exif
     30 24:Exif
   8975 6:Exif
$ 

大多数清晰明了,只占整个文件的少量字节;还有一些零散地分布在其他位置,但最糟糕的也仅出现在文件的三兆字节左右。(取决于具体情况)

我写了一个小测试脚本,似乎可以对单个URL做必要的事情。(通过在可用的巨大二进制文件的块中寻找字符串 AA 进行测试。)这肯定不是我写过的最漂亮的程序,但可能是解决方案的一个足够好的起点。请注意,如果 Exif 文本跨越了块,则会检索整个文件。这很不幸,希望它不会经常发生。 66000 出现的原因是因为JPEG AAP1块的大小限制为64千字节,稍微多获取一点比稍微获取少一点更好。

#!/usr/bin/ruby

require 'net/http'
require 'uri'

url = URI.parse("http://....")

begin
    looking = true
    extra_size = 0
    File.open("/tmp/output", "w") do |f|
            Net::HTTP.start(url.host, url.port) do |http|
                    request = Net::HTTP::Get.new url.request_uri
                    http.request request do |resp|
                            resp.read_body do |chunk|
                                    f.write chunk
                                    if (looking)
                                            if (chunk.match(/Exif/))
                                                    looking = false
                                            end
                                    elsif (extra_size < 66000)
                                            extra_size += chunk.length
                                    else
                                            throw "done"
                                    end
                            end
                    end
            end
    end
rescue
    puts "done"
    exit(0)
end

这太棒了。但我们如何将其馈入程序中,例如另一个期望文件的Exif gem?我是否可以在“chunk”或其他地方访问到该点之前的完整“部分文件”?我能否像处理文件一样处理它? - Tallboy

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