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.
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
|Image with the "eciRGB" Format (original)||original image
||original image converted to "sRGB" before
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:
convert original.jpg -profile sRGB2014.icc -strip -set profile sRGB2014.icc version.jpg
mogrify if you want to alter the original image. If you are using Carrierwave, this image processor might come in handy:
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.
-profiletriggers an (expensive) image conversion each time it is called. It implies
-set profileonly adds the profile's metadata to the image.
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).
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
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
comparewrites it's result to STDERR. A lower score is better, I'd test for a threshold like <= 5.
- There are different comparison metrics available
- Link to the profile used in the example
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.
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.