Graphics.DrawImage создает различные данные изображения на x86 и x64 - PullRequest
4 голосов
/ 30 ноября 2011

Привет!

Вот мои настройки :
У меня есть приложение ac #, которое извлекает функции из серии изображений.Из-за размера набора данных (несколько тысяч изображений) он сильно распараллелен, поэтому у нас есть высокопроизводительная машина с ssd, которая работает на Windows7 x64 (среда выполнения .NET4), чтобы поднять тяжелую работу.Я разрабатываю его на компьютере с Windows XP SP3 x86 под Visual Studio 2008 (.NET3.5) с Windows Forms - кстати, нет возможности перейти в WPF.

Edit3: Это странно, но, думаю, я наконец узнал, что происходит.Кажется, кодек для формата изображения, который дает разные результаты на двух компьютерах!Я точно не знаю, что там происходит, но декодер на xp-машине дает более вменяемые результаты, чем win7.К сожалению, лучше версия все еще находится в системе x86 XP :(. Я думаю, что единственное решение этого - это изменить формат входного изображения на что-то без потерь, как png или bmp (глупо, я не думаю о формате файлаво-первых:)).

Edit2: Спасибо за ваши усилия.Я думаю, что буду придерживаться реализации конвертера самостоятельно, это не совсем то, что я хотел, но я должен решить это как-то :).Если кто-то читает это, у кого есть какие-то идеи для меня, пожалуйста, дайте мне знать.

Редактировать: В комментариях мне было рекомендовано использовать сторонний lib для этого.Я думаю, что я не прояснил себя достаточно, потому что я действительно не хочу использовать подход DrawImage в любом случае - это просто некорректный быстрый хак, чтобы получить реально работающий new Bitmap(tmp, ... myPixelFormat), который, мы надеемся, будет использовать некоторую интерполяцию.Единственное, чего я хочу достичь, - это преобразовать входящее изображение в обычный PixelFormat с некоторой стандартной интерполяцией.

Моя проблема заключается в следующем.Некоторые исходные изображения имеют формат Indexed8bpp jpg , который не очень хорошо сочетается с графическими материалами WinForms.Поэтому в моей логике загрузки изображений есть проверка на наличие проиндексированных изображений, которые преобразуют изображение в формат по умолчанию для моих приложений (например, Format16bpp) следующим образом:

Image GetImageByPath(string path)
{
    Image result = null;

    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        Image tmp = Image.FromStream(fs); // Here goes the same image ...

        if (tmp.PixelFormat == PixelFormat.Format1bppIndexed ||
            tmp.PixelFormat == PixelFormat.Format4bppIndexed ||
            tmp.PixelFormat == PixelFormat.Format8bppIndexed ||
            tmp.PixelFormat == PixelFormat.Indexed)
        {
            // Creating a Bitmap container in the application's default format
            result = new Bitmap(tmp.Width, tmp.Height, DefConf.DefaultPixelFormat);
            Graphics g = Graphics.FromImage(result);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            // We need not to scale anything in here
            Rectangle drawRect = new Rectangle(0, 0, tmp.Width, tmp.Height);

            // (*) Here is where the strange thing happens - I know I could use
            // DrawImageUnscaled - that isn't working either
            g.DrawImage(tmp, drawRect, drawRect, GraphicsUnit.Pixel);

            g.Dispose();
        }
        else 
        {
            result = new Bitmap(tmp); // Just copying the input stream
        }

        tmp.Dispose();
    }

    // (**) At this stage the x86 XP memory image differs from the 
    // the x64 Win7 image despite having the same settings
    // on the very same image o.O
    result.GetPixel(0, 0).B; // x86: 102, x64: 102
    result.GetPixel(1, 0).B; // x86: 104, x64: 102
    result.GetPixel(2, 0).B; // x86:  83, x64:  85
    result.GetPixel(3, 0).B; // x86: 117, x64: 121
    ...
    return result;
}

Я отследил проблему до (*).Я думаю, что InterpolationMode как-то связан с этим, но нет разницы, какие из них я выбираю, результаты все равно различаются при (**) на двух системах.Я исследовал данные тестовых изображений с некоторыми глупыми линиями копирования и вставки, чтобы убедиться, что нет проблем с неправильным доступом к данным.

Все вместе изображения выглядят так: Диаграмма дифракции электронного рассеяния назад.Фактические значения цвета слегка различаются, но они несут много информации - интерполяция даже усиливает ее.Похоже, что алгоритм компоновки на машине x86 использует свойство InterpolationMode, тогда как x64 штуковина просто распределяет значения палитры без учета какой-либо интерполяции.

Я никогда не замечал никакой разницы между выходными данными двух машин до того дня, когда я реализовал функцию просмотра гистограммы для данных в моем приложении.На компьютере x86 он сбалансирован, как и следовало ожидать от просмотра изображений.С другой стороны, машина x64 скорее выдает некую разреженную гистограмму, указывающую на индексированные данные изображения.Это даже влияет на общие выходные данные всего приложения - выходные данные отличаются на обеих машинах с одинаковыми данными, это не очень хорошая вещь.

Для меня это выглядит как ошибка в реализации x64, но это простомне :-).Я просто хочу, чтобы изображения на машине x64 имели те же значения, что и на x86.

Если у кого-то есть идея, я был бы очень рад.Я искал подобное поведение в сети целую вечность, но сопротивление кажется бесполезным:)

О, берегись ... кит!

Ответы [ 3 ]

1 голос
/ 30 ноября 2011

Я смутно вспоминаю, что классы графики .NET полагаются на GDI +.Если и сегодня это так, то нет смысла пробовать ваше приложение на разных 64-битных системах с разными видеодрайверами.Лучше всего будет либо выполнить интерполяцию с использованием необработанных операций GDI (P / Invoke), либо написать свою собственную процедуру интерполяции пикселей в программном обеспечении.Ни один из вариантов не является особенно привлекательным.

1 голос
/ 30 ноября 2011

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

Ваше изображение 8bpp имеет палитру, которая содержит фактические значения цвета. Вам нужно прочитать эту палитру и преобразовать значения цвета (которые, если я правильно помню, составляют 24 бита) в 16-разрядные значения цвета. Вы потеряете информацию при конверсии, но уже теряете информацию при конверсии. По крайней мере, таким образом вы потеряете информацию предсказуемым образом.

Поместите преобразованные значения цвета (их будет не более 256) в массив, который вы можете использовать для поиска. Тогда ...

Создайте ваше целевое растровое изображение и вызовите LockBits, чтобы получить указатель на фактические данные растрового изображения. Вызовите LockBits, чтобы получить указатель на растровые данные исходного растрового изображения. Тогда для каждого пикселя:

read the source bitmap pixel (8 bytes)
get the color value (16 bits) from your converted color array
store the color value in the destination bitmap

Вы можете сделать это с GetPixel и SetPixel, но это будет очень-очень медленно.

0 голосов
/ 30 ноября 2011

Вы действительно должны использовать OpenCV для такой обработки изображений, она доступна в C # здесь: OpenCVSharp .

...