Преобразование изображений Jpeg в Bmp - некоторые изображения выходят синими

есть некоторые Jpg-изображения, которые Delphi, похоже, не нравятся. Кажется, это специфично для файлов, которые я загружаю. И процедура проста-a) загрузите Jpg-изображение в TJpegImage, b) назначьте объект Jpg a TBitmap object и c) сохранить и/или отобразить Bmp-изображение. По какой-то причине эти фотографии продолжают выходить с голубоватым оттенком.

эти изображения показывают отлично в любом месте и везде, где я их загружаю (Windows picture viewer, paint, photoshop и т. д.).

здесь ссылка на один конкретный файл (из сотен), в котором нет синего цвета, но при загрузке в Delphi выходит синим...

http://www.sendspace.com/file/1owbzh

(используется sendspace.com так что я знаю, что сайт не пытается преобразовать формат изображения)

и то, что я делаю, очень просто...

procedure Load;
var
  J: TJpegImage;
  B: TBitmap;
begin
  J:= TJpegImage.Create;
  B:= TBitmap.Create;
  J.LoadFromFile('C:SomeFile.jpg');
  B.Assign(J);
  //Either save or display `B` and it appears blueish at this point
....

Я хочу, чтобы избежать каких-либо сторонних вещей как можно больше. Эта проблема существовала в Delphi версии 7, 2010, и XE2. По крайней мере, элемент управления TImage в XE2 отображает его правильно (в отличие от старых двух), но это не имеет значения, если TBitmap все еще не работает. Что случилось с этим файлом? И / или, что не так с рендерингом Delphi?

Добавлена Информация

Я недавно узнал кое-что об этих изображениях. Когда они пришли от поставщиков (изображения продукта), они были в формате CMYK. В то время Delphi 7 неправильно поддерживал эти файлы (с доступом нарушения и плохие изображения), поэтому все изображения были отфильтрованы через конвертер в цветовой формат RGB. Многие оригинальные изображения также были TIFF и были преобразованы в JPG. Таким образом, кажется, что программное обеспечение FastStone Image Resizer не должны сохранить эти файлы, когда они проходят. Синее изображение не происходит на всех из них, просто некоторые случайные партии за раз. Программное обеспечение обрабатывает тысячи продуктов, поэтому есть тысячи возможных изображений.

4 ответов


Я понял проблему. Скорее всего, это ошибка в Дельфи.

предоставленное изображение является особым форматом для файла JPEG под названием Adobe JPEG. Вероятно, самое странное в Adobe JPEG заключается в том, что он позволяет хранить изображение в формате RGB, хотя он также позволяет другие форматы. Большинство JPEG-это формат JFIF или EXIF, который не использует RGB.

при копировании данных RGB, независимо от того, что делает Delphi, он меняет красные и синие данные при загрузке на холст. Он загружает его как BGR вместо RGB. Это может быть связано с тем, что Windows (24-разрядные и 32-разрядные) DIBs (BMPs) хранятся в формате BGR.

Я предполагаю, что ошибка появится в Delphi для любого RGB JPEG. Поскольку большинство JPEG не используют RGB, частота ошибок низкая. Простое исправление, если у вас есть источник для JPEG-блока, заключается в обратном порядке при загрузке RGB JPEG.

если у вас нет источника, продолжайте.

программа Adobe в формате JPEG задает порядок цветов в таком формате (в шестнадцатеричном формате) 43 11 00 47 11 00 42 11 00 Это выглядит так в шестнадцатеричном редакторе R..G..B. Если вы отмените R и B здесь через шестнадцатеричный редактор, он показывает неправильно в Windows, и прямо в Delphi.

чтобы распознать Adobe JPEG, первые четыре байта либо (в шестнадцатеричном формате) FF D8 FF ED или FF D8 FF EE С ED и EE будучи дифференцирующими байтами. Все файлы JPEG начинаются с FF D8 FF.

после этих байт, два байта, представляют длину маркера типа, за которым следует (в ASCII)Adobe, а затем еще шесть байтов (представляющих версию и т. д.) и, наконец, (18-й байт) - это байт, который определяет формат. 0 означает RGB. Итак, проверьте эти значимые байты, а затем действуйте соответственно.

вам придется изменить порядок RGB в заголовке файла (чтобы лгать Delphi) или скопировать его в TBitmap и использовать ScanLine для изменения RGB в правильном порядке.

формат подробности из чтения libJPEG источник в с.


причина, по которой ваш файл синий, потому что кодировка BGR isntead RGB.
Если вы измените jpeg.pas исходный файл и используйте замену пикселей (удалить {.$IFDEF JPEGSO} на TJPEGImage.GetBitmap) вы увидите ваш образец файла правильно коричневый.

Итак, я думаю, суть в том, что источник JPEG не обнаруживает правильную (обратную) кодировку; вероятно, в jc.d.out_color_space...

обновление:
Исходный файл C (и jpeg.pas) должен объявить (и использовать) цвет Пробелы с новыми расширениями JCS_EXT_...:

enum J_COLOR_SPACE {
  JCS_UNKNOWN, JCS_GRAYSCALE, JCS_RGB, JCS_YCbCr,
  JCS_CMYK, JCS_YCCK, JCS_EXT_RGB, JCS_EXT_RGBX,
  JCS_EXT_BGR, JCS_EXT_BGRX, JCS_EXT_XBGR, JCS_EXT_XRGB
}

обновление 2:
jpeg.pas можно найти (XE) в C:...\RAD Studio.0\source\vcl с файлами C в jpg папку.

если вы готовы поспорить, что все файлы Adobe с цветовым пространством RGB должны иметь свои биты, вы можете легко взломать jpeg.пас источника для выявления ваших специальных файлов и conditionnally обмена, упомянутых выше в TJPEGImage.GetBitmap

{.$IFDEF JPEGSO}
          if (jc.c.in_color_space=JCS_RGB)and
            (smallint(jc.c.jpeg_color_space)=Ord(JCS_UNKNOWN))and   //comes 1072693248 = FF00000 = 111111111100000000000000000000
            jc.d.saw_Adobe_marker  and
            (PixelFormat = jf24bit) then
          begin

WIC (доступно для XP и выше) может обрабатывать это изображение. Этот компонент хорошо упакован в Delphi 2010 и выше. Для более ранних версий Delphi достаточно легко вызвать WIC с помощью COM-интерфейсов.

вот мое доказательство кода концепции:

var
  Image: TWICImage;
  Bitmap: TBitmap;
begin
  Image := TWICImage.Create;
  Image.LoadFromFile('C:\desktop\ABrownImage.jpg');
  Bitmap := TBitmap.Create;
  Bitmap.Assign(Image);
  Bitmap.SaveToFile('C:\desktop\ABrownImage.bmp');
end;

Примечание 1: WIC поставляется с Vista, но должен быть перераспределен для XP. Один очевидный вариант - использовать WIC, если он доступен, но вернуться к декодеру Delphi JPEG иначе.

примечание 2.: Я не могу найти повторно распространяемый пакет для WIC. Я подозреваю, что для этого может потребоваться загрузка конечного пользователя для XP. Тем не менее, я бы не удивился, если бы подавляющее большинство машин XP уже установили его.


в ответ на ваш другой вопрос, вот код для загрузки файла JPG в растровое изображение и условно применить исправление к полученному растровому изображению. Обратите внимание, что это работает для вашего коричневого.JPG, но я понятия не имею, что в этих первых 18 байтах, поэтому я понятия не имею, будет ли это работать в долгосрочной перспективе или нет. Я лично предпочел бы использовать готовую, известную для работы, широко используемую библиотеку. Кроме того я хотел использовать идею Давида через WIC если доступен и возвращаясь к этому стилю хаков если нет.

здесь полное код блока, поэтому вы можете увидеть все используемые блоки. Форма ожидает только одно TImage имени Image1 на форме, чтобы вы могли создать свою форму, сначала поместите TImage там, затем переключитесь на представление исходного кода и скопируйте мой код поверх кода, созданного Delphi.

как работает код:

код открывает файл с изображением JPG и загружает его в TJpgImage. Затем он сравнивает первые 18 байт файл маркера. Если есть совпадение, оно применяет преобразование к каждому пикселю созданного растрового изображения. Поскольку запись фактических констант маркера затруднена, существует процедура (CopyConstToClipboard), которая берет байты из файла, преобразует их в константу в стиле Delphi и копирует ее в буфер обмена. Когда вы найдете новый файл, который не работает, вы должны использовать эту процедуру для подготовки новой константы.

код:

unit Unit9;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Jpeg, Clipbrd;

type
  TForm9 = class(TForm)
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form9: TForm9;

implementation

{$R *.dfm}

type
  TRGB_Pixel = packed record
    B1: Byte;
    B2: Byte;
    B3: Byte;
  end;
  TScanLine = array[0..(System.MaxInt div SizeOf(TRGB_Pixel))-1] of TRGB_Pixel;
  PScanLine = ^TScanLine;

procedure CopyConstToClipboard(const FB:array of byte);
var s: string;
    i: Integer;
begin
  s := 'Name: array[0..' + IntToStr(High(FB)) + '] of Byte = ($' + IntToHex(FB[0], 2);
  for i:=1 to High(FB) do
    s := s + ', $' + IntToHex(FB[i],2);
  s := s + ');';
  Clipboard.AsText := s;
end;

function LoadJpegIntoBitmap(const FileName:string): TBitmap;
var F: TFileStream;
    Jpg: TJPEGImage;
    FirstBytes:array[0..17] of Byte;
    y,x: Integer;
    ScanLine: PScanLine;
const Marker_1: array[0..17] of Byte = ($FF, $D8, $FF, $EE, , E, , , F, , , , , , , , , );

  procedure SwapBytes(var A, B: Byte);
  var T: Byte;
  begin
    T := A;
    A := B;
    B := T;
  end;

begin
  F := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Jpg := TJPEGImage.Create;
    try
      Jpg.LoadFromStream(F);
      F.Position := 0;
      F.Read(FirstBytes, SizeOf(FirstBytes));

      // CopyConstToClipboard(FirstBytes); // Uncomment this to copy those first bytes to cliboard

      Result := TBitmap.Create;
      Result.Assign(Jpg);

      if (Result.PixelFormat = pf24bit) and CompareMem(@Marker_1, @FirstBytes, SizeOf(FirstBytes)) then
      begin
        for y:=0 to Result.Height-1 do
        begin
          ScanLine := Result.ScanLine[y];
          for x:=0 to Result.Width-1 do
          begin
            SwapBytes(ScanLine[x].B1, ScanLine[x].B3);
          end;
        end;
      end;

    finally Jpg.Free;
    end;
  finally F.Free;
  end;
end;

procedure TForm9.FormCreate(Sender: TObject);
var B: TBitmap;
begin
  B := LoadJpegIntoBitmap('C:\Users\Cosmin Prund\Downloads\ABrownImage.jpg');
  try
    Image1.Picture.Assign(B);
  finally B.Free;
  end;
end;

end.