Search code examples
c++winapiimage-processingdirect2d

How to remove green screen from bmp in c++?


so i am new to Direct2D and image processing world, and kind of lost right now. I have this image with green background, that i need to load. But green background must be removed before displaying it to the screen(using c++ chroma key).

So far, i have been successful in loading d2d bitmap from a jpg/bmp file onto screen. Sorry, if this is a stupid question, but i couldn't find much information anywhere else. Here's the image that i'm trying to process.

So the steps that i used to load bitmap are, creating WIC factory, creating decoder to be able to read file into WIC, create converter for WIC and configuring it.

The final step was to create bitmap.

Then i draw it on screen with RendetTarget->DrawBitmap() method passing appropriate parameters.

The only thing i can't figure out right now is how to remove green screen from the image below. Let me know if my question is lacking some necessary information. Any help will be appreciated.

Here's code to drawing bitmap on the screen

gfx->GetRenderTarget()->DrawBitmap(
    bmp, //Bitmap we built from WIC
    D2D1::RectF(0, 0,
        bmp->GetSize().width, bmp->GetSize().height), 
    0.8f, // opacity
D2D1_BITMAP_INTERPOLATION_MODE::D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,

    D2D1::RectF(0, 0, bmp->GetSize().width, bmp->GetSize().height) //Source Rect
    );

! [Planet_Image

Here's the screenshot of my executable (window) and what i've so far. Main Window

Here's the code loading bitmaps

this->gfx = gfx;
bmp = NULL; 
HRESULT hr;


IWICImagingFactory *wicFactory = NULL;

hr = CoCreateInstance(
    CLSID_WICImagingFactory,
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IWICImagingFactory, 

    (LPVOID*)&wicFactory); // pointer to the WICFactory

// decoder
IWICBitmapDecoder *wicDecoder = NULL;
hr = wicFactory->CreateDecoderFromFilename(
    filename, // filename passed as parameter
    NULL, 
    GENERIC_READ, 
    WICDecodeMetadataCacheOnLoad, 
    &wicDecoder); // pointer to the Decoder 


IWICBitmapFrameDecode* wicFrame = NULL;
hr = wicDecoder->GetFrame(0, &wicFrame); 


// create wic converter
IWICFormatConverter *wicConverter = NULL;

hr = wicFactory->CreateFormatConverter(&wicConverter);


hr = wicConverter->Initialize(
    wicFrame, 
    GUID_WICPixelFormat32bppPBGRA, 
    WICBitmapDitherTypeNone, 
    NULL, 
    0.0, 
    WICBitmapPaletteTypeCustom 
    );


// create bitmap
gfx->GetRenderTarget()->CreateBitmapFromWicBitmap(
    wicConverter, 
    NULL, 
    &bmp // destination bmp defined in header
);

Solution

  • This code will draw a background and sprite with transparency on top of it. All you have to do is load a png file with transparency. Use an image editor to convert the bitmap to png. This requires Visual Studio Atl COM classes CComPtr, but you can do it without that.

    #include <Windows.h>
    #include <atlbase.h>
    #include <d2d1.h>
    #include <Wincodec.h>
    
    HRESULT load_image(ID2D1HwndRenderTarget *target, 
        const wchar_t *filename, ID2D1Bitmap** bitmap)
    {
        HRESULT hr = S_FALSE;
        CComPtr<IWICImagingFactory> factory;
        CComPtr<IWICBitmapDecoder> decoder;
        CComPtr<IWICBitmapFrameDecode> frame;
        CComPtr<IWICFormatConverter> converter;
    
        hr = CoCreateInstance(CLSID_WICImagingFactory, 
            NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (void**)&factory);
        if (FAILED(hr)) return hr;
    
        hr = factory->CreateDecoderFromFilename(filename, NULL, 
            GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder);
        if(FAILED(hr)) return hr;
    
        hr = decoder->GetFrame(0, &frame);
        if(FAILED(hr)) return hr;
    
        hr = factory->CreateFormatConverter(&converter);
        if(FAILED(hr)) return hr;
    
        hr = converter->Initialize(frame, GUID_WICPixelFormat32bppPBGRA, 
            WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);
        if(FAILED(hr)) return hr;
    
        hr = target->CreateBitmapFromWicBitmap(converter, 0, bitmap);
        return hr;
    }
    
    void on_render(ID2D1HwndRenderTarget *render)
    {
        render->BeginDraw();
        render->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
        CComPtr<ID2D1Bitmap> background;
        if SUCCEEDED(load_image(render, L"c:\\background.jpg", &background))
        {
            D2D1_SIZE_F size = background->GetSize();
            render->DrawBitmap(background, D2D1::RectF(0, 0, size.width, size.height));
        }
    
        CComPtr<ID2D1Bitmap> sprite;
        if SUCCEEDED(load_image(render, L"c:\\transparent.png", &sprite))
        {
            D2D1_SIZE_F size = sprite->GetSize();
            render->DrawBitmap(sprite, D2D1::RectF(0, 0, size.width, size.height));
        }
    
        render->EndDraw();
    }
    

    TransparentBlt is a GDI function and can only handle single transparency. If you are going to use GDI functions then maybe you don't need the complexity of Direct2D! Alternatively you can use GDI+

    if(msg == WM_PAINT)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        auto hbitmap = LoadImage(NULL, L"test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        auto memdc = CreateCompatibleDC(hdc);
        auto oldbmp = SelectObject(memdc, hbitmap);
    
        BITMAP bm;
        GetObject(hbitmap, sizeof(bm), &bm);
        int w = bm.bmWidth;
        int h = bm.bmHeight;
        TransparentBlt(hdc, 0, 0, w, h, memdc, 0, 0, w, h, RGB(0, 255, 0));
    
        SelectObject(memdc, oldbmp);
        DeleteDC(memdc);
        DeleteObject(hbitmap);//<- edit
        EndPaint(hwnd, &ps);
        return 0;
    }