From d49df1ce795a012b667d88c3eff40f992b2221b3 Mon Sep 17 00:00:00 2001 From: Petrik Date: Thu, 21 Sep 2023 21:52:03 +0200 Subject: [PATCH] Add `analyze` methods for getting metadata Besides processing images it would be nice to analyze images as well to get metadata like: width, height and rotation. This is currently based on the ActiveStorage::Analyzer::ImageAnalyzer. --- lib/image_processing.rb | 1 + lib/image_processing/analyzer.rb | 12 ++++++++++++ lib/image_processing/mini_magick.rb | 21 +++++++++++++++++++++ lib/image_processing/vips.rb | 22 ++++++++++++++++++++++ test/mini_magick_test.rb | 18 ++++++++++++++++++ test/vips_test.rb | 18 ++++++++++++++++++ 6 files changed, 92 insertions(+) create mode 100644 lib/image_processing/analyzer.rb diff --git a/lib/image_processing.rb b/lib/image_processing.rb index 944e891..024e04a 100644 --- a/lib/image_processing.rb +++ b/lib/image_processing.rb @@ -1,6 +1,7 @@ require "image_processing/chainable" require "image_processing/builder" require "image_processing/pipeline" +require "image_processing/analyzer" require "image_processing/processor" require "image_processing/version" diff --git a/lib/image_processing/analyzer.rb b/lib/image_processing/analyzer.rb new file mode 100644 index 0000000..9aed416 --- /dev/null +++ b/lib/image_processing/analyzer.rb @@ -0,0 +1,12 @@ +module ImageProcessing + # Abstract class inherited by individual analyzers. + class Analyzer + def initialize(image) + @image = image + end + + def analyze + { width: @image.width, height: @image.height, rotated: rotated? } + end + end +end diff --git a/lib/image_processing/mini_magick.rb b/lib/image_processing/mini_magick.rb index 7ef0444..9b79517 100644 --- a/lib/image_processing/mini_magick.rb +++ b/lib/image_processing/mini_magick.rb @@ -16,6 +16,27 @@ def self.valid_image?(file) false end + def self.analyze(file) + if valid_image?(file) + image = ::MiniMagick::Image.new(file.path) + Analyzer.new(image).analyze + else + {} + end + end + + class Analyzer < ImageProcessing::Analyzer + ROTATIONS = %w[ RightTop LeftBottom TopRight BottomLeft ] + + private + + def rotated? + ROTATIONS.include?(@image["%[orientation]"]) + rescue ::MiniMagick::Error + false + end + end + class Processor < ImageProcessing::Processor accumulator :magick, ::MiniMagick::Tool diff --git a/lib/image_processing/vips.rb b/lib/image_processing/vips.rb index d0e9f45..8cbe006 100644 --- a/lib/image_processing/vips.rb +++ b/lib/image_processing/vips.rb @@ -6,6 +6,7 @@ module ImageProcessing module Vips extend Chainable + ROTATIONS = /Right-top|Left-bottom|Top-right|Bottom-left/ # Returns whether the given image file is processable. def self.valid_image?(file) @@ -15,6 +16,27 @@ def self.valid_image?(file) false end + def self.analyze(file) + if valid_image?(file) + image = ::Vips::Image.new_from_file(file.path, access: :sequential) + Analyzer.new(image).analyze + else + {} + end + end + + class Analyzer < ImageProcessing::Analyzer + ROTATIONS = /Right-top|Left-bottom|Top-right|Bottom-left/ + + private + + def rotated? + ROTATIONS === @image.get("exif-ifd0-Orientation") + rescue ::Vips::Error + false + end + end + class Processor < ImageProcessing::Processor accumulator :image, ::Vips::Image diff --git a/test/mini_magick_test.rb b/test/mini_magick_test.rb index 868d41d..7ab6b51 100644 --- a/test/mini_magick_test.rb +++ b/test/mini_magick_test.rb @@ -106,6 +106,24 @@ assert_dimensions [300, 400], pipeline.loader(geometry: "400x400").call end unless ENV["GM"] + it "analyzes the image" do + result = ImageProcessing::MiniMagick.analyze(@portrait) + expected = { width: 600, height: 800, rotated: false } + assert_equal expected, result + end + + it "analyzes the rotated image" do + result = ImageProcessing::MiniMagick.analyze(fixture_image("rotated.jpg")) + expected = { width: 800, height: 600, rotated: true } + assert_equal expected, result + end + + it "does not analyze an invalid image" do + result = ImageProcessing::MiniMagick.analyze(fixture_image("invalid.jpg")) + expected = {} + assert_equal expected, result + end + it "auto orients by default" do result = ImageProcessing::MiniMagick.call(fixture_image("rotated.jpg")) assert_dimensions [600, 800], result diff --git a/test/vips_test.rb b/test/vips_test.rb index 8a4fa2e..14a7ad6 100644 --- a/test/vips_test.rb +++ b/test/vips_test.rb @@ -76,6 +76,24 @@ assert_dimensions [600, 800], result end + it "analyzes a image" do + result = ImageProcessing::Vips.analyze(@portrait) + expected = { width: 600, height: 800, rotated: false } + assert_equal expected, result + end + + it "analyzes a rotated image" do + result = ImageProcessing::Vips.analyze(fixture_image("rotated.jpg")) + expected = { width: 800, height: 600, rotated: true } + assert_equal expected, result + end + + it "does not analyze an invalid image" do + result = ImageProcessing::Vips.analyze(fixture_image("invalid.jpg")) + expected = {} + assert_equal expected, result + end + it "applies loader options" do result = ImageProcessing::Vips.loader(shrink: 2).call(@portrait) assert_dimensions [300, 400], result