Проблемы с переносом при импорте BMP в C

#c #c #opengl #glut #bmp

#c #c #opengl #перенасыщение #bmp

Вопрос:

Прямо сейчас я работаю над программой для моего курса по обработке визуальной информации. Нам предоставлены скелетные шаблоны для всех домашних заданий, поскольку основное внимание в классе уделяется не изучению MFC. Я программирую на Mac и не имею доступа к библиотеке Window, которая упрощает импорт BMP. Поэтому я использовал (и немного изменил) код, найденный на этом веб-сайте:paulbourke.net/dataformats/bmp /

На самом деле я использовал этот код в течение прошлого года, и он отлично работал для 24-битных BMP (то есть с пикселями, представленными как RGB). Основная корректировка, которую мне нужно было внести в код, заключалась в добавлении специальной процедуры, которая инвертирует строки изображения, если высота BMP выражена как отрицательное число.

Когда я импортирую BMP в массив типа GLubyte, а изображение имеет biBitCount = 24, использование glDrawPixels работает отлично:

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

Однако, когда я импортирую BMP с biBitCount = 8 и отображаю его с помощью 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.

Если у вас, ребята, есть какие-либо идеи относительно источника этой ошибки, пожалуйста, поделитесь. Я буду невероятно признателен. Это сводит меня с ума.

Комментарии:

1. Каждая строка данных начинается с позиции, выровненной по 24 битам (или это было 32?), Вы учитываете это?

Ответ №1:

Я думаю, что вы вычитаете здесь неправильную сумму:

 infosize = header.bfOffBits - 18;
  

Похоже, что это связано с размером информации заголовка, но заголовок составляет 14 байт (3 слова и 2 dw), а не 18 байт.

Вместо того, чтобы полагаться на чтение точно нужного количества заголовков и информационных структур, почему бы не использовать напрямую:

 fseek(fp, header.bfOffBits, SEEK_SET);
  

перед чтением данных изображения?

Комментарии:

1. Прежде всего, большое вам спасибо за обнаружение ошибки. Изменение 18 на 14 устранило проблему.

2. Будет ли fseek работать в этой ситуации? Содержимое заголовка и информационные структуры имеют разные характеристики. Компоненты различаются по размеру, и некоторые из них являются указателями, и содержимое из файла должно быть выгружено в выделенную для него память. Кроме того, я взял код для импорта BMP из ссылки, которую я предоставил в верхней части моего OP. Итак, я просто следовал его примеру (и, на самом деле, также привел статью gamedev). Из любопытства, почему ошибка не была достаточно выражена, чтобы ее можно было обнаружить для 24-битного изображения?

3. @bchiller: fseek() кажется, что это правильный метод для использования, поскольку формат BMP указывает, что данные изображения начинаются с определенного смещения от начала файла (что именно SEEK_SET и делает). Я подозреваю, что вы не увидели ошибку для 24-битного изображения, потому что оно было сдвинуто всего на один пиксель, а не на 4.