Automatic change of RGB images to greyscale - in jpgs

Jul 20, 2015 at 11:32 AM
Hello,

I'm trying to do some metadata editing / resizing to images, and am having trouble retaining the colorspace / colorprofile / colortype of images when the original image was an RGB that's mostly grey. They get automatically changed to greyscale.

Specifically, the big problem is with jpegs, because they get converted at some point after opening the file, before I can find out what the original colorspace was.

This is what is happening - it's fairly strange - I open a jpg which is an RGB image that is mostly full of grey pixels, and put a breakpoint in immediately afterwards.

using (var image = new MagickImage(imageFilePath))

then my breakpoint sits on the opening "{" straight after.

If I examine the variable "image" at this point, it tells me its colorspace is RGB.

If I don't step forwards, but examine the variable "image" for a second time, the colorspace becomes Greyscale.

I checked the actual file, and when it changes in memory, it's not because it's changed on disk.

So:

a) What is changing it, and
b) how can I stop it?

It's important for me to retain the original colorspace of a file, whether it was RGB or greyscale or whatever. I must not change the colours, even if the result is more "correct".

This specific behaviour only happens with .jpeg files too.

If I try the same thing with a .tiff which was an RGB image that is mostly grey, the colorspace does not immediately change, so I can capture the original colorspace of a file when I open it, then re-apply it to a file after processing - so if it was automatically converted to greyscale, I can change it back.
Coordinator
Jul 20, 2015 at 12:24 PM
Can you post an example file that I can use to reproduce the issue? If I remember correctly you got my e-mail so feel free to e-mail my a link to the file if you don't want to share it publicly. And did you test this with the latest version of Magick.NET?
Jul 20, 2015 at 3:04 PM
I've just updated to the latest version, and it still does it.

I sent you a link to an image.

According to command-line ImageMagick it's got a colorspace and colortype of Gray / Grayscale. But it also has an RGB ICC profile.

In Adobe Bridge, it just shows the image has a colour mode "RGB", and has that Adobe RGB 1998 profile, no mention of Grayscale.

I think that what I'm seeing change on a breakpoint is not actually a conversion to greyscale, it was a red herring.

I think the actual problem is that these greyscale jpgs have ClassType set to "Direct", but when they are written out, they end up with ClassType set to "Pseudo" for some reason.
Coordinator
Jul 20, 2015 at 7:56 PM
Edited Jul 20, 2015 at 9:29 PM
I just tested your image in Magick.NET and the following test fails:
using(MagickImage image = new MagickImage("grey_adobe_1998.jpg"))
{
  // This works
  Assert.AreEqual(ColorSpace.sRGB, image.ColorSpace);

  string tempFile = Path.GetTempFileName();
  image.Write(tempFile);
  image.Read(tempFile);
  File.Delete(tempFile);

  // This fails
  Assert.AreEqual(ColorSpace.sRGB, image.ColorSpace);
}
I also checked what happens in 'identify' on the command line and it turns out it will convert the image to grayscale when that is possible. So that is not really a good way to test it. I will talk with the rest of the IM team to see if we can change this. I think it should not attempt to change the image to grey.

The same is happening inside the JPEG writer. When it sees it can change the image to grey it will do that and write a grey image instead. You can avoid this by setting the colortype to true color:
string tempFile = Path.GetTempFileName();
image.ColorType = ColorType.TrueColor;
image.Write(tempFile);
image.Read(tempFile);
File.Delete(tempFile);

// This will now pass
Assert.AreEqual(ColorSpace.sRGB, image.ColorSpace);
Jul 20, 2015 at 8:56 PM
Thank you!

It would be excellent if either it stopped automatically converting images to greyscale, or at least there was an option you could use to disable automatic colour conversion.

Sometime last week, when I was looking into this, I found this article:

http://www.imagemagick.org/script/color-management.php

It looks like automatic greyscaling was added to IM deliberately.

So what I was doing was just coming up with workarounds.

I haven't tried it for all image file types, but for .tif and .jpg it always tries to automatically greyscale any RGB images which are grey.

The difference is that for .tif files, I could detect the original colour info and store it in some variables, handle profiles / resize images, then set the colour info back on the image before writing the file out. So that was my workaround.

For .jpg files, it was converting the image before I could get a chance to detect the original colour info.

As I have one piece of code to handle images of multiple different types, the problem was that I didn't know how to detect that an image was originally an RGB jpg which is grey. My workaround for .jpg is now:
if (image.ClassType == ClassType.Direct && image.ColorType == ColorType.Grayscale)
{
    image.ColorType = ColorType.TrueColor;
}
i.e. if the class type was previously Direct, that implies that it should be an RGB image. If it is now greyscale, that doesn't match - so then it sets the ColorType to TrueColor.

The better solution would be for IM to not automatically convert grey RGB images to greyscale, though.
Coordinator
Jul 20, 2015 at 9:34 PM
This is an automatic optimization that normally would not cause any issues. But it I can understand that you would like to have the choice to avoid this behavior. I will talk with the rest of the team and see if we can add an 'option' to avoid this behavior. I'll get back to this topic later this week.
Coordinator
Aug 1, 2015 at 10:46 PM
Magick.NET 7.0.0.0017 has been release and includes an option to prevent this behavior. If you set the attribute "colorspace:auto-grayscale" to "false" the automatic conversion to grayscale won't happen.
using (MagickImage image = new MagickImage("Input.jpg"))
{
  image.SetAttribute("colorspace:auto-grayscale", "false");
  image.Write("Output.jpg");
}