Как идентифицировать изображения CMYK в ASP.NET с помощью C#

Кто-нибудь знает, как правильно идентифицировать изображения CMYK в ASP.NET с помощью С#? Когда я проверяю атрибут Flags экземпляра Bitmap, я получаю неправильные результаты.

Для проверки я создал три изображения: cmyk.jpg, rgb.jpg и grey.jpg. Это соответственно изображения CMYK, RGB и оттенки серого.

Это мой тестовый код:

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;
}

Это дает следующий результат: Test results

Я проверил реальные изображения, и cmyk.jpg действительно является изображением CMYK.

Судя по всему, это "известная проблема". У Алекса Гила была такая же проблема в WPF (см. этот вопрос: Как идентифицировать изображения CMYK используя C#), и ему удалось решить эту проблему, используя класс BitmapDecoder для загрузки изображений. Мне немного неудобно использовать это решение в ASP.NET, потому что оно требует добавления ссылок на WindowsBase.dll и PresentationCore.dll, а я не уверен, что хочу их в веб-проекте.

Кто-нибудь знает какие-либо другие чистые решения .NET для проверки того, находится ли изображение в формате CMYK, которое я могу безопасно использовать в ASP.NET?


person Kristof Claes    schedule 21.02.2011    source источник


Ответы (6)


Я использую комбинацию значений ImageFlags и PixelFormat. Обратите внимание, что PixelFormat.Forma32bppCMYK отсутствует в .NET — я взял его из GdiPlusPixelFormats.h в Windows SDK.

Хитрость заключается в том, что Windows 7 и Server 2008 R2 возвращают правильный формат пикселей, но отсутствуют флаги изображения. Vista и Server 2008 возвращают недопустимый формат пикселей, но правильные флаги изображения. Безумие.

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
}
person ShadowChaser    schedule 28.03.2012

Идея: если вы не хотите ссылаться на эти dll в своем веб-проекте, вы можете выполнить обработку вне веб-проекта, в службе, что в любом случае может быть лучше?

person Mark Redman    schedule 21.02.2011
comment
Это действительно может быть решением, но я пытаюсь создать небольшую многоразовую библиотеку для сохранения и изменения размера изображений в веб-приложениях. Я не думаю, что в этом случае будет хорошей идеей позволить этому зависеть от службы. - person Kristof Claes; 22.02.2011
comment
+1 Я собирался дать более или менее тот же ответ: отделить манипулирование изображениями от веб-приложения. Я делал это в прошлом, и, честно говоря, как только вы начинаете изменять размер действительно больших изображений (не обязательно быть большим на диске, чтобы быть большим в памяти), он быстро становится беспорядочным, память и/или процессор и/или диск . Теперь все манипуляции с изображениями обрабатываются простым сервисом, который использует Graphics Magick: я помещаю файл задания (изображение src, параметры изменения размера, изображение dst) в папку, сервис подхватывает его и изменяет размер изображения. Веб-сервис никогда не перегружается таким образом. - person Sébastien Nussbaumer; 01.03.2011

Вы можете проверить FreeImage, который является Win32 DLL, но имеет оболочку .NET, я использую его в производственной среде, и это здорово.

Я был бы удивлен, если бы он не мог предоставить эту информацию.

(редактировать) Я не заметил, прежде чем вы попросили чистые решения .NET — так что, возможно, это не сработает — но я нашел это полезным дополнением к ограничениям .NET framework для изображений манипуляция.

Другая идея, если вам нужно только определить формат, состоит в том, чтобы извлечь его непосредственно из файла. Я понятия не имею, насколько сложной может быть спецификация для формата JPEG, но это всего лишь 29 страниц!

person Jamie Treworgy    schedule 24.02.2011

Как отвечено ранее, наиболее надежным способом будет для анализа заголовка файла для получения этих данных.

person FreeAsInBeer    schedule 02.03.2011

Итак, вот как я решил проблему, с которой вы столкнулись, которая была такой же, как и у меня. Все в csharp выглядит так, чтобы вернуть информацию rgb, когда вы знаете, что это 100% изображение cymk. Так что же делать, ну зайти в корень и прочитать файл. Вот что я сделал и протестировал, чтобы работать хорошо и должно охватывать все ОС, и 50 на 50 изображений проверено правильно. Это тоже 2.0 на всякий случай.

     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;
            }
        }
    }

ура - Джереми

person Quantum    schedule 28.02.2012

Я предполагал, что все в .NET основано на RGB, aRGB и оттенках серого (поскольку оттенки серого - это RGB (128, 128, 128)).

Если мое предположение верно, вам придется пойти по пути третьей стороны.

person The Muffin Man    schedule 24.02.2011