Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.89/9: Рейтинг темы: голосов - 9, средняя оценка - 4.89
16 / 0 / 0
Регистрация: 28.11.2016
Сообщений: 6
1

Загрузка и сохранение BMP файла

29.11.2016, 19:20. Просмотров 1861. Ответов 6

Добрый день!
Мне нужно загружать, а затем сохранять BMP файл, пока без обработки, просто загрузить в массив и потом из этого массива опять сохранить в файл. Я ознакомился с форматом файла, понял, что после заголовка идет сплошной массив байтов, в конце каждой строки паддинги для обеспечения кратности 4 байтам.
я создал тип данных и структуру для заголовка (это нужно из-за выравнивания данных)

C++
1
2
3
4
5
6
7
8
9
10
typedef unsigned __int16    FILE_ID;
 
typedef struct
{
    unsigned __int32 size = 54;     //сюда надо будет прибавить размер массива c паддингами
    unsigned __int16 rzrv1 = 0;
    unsigned __int16 rzrv2 = 0;
    unsigned __int32 offset = 54;   //смещение массива данных относительно начала файла = sizeof(file_id) + sizeof(BMPheader)
    BITMAPINFOHEADER dib;
}       BMP_HEADER;
далее я написал 2 функции для загрузки и сохранения файлов

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
int saveBMP(const char *fname, unsigned char *v, int x_pix, int y_pix)
{
    int bytes, pad, i, j;
    unsigned char tmp = 255;
    BMP_HEADER bh;
    memset(&bh, 0, sizeof(bh));
    FILE_ID id = 0x4d42;
    //находим длину строки в байтах и округляем с учетом паддингов
    bytes = x_pix * 3;              //каждый пиксель занимает 3 байта в строке
    if ((bytes % 4) != 0)
    {
        pad = bytes + (4 - (bytes % 4));
    }
    else
    {
        pad = bytes;
    }
    //размер файла 54 байта заголовок + массив данных
    bh.size += pad * y_pix;
    //заполняем DIB заголовок
    bh.dib.biSize = 40;                 //размер заголовка
    bh.dib.biWidth = x_pix;             //ширина картинки
    bh.dib.biHeight = y_pix;                //высота картинки
    bh.dib.biPlanes = 1;
    bh.dib.biBitCount = 24;
    bh.dib.biCompression = 0;
    bh.dib.biSizeImage = pad * y_pix;   //размер массива данных 
    bh.dib.biXPelsPerMeter = 2835;
    bh.dib.biYPelsPerMeter = 2835;
    bh.dib.biClrUsed = 0;
    bh.dib.biClrImportant = 0;
    //открываем файл
    FILE *f = fopen(fname, "wb");
    if (!f) return -1;
    //пишем заголовок
    size_t res;
    res = fwrite(&id, 1, sizeof(FILE_ID), f);
    if (res != sizeof(FILE_ID))
    {
        fclose(f);
        return -1;
    }
    res = fwrite(&bh, 1, sizeof(BMP_HEADER), f);
    if (res != sizeof(BMP_HEADER))
    {
        fclose(f);
        return -1;
    }
    //пишем массив данных
    for (i = 0; i < y_pix; i++)
    {
        for (j = 0; j < pad; j++)
        {
            if (j < bytes)
            {
                fwrite((v + j + 0 + (i * x_pix)), 1, 1, f);
                fwrite((v + j + 1 + (i * x_pix)), 1, 1, f);
                fwrite((v + j + 2 + (i * x_pix)), 1, 1, f);
                j += 2;
            }
            else
            {
                fwrite(&tmp, 1, 1, f);
            }
        }
    }
    fclose(f);
 
    return 0;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
unsigned char* loadBMP(const char *fname, int &x_pix, int &y_pix)
{
    FILE_ID id;
    BMP_HEADER bh;
    size_t res;
    int bytes, pad, i, j;
    unsigned char *v, tmp;
 
    x_pix = y_pix = -1;
    FILE *f = fopen(fname, "rb");
    if (!f)
    {
        return NULL;
    }
    res = fread(&id, 1, sizeof(FILE_ID), f);
    if (res != sizeof(FILE_ID))
    {
        fclose(f);
        return NULL;
    }
    res = fread(&bh, 1, sizeof(BMP_HEADER), f);
    if (res != sizeof(BMP_HEADER))
    {
        fclose(f);
        return NULL;
    }
 
    /* */
    x_pix = bh.dib.biWidth;
    y_pix = bh.dib.biHeight;
 
    //находим длину строки в байтах и округляем с учетом паддингов
    bytes = x_pix * 3;              //каждый пиксель занимает 3 байта в строке
    if ((bytes % 4) != 0)
    {
        pad = bytes + (4 - (bytes % 4));
    }
    else
    {
        pad = bytes;
    }
    v = new unsigned char[pad * y_pix];
 
    for (i = 0; i < y_pix; i++)
    {
        for (j = 0; j < pad; j++)
        {
            if (j < bytes)
            {
                fread((v + j + 0 + (i * x_pix)), 1, 1, f);
                fread((v + j + 1 + (i * x_pix)), 1, 1, f);
                fread((v + j + 2 + (i * x_pix)), 1, 1, f);
                j += 2;
            }
            else
            {
                fread(&tmp, 1, 1, f);
            }
        }
    }
 
    return v;
}
проблема в том, что исходный и конечный файлы не совпадают, а получается что первая треть изображения дублируется
в разных цветовых гаммах...
Загрузка и сохранение BMP файла

Загрузка и сохранение BMP файла
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
29.11.2016, 19:20
Ответы с готовыми решениями:

Долгая загрузка .bmp файла после простоя компьютера
Подгружаю по мере надобности .bmp - размер порядка 129 метров каждый. При подгрузке нового...

Сохранение и загрузка из файла
Столкнулся с проблемой загрузки из файла *.ini. для сохранения параметров программы использую...

Сохранение/загрузка xml файла
Привет, нашел вот кодик сохранения/загрузки в/из xml, хочу прикрутить эти функции в отдельный cpp...

Загрузка bmp из файла
Необходимо по нажатию кнопки создать диалоговое окно и открыть выбранный bmp файл. Имеется такой...

6
Эксперт С++
1659 / 1031 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
29.11.2016, 21:16 2
Тем непослушным детишкам, которые не хотят пользоваться уже определёнными в windows.h структурами заголовков BMP (а именно - BITMAPFILEHEADER) и пишут вместо них свои собственные, следует помнить о выравнивании полей в структурах и #pragma pack.
0
16 / 0 / 0
Регистрация: 28.11.2016
Сообщений: 6
30.11.2016, 08:47  [ТС] 3
Цитата Сообщение от Nick Alte Посмотреть сообщение
Тем непослушным детишкам, которые не хотят пользоваться уже определёнными в windows.h структурами заголовков BMP (а именно - BITMAPFILEHEADER) и пишут вместо них свои собственные, следует помнить о выравнивании полей в структурах и #pragma pack.
С заголовком файла я разобрался по-своему. Как вы знаете, структуры выравниваются по умолчанию по границе 4 байт. Из этой концепции выбивается только самая первая переменная int16 id. Я просто напросто сделал ее в виде отдельной переменной и записываю в файл сначала ее, а потом остальную структуру.

Тем не менее, остается проблема с непосредственно массивом данных, который идет после заголовка. Почему на картинке три раза повторяется первая треть изображения?
0
Эксперт С++
1659 / 1031 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
30.11.2016, 19:58 4
Лучший ответ Сообщение было отмечено theARTofJUKING как решение

Решение

Цитата Сообщение от theARTofJUKING Посмотреть сообщение
Почему на картинке три раза повторяется первая треть изображения?
Потому что размер обработанных строк (i * x_pix) не домножается на 3.
1
16 / 0 / 0
Регистрация: 28.11.2016
Сообщений: 6
30.11.2016, 21:12  [ТС] 5
Nick Alte в моем коде x_pix это ширина изображения в пикселях. Зная количество пикселей, необходимо умножить их на 3, так как каждый пиксель занимает три байта (0-255R, 0-255G, 0-255B), тогла можно будет вычислить количество байт в строке. Например 5 пикселей займет 5*3 15 и округлим до кратного 4, то есть 16 байтов RGBRGBRGBRGBRGBP (каждая буква это байт цвета, P это паддинг)

В плане дублирования этого фрагмента изображения явно где-то ошибка в записи либо считывании... Плюс похоже сдвинулся порядок следования RGB, раз в каждом фрагменте меняются цвета... Сам пока не смог локализовать...
0
Эксперт С++
1659 / 1031 / 174
Регистрация: 27.09.2009
Сообщений: 1,945
30.11.2016, 21:15 6
Мне не надо объяснять все глубины своего замысла. Я и так уже понял, как оно работает, и указал на место с ошибкой. Не хотите верить - дело ваше.
1
16 / 0 / 0
Регистрация: 28.11.2016
Сообщений: 6
30.11.2016, 22:01  [ТС] 7
Nick Alte, в таком случае я не совсем понял, где именно не нужно домножать... Вы не могли бы объяснить конкретнее?

Добавлено через 4 минуты
Nick Alte, кажется понял... При записи в файл я прибавляю к указателю индекс внешнего цикла * ширину строки...
А ширину строки-то взял в пикселях, а не в байтах))))) спасибо за ответ, сейчас попробую!!!

Добавлено через 40 минут
Совет Nick Alte помог! Выкладываю финальную версию функций загрузки и сохранения, которые заработали нормально, может быть, кому-то поможет мое творчество)

id файла и структура заголовка
C++
1
2
3
4
5
6
7
8
9
10
11
12
#include "windows.h"
 
typedef unsigned __int16    FILE_ID;
 
typedef struct
{
    unsigned __int32 size = 54;     //сюда надо будет прибавить размер массива c паддингами
    unsigned __int16 rzrv1 = 0;
    unsigned __int16 rzrv2 = 0;
    unsigned __int32 offset = 54;   //смещение массива данных относительно начала файла = sizeof(file_id) + sizeof(BMPheader)
    BITMAPINFOHEADER dib;
}       BMP_HEADER;
функция загрузки изображения в массив char. При этом удаляются ненужные паддинги, и полученный массив представляет собой набор идущих подряд пикселей.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
unsigned char* loadBMP(const char *fname, int &x_pix, int &y_pix)
{
    FILE_ID id;
    BMP_HEADER bh;
    size_t res;
    unsigned int x_bytes, bytes, i, j, padding;
    unsigned char *v, tmp;
 
    x_pix = y_pix = -1;
    FILE *f = fopen(fname, "rb");
    if (!f)
    {
        return NULL;
    }
    res = fread(&id, 1, sizeof(FILE_ID), f);
    if (res != sizeof(FILE_ID))
    {
        fclose(f);
        return NULL;
    }
    res = fread(&bh, 1, sizeof(BMP_HEADER), f);
    if (res != sizeof(BMP_HEADER))
    {
        fclose(f);
        return NULL;
    }
 
    /* */
    x_pix = bh.dib.biWidth;
    y_pix = bh.dib.biHeight;
 
    //находим длину строки в байтах и округляем с учетом паддингов
    x_bytes = x_pix * 3;                //каждый пиксель занимает 3 байта в строке
    if ((x_bytes % 4) != 0)
    {
        padding = (4 - (x_bytes % 4));
        bytes = x_bytes + padding;
    }
    else
    {
        bytes = x_bytes;
    }
    v = new unsigned char[bytes * y_pix];
 
    for (i = 0; i < y_pix; i++)
    {
        for (j = 0; j < bytes; j++)
        {
            if (j < x_bytes)
            {
                fread((v + j + 0 + (i * x_bytes)), 1, 1, f);
                fread((v + j + 1 + (i * x_bytes)), 1, 1, f);
                fread((v + j + 2 + (i * x_bytes)), 1, 1, f);
                j += 2;
            }
            else
            {
                fread(&tmp, 1, 1, f);
            }
        }
    }
 
    return v;
}
функция сохранения
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
int saveBMP(const char *fname, unsigned char *v, int x_pix, int y_pix)
{
    unsigned int x_bytes, bytes, i, j, padding;
    unsigned char tmp = 255;
    BMP_HEADER bh;
    memset(&bh, 0, sizeof(bh));
    FILE_ID id = 0x4d42;
    //находим длину строки в байтах и округляем с учетом паддингов
    x_bytes = x_pix * 3;                //каждый пиксель занимает 3 байта в строке
    if ((x_bytes % 4) != 0)
    {
        padding = (4 - (x_bytes % 4));
        bytes = x_bytes + padding;
    }
    else
    {
        bytes = x_bytes;
    }
    //размер файла 54 байта заголовок + массив данных
    bh.size += bytes * y_pix;
    //заполняем DIB заголовок
    bh.dib.biSize = 40;                 //размер заголовка
    bh.dib.biWidth = x_pix;             //ширина картинки
    bh.dib.biHeight = y_pix;                //высота картинки
    bh.dib.biPlanes = 1;
    bh.dib.biBitCount = 24;
    bh.dib.biCompression = 0;
    bh.dib.biSizeImage = bytes * y_pix; //размер массива данных 
    bh.dib.biXPelsPerMeter = 2835;
    bh.dib.biYPelsPerMeter = 2835;
    bh.dib.biClrUsed = 0;
    bh.dib.biClrImportant = 0;
    //открываем файл
    FILE *f = fopen(fname, "wb");
    if (!f) return -1;
    //пишем заголовок
    size_t res;
    res = fwrite(&id, 1, sizeof(FILE_ID), f);
    if (res != sizeof(FILE_ID))
    {
        fclose(f);
        return -1;
    }
    res = fwrite(&bh, 1, sizeof(BMP_HEADER), f);
    if (res != sizeof(BMP_HEADER))
    {
        fclose(f);
        return -1;
    }
    //пишем массив данных
    for (i = 0; i < y_pix; i++)
    {
        for (j = 0; j < bytes; j++)
        {
            if (j < x_bytes)
            {
                fwrite((v + j + 0 + (i * x_bytes)), 1, 1, f);
                fwrite((v + j + 1 + (i * x_bytes)), 1, 1, f);
                fwrite((v + j + 2 + (i * x_bytes)), 1, 1, f);
                j += 2;
            }
            else
            {
                fwrite(&tmp, 1, 1, f);
            }
        }
    }
    fclose(f);
 
    return 0;
}
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.11.2016, 22:01

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Загрузка bmp файла
установил RAD 2010, auxDIBImageLoad перестал работать(символ не найден пишет мне студия). можете...

Сохранение файла в bmp формате
Кaк сделaть чтoбы пoсле рисoвaния метoдaми line() и circle() мoжнo былo этo сoхрaнить кaк .БМП...

Сохранение и загрузка в файла
На форме есть StringGrid со заполнеными полями, и много заполняемых Edit и кнопка...

Загрузка и сохранение файла на сервере
Доброго времени суток. Сейчас я пытаюсь реализовать загрузку чего либо на сервер и дальнейшего его...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2020, vBulletin Solutions, Inc.