【问题标题】:converting a PNGImage to grayscale using delphi使用delphi将PNGImage转换为灰度
【发布时间】:2011-05-05 08:24:28
【问题描述】:

你好 这是我的代码:

procedure TForm4.Button1Click(Sender: TObject);
var
  png: TPNGImage;
  data: PRGBQarray;
  p: ^tagRGBQuad;
  i, o: integer;
begin
  png := TPNGImage.Create;
  try
    png.LoadFromFile('C:\Untitled.png');
    for o := 1 to 100 do
    begin
      data:=png.Scanline[o];
      for I := 1 to 400 do
      begin
        p := @data^[i];
        p.rgbGreen := p.rgbBlue;
        p.rgbRed := p.rgbGreen;
      end;
    end;
    img.picture.Assign(png);
  finally
    png.Free;
  end;
end;

它不起作用,它使图片混乱,我确定这是因为 rgbReserved。 我该怎么办?

【问题讨论】:

  • 能否让您的代码可读?
  • 图片是怎么弄得乱七八糟的?您确定这是 32 位图像而不是 24 位图像吗?可以看截图吗?
  • @Andreas:这是一个有趣的结构......它应该看起来更漂亮@data^[@i^],你觉得呢? :D
  • @Andreas: 怎么了?!

标签: delphi image bitmap grayscale scanline


【解决方案1】:

这是如何使位图变灰。 (而且,是的,如果你想对 PNG 进行灰色化,你首先需要从中获取位图数据。我认为 VCL 会为你做到这一点。)

type
  PRGB32Array = ^TRGB32Array;
  TRGB32Array = packed array[0..MaxInt div SizeOf(TRGBQuad)-1] of TRGBQuad;

procedure MakeGrey(Bitmap: TBitmap);
var
  w, h: integer;
  y: Integer;
  sl: PRGB32Array;
  x: Integer;
  grey: byte;
begin
  Bitmap.PixelFormat := pf32bit;
  w := Bitmap.Width;
  h := Bitmap.Height;
  for y := 0 to h - 1 do
  begin
    sl := Bitmap.ScanLine[y];
    for x := 0 to w - 1 do
      with sl[x] do
      begin
        grey := (rgbBlue + rgbGreen + rgbRed) div 3;
        rgbBlue := grey;
        rgbGreen := grey;
        rgbRed := grey;
      end;
  end;
end;

示例用法:

procedure TForm4.Button1Click(Sender: TObject);
var
  bm: TBitmap;
begin
  bm := TBitmap.Create;
  try
    bm.LoadFromFile('C:\Users\Andreas Rejbrand\Pictures\Porträtt, litet, kvadratiskt.bmp');
    MakeGrey(bm);
    Canvas.Draw(0, 0, bm);
  finally
    bm.Free;
  end;
end;

【讨论】:

  • 请注意,grey := (rgbBlue + rgbGreen + rgbRed) div 3; 行暗示grey in [0, 255] 是必需的。
  • 所以,这会自动将图像转换为 pf32。这是一个缺点吗?
【解决方案2】:

Andreas 的答案会给你一个很好、快速的近似值,但你会失去一些质量,因为红色、绿色和蓝色在人眼中不会以相同的强度混合。如果你想“做对”,而不是

grey := (rgbBlue + rgbGreen + rgbRed) div 3;

试试这个:

grey := round(rgbRed * .3) + round(rgbGreen * .59) + round(rgbBlue * .11);

与简单平均值相比,您的性能会受到一些影响,但除非您在非常大的图像上,否则它可能不会很明显。

【讨论】:

  • 值得注意的是,这是 NTSC 公式,还有其他公式。
  • 梅森,你不是通过一次舍入操作得到几乎相同的结果(更快),即“灰色:=圆形(rgbRed * .3 + rgbGreen * .59 + rgbBlue * .11) ;"?
【解决方案3】:

这确实应该是对@Mason 将 RGB 转换为 GreyScale 的例程的评论,但由于我不知道如何制作评论显示代码,所以我将其作为答案。

这就是我进行转换的方式:

FUNCTION RGB2GRAY(R,G,B : BYTE) : BYTE; Register; ASSEMBLER;
  ASM
                IMUL    EAX,19595
                IMUL    EDX,38470
                IMUL    ECX,7471
                ADD     EAX,EDX
                ADD     EAX,ECX
                SHR     EAX,16
  END;

FUNCTION GreyScale(C : TColor) : TColor; Register; ASSEMBLER;
  ASM
                MOVZX   EDX,AH
                MOV     ECX,EAX
                SHR     ECX,16
                MOVZX   EAX,AL
                CALL    RGB2GRAY
                MOVZX   EAX,AL
                MOV     AH,AL
                SHL     EAX,8
                MOV     AL,AH
  END;

我不知道它是 NTSC 公式还是什么,但它们似乎在我的程序中工作:-)。

【讨论】:

  • 不,我不是 :-( 作为一个老汇编程序员,如果去掉内联汇编,那将是一个主要缺点。像这样的微小例程可以被调用数千次,一些手工优化的组装极大地受益......
【解决方案4】:

我知道问题已经得到解答,但这是我的 2c 价值...

以下代码来自 Thany 出品的 PNGComponents 包 (PngFunctions.pas)。

//
//The Following code comes from the PNGComponents package from Thany...
//
procedure MakeImageGrayscale(Image: TPNGObject; Amount: Byte = 255);

  procedure GrayscaleRGB(var R, G, B: Byte);
  var
     X: Byte;
  begin
  X := Round(R * 0.30 + G * 0.59 + B * 0.11);
  R := Round(R / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
  G := Round(G / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
  B := Round(B / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
  end;

var
   X, Y, PalCount: Integer;
   Line: Pointer;
   PaletteHandle: HPalette;
   Palette: array[Byte] of TPaletteEntry;
begin
//Don't do anything if the image is already a grayscaled one
if not (Image.Header.ColorType in [COLOR_GRAYSCALE, COLOR_GRAYSCALEALPHA])
then begin
     if Image.Header.ColorType = COLOR_PALETTE
     then begin
          //Grayscale every palette entry
          PaletteHandle := Image.Palette;
          PalCount := GetPaletteEntries(PaletteHandle, 0, 256, Palette);
          for X := 0 to PalCount - 1
          do GrayscaleRGB(Palette[X].peRed, Palette[X].peGreen, Palette[X].peBlue);
          SetPaletteEntries(PaletteHandle, 0, PalCount, Palette);
          Image.Palette := PaletteHandle;
          end
     else begin
          //Grayscale every pixel
          for Y := 0 to Image.Height - 1
          do begin
             Line := Image.Scanline[Y];
             for X := 0 to Image.Width - 1
             do GrayscaleRGB(PRGBLine(Line)^[X].rgbtRed, PRGBLine(Line)^[X].rgbtGreen, PRGBLine(Line)^[X].rgbtBlue);
             end;
          end;
     end;
end;

有一组例程,最初由 PNGImage 组件的作者发布,可以在 Code Central 上找到,它展示了如何执行其他操作,例如 Alpha 混合两个图像、旋转、叠加等。@987654321 @

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-21
    • 2015-02-13
    相关资源
    最近更新 更多