从数字数组创建 8 位位图

Posted

技术标签:

【中文标题】从数字数组创建 8 位位图【英文标题】:Creating 8 bit bitmap from array of numbers 【发布时间】:2014-01-21 17:10:18 【问题描述】:

这一直困扰着我一段时间,我想确保我对位图的理解是正确的,并获得一些帮助发现错误。基本上我要做的是保存一个 8 位位图文件,同时在 MFC 应用程序的图片框中显示它。我想避免繁琐的保存位图然后重新加载的方法。

保存文件的操作大部分是成功的,但是我更改了我的代码,现在文件中过去是白色的(在本例中是黑白图像)通常是绿色的,但它发生了变化。我猜这是因为我的数据可能引用了颜色表中的信息,哪个值是白色的?

HBITMAP ReadWrite::SaveFile(LPCTSTR file, double* data) 

    BYTE* bmp = ConvertData(data);
    HANDLE hf;
    BITMAPINFO* pbmi = WriteHeader(file);

    BITMAPFILEHEADER* bmfh = (BITMAPFILEHEADER*)alloca(sizeof(BITMAPFILEHEADER));
    bmfh->bfType = 0x4d42; // 'BM'
    bmfh->bfReserved1 = 0;
    bmfh->bfReserved2 = 0;
    bmfh->bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbmi->bmiHeader.biSize + 256 * sizeof(RGBQUAD);
    bmfh->bfSize = (DWORD)(bmfh->bfOffBits + pbmi->bmiHeader.biSizeImage);


    hf = CreateFile(file, GENERIC_READ | GENERIC_WRITE, (DWORD) 0,
        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL );

    if (hf == NULL) // error creating
    
        CloseHandle (hf);
        return NULL;
    

    // write header
    unsigned long bwritten;
    if (!WriteFile(hf, (LPCVOID)bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL))
    
        CloseHandle (hf);
        return NULL;
    

    // write infoheader
    if (!WriteFile(hf, (LPCVOID)pbmi, sizeof (BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD), &bwritten, NULL ))
       
        CloseHandle (hf);
        return NULL;
    

    // write image data
    if (!WriteFile(hf, (LPCVOID)bmp_data, (int) pbmi->bmiHeader.biSizeImage, &bwritten, NULL ))
       
        CloseHandle (hf);
        return NULL;
    

    // Close
    CloseHandle(hf);

    // Send back a file to display
    return CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, (void**)bmp_data, NULL, 0);

编写 infoheader + 调色板的代码(应该是从黑色到白色的值??)

BITMAPINFO* ReadWrite::WriteHeader(LPCTSTR fn)

    int R = ReadWrite::getR();
    int C = ReadWrite::getC();

    BITMAPINFO* pbmi = (BITMAPINFO*)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
    pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = R;
    pbmi->bmiHeader.biHeight = -C;
    pbmi->bmiHeader.biPlanes = 1;
    pbmi->bmiHeader.biBitCount = 8;
    pbmi->bmiHeader.biCompression = BI_RGB;
    pbmi->bmiHeader.biSizeImage = (((R * pbmi->bmiHeader.biBitCount + 31) & ~31) >> 3) * C;
    pbmi->bmiHeader.biClrUsed = 256;
    pbmi->bmiHeader.biClrImportant = 0; 
    for(int i=0; i<256; i++)
    
        pbmi->bmiColors[i].rgbRed = i;
        pbmi->bmiColors[i].rgbGreen = i;
        pbmi->bmiColors[i].rgbBlue = i;
        pbmi->bmiColors[i].rgbReserved = 0;
    

    //return true;
    return pbmi;

最后将我的数字数组转换为 unsigned char 'Bytes':

BYTE* ReadWrite::ConvertData(double* data) 

    BYTE* bmp_data;
    int R = ReadWrite::getR();
    int C = ReadWrite::getC();
    bool binary = ReadWrite::getBinary();

    bmp_data = new BYTE [R*C];

    // convert the values to unsigned char (BYTE)
    for(int i=0; i<R*C; i++)
        if (data[i] == 1)
            data[i] = 255;
        
        bmp_data[i] = (unsigned char)data[i];
    

    delete [] data;

    return bmp_data;

所以回顾一下问题/问题:

    白色变成石灰绿色。 HBITMAP 无法在图片框内显示,调用 setimage 后该框变为纯黑色(是的,它设置为 SS_BITMAP) 我相信我可能在创建位图时遗漏了一些信息,并且我认为我需要实现一个设备上下文,尽管我不确定。我可以在 Windows 中打开文件,但如果我尝试在线上传它,它不喜欢这种格式。 我不知道如何管理对象的内存,所以我遇到了泄漏,如何在应用程序关闭之前使用 DeleteObject 进行清理? (可能是对话框的析构函数?)泄漏是 262144 字节,大约是图像的大小。

感谢您阅读这篇愚蠢的长篇文章。

编辑 我设法解决了绿色问题(第 1 号),我不知道如何解决,我认为这与标题上的内存大小不正确有关。它仍然无法上传到互联网或在程序中显示,所以它一定有问题。

【问题讨论】:

看不到您释放此行分配的缓冲区的位置:BITMAPINFO* pbmi = (BITMAPINFO*)alloca....。另外,你在哪里删除这个缓冲区:bmp_data = new BYTE [R*C]; 它们用于 CreateDIBSection,如果我在返回之前删除它们,我会得到错误。我是否认为一旦 HBITMAP 对象被删除(在某个时候)它们都不再存在?我确实删除了bmfh,因为它在写入文件后就不需要了,对吗? 【参考方案1】:

所以我发现我的代码存在导致其他应用程序/互联网出错的问题。

在其中我为图像的高度分配了一个负值以翻转数据,但这是一个完全不正确的解决方案。而是:

// convert the integer values to unsigned char (BYTE) BACKWARDS
for(int i=0; i<R; i++)
    for(int j=0; j<C; j++)
        if (data[i*R + j] == 1 && binary)
            data[i*R + j] = 255;
        
        bmp_data[(R-i)*C + (j-C)] = (unsigned char)data[i*C + j];
    

这样做是将原始值复制到 bmp_data 中,同时向后 翻转。这可确保数据正确存储并将在应用程序中打开。我目前对 DIBSection 和内存管理的解决方案是删除所有数据并从文件中重新加载图像。通过 HBITMAP 太麻烦了。我欢迎就这部分提出意见,但现在我的所有问题都已解决。

【讨论】:

以上是关于从数字数组创建 8 位位图的主要内容,如果未能解决你的问题,请参考以下文章

如何从字节数组创建位图?

如何将字节数组转换为位图实例 (.NET)?

一种从输入数组创建位图的更快方法,而不是使用 for 循环和 SetPixel()

Java算法 -- 位图的概念和实现

Java算法 -- 位图的概念和实现

Java算法 -- 位图的概念和实现