[patch] Support writing EXR files with different color types

Questions and postings pertaining to the development of ImageMagick, feature enhancements, and ImageMagick internals. ImageMagick source code and algorithms are discussed here. Usage questions which are too arcane for the normal user list should also be posted here.
Post Reply
roelandsch
Posts: 20
Joined: 2014-10-27T16:10:38-07:00
Authentication code: 6789

[patch] Support writing EXR files with different color types

Post by roelandsch »

EXR allows writing files using 2x2 color subsampling (aka 4:2:0). You can find a reference to this on the old release notes at http://www.openexr.com/:
OpenEXR now supports high dynamic-range YCA (luminance/chroma/alpha) images with subsampled chroma channels. These files are supported via the RGBA convenience interface, so that data is presented to the application as RGB(A) but stored in the file as YC(A). OpenEXR also supports Y and YA (black-and-white/black-and-white with alpha) images.
I needed a few EXR files with different color types for testing, so I added an option to specify a color type to the exr coder. It adds an exr:color-type define, which can be set to the various EXR color output types, and the -sampling-rate option can be used (set to 2x2 or 4:2:0) to create a YC(A) file.

I added my patch below (but probably still requires some work before integration). However I'm working on Windows so I'm not able to use an SVN checkout. I'm using the source archive here instead: http://www.imagemagick.org/download/win ... indows.zip (version 6.9.1-4).

For reading these files no change is required, since ImageMagick uses the RGBA convenience interface.

--
Roeland

Patch:

Code: Select all

--- coders\exr.c.original	2014-12-15 11:36:27.000000000 +1200
+++ coders\exr.c	2015-06-04 16:05:22.000000000 +1200
@@ -350,33 +350,39 @@
 %
 %    o image_info: the image info.
 %
 %    o image:  The image.
 %
 */
 static MagickBooleanType WriteEXRImage(const ImageInfo *image_info,Image *image)
 {
+  const char
+      *sampling_factor,
+      *value;
+
   ImageInfo
     *write_info;
 
   ImfHalf
     half_quantum;
 
   ImfHeader
     *hdr_info;
 
   ImfOutputFile
     *file;
 
   ImfRgba
     *scanline;
 
   int
-    compression;
+    compression,
+    channels,
+    factors[3];
 
   MagickBooleanType
     status;
 
   register const PixelPacket
     *p;
 
   register ssize_t
@@ -417,19 +423,105 @@
 #if defined(B44Compression)
   if (write_info->compression == B44Compression)
     compression=IMF_B44_COMPRESSION;
 #endif
 #if defined(B44ACompression)
   if (write_info->compression == B44ACompression)
     compression=IMF_B44A_COMPRESSION;
 #endif
+
+  channels = 0;
+  value=GetImageOption(image_info,"exr:color-type");
+  if (value!=NULL)
+    {
+       if (LocaleCompare(value, "RGB") == 0)
+         channels = IMF_WRITE_RGB;
+
+       else if (LocaleCompare(value, "RGBA") == 0)
+         channels = IMF_WRITE_RGBA;
+
+       else if (LocaleCompare(value, "YC") == 0)
+         channels = IMF_WRITE_YC;
+
+       else if (LocaleCompare(value, "YCA") == 0)
+         channels = IMF_WRITE_YCA;
+
+       else if (LocaleCompare(value, "Y") == 0)
+         channels = IMF_WRITE_Y;
+
+       else if (LocaleCompare(value, "YA") == 0)
+         channels = IMF_WRITE_YA;
+
+       else if (LocaleCompare(value, "R") == 0)
+         channels = IMF_WRITE_R;
+
+       else if (LocaleCompare(value, "G") == 0)
+         channels = IMF_WRITE_G;
+
+       else if (LocaleCompare(value, "B") == 0)
+         channels = IMF_WRITE_B;
+
+       else if (LocaleCompare(value, "A") == 0)
+         channels = IMF_WRITE_A;
+
+       else
+        (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
+             "ignoring invalid defined exr:color-type",
+             "=%s",value);
+    }
+
+  sampling_factor=(const char *) NULL;
+  factors[0] = 0;
+  if (image_info->sampling_factor != (char *) NULL)
+    sampling_factor=image_info->sampling_factor;
+  /* parse sampling factors, valid values are 1x1 or 2x2 */
+  if (sampling_factor != NULL)
+    {
+      if (sscanf(sampling_factor, "%d:%d:%d", 
+          factors, factors+1, factors+2) == 3)
+        {
+          if (factors[0] == factors[1] && factors[1] == factors[2])
+            factors[0] = 1;
+          else if (factors[0] == factors[1] * 2 && factors[2] == 0)
+            factors[0] = 2;
+        }
+      else if (sscanf(sampling_factor, "%dx%d", 
+          factors, factors+1) == 2)
+        {
+          if (factors[0] != factors[1])
+            factors[0] = 0;
+        }
+
+      if (factors[0] != 1 && factors[0] != 2)
+        {
+          (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
+             "ignoring sampling-factor",
+             "=%s",sampling_factor);
+        }
+      else if (channels != 0)
+        {
+          /* cross check given color type and subsampling */
+          factors[1] = (channels == IMF_WRITE_YCA || channels == IMF_WRITE_YC) ? 2 : 1;
+          if (factors[0] != factors[1])
+            (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
+               "sampling-factor and color type mismatch",
+               "=%s",sampling_factor);
+        }
+    }
+  /* if no color type given, select it now */
+  if (channels == 0)
+    if (factors[0] == 2)
+      channels = image->matte ? IMF_WRITE_YCA : IMF_WRITE_YC;
+    else
+      channels = image->matte ? IMF_WRITE_RGBA : IMF_WRITE_RGB;
+
   ImfHeaderSetCompression(hdr_info,compression);
   ImfHeaderSetLineOrder(hdr_info,IMF_INCREASING_Y);
-  file=ImfOpenOutputFile(write_info->filename,hdr_info,IMF_WRITE_RGBA);
+  file=ImfOpenOutputFile(write_info->filename,hdr_info,channels);
   ImfDeleteHeader(hdr_info);
   if (file == (ImfOutputFile *) NULL)
     {
       (void) RelinquishUniqueFileResource(write_info->filename);
       write_info=DestroyImageInfo(write_info);
       ThrowFileException(&image->exception,BlobError,"UnableToOpenBlob",
         ImfErrorMessage());
       return(MagickFalse);
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: [patch] Support writing EXR files with different color types

Post by magick »

Thanks for the EXR enhancement and patch. We'll get it into the next point release of ImageMagick, 6.9.1-5.
roelandsch
Posts: 20
Joined: 2014-10-27T16:10:38-07:00
Authentication code: 6789

Re: [patch] Support writing EXR files with different color types

Post by roelandsch »

OK, so this is the list of color types supported. I'm not sure how to put this in the documentation.
  • RGB: RGB colors
  • RGBA: RGB with alpha channel
  • YC: Write a brightness channel (Y) and two chromaticy channels, defined as R/Y and B/Y. The two chromaticy channels are subsampled by a factor 2x2. This type (or YCA) is selected by default if -sampling-factor is set to 2x2.
  • YCA: Brightness+chroma with alpha channel
  • Y: Grayscale image
  • YA: Grayscale image with alpha channel
  • R: Red channel only
  • G: Green channel only
  • B: Blue channel only
  • A: Alpha channel (matte) only
Post Reply