Al trabajar con imágenes se nos puede plantear la necesidad de sustraer dos, con el fín de hacer patente las diferencias entre ambas.
Mediante esta técnica se puede ahorrar espacio al guardar secuencias de vídeo, por ejemplo. También puede indicar movimiento en sistemas de vigilancia. La sustracción digital también tiene utilidades para aislar ciertas diferencias tras realizar un marcado de ciertas estructuras.
Propongo un sencillo código que resta dos imágenes y devuelbe un HBITMAP con el resultado. Está basado en la potente y veloz API BitBlt y su capacidad de realizar operaciones binarias con los pixels de las imágenes.
Un XOR entre imágenes nos pondrán en negro sus semejanzas. A partir del resultado crearemos una máscara bicolor. Esta máscara tendrá en blanco las diferencias de imágenes y en negro las similitudes. Con la mascara realizamos un AND sobre una de las imágenes iniciales con lo que se preservan las diferencias. El resultado será negro en zonas iguales de las dos imágenes y el resto mostrará una imagen que resulta ser lo diferente entre las imágenes que hemos restado.
Unas imágenes de muestra:
HBITMAP CreateSubstractBitmap(HDC DC1, HDC DC2) { BITMAP Bitmap; HBITMAP hMaskBmp, hResultBmp; HDC hScreen = GetDC(0); GetObject(GetCurrentObject(DC1, OBJ_BITMAP), sizeof(BITMAP), &Bitmap); hMaskBmp = CreateBitmap(Bitmap.bmWidth, Bitmap.bmHeight, 1, 1, NULL); hResultBmp = CreateBitmap(Bitmap.bmWidth, Bitmap.bmHeight, 1, 32, NULL); HDC hResultDC = CreateCompatibleDC(0); HDC hMaskDC = CreateCompatibleDC(0); HGDIOBJ Old = SelectObject(hResultDC, hResultBmp); SelectObject(hMaskDC, hMaskBmp); SetBkColor(hResultDC, 0); BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, DC1, 0, 0, SRCCOPY); BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, DC2, 0, 0, SRCINVERT); BitBlt(hMaskDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hResultDC, 0, 0, SRCCOPY); BitBlt(hMaskDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, 0, 0, 0, DSTINVERT); SetBkColor(hResultDC, clWhite); //clWhite = 0xFFFFFF BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, DC1, 0, 0, SRCCOPY); BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hMaskDC, 0, 0, SRCAND); SelectObject(hResultDC, Old); DeleteObject(hMaskBmp); ReleaseDC(0, hScreen); DeleteDC(hResultDC); DeleteDC(hMaskDC); return hResultBmp; }
Ejemplo:
Image3->Picture->Bitmap->Handle = CreateSubstractBitmap(Image1->Canvas->Handle, Image2->Canvas->Handle);
function CreateSubstractBitmap(hDC1, hDC2: HDC): HBITMAP; var hMaskBmp, Old: HBITMAP; hScreenDC, hMaskDC, hResultDC: HDC; Bitmap: TagBITMAP; begin hScreenDC:= GetDC(0); GetObject(GetCurrentObject(hDC1, OBJ_BITMAP), sizeof(BITMAP), @Bitmap); hMaskBmp:= CreateBitmap(Bitmap.bmWidth, Bitmap.bmHeight, 1, 1, nil); Result:= CreateBitmap(Bitmap.bmWidth, Bitmap.bmHeight, 1, 32, nil); hResultDC:= CreateCompatibleDC(0); hMaskDC:= CreateCompatibleDC(0); Old:= SelectObject(hResultDC, Result); SelectObject(hMaskDC, hMaskBmp); SetBkColor(hResultDC, 0); BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hDC1, 0, 0, SRCCOPY); BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hDC2, 0, 0, SRCINVERT); // XOR BitBlt(hMaskDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hResultDC, 0, 0, SRCCOPY); BitBlt(hMaskDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, 0, 0, 0, DSTINVERT); // NOT SetBkColor(hResultDC, clWhite); //clWhite:= $FFFFFF BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hDC1, 0, 0, SRCCOPY); BitBlt(hResultDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hMaskDC, 0, 0, SRCAND); // AND SelectObject(hResultDC, Old); DeleteObject(hMaskBmp); ReleaseDC(0, hScreenDC); DeleteDC(hResultDC); DeleteDC(hMaskDC); end;
Ejemplo:
Image3.Picture.Bitmap.Handle:= CreateSubstractBitmap(Image1.Canvas.Handle, Image2.Canvas.Handle);
Espero que os sea de utilidad.
Saludos.