在C中导入BMP时的环绕问题

Wrap-around issues when importing a BMP in C

本文关键字:问题 BMP 导入      更新时间:2023-10-16

现在我正在为我的视觉信息处理课程做一个程序。我们给所有的家庭作业骨架模板,因为类的重点不是学习MFC。我在Mac电脑上编程,无法访问windows的库,因此导入bmp很容易。因此,我使用(并稍微修改)从这个网站找到的代码:paulbourke.net/dataformats/bmp/

我实际上在过去的一年里一直在使用这个代码,它对于24位bmp(即像素表示为RGB的bmp)工作得很好。我需要对代码进行的主要调整是添加一个特殊的例程,如果BMP的高度表示为负数,则该例程将反转图像的行。

当我将BMP导入到GLubyte类型的数组中,并且图像具有biBitCount = 24,使用GLDrawPixels可以完美地工作:

https://i.stack.imgur.com/aL3JN.png

然而,当我导入biBitCount = 8的BMP并使用GLDrawPixels显示它时,我得到以下结果(注意红色矩形中突出显示的绕行错误):

https://i.stack.imgur.com/PQv7i.png

我必须为我的最后一个任务实现一个自动阈值算法,以方便基于解释区域分割图像。我认为这种缠绕的错误源于导入BMP,而不是来自GLDrawPixels调用。这是因为我所做的区域划分算法比它应该识别的区域要多。这似乎意味着在BMP的数组表示中,环绕的部分确实是不相交的。

我已经把我的代码筛选了很多次,但无论如何也找不出是什么原因导致了这个问题。

下面的代码显示导入后的BMP:

void drawBMP(BITMAPINFO *bitmapInfo, GLubyte *bitmapIn, GLfloat xOffset, GLfloat yOffset) {
if (bitmapInfo) {
    glRasterPos2f(xOffset, yOffset);
    if (bitmapInfo->bmiHeader.biBitCount == 24) {
        glDrawPixels(bitmapInfo->bmiHeader.biWidth,
                     bitmapInfo->bmiHeader.biHeight,
                     GL_BGR, GL_UNSIGNED_BYTE, bitmapIn);
    } else {
        glDrawPixels(bitmapInfo->bmiHeader.biWidth,
                     bitmapInfo->bmiHeader.biHeight,
                     GL_LUMINANCE, GL_UNSIGNED_BYTE, bitmapIn);
    }
}
glFinish();
}

也许是GL_LUMINANCE设置导致了问题?

下面是实际导入BMP的函数:
GLubyte *                          /* O - Bitmap data */
LoadDIBitmap(const char *filename, /* I - File to load */
         BITMAPINFO **info)    /* O - Bitmap information */
{
FILE             *fp;          /* Open file pointer */
GLubyte          *bits;        /* Bitmap pixel bits */
GLubyte          *ptr;         /* Pointer into bitmap */
GLubyte          temp;         /* Temporary variable to swap red and blue */
int              x, y;         /* X and Y position in image */
int              length;       /* Line length */
int              bitsize;      /* Size of bitmap */
int              infosize;     /* Size of header information */
BITMAPFILEHEADER header;       /* File header */

/* Try opening the file; use "rb" mode to read this *binary* file. */
if ((fp = fopen(filename, "rb")) == NULL)
    return (NULL);
/* Read the file header and any following bitmap information... */
header.bfType      = read_word(fp);
header.bfSize      = read_dword(fp);
header.bfReserved1 = read_word(fp);
header.bfReserved2 = read_word(fp);
header.bfOffBits   = read_dword(fp);
if (header.bfType != BF_TYPE) /* Check for BM reversed... */
    {
    /* Not a bitmap file - return NULL... */
    fclose(fp);
    return (NULL);
    }
infosize = header.bfOffBits - 18;
if ((*info = (BITMAPINFO *)malloc(sizeof(BITMAPINFO))) == NULL)
    {
    /* Couldn't allocate memory for bitmap info - return NULL... */
    fclose(fp);
    return (NULL);
    }
(*info)->bmiHeader.biSize          = read_dword(fp);
(*info)->bmiHeader.biWidth         = read_long(fp);
(*info)->bmiHeader.biHeight        = read_long(fp);
(*info)->bmiHeader.biPlanes        = read_word(fp);
(*info)->bmiHeader.biBitCount      = read_word(fp);
(*info)->bmiHeader.biCompression   = read_dword(fp);
(*info)->bmiHeader.biSizeImage     = read_dword(fp);
(*info)->bmiHeader.biXPelsPerMeter = read_long(fp);
(*info)->bmiHeader.biYPelsPerMeter = read_long(fp);
(*info)->bmiHeader.biClrUsed       = read_dword(fp);
(*info)->bmiHeader.biClrImportant  = read_dword(fp);
if (infosize > 40)
if (fread((*info)->bmiColors, infosize - 40, 1, fp) < 1)
        {
        /* Couldn't read the bitmap header - return NULL... */
        free(*info);
        fclose(fp);
        return (NULL);
        }
/* Now that we have all the header info read in, allocate memory for *
 * the bitmap and read *it* in...                                    */
if ((bitsize = (*info)->bmiHeader.biSizeImage) == 0)
    bitsize = ((*info)->bmiHeader.biWidth *
               (*info)->bmiHeader.biBitCount+7) / 8 *
           abs((*info)->bmiHeader.biHeight);
if ((bits = malloc(bitsize)) == NULL)
    {
    /* Couldn't allocate memory - return NULL! */
    free(*info);
    fclose(fp);
    return (NULL);
    }
if (fread(bits, 1, bitsize, fp) < bitsize)
    {
    /* Couldn't read bitmap - free memory and return NULL! */
    free(*info);
    free(bits);
    fclose(fp);
    return (NULL);
    }
//This needs to be done when the height is negative
if ((*info)->bmiHeader.biHeight < 0) {
    (*info)->bmiHeader.biHeight *= -1;
    int bitsPerPixel = (*info)->bmiHeader.biBitCount;
    int bytesPerPixel;
    if (bitsPerPixel >= 8) {
        bytesPerPixel = bitsPerPixel/8;
    } else {
        exit(1);
    }       
    int i;  //Row
    int j;  //Column
    for (i = 0; i < floor((*info)->bmiHeader.biHeight/2); i++) {
        int inlineRowValue = i * (*info)->bmiHeader.biWidth * bytesPerPixel;
        int inlineInvRowValue = ((*info)->bmiHeader.biHeight - i) * (*info)->bmiHeader.biWidth * bytesPerPixel;
        for (j = 0; j < (*info)->bmiHeader.biWidth; j++) {
            int inlineColumnValue = j * bytesPerPixel;
            int currentPos = inlineRowValue + inlineColumnValue;
            int invCurrentPos = inlineInvRowValue + inlineColumnValue;
            int k;
            GLubyte *temp = malloc(sizeof(GLubyte)*bytesPerPixel);
            for (k = 0; k < bytesPerPixel; k++) {
                temp[k] = bits[currentPos+k];                   
            }
            for (k = 0; k < bytesPerPixel; k++) {
                bits[currentPos+k] = bits[invCurrentPos+k];
                bits[invCurrentPos+k] = temp[k];    
            }               
            free(temp);
        }
    }
}
/* OK, everything went fine - return the allocated bitmap... */
fclose(fp);
return (bits);
}

我的直觉告诉我,当从BMP文件中读取内容时,文件指针没有正确地增加。

如果你们知道这个bug的来源,请分享。我会非常感激。

我想你减错了。

infosize = header.bfOffBits - 18;

这似乎是报头信息的大小,但报头是14字节(3个字和2个字),而不是18字节。

与其依赖于读取正确数量的头和信息结构,为什么不直接使用:

fseek(fp, header.bfOffBits, SEEK_SET);

在读取图像数据之前?