Как преобразовать растровое изображение в оттенки серого по интенсивности пикселей с помощью GDI?

Я ищу простое решение, как преобразовать 32-битное растровое изображение в оттенки серого с помощью GDI (не GDI+). Есть ли возможность, например. изменив палитру растрового изображения или что-то в этом роде?

Конечно, в Delphi есть множество примеров, таких как этот, но я Я ищу функцию WinAPI, которая делала бы это без перебора строк.


person Martin Reiner    schedule 19.12.2011    source источник
comment
Я не знаю ни об одной функции GDI, которая могла бы это сделать, ни о преобразовании палитры, но третья функция из упомянутой вами ссылки делает это. Я думаю, вы боитесь выступления, не так ли?   -  person TLama    schedule 19.12.2011
comment
Дело не только в производительности, я ищу более простое решение (возможно, путем преобразования палитры или чего-то еще).   -  person Martin Reiner    schedule 19.12.2011
comment
32-битное растровое изображение не содержит палитры.   -  person MBo    schedule 19.12.2011
comment
Итерация через строки сканирования - это ответ   -  person David Heffernan    schedule 19.12.2011


Ответы (2)


Я не нашел ни одной функции GDI, делающей это. Самый простой способ, как упомянул Дэвид в своем комментарии, — это сканировать каждую строку и вычислять цвета пикселей. Возможно, вам нужна формула luminance.

Существует несколько вариантов этой формулы, и в следующем примере я использовал вариант, рекомендованный ITU, см. < a href="http://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-6-200701-S!!PDF-E.pdf#page=2" отн. ="noreferrer">this document раздел 2.5.1. Как я где-то нашел, эта формула используется, например. даже хорошо известным Adobe 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;
person TLama    schedule 19.12.2011
comment
Спасибо TLama, кажется, для этого действительно нет функции WinAPI. - person Martin Reiner; 19.12.2011
comment
Bitmap.PixelFormat := pf24Bit; // необходимо для шкалы серого - person Nasreddine Galfout; 20.11.2019

Вы можете создать раздел DIB с палитрой, 8 бит на пиксель и 256 цветов, и инициализировать палитру оттенками серого {0, 0, 0}, {1, 1, 1}, ... {255, 255, 255}.

Один GDI BitBlt в этом растровом изображении сделает исходное изображение серым. Вот фрагмент кода (на 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));
person Roman R.    schedule 19.12.2011
comment
Позволит ли GDI отказаться от зарезервированных системных цветов из палитры полутонов? - person OnTheFly; 21.12.2011
comment
Это создает полностью палитру оттенков серого с 256 оттенками серого, без системных цветов. - person Roman R.; 21.12.2011
comment
@RomanR.: Я не понимаю, где здесь играет роль исходное растровое изображение (тот, который нужно преобразовать). Не могли бы вы уточнить, пожалуйста? - person Uli Gerhardt; 09.04.2013
comment
@UliGerhardt: предполагается, что он выбран в DC, переданный в качестве 6-го аргумента для вызова BitBlt. Здесь это изображение рабочего стола, поэтому вы не видите его как растровое изображение. - person Roman R.; 09.04.2013