LoginSignup
3
2

More than 3 years have passed since last update.

ActiveStorageで保存するJPEG画像のEXIFをmetaデータにしたい時

Last updated at Posted at 2019-08-25

やりたい事

YourModel.first.image.metadata # => {latitude: ..., longitude: ...}

デフォルトで入っているImageAnalyzerは画像のwidth、heightしかmetadataとして取ってくれない。
https://api.rubyonrails.org/classes/ActiveStorage/Analyzer/ImageAnalyzer.html

解決策

  • exifr/jpeg
  • timezone_finder

を使う。

まず、exifr/jpegでJPEG画像のEXIFを取り出し、GPS情報を取得。

  • latitude
  • longitude
  • gps_date_stamp
  • gps_time_stamp

gps_date_stamp、gps_time_stampはUTC。 https://www.exif.org/Exif2-2.PDF
95e82e537d78fc41bae5e6ea2ccde143.png
ed13c3b3394c498daee58c7122e16590.png

UTCの時間をなるべく正確な現地時間にするためにtimezone_finderを使う。

exif_jpeg_analyzer.rbなどとしてconfig/initializersに入れる
# 元ネタはここ
# https://www.georg-ledermann.de/blog/2018/05/15/exif-analyzer-for-active-storage/

require "timezone_finder"
require "exifr/jpeg"
class ExifJpegAnalyzer < ActiveStorage::Analyzer::ImageAnalyzer
  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(gps_from_exif(image))
    end
  rescue LoadError
    logger.info "Skipping image analysis because the mini_magick gem isn't installed"
    {}
  end

  private

  def ref(gps_ref)
    %w[N E].include?(gps_ref) ? 1 : -1
  end

  def gps_from_exif(image)
    {}.tap do |hash|
      next unless image.type == "JPEG"
      next if image.is_a? ActiveStorage::Variant

      image_data = EXIFR::JPEG.new(image.path)
      if image_data.exif&.fields[:gps]
        latitude = image_data.gps_latitude.to_f * ref(image_data.gps_latitude_ref)
        longitude = image_data.gps_longitude.to_f * ref(image_data.gps_longitude_ref)
        time_zone = TimezoneFinder.create.timezone_at(lat: latitude, lng: longitude)
        date_time =
          "#{image_data.gps_date_stamp.split(":").join("-")} #{
            image_data.gps_time_stamp.collect(&:to_i).join(":")
          } UTC"
        hash[:latitude] = latitude
        hash[:longitude] = longitude
        hash[:altitude] = image_data.gps_altitude.to_f
        hash[:time_zone] = time_zone
        hash[:date_time] = date_time
      end
    end

  rescue EXIFR::MalformedImage, EXIFR::MalformedJPEG

  end
end

# https://api.rubyonrails.org/classes/ActiveStorage/Blob/Analyzable.html
Rails.application.config.active_storage
  .analyzers.delete ActiveStorage::Analyzer::ImageAnalyzer
Rails.application.config.active_storage.analyzers.append ExifJpegAnalyzer

これによりmetadataにGPS関連が入る。

YourModel.first.image.metadata 
# => {latitude: ..., longitude: ..., altitude: ..., time_zone: ..., date_time: ...}

# これで撮影時の現地時間が取れる
metadata = YourModel.first.image.metadata
Time.parse(metadata[:date_time]).in_time_zone(metadata[:time_zone])
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2