Does 开发者_Go百科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:
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?
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
}
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?
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!
As previously answered, the most reliable way will be to parse the file's header to retrieve this data.
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
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.
精彩评论