在 Lazarus 中将图像转换为灰度的问题

Issue converting an image to gray scale in Lazarus

我写了一个叫做gray_scaleprocedure,假设有一个输入TImage组件,将它包含的图像变成灰度,然后在同一个TImage组件中打印它。它的问题是它打印出黑色图像。我的代码如下,请注意,我创建了一个名为 img2: TPicture 的局部变量,其目的是作为过程输入和输出之间的中间阶段。

procedure gray(var img1: TImage);
var
  i,j: Integer;
  y: integer;
  color: TColor;
  img2: TPicture;
begin
  i := 0; j := 0;
  img2 := TPicture.create;
  img2.Bitmap.Width:= img1.width;
  img2.Bitmap.Height:= img1.height;
  for i := 0 to img1.width  do begin
    for j := 0 to img1.height do begin
      y:= trunc((255 * luminance(img1,i,j)));
      color := RGBToColor(byte(y), byte(y), byte(y));
      img2.Bitmap.Canvas.DrawPixel(i,j, TColorToFPColor(color));
    end;
    img1.Picture.Assign(img2);
  end;
end;                                                 

看看这段代码,其中有两个嵌套循环。

  for i := 0 to img1.width  do begin
    for j := 0 to img1.height do begin
      y:= trunc((255 * luminance(img1,i,j)));
      color := RGBToColor(byte(y), byte(y), byte(y));
      img2.Bitmap.Canvas.DrawPixel(i,j, TColorToFPColor(color));
    end;
    img1.Picture.Assign(img2);
  end;

在内部循环之后,您将 img2 分配给 img1.Picture,您将在访问外部循环后继续阅读。结果,在外循环进入第二次迭代时,img1 变为空(除了最左边的像素列)。

修改代码如下:

  for i := 0 to img1.width  do begin
    for j := 0 to img1.height do begin
      y:= trunc((255 * luminance(img1,i,j)));
      color := RGBToColor(byte(y), byte(y), byte(y));
      img2.Bitmap.Canvas.DrawPixel(i,j, TColorToFPColor(color));
    end;
  end;
  img1.Picture.Assign(img2);

TPicture 命名为 img2 也会产生误导,尤其是当 img1 指代 TImage.

此外,为了使您的代码更有效率,您应该考虑几点。最重要的是在扫描线的帮助下一次扫描一行位图图像。

Look at this SO post

unit rhsBitmapGrayscale;

interface

uses
  SysUtils, Classes, Graphics, IntfGraphics, FPImage;

  procedure BitmapGrayscale(BM: TCustomBitmap; R, G, B: Single);

implementation

// BitmapGrayscale(Bitmap, 0.30, 0.59, 0.11);  // ISO-Neutral-Gray

procedure BitmapGrayscale(BM: TCustomBitmap; R, G, B: Single);
var
  IntfImg: TLazIntfImage = nil;
  x, y, w, h: Integer;
  TempColor: TFPColor;
  Gray: Word;
begin
  try
    IntfImg := BM.CreateIntfImage;

    w := IntfImg.Width - 1;
    h := IntfImg.Height - 1;

    IntfImg.BeginUpdate;
    for y := 0 to h do
      for x := 0 to w do
      begin
        TempColor := IntfImg.Colors[x, y];
        Gray := Round(TempColor.Red * R + TempColor.Green * G + TempColor.Blue * B);
        TempColor.Red := Gray;
        TempColor.Green := Gray;
        TempColor.Blue := Gray;
        IntfImg.Colors[x, y] := TempColor;
      end;
    IntfImg.EndUpdate;

    BM.LoadFromIntfImage(IntfImg);
  finally
    IntfImg.Free;
  end;
end;

end.

使用 TLazIntfImage 可以提高处理速度。 使用红色、绿色和蓝色的参数,可以单独影响结果。

这里有一些例子:

BitmapGrayscale(Image1.Picture.Bitmap, 0.30, 0.59, 0.11);  // Neutral filter
BitmapGrayscale(Image1.Picture.Bitmap, 1.00, 0.00, 0.00);  // Red filter
BitmapGrayscale(Image1.Picture.Bitmap, 0.00, 1.00, 0.00);  // Green filter
BitmapGrayscale(Image1.Picture.Bitmap, 0.00, 0.00, 1.00);  // Blue filter
BitmapGrayscale(Image1.Picture.Bitmap, 0.00, 0.50, 0.50);  // Cyan filter
BitmapGrayscale(Image1.Picture.Bitmap, 0.50, 0.00, 0.50);  // Magenta filter
BitmapGrayscale(Image1.Picture.Bitmap, 0.50, 0.50, 0.00);  // Yellow filter