【问题标题】:c++ - Conversion from ARGB to Grayscale - Results are well, but upside downc++ - 从 ARGB 到灰度的转换 - 结果很好,但颠倒了
【发布时间】:2018-02-17 11:18:40
【问题描述】:

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

代码: #包括 #包括

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);
}

图片如下:

转换后的图片,没有设置A——只有RGB:

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

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

【问题讨论】:

  • 提示:在位图数据填充的情况下,您应该使用bm.bmWidthBytes 而不是iWidthBytes(虽然不确定ARGB位图是否会发生,但通常可能会发生)
  • 一旁的问题:你为什么把灰度值也设置为A通道?将它们写入我理解的所有颜色通道,这就是灰色。但我不希望图片像暗(或亮,取决于型号)那样不透明。
  • 如果您禁用(暂时,就像调试实验一样)灰色制作并且只复制未更改的值会发生什么?我想知道输入和输出文件格式在 y 坐标的含义上是否完全不同。
  • 我认为颠倒错误更有可能在您的灰色功能之外,例如渲染设置或投影矩阵错误。所以请多显示一些周围的代码。最好是minimal reproducible example。还是您将图像文件转换为图像文件?请描述。

标签: c++ bitmap grayscale argb hbitmap


【解决方案1】:

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
}

结果:

真彩色屏幕截图:

转换后的截图:

【讨论】:

    【解决方案2】:

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

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

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

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

    解决方案

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

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

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

    【讨论】:

    • 嗨,阿德里安,非常感谢您提供这些额外信息,直到现在我才知道。我会看看那个。这也解释了为什么灰度位图的大小与真彩色位图的大小不同,因为灰度也使用相同的像素格式。
    • 周末,我在 Petzold 的 Win32 编程一书中读到了关于 DIB 的内容。他写道,自下而上的问题是历史性的,因为 IBM 的数学家想要左下角的坐标系,而不是左上角的计算机科学家。您可以通过为 bmHeight 定义负值来定义上下线设置。
    • 是的,但是当您请求 BITMAP 结构而不是 BITMAPINFO 时,这是您丢失的信息的一部分。 BITMAP 将始终为高度提供正值,即使位图是自上而下的。
    猜你喜欢
    • 1970-01-01
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 2016-11-20
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多