【问题标题】:Reading bitmap of sizes not multiple of 8 results in shift (and wrap) of pixels读取大小不是 8 倍数的位图会导致像素移位(和环绕)
【发布时间】:2015-03-03 19:31:56
【问题描述】:

我正在尝试了解如何输入/输出/处理图像,并且从一个错误到另一个错误,我得到了以下信息:

位图输出:

void createBMPFile(PBYTE image, BITMAPINFOHEADER bmi)
{
    //DWORD stride = (((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3;
    //bmi.biSizeImage = bmi.biHeight * stride;

    BITMAPFILEHEADER    bmf;
    memset(&bmf, 0, sizeof(bmf));

    // Fill BitmapFileHeader
    INT cbHeaderOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    DWORD dwTotalBytes = cbHeaderOffBits + bmi.biSizeImage;        // File size
    bmf.bfType = 0x4d42;     // Signature = 'BM'
    bmf.bfSize = dwTotalBytes;  // Bytes in whole file.
    bmf.bfReserved1 = 0;
    bmf.bfReserved2 = 0;
    bmf.bfOffBits = cbHeaderOffBits; // Offset to bits in file.

    // Flip the biHeight member so that it denotes top-down bitmap 
    // bmi.biHeight *= -1;  

    DWORD      dwWritten = 0;
    HANDLE     hFile = NULL;

    WCHAR wFileName[MAX_PATH] = TEXT("output.bmp");
    hFile = CreateFileW(wFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return;

    // Dump headers first
    if (!WriteFile(hFile, &bmf, sizeof(BITMAPFILEHEADER), &dwWritten, NULL))
        return;
    if (!WriteFile(hFile, &bmi, sizeof(BITMAPINFOHEADER), &dwWritten, NULL))
        return;

    VERBOSE(TEXT("createBMPFile24: imageSize=%d width=%d height=%d \nbitCount=%d image=0x%08x\n"),
        bmi.biSizeImage, bmi.biWidth, bmi.biHeight, bmi.biBitCount, image);

    // Dump the data now
    if (!WriteFile(hFile, image, bmi.biSizeImage, &dwWritten, NULL))
        return;

    CloseHandle(hFile);
}

位图输入:

PBYTE inputBMP(LPCWSTR filename, BITMAPINFOHEADER *bmi)
{
    BITMAPFILEHEADER bmf;
    memset(&bmf, 0, sizeof(bmf));
    DWORD bytesread = 0;

    HANDLE  file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 
        NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);  

    if (file == INVALID_HANDLE_VALUE)
    {
        ERR(TEXT("Error creating file\n"));
        return NULL;
    }

    if (!ReadFile(file, &bmf, sizeof(BITMAPFILEHEADER), &bytesread, NULL))
    {
        CloseHandle(file);
        return NULL;
    }

    if (!ReadFile(file, bmi, sizeof(BITMAPINFOHEADER), &bytesread, NULL))
    {
        CloseHandle(file);
        return NULL;
    }

    LONG width = bmi->biWidth;
    LONG height = abs(bmi->biHeight);

    if (bmi->biCompression != BI_RGB)
    {
        CloseHandle(file);
        return NULL;
    }

    if (bmi->biBitCount != 24)
    {
        CloseHandle(file);
        return NULL;
    }

    unsigned long size = bmi->biSizeImage - bmf.bfOffBits;
    PBYTE Buffer = new BYTE[size];
    if (SetFilePointer(file, bmf.bfOffBits, NULL, FILE_BEGIN) == 0xFFFFFFFF)
    {
    }

    if (!ReadFile(file, Buffer, size, &bytesread, NULL) || bytesread == 0)
    {
        delete[] Buffer;
        CloseHandle(file);
        return NULL;
    }
    CloseHandle(file);

    return Buffer;
}

调用者 - 输入位图,显示其内容,将其发送回输出

int main()
{
    BITMAPINFOHEADER bitmapInfoHeader;
    memset(&bitmapInfoHeader, 0, sizeof(bitmapInfoHeader));
    PBYTE pSrcBitmap = inputBMP(TEXT("input.bmp"), &bitmapInfoHeader);

    if (!pSrcBitmap)
        return 1;

    createBMPFile(pSrcBitmap, bitmapInfoHeader);

    unsigned int h = abs(bitmapInfoHeader.biHeight);
    unsigned int w = bitmapInfoHeader.biWidth;
    for (unsigned int y = 0; y < h; ++y)
    {
        for (unsigned int x = 0; x < w; ++x)
        {
            VERBOSE(TEXT("(%2d %2d %3d %3d %3d) "), x, y,
                pSrcBitmap[3 * (x + y * w)],
                pSrcBitmap[3 * (x + y * w) + 1],
                pSrcBitmap[3 * (x + y * w) + 2]);
        }
        VERBOSE(TEXT("\n"));
    }

    delete[] pSrcBitmap;
    pSrcBitmap = NULL;

    return 0;
}

我得到了非常奇怪的信息。

为了便于查看,我使用 Paint 制作了一个小矩形(黑白,但图像类型为 24 bpp)。

输出似乎有颜色...

但这不是我最大的问题。显示屏显示不均匀的信息(参见 x=14,y=5 。

我的字节似乎发生了变化……我不明白为什么,或者如何能够在一行上看到整行。如果图像不是矩形而是其他形状,这会导致数据的奇怪包装(第 1 行的字节放在第 2 行,移动更多字节......)

我怀疑这与步幅有关...但我不明白如何,因为图像是 24bpp...

不过,我尝试将 1 添加到这个特定的 bmp(w=35,h=10)

unsigned int w = bitmapInfoHeader.biWidth + 1;

突然间我的像素列表看起来很好(没有移位或换行):

我不明白为什么...或如何对任何尺寸的图像进行校正。

我试过了

if (w != (w / 4) * 4)   w = (w / 4) * 4 + 1;

没用。

我需要能够循环遍历图像数据,而不是将其移位....有人可以解释一下这种移位/换行/字节未对齐的逻辑以及如何修复它们吗?

【问题讨论】:

  • 位图文件格式比大多数人想象的要复杂得多。在读取/写入位图文件时,我总是建议使用预构建的解决方案。 Windows Imaging Component 是一个可行的选择。

标签: c++ windows bitmap


【解决方案1】:

位图扫描线用 0、1、2 或 3 个字节补零,以便
扫描线大小 % 4 == 0

这就是您(从概念上)读取位图(未压缩,24 位)的方式

// bmi is a BITMAPINFOHEADER 
// bmf is a BITMAPFILEHEADER
// fp is a FILE*
int w = bmi.biWidth;
int h = bmi.biHeight;
int scanlinesize = w*3;
while( scanlinesize%4 ) ++scanlinesize;
for(int y=0;y<h;++y)
{
    fseek( fp, bmf.bfOffBits + scanlinesize*y, SEEK_SET );
    for(int x=0;x<w;++x)
    {
        unsigned char rgb[3];
        fread( rgb, 1, 3, fp );
        // put rgb in the output here
    }
}

【讨论】:

  • 这太棒了......在我创建上面的测试程序之前,我一直在努力解决这个问题......我不知道这个填充。非常感谢!
  • BITMAPINFOHEADER structure 的文档提供了一个更强大的解决方案来计算扫描线步幅:stride = ((((biWidth * biBitCount) + 31) &amp; ~31) &gt;&gt; 3),同时考虑了位深度。
猜你喜欢
  • 2014-12-05
  • 1970-01-01
  • 2011-03-19
  • 2014-09-10
  • 1970-01-01
  • 2012-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多