Posted 5 months ago. Visible to the public. Repeats.

Always convert and strip user-provided images to sRGB

Debugging image color profiles is hard. You can't trust your eyes in this matter, as the image rendering depends on multiple factors. At least the operation system, browser or image viewer software and monitor influence the resulting image colors on your screen.

When we offer our users the possibility to upload images, they will most likely contain tons of EXIF metadata and sometimes exotic color profiles like eciRGB. We want to get rid of the metadata, as it might contain sensitive information like GPS coordinates. Since we cannot be sure that every browser is able to display the user provided color profile, we recommend converting it to a well known color space (like sRGB) and thus removing all other color profiles.

The following article guides you through the implementation of the color profile conversion with ImageMagick and CarrierWave.

TL;DR: If you don't convert the image before stripping the metadata and profiles, it will be displayed with wrong colors on your website. Always set a color profile after using ImageMagick's -strip method.

Image with the "eciRGB" Format (original) original image -stripped original image converted to "sRGB" before -strip
Image Image Image

The profile conversion - metadata stripping pipeline

  • Chose an appropriate target color profile like sRGB2014.icc from the International Color Consortium
  • Convert all uploaded images to that color profile
  • Strip all metadata from the image - including all profiles
  • Re-attach the (e.g. sRGB) profile information to the image

With ImageMagick, it can be achieved like this:

Copy
convert original.jpg -profile sRGB2014.icc -strip -set profile sRGB2014.icc version.jpg

Use mogrify if you want to alter the original image. If you are using Carrierwave, this image processor might come in handy:

Copy
def convert_to_srgb if /^image/.match?(content_type) manipulate! do |image| image.combine_options do |c| c.profile('path/to/sRGB2014.icc') c.strip c.set('profile', 'path/to/sRGB2014.icc') end image = yield(image) if block_given? image end end end

With this conversion in place, user provided images should be displayed correctly in all major browsers. Only Firefox Mobile on Android does not seem to use the attached profile.

Notes

  • -profile triggers an (expensive) image conversion each time it is called. It implies -set profile.
  • -set profile only adds the profile's metadata to the image.

Testing

ImageMagick provides a compare utility which can be used to measure color differences between two images. You just have to resize and convert them to the same dimensions and color profile (which should be different from both used profiles for this purpose).

With Imagemagick:

Copy
mogrify -resize '500x500' -profile sRGB_v4_ICC_preference.icc original.jpg convert -resize '500x500' -profile sRGB_v4_ICC_preference.icc version.jpg compare original.jpg version.jpg -metric PHASH /dev/null

With Ruby / CarrierWave

Copy
def normalize_image!(image) # For images to be comparable with ImageMagick, they must # be of the same size and color profile. image.manipulate! do |m| m.combine_options do |c| c.resize '500x500' c.profile 'sRGB_v4_ICC_preference.icc' end end end def color_difference!(image, other_image) normalize_image!(image) normalize_image!(other_image) compare = ['compare'] compare << image.path compare << other_image.path compare << '-metric' compare << 'PHASH' # See https://imagemagick.org/script/command-line-options.php#metric compare << '/dev/null' # For reasons, the color difference is written to STDERR stdout, stderr, status = MiniMagick::Shell.new.execute(compare) color_difference = stderr.to_f color_difference end # A spec might look like this, given original and version to be CarrierWave versions expect(color_difference!(original, version)).to <= 5

Notes

Data Migration

If you add the color profile conversion to an existing Uploader, don't forget to recreate all versions. You could use a Sidekiq Worker on a low-priority queue if many image records and versions are affected.

References

The v4 ICC specification is widely used and is referred to in many International and other de-facto standards. It was first approved as an International Standard, ISO 15076-1, in 2005 and revised in 2010.

Does your version of Ruby on Rails still receive security updates?
Rails LTS provides security patches for old versions of Ruby on Rails (3.2 and 2.3).

Owner of this card:

Avatar
Michael Leimstädtner
Last edit:
2 months ago
by Michael Leimstädtner
Attachments:
lenna_converted.jpg, lenna_ecirgb.jpg, lenna_without_profile.jpg
Keywords:
imagemagick, carrierwave, optimizing, identify
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Michael Leimstädtner to makandra dev
This website uses cookies to improve usability and analyze traffic.
Accept or learn more