【问题标题】:How to convert bitmap to grayscale by pixel intensity using GDI?如何使用 GDI 按像素强度将位图转换为灰度?
【发布时间】:2012-01-23 11:10:47
【问题描述】:

我正在寻找如何使用 GDI(不是 GDI+)将 32 位位图转换为灰度的简单解决方案。有没有可能,例如通过更改位图的调色板或其他什么?

当然,在 Delphi 中有很多示例,例如 this one,但我正在寻找一个 WinAPI 函数,它可以在不通过行迭代的情况下做到这一点。

【问题讨论】:

  • 我不知道单个 GDI 函数可以做到这一点,也不知道调色板转换,但是你提到的链接中的第三个函数可以做到这一点。我猜你害怕表现,不是吗?
  • 这不仅仅是关于性能,我正在寻找更简单的解决方案(可能通过转换调色板或其他方式)。
  • 32 位位图不包含调色板。
  • 遍历扫描线就是答案

标签: delphi winapi bitmap gdi grayscale


【解决方案1】:

我还没有发现任何单一的 GDI 函数可以做到这一点。正如大卫在他的评论中提到的,最简单的方法是扫描每一行并计算像素颜色。您正在寻找的可能是luminance 公式。

这个公式的变体很少,在以下示例中,我使用了ITU 推荐的公式,请参阅this document 第 2.5.1 节。正如我在某处发现的那样,使用了这个公式,例如即使是众所周知的 Adob​​e Photoshop。以下代码示例支持并仅期望 24 位像素格式的位图作为输入:

procedure BitmapGrayscale(ABitmap: TBitmap);
type
  PPixelRec = ^TPixelRec;
  TPixelRec = packed record
    B: Byte;
    G: Byte;
    R: Byte;
  end;
var
  X: Integer;
  Y: Integer;
  Gray: Byte;
  Pixel: PPixelRec;
begin
  for Y := 0 to ABitmap.Height - 1 do
  begin
    Pixel := ABitmap.ScanLine[Y];
    for X := 0 to ABitmap.Width - 1 do
    begin
      Gray := Round((0.299 * Pixel.R) + (0.587 * Pixel.G) + (0.114 * Pixel.B));
      Pixel.R := Gray;
      Pixel.G := Gray;
      Pixel.B := Gray;
      Inc(Pixel);
    end;
  end;
end;

【讨论】:

  • 谢谢 TLama,好像真的没有 WinAPI 功能。
  • Bitmap.PixelFormat := pf24Bit; // 灰度缩放需要
【解决方案2】:

您可以创建调色板 DIB 部分,每像素 8 位和 256 种颜色,并将调色板初始化为灰色阴影 { 0, 0, 0 }, { 1, 1, 1 }, ... { 255, 255, 255 }.

将单个 GDI BitBlt 插入此位图会使您的原始图像变灰。这是代码 sn-p(在 C++、ATL 和 WTL 中 - 但你应该明白)。

CWindowDC DesktopDc(NULL);
CDC BitmapDc;
ATLVERIFY(BitmapDc.CreateCompatibleDC(DesktopDc));
CBitmap Bitmap;
CTempBuffer<BITMAPINFO> pBitmapInfo;
const SIZE_T nBitmapInfoSize = sizeof (BITMAPINFO) + 256 * sizeof (RGBQUAD);
pBitmapInfo.AllocateBytes(nBitmapInfoSize);
ZeroMemory(pBitmapInfo, nBitmapInfoSize);
pBitmapInfo->bmiHeader.biSize = sizeof pBitmapInfo->bmiHeader;
pBitmapInfo->bmiHeader.biWidth = 320;
pBitmapInfo->bmiHeader.biHeight = 240;
pBitmapInfo->bmiHeader.biPlanes = 1;
pBitmapInfo->bmiHeader.biBitCount = 8;
pBitmapInfo->bmiHeader.biCompression = BI_RGB;
pBitmapInfo->bmiHeader.biSizeImage = 240 * 320;
pBitmapInfo->bmiHeader.biClrUsed = 256;
pBitmapInfo->bmiHeader.biClrImportant = 256;
for(SIZE_T nIndex = 0; nIndex < 256; nIndex++)
{
    pBitmapInfo->bmiColors[nIndex].rgbRed = (BYTE) nIndex;
    pBitmapInfo->bmiColors[nIndex].rgbGreen = (BYTE) nIndex;
    pBitmapInfo->bmiColors[nIndex].rgbBlue = (BYTE) nIndex;
}
Bitmap.Attach(CreateDIBSection(DesktopDc, pBitmapInfo, 0, DIB_RGB_COLORS, NULL, 0));
ATLVERIFY(Bitmap);
BitmapDc.SelectBitmap(Bitmap);
////////////////////////////////////////////////
// This is what greys it out:
ATLVERIFY(BitBlt(BitmapDc, 0, 0, 320, 240, DesktopDc, 0, 0, SRCCOPY));
////////////////////////////////////////////////
ATLVERIFY(BitBlt(DesktopDc, 0, 240, 320, 240, BitmapDc, 0, 0, SRCCOPY));

【讨论】:

  • GDI 是否允许放弃半色调调色板中保留的系统颜色?
  • 这使得一个全灰度调色板具有 256 种灰度,没有系统颜色。
  • @RomanR.:我不明白初始位图(要转换的位图)在这里发挥作用。你能澄清一下吗?
  • @UliGerhardt:假设它被选入 DC,作为第 6 个参数传递给 BitBlt 调用。这里是桌面图片,所以看不到位图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-11
  • 2018-11-28
  • 2021-02-24
  • 2017-01-17
  • 1970-01-01
  • 1970-01-01
  • 2013-10-07
相关资源
最近更新 更多