.NET — Bitmap.Save игнорирует Bitmap.SetResolution в Windows 7

Я пишу приложение .NET 4, которое импортирует и сохраняет изображения для печати. Важно, чтобы разрешение сохраненных изображений (DPI, а не размеры в пикселях) было установлено на указанное нами значение, чтобы они печатались правильно.

Некоторые из изображений, которые мы импортируем, поставляются без значения разрешения (неправильный EXIF ​​при их создании), поэтому мы должны исправить это перед их записью. Для этого мы используем Bitmap.SetResolution. Он отлично работает в XP и Windows 8, но когда мы записываем (Bitmap.Save) изображения в Windows 7, они всегда записываются с исходной метаинформацией о разрешении, игнорируя SetResolution.

Вот тест, который мы сделали, работает на XP и 8, не на 7.

string originalFile = @"D:\temp\img\original_img.jpg";
string newFile = @"D:\temp\img\new_img.jpg";

Bitmap bitmap = (Bitmap)Image.FromFile(originalFile);
bitmap.SetResolution(200, 200);
bitmap.Save(newFile, ImageFormat.Jpeg);

Image image = Image.FromFile(newFile);
int dpiX = (int)Math.Round(image.HorizontalResolution, MidpointRounding.ToEven);
int dpiY = (int)Math.Round(image.VerticalResolution, MidpointRounding.ToEven);
Console.WriteLine("DPI is {0} x {1}", dpiX, dpiY);

Перед сохранением отладка всегда показывает правильное разрешение, назначенное SetResolution, проблема заключается в сохраненном изображении.

Вероятно, это то, о чем сообщалось здесь: -on-windows-7?forum=netfxbcl" rel="noreferrer">http://social.msdn.microsoft.com/Forums/vstudio/en-US/62368caa-05f4-4798-9c59-5d82f881a97c/systemdrawingbitmapsetresolution-is -полностью сломанный-на-windows-7?forum=netfxbcl

Но проблема там, кажется, остается нерешенной. Неужели нет способа просто заставить его работать? Нужно ли для этого использовать дополнительные библиотеки?


person Ricardo Rempel    schedule 04.10.2013    source источник


Ответы (2)


Хмя, это баг в компоненте винды. Группа Windows всегда очень неохотно исправляет подобные ошибки, критические изменения откладываются до следующей версии Windows. Это было исправлено в Windows 8. Подумайте, насколько необычно то, что вы делаете, DPI изображения всегда должно устанавливаться устройством, которое записало изображение. Как камера или сканер, они никогда не ошибаются. Просто нет ни одного устройства с разрешением 200 точек на дюйм.

Если вы достаточно отчаянны, чтобы найти обходной путь, вы можете подумать об исправлении самого файла. Это несложно сделать для файла JPEG, до полей в заголовке файла довольно легко добраться:

using System.IO;
...
    public static void SetJpegResolution(string path, int dpi) {
        using (var jpg = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) 
        using (var br = new BinaryReader(jpg)) {
            bool ok = br.ReadUInt16() == 0xd8ff;        // Check header
            ok = ok && br.ReadUInt16() == 0xe0ff;
            br.ReadInt16();                             // Skip length
            ok = ok && br.ReadUInt32() == 0x4649464a;   // Should be JFIF
            ok = ok && br.ReadByte() == 0;
            ok = ok && br.ReadByte() == 0x01;           // Major version should be 1
            br.ReadByte();                              // Skip minor version
            byte density = br.ReadByte();
            ok = ok && (density == 1 || density == 2);
            if (!ok) throw new Exception("Not a valid JPEG file");
            if (density == 2) dpi = (int)Math.Round(dpi / 2.56);
            var bigendian = BitConverter.GetBytes((short)dpi);
            Array.Reverse(bigendian);
            jpg.Write(bigendian, 0, 2);
            jpg.Write(bigendian, 0, 2);
        }
    }
person Hans Passant    schedule 04.10.2013
comment
Я работаю с рентгеновскими снимками зубов, поэтому изображения имеют очень высокое разрешение, часто более 1000 DPI. И, поверьте мне, некоторые системы, с которыми я сталкивался, оставляют информацию о разрешении x и разрешении y пустой (я проверил изображения с помощью IrfanView). Спасибо за ответ, я посмотрю. Между тем, я нашел хак, который я публикую здесь, который делает эту работу. - person Ricardo Rempel; 04.10.2013
comment
О, и я знаю, какой беспорядок может получиться, когда вы позволяете людям выбирать разрешение своих изображений. Нам придется откалибровать значение для каждого устройства обработки изображений. - person Ricardo Rempel; 04.10.2013
comment
Нет, это для JPEG. PNG требует исправления фрагмента pHY. - person Hans Passant; 14.03.2014

Я нашел обходной путь, который сделает эту работу. Это не элегантно, но...

Вместо того, чтобы применять разрешение к исходному изображению, сделайте его копию и работайте с ней:

Bitmap bitmap = (Bitmap)Image.FromFile(originalFile);
Bitmap newBitmap = new Bitmap(bitmap)
newBitmap.SetResolution(200, 200);
newBitmap.Save(newFile, ImageFormat.Jpeg);

Теперь это работает на Windows 7. Поди разберись.

Хотя идея Ханса Пассана мне нравится, она чище. Я не знаю, испортит ли то, что я сделал, изображение, есть ли рекомпрессия или нет.

person Ricardo Rempel    schedule 07.10.2013
comment
Это исправление работает и не требует вмешательства в двоичный файл изображения. Просто не забудьте поместить эти растровые изображения в блоки using, чтобы они освободились. - person billpg; 01.10.2014
comment
Хорошо, за исключением того, что newBitmap не копирует глубину цвета растрового изображения. - person rayzinnz; 02.12.2016