Improving the grey scale conversion result

148 views Asked by At

Here is the colour menu:

Menu with colour bitmaps

Here is the same menu with some of the menu items disabled, and the bitmaps set as greyscale:

Menu with greyscale bitmaps

The code that converts to grey scale:

auto col = GetRValue(pixel) * 0.299 + 
           GetGValue(pixel) * 0.587 + 
           GetBValue(pixel) * 0.114;
pixel = RGB(col, col, col);

I am colourblind but it seems that some of them don’t look that much different. I assume it relates to the original colours in the first place?

It would just be nice if it was more obvious they are disabled. Like, it is very clear with the text.

Can we?

2

There are 2 answers

4
Piglet On BEST ANSWER

For people who are not colour blind it's pretty obvious.

Just apply the same intensity reduction to the images that you do to the text.

I did not check your values. Let's assume the text is white (100% intensity).

And the grayed out text is 50% intensity.

Then the maximum intensity of the bitmap should be 50% as well.

for each gray pixel:
  pixel_value = pixel_value / max_pixel_value * gray_text_value

This way you decrease further decrease the contrast of each bitmap and avoid having any pixel brighter than the text.

14
Barmak Shemirani On

This is not directly related to your question, but since you are changing colors you can also fix the corner pixels which stand out (by corner pixels I don't mean pixels at the edges of bitmap rectangle, I mean the corner of human recognizable image)

Example, in image below, there is a red pixel at the corner of the page. We want to find that red pixel and blend it with background color so that it doesn't stand out.

enter image description here

To find if the corner pixels, check the pixels at left and top, if both left and top are the background color then you have a corner pixel. Repeat the same for top-right, bottom-left, and bottom-right. Blend the corner pixels with background.

Instead of changing to grayscale you can change the alpha transparency as suggested by zett42.

void change(HBITMAP hbmp, bool enabled)
{
    if(!hbmp)
        return;
    HDC memdc = CreateCompatibleDC(nullptr);

    BITMAP bm;
    GetObject(hbmp, sizeof(bm), &bm);
    int w = bm.bmWidth;
    int h = bm.bmHeight;
    BITMAPINFO bi = { sizeof(BITMAPINFOHEADER), w, h, 1, 32, BI_RGB };

    std::vector<uint32_t> pixels(w * h);
    GetDIBits(memdc, hbmp, 0, h, &pixels[0], &bi, DIB_RGB_COLORS);

    //assume that the color at (0,0) is the background color
    uint32_t old_color = pixels[0];

    //this is the new background color
    uint32_t bk = GetSysColor(COLOR_MENU);

    //swap RGB with BGR
    uint32_t new_color = RGB(GetBValue(bk), GetGValue(bk), GetRValue(bk));

    //define lambda functions to swap between BGR and RGB
    auto bgr_r = [](uint32_t color) { return GetBValue(color); };
    auto bgr_g = [](uint32_t color) { return GetGValue(color); };
    auto bgr_b = [](uint32_t color) { return GetRValue(color); };

    BYTE new_red = bgr_r(new_color);
    BYTE new_grn = bgr_g(new_color);
    BYTE new_blu = bgr_b(new_color);

    //change background and modify disabled bitmap
    for(auto &p : pixels)
    {
        if(p == old_color)
        {
            p = new_color;
        }
        else if(!enabled)
        {
            //blend color with background, similar to 50% alpha
            BYTE red = (bgr_r(p) + new_red) / 2;
            BYTE grn = (bgr_g(p) + new_grn) / 2;
            BYTE blu = (bgr_b(p) + new_blu) / 2;
            p = RGB(blu, grn, red); //<= BGR/RGB swap
        }
    }

    //fix corner edges
    for(int row = h - 2; row >= 1; row--)
    {
        for(int col = 1; col < w - 1; col++)
        {
            int i = row * w + col;
            if(pixels[i] != new_color)
            {
                //check the color of neighboring pixels:
                //if that pixel has background color,
                //then that pixel is the background 

                bool l = pixels[i - 1] == new_color; //left pixel is background
                bool r = pixels[i + 1] == new_color; //right  ...
                bool t = pixels[i - w] == new_color; //top    ...
                bool b = pixels[i + w] == new_color; //bottom ...

                //we are on a corner pixel if:
                //both left-pixel and top-pixel are background or
                //both left-pixel and bottom-pixel are background or
                //both right-pixel and bottom-pixel are background or
                //both right-pixel and bottom-pixel are background
                if(l && t || l && b || r && t || r && b)
                {
                    //blend corner pixel with background
                    BYTE red = (bgr_r(pixels[i]) + new_red) / 2;
                    BYTE grn = (bgr_g(pixels[i]) + new_grn) / 2;
                    BYTE blu = (bgr_b(pixels[i]) + new_blu) / 2;
                    pixels[i] = RGB(blu, grn, red);//<= BGR/RGB swap
                }
            }
        }
    }

    SetDIBits(memdc, hbmp, 0, h, &pixels[0], &bi, DIB_RGB_COLORS);
    DeleteDC(memdc);
}

Usage:

CBitmap bmp1, bmp2;

bmp1.LoadBitmap(IDB_BITMAP1);
bmp2.LoadBitmap(IDB_BITMAP2);

change(bmp1, enabled);
change(bmp2, disabled);