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

Posted

技术标签:

【中文标题】在 C 中导入 BMP 时的环绕问题【英文标题】:Wrap-around issues when importing a BMP in C 【发布时间】:2011-10-12 03:28:24 【问题描述】:

现在我正在为我的视觉信息处理课程开发一个程序。由于课程的重点不是学习 MFC,因此我们为所有作业提供了骨架模板。我在 Mac 上编程,无法访问 Window 的库,这使得导入 BMP 变得容易。因此,我使用(并稍作修改)从该网站找到的代码:paulbourke.net/dataformats/bmp/

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

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

http://i.imgur.com/41TVo.png

但是,当我导入 biBitCount = 8 的 BMP 并使用 GLDrawPixels 显示它时,我得到以下信息(请注意红色矩形中突出显示的环绕错误):

http://i.imgur.com/xws5j.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 文件中读取内容时,文件指针没有正确递增。

如果你们知道这个错误的来源是什么,请分享。我会非常感激。这让我发疯了。

【问题讨论】:

每一行数据都从 24 位对齐的位置开始(或者是 32 位?),你考虑到了吗? 【参考方案1】:

我认为您在这里减去了错误的金额:

infosize = header.bfOffBits - 18;

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

与其依赖读取准确数量的标题和信息结构,不如直接使用:

fseek(fp, header.bfOffBits, SEEK_SET);

在读入图像数据之前?

【讨论】:

首先,非常感谢您发现错误。将 18 更改为 14 解决了问题。 fseek 在这种情况下可以工作吗? header 和 info 结构的内容都具有不同的特征。这些组件的大小各不相同,有些是指针,文件中的内容应该被转储到为其分配的内存中。此外,我从我在我的 OP 顶部提供的链接中获取了用于导入 BMP 的代码。所以我只是跟随他的领导(实际上也是gamedev文章的领导)。出于好奇,为什么对于 24 位图像,错误不够明显? @bchiller: fseek() 似乎是正确的使用方法,因为 BMP 格式指定图像数据从文件开头的特定偏移量开始(这正是 SEEK_SET做)。我怀疑您没有看到 24 位图像的错误,因为它仅移动了一个像素,而不是 4。

以上是关于在 C 中导入 BMP 时的环绕问题的主要内容,如果未能解决你的问题,请参考以下文章

在 SSMS 中导入平面文件时的小数分隔符

在 SSMS 中导入平面文件时的小数分隔符

如何在 R 中导入的 jpeg 上绘制数据?

如何在Python 中导入access 数据

在 Xcode 中导入 C++ 项目

在eclipse中导入hadoop jar包,和必要时导入源码包。