Быстрый способ замены красных / синих байтов с помощью ScanLine - PullRequest
1 голос
/ 19 января 2012

В настоящее время я зацикливаю свойство Canvas.Pixels[] и считываю каждый пиксель на холсте, чтобы поменять местами красные / синие байты (по определенным причинам).Тем не менее, это занимает в среднем 2 секунды на изображение, и у меня есть более 8000 изображений, которые нужно преобразовать (за ночь).Я понимаю, что могу использовать метод ScanLine, чтобы выполнить это намного быстрее, но я ничего не знаю о ScanLine - это гораздо более низкий уровень кодирования, чем мне удобно.Какой самый быстрый способ сделать это?Я готов подождать некоторое время, пока это пройдет, но было бы неплохо, если бы я мог нарезать это время пополам или больше.

Прямо сейчас, я использую эту процедуру:

procedure SwapBytes(var Bmp: TBitmap);
var
  X, Y: Integer;
  R, G, B: Byte;
  C: TColor;
begin
  for Y := 0 to Bmp.Height - 1 do begin
    for X := 0 to Bmp.Width - 1 do begin
      C:= Bmp.Canvas.Pixels[X,Y];
      R:= GetRValue(C);
      G:= GetGValue(C);
      B:= GetBValue(C);
      Bmp.Canvas.Pixels[X,Y]:= RGB(B, G, R)
    end;
  end;
end;

Добавлено примечание : Первоначальное преобразование более 8000 изображений является первым шагом, почему мне это нужно,Однако я также буду использовать то же самое в нашем программном обеспечении для автоматического преобразования любого изображения на месте по мере необходимости.Поэтому сторонний конвертер не будет работать, потому что я не могу распространять его среди наших клиентов.

1 Ответ

9 голосов
/ 19 января 2012

Я бы попробовал что-то вроде следующего. Эта версия предназначена только для 24-битных растровых изображений:

procedure SwapRedBluePixels(ABitmap: TBitmap);
var
  X: Integer;
  Y: Integer;
  Red: Byte;
  Pixel: PRGBTriple;
begin
  // check for the bit depth, it must be 24-bit if you use PRGBTriple pointer
  // for line scan; if it wouldn't the iterated line pointers would point to 
  // another place in the memory
  if ABitmap.PixelFormat <> pf24bit then
  begin
    ShowMessage('Your bitmap has color depth different from 24-bit');
    Exit;
  end;
  // iterate through the image vertically
  for Y := 0 to (ABitmap.Height - 1) do
  begin
    // access the line of pixels and get the pointer to the first pixel of 
    // that line
    Pixel := ABitmap.ScanLine[Y];
    // iterate through the scanned line pixels horizontally
    for X := 0 to (ABitmap.Width - 1) do
    begin
      // store the pixel's red channel value
      Red := Pixel.rgbtRed;
      // modify the pixel's red channel value
      Pixel.rgbtRed := Pixel.rgbtBlue;
      // modify the pixel's blue channel value
      Pixel.rgbtBlue := Red;
      // increment to get the next pixel pointer of the scanned line
      Inc(Pixel);
    end;
  end;
end;

Обновление 2:

Эта версия для 24-битных и 32-битных растровых изображений:

procedure SwapRedBluePixels(ABitmap: TBitmap);
var
  X: Integer;
  Y: Integer;
  Red: Byte;
  Size: Integer;
  Pixels: PByteArray;
begin
  // check the color depth and set the size of the pixel arrangement
  case ABitmap.PixelFormat of
    pf24bit: Size := SizeOf(TRGBTriple);
    pf32bit: Size := SizeOf(TRGBQuad);
  else
    // if the image is not 24-bit or 32-bit go away
    begin
      ShowMessage('Your bitmap has unsupported color depth!');
      Exit;
    end;
  end;

  // iterate through the image vertically
  for Y := 0 to (ABitmap.Height - 1) do
  begin
    // access the line of pixels and get the pointer to the first pixel of
    // that line
    Pixels := ABitmap.ScanLine[Y];
    // iterate through the scanned line pixels horizontally
    // for 24-bit images the pixels are stored like
    // B -> G -> R -> B -> G -> R etc.
    // for 32-bit images the pixels are stored like
    // B -> G -> R -> A -> B -> G -> R -> A etc.
    // so we can simply use e.g. byte array and iterate through
    // it, if we have 24-bit image, we have to read each element,
    // if 32-bit we have to skip the alpha (reserved) channel
    for X := 0 to (ABitmap.Width - 1) do
    begin
      // store the pixel's red channel value
      Red := Pixels^[(X * Size) + 2];
      // modify the pixel's red channel value
      Pixels^[(X * Size) + 2] := Pixels^[(X * Size)];
      // modify the pixel's blue channel value
      Pixels^[(X * Size)] := Red;
    end;
  end;
end;
...