c ++ - 从ARGB转换为灰度 - 结果很好,但是颠倒了

时间:2017-09-08 13:47:56

标签: c++ bitmap grayscale argb hbitmap

我写了一个小函数来将ARGB中的位图转换为灰度。转换本身效果很好,但结果是颠倒的。 我找不到错误。

代码:     #包括     #include

inline BYTE GrayScaleValue(BYTE* r, BYTE* g, BYTE* b) { return /*ceil*/(0.21f * (*r) + 0.72f * (*g) + 0.07f * (*b)); }

extern "C" __declspec(dllexport) HBITMAP ConvertToMonocrom(HBITMAP bmp) {
    INT x = 0, y = 0;
    char Gray;
    BITMAP bm;
    GetObject(bmp, sizeof(BITMAP), (LPSTR)&bm);
    BYTE * pImgByte = (BYTE *)bm.bmBits;
    INT iWidthBytes = bm.bmWidth * 4;
    for (y = 0; y < bm.bmHeight; y++) {
        for (x = 0; x < bm.bmWidth; x++) {
            Gray = GrayScaleValue(&pImgByte[y * iWidthBytes + x * 4 + 3], &pImgByte[y * iWidthBytes + x * 4 + 2], &pImgByte[y * iWidthBytes + x * 4 + 1]);
            pImgByte[y * iWidthBytes + x * 4] = Gray;
            pImgByte[y * iWidthBytes + x * 4 + 1] = Gray;
            pImgByte[y * iWidthBytes + x * 4 + 2] = Gray;
            pImgByte[y * iWidthBytes + x * 4 + 3] = Gray;
        }
    }
    return CreateBitmapIndirect(&bm);
}

这是图片:

Basic picture

转换后的图片,不设置A - 仅RGB: Conversion without setting Alpha-Value

转换后的图片,如代码所示(设置Alpha-Value: Conversion with setting Alpha-Value

嗯,我不知道,为什么他将“透明”设置为黑色...

2 个答案:

答案 0 :(得分:0)

HBITMAP可以引用以多种不同格式存储的位图。它可以是DIBSECTION或依赖于设备的位图。其中任何一个都可以以各种方式表示像素值。请注意,GetObject documentation列出了两种获取HBITMAP信息的方法。

我的猜测是你的输入是一个DIBSECTION,扫描线从上到下存储。当您要求BITMAP(使用GetObject)时,您会丢失一些格式信息,特别是图像是自下而上还是自上而下,是否具有Alpha通道以及通道的顺序(例如,BGRA V) 。ARGB)。 BITMAP不能表示与DIBSECTION一样多的格式细节。

操作像素数据后,您将使用CreateBitmapIndirect创建新的位图。由于BITMAP结构不包含有关数据是自下而上还是自上而下的信息,因此它使用默认值,即自下而上。由于您(显然)从自上而下的像素数据开始,您可以有效地颠倒图像。

您在使用Alpha通道时遇到的困难也可以解释为当您尝试使用BITMAP描述格式时丢失了信息。如果颜色通道的顺序与CreateBitmapIndirect假定的默认顺序不同,那么您将看到这个问题。通过将alpha设置为与所有其他通道相同的灰度值,您可以有效地隐藏您已经扰乱了颜色通道顺序的事实。

解决方案

有几种不同的方法可以解决这个问题。这是一个可能的解决方案:

您可以要求Windows使用GetDIBits为您提供指向您要使用的格式的像素数据的指针,而不管其原生格式如何。然后,您可以让Windows将修改后的像素值转换回使用SetDIBits的位图格式。

您必须填写BITMAPINFO(有几种变体)来描述您期望的内存格式。您将此BITMAPINFO传递给GetDIBits和SetDIBits。

答案 1 :(得分:0)

Adrian McCarthy向我们展示了更好的解决方案,但我需要几天的时间来阅读有关DIB的内容。我需要一个快速的解决方案,并通过再次颠倒位图来获得它。

关于如何将HBITMAP从TrueColor转换为GrayScale的问题经常在互联网上找到并且答案并不令人满意,我现在想要发布我的代码:

#include <stdio.h>
#include <Windows.h>
#include <math.h>

inline BYTE GrayScaleValue(BYTE* r, BYTE* g, BYTE* b) { return /*ceil*/(0.21f * (*r) + 0.72f * (*g) + 0.07f * (*b)); }

extern "C" __declspec(dllexport) HBITMAP ConvertToMonocrom(HBITMAP bmp) {
    #pragma region Local variables
    INT x = 0, y = 0;
    char Gray;
    BITMAP bm;
    #pragma endregion

    #pragma region Activating HBITMAP
    GetObject(bmp, sizeof(BITMAP), (LPSTR)&bm);
    #pragma endregion

    #pragma region Assigning pointer
    BYTE * pImgByte = (BYTE *)bm.bmBits;
    BYTE* fpImgByte = (BYTE*)malloc(bm.bmHeight * bm.bmWidth * sizeof(BYTE));
    if (fpImgByte == nullptr) { printf("ERROR: Unable to allocate memory for buffer array!"); return nullptr; }
    INT iWidthBytes = bm.bmWidth * 4;
    #pragma endregion

   #pragma region TrueColor to GrayScale
    for (y = 0; y < bm.bmHeight; y++) {
        for (x = 0; x < bm.bmWidth; x++) {
            Gray = GrayScaleValue(&pImgByte[y * iWidthBytes + x * 4 + 3], &pImgByte[y * iWidthBytes + x * 4 + 2], &pImgByte[y * iWidthBytes + x * 4 + 1]);
            fpImgByte[y * bm.bmWidth + x] = Gray;
        }
    }
    #pragma endregion

    #pragma region Flipping bitmap
    for (y = 0; y < bm.bmHeight; y++) {
        for (x = 0; x < bm.bmWidth; x++) {
            Gray = fpImgByte[(bm.bmHeight - y - 1)*bm.bmWidth + x];
            pImgByte[y * iWidthBytes + x * 4] = Gray;
            pImgByte[y * iWidthBytes + x * 4 + 1] = Gray;
            pImgByte[y * iWidthBytes + x * 4 + 2] = Gray;
            pImgByte[y * iWidthBytes + x * 4 + 3] = Gray;
        }
    }
    #pragma endregion 

    #pragma region Releasing memory
    free(fpImgByte);
    #pragma endregion

    #pragma region Returning converted bitmap
    return CreateBitmapIndirect(&bm);
    #pragma endregion
}

结果:

TrueColor中的屏幕截图:

ScreenShot in TrueColor

转换后的屏幕截图:

enter image description here