How to identify CMYK images in ASP.NET using C#

2020-05-21 04:35发布

Does anybody know how to properly identify CMYK images in ASP.NET using C#? When I check the Flags attribute of a Bitmap instance, I get incorrect results.

I have created three images to test this: cmyk.jpg, rgb.jpg and gray.jpg. These are respectively CMYK, RGB and Grayscale images.

This is my test code:

static void Main(string[] args)
{
    Bitmap bmpCMYK = new Bitmap("cmyk.jpg");
    Bitmap bmpRGB = new Bitmap("rgb.jpg");
    Bitmap bmpGray = new Bitmap("gray.jpg");

    Console.WriteLine("\t\tRgb\tCmyk\tGray\tYcbcr\tYcck\tPixelFormat");

    Console.WriteLine("cmyk.jpg\t{0}\t{1}\t{2}\t{3}\t{4}\t{5}",
        IsSet(bmpCMYK, System.Drawing.Imaging.ImageFlags.ColorSpaceRgb),
        IsSet(bmpCMYK, System.Drawing.Imaging.ImageFlags.ColorSpaceCmyk),
        IsSet(bmpCMYK, System.Drawing.Imaging.ImageFlags.ColorSpaceGray),
        IsSet(bmpCMYK, System.Drawing.Imaging.ImageFlags.ColorSpaceYcbcr),
        IsSet(bmpCMYK, System.Drawing.Imaging.ImageFlags.ColorSpaceYcck),
        bmpCMYK.PixelFormat);

    Console.WriteLine("rgb.jpg\t\t{0}\t{1}\t{2}\t{3}\t{4}\t{5}",
        IsSet(bmpRGB, System.Drawing.Imaging.ImageFlags.ColorSpaceRgb),
        IsSet(bmpRGB, System.Drawing.Imaging.ImageFlags.ColorSpaceCmyk),
        IsSet(bmpRGB, System.Drawing.Imaging.ImageFlags.ColorSpaceGray),
        IsSet(bmpRGB, System.Drawing.Imaging.ImageFlags.ColorSpaceYcbcr),
        IsSet(bmpRGB, System.Drawing.Imaging.ImageFlags.ColorSpaceYcck),
        bmpRGB.PixelFormat);

    Console.WriteLine("gray.jpg\t{0}\t{1}\t{2}\t{3}\t{4}\t{5}",
        IsSet(bmpGray, System.Drawing.Imaging.ImageFlags.ColorSpaceRgb),
        IsSet(bmpGray, System.Drawing.Imaging.ImageFlags.ColorSpaceCmyk),
        IsSet(bmpGray, System.Drawing.Imaging.ImageFlags.ColorSpaceGray),
        IsSet(bmpGray, System.Drawing.Imaging.ImageFlags.ColorSpaceYcbcr),
        IsSet(bmpGray, System.Drawing.Imaging.ImageFlags.ColorSpaceYcck),
        bmpGray.PixelFormat);

    bmpCMYK.Dispose();
    bmpRGB.Dispose();
    bmpGray.Dispose();

    Console.ReadLine();
}

private static bool IsSet(Bitmap bitmap, System.Drawing.Imaging.ImageFlags flag)
{
    return (bitmap.Flags & (int)flag) == (int)flag;
}

This produces the following output: Test results

I have checked the actual images and cmyk.jpg really is a CMYK image.

Apparently, this is a "known issue". Alex Gil had the same problem in WPF (see this question: How to identify CMYK images using C#) and he managed to solve it by using a BitmapDecoder class to load the images. I'm a bit uncomfortable using that solution in ASP.NET because it requires me to add references to WindowsBase.dll and PresentationCore.dll and I'm not sure I want those in a web project.

Does anyone know of any other pure .NET solutions to check if an image is in the CMYK format that I can safely use in ASP.NET?

6条回答
我想做一个坏孩纸
2楼-- · 2020-05-21 05:13

You might check out FreeImage which is a win32 DLL but has a .NET wrapper, I am using it in a production enviroment and it's great.

I would be surprised if it couldn't provide this information.

(edit) I didn't notice before you asked for pure .NET solutions - so maybe this won't work - but I have found it a useful supplement to the limitations of the .NET framework for image manipulation.

Another idea, if you only need to identify the format, is to extract that directly from the file. I have no idea how complex the specification for the JPEG format might be, but hey, it's only 29 pages!

查看更多
Luminary・发光体
3楼-- · 2020-05-21 05:18

As previously answered, the most reliable way will be to parse the file's header to retrieve this data.

查看更多
成全新的幸福
4楼-- · 2020-05-21 05:22

An idea: If you dont want to reference those dll's in your web project, you could do the processing outside the web project, in a service, which may be better anyway?

查看更多
相关推荐>>
5楼-- · 2020-05-21 05:24

So here is how I solved the issue you were having which was the same as what I was having. Everything in csharp looks to return rgb info when you know it's a 100% a cymk image. So what to do, well go to the root and read the file. Here is what I had done and tested to work well and should cover all OS's, and 50 for 50 imgs tested right. This is 2.0 too just in case.

     public bool isByteACMYK(Stream image)
    {
        using (StreamReader sr = new StreamReader(image)) 
        { 
            string contents = sr.ReadToEnd(); 
            if (contents.ToLower().Contains("cmyk")) 
            { 
                return true;
            } 
        }
        return false;
    }

    public bool isFileACMYKJpeg(System.Drawing.Image image)
    {
        System.Drawing.Imaging.ImageFlags flagValues = (System.Drawing.Imaging.ImageFlags)Enum.Parse(typeof(System.Drawing.Imaging.ImageFlags), image.Flags.ToString());
        if (flagValues.ToString().ToLower().IndexOf("ycck") == -1)
        {
            // based on http://www.maxostudio.com/Tut_CS_CMYK.cfm

            bool ret = false;
            try{
                int cmyk = (image.Flags & (int)ImageFlags.ColorSpaceCmyk);
                int ycck = (image.Flags & (int)ImageFlags.ColorSpaceYcck);

                ret = ((cmyk > 0) || (ycck > 0));
            } catch (Exception ex){

            }
            return ret;
        }
        return true;
    } 
    // my upload test .. but you could turn a file to stream and do the same
    public void UpdatePool(HttpPostedFile newimage)
    {
        if (newimage.ContentLength != 0)
        {
            Stream stream = newimage.InputStream;
            MemoryStream memoryStream = new MemoryStream();
            CopyStream(stream,memoryStream);
            memoryStream.Position = 0;
            stream = memoryStream;


            System.Drawing.Image processed_image = null;

            processed_image = System.Drawing.Image.FromStream(newimage.InputStream);

            if (imageService.isFileACMYKJpeg(processed_image) || imageService.isByteACMYK(stream))
            {
                Flash["error"] = "You have uploaded a CMYK image.  Please conver to RGB first.";
                RedirectToReferrer();
                return;
            }
        }
    }

cheers - Jeremy

查看更多
乱世女痞
6楼-- · 2020-05-21 05:26

I use a combination of the ImageFlags and PixelFormat values. Note that PixelFormat.Forma32bppCMYK is missing from .NET - I grabbed it out of GdiPlusPixelFormats.h in the Windows SDK.

The trick is that Windows 7 and Server 2008 R2 returns the correct pixel format but is missing the image flags. Vista and Server 2008 return an invalid pixel format but the correct image flags. Insanity.

public ImageColorFormat GetColorFormat(this Bitmap bitmap)
{
    const int pixelFormatIndexed = 0x00010000;
    const int pixelFormat32bppCMYK = 0x200F;
    const int pixelFormat16bppGrayScale = (4 | (16 << 8);

    // Check image flags
    var flags = (ImageFlags)bitmap.Flags;
    if (flags.HasFlag(ImageFlags.ColorSpaceCmyk) || flags.HasFlag(ImageFlags.ColorSpaceYcck))
    {
        return ImageColorFormat.Cmyk;
    }
    else if (flags.HasFlag(ImageFlags.ColorSpaceGray))
    {
        return ImageColorFormat.Grayscale;
    }

    // Check pixel format
    var pixelFormat = (int)bitmap.PixelFormat;
    if (pixelFormat == pixelFormat32bppCMYK)
    {
        return ImageColorFormat.Cmyk;
    }
    else if ((pixelFormat & pixelFormatIndexed) != 0)
    {
        return ImageColorFormat.Indexed;
    }
    else if (pixelFormat == pixelFormat16bppGrayScale)
    {
        return ImageColorFormat.Grayscale;
    }

    // Default to RGB
    return ImageColorFormat.Rgb;
}    

public enum ImageColorFormat
{
    Rgb,
    Cmyk,
    Indexed,
    Grayscale
}
查看更多
淡お忘
7楼-- · 2020-05-21 05:36

I was under the assumption that everything in .NET was based on RGB, aRGB and grayscale (as grayscale is RGB(128, 128, 128)).

If my assumption is correct then you will have to go the third party route.

查看更多
登录 后发表回答