В последнее время я не на шутку заинтересовался форматом bmp. Он довольно прост по составу, но в то же время довольно и интересен, чтобы ковырятся в его внутренностях. Сегодня я сначала несколько обрисую этот формат, а затем приведу программку, переводящую цветную картинку в черно-белую. Работать, для простоты, я буду с 24-разрядной картинкой. Итак, вот пример того, что сегодня я сделаю:

pic BMP формат

Ну что же, начнем пожалуйИтак, 24-разрядный bmp файл состоит из следующих компонент (все структуры описаны в файле windows.h):

  1. Структура BITMAPFILEHEADER (OFFSET = 0)
  2. Структура BITMAPINFOHEADER (OFFSET = sizeof(BITMAPFILEHEADER ))
  3. Кучка пикселов (OFFSET = BITMAPFILEHEADER.bfOffBits)

Каждый пиксель из кучки описывается структурой RGBTRIPLE, в которой как раз и закодирован цвет каждого пиксела. Масло масляное… Теории достаточно для начала, давайте реализовывать! Код функции цвет->чб таков:

void ToBlackWhite(char *szBmp)
{
        BITMAPFILEHEADER bfh;
        BITMAPINFOHEADER bih;
        RGBTRIPLE pix;

        DWORD dwRWBuf;

        HANDLE hFile = CreateFileA(szBmp,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL);

        ReadFile(hFile,&bfh,sizeof(BITMAPFILEHEADER),&dwRWBuf,NULL);
        ReadFile(hFile,&bih,sizeof(BITMAPINFOHEADER),&dwRWBuf,NULL);

        SetFilePointer(hFile,bfh.bfOffBits,NULL,FILE_BEGIN);

        DWORD nPixel = bih.biWidth * bih.biHeight;

        while(nPixel--) 
        {
                memset(&pix,0,sizeof(RGBTRIPLE));
                ReadFile(hFile,&pix,sizeof(RGBTRIPLE),&dwRWBuf,NULL);

                BYTE scale = (BYTE)(0.3*pix.rgbtRed + 0.59*pix.rgbtGreen + 0.11*pix.rgbtBlue);
                pix.rgbtRed = scale;
                pix.rgbtGreen = scale;
                pix.rgbtBlue = scale;

                SetFilePointer(hFile,-sizeof(RGBTRIPLE),NULL,FILE_CURRENT);
                WriteFile(hFile,&pix,sizeof(RGBTRIPLE),&dwRWBuf,NULL);
        }

        CloseHandle(hFile);
}

Что же происходит в этой функции? Для начала скажу, что для того чтобы сделать картинку черно-белой, достаточно всего лишь вызвать эту функцию с именем файла. В функции мы сразу объявляем три структуры — две заголовочные, а в третью будем читать/записывать пиксели. Затем открываем файлик на чтение и запись. Потом считываем две заголовочные структуры. Зачем? Нам нужно смещение, и мы его получаем в первой структуре. И еще нам нужно количество пикселей — это мы можем узнать, перемножив ширину на длину (их получаем из второй структуры). Дальше, пока мы не перебрали все пиксели, мы читаем очередной, преобразуем пиксель по магической формуле, идем на одну позицию назад (указатель же указывает уже на следующий, что не очень корректно) и записываем уже новый пиксель. Вот и все, повторив данное действо над каждым пикселом мы получим черно-белое изображение. В принципе с картинками, у которых есть палитра, все эти действия нужно производить над палитрой а не над пикселами — разница, согласитесь, довольно не большая. В следующих статьях я планирую продолжить эту тему…

Надеюсь что грусть и печаль осени на меня действовать будет не очень сильно и троллить на парах меня будут меньше, так что постараюсь завершить цикл статеек на эту тему к октябрю. Ну вот и все, удачи в экспериментах!

tt twitter big4 BMP формат tt digg big4 BMP формат tt facebook big4 BMP формат tt gmail big4 BMP формат tt myspace big4 BMP формат tt reddit big4 BMP формат

Комментарии (6) на “BMP формат”

  • Алекс:

    Добрый день.

    Прошу подсказать — Ваш код вылетает с ошибкой:
    Ошибка 4 error C4146: применение унарного минуса к типу без знака; результат оставлен без знака

    строка: SetFilePointer(hFile,-sizeof(RGBTRIPLE),NULL,FILE_CURRENT);
    место: применение знака минус перед sizeof(RGBTRIPLE)
    среда: visual studio 2012

    Насколько я понимаю, надо просто указать величину смещения с минусом в байтах?

    Прошу помочь, т.к. сам я только изучаю с++.

    моя задача состоит в том, что мне нужно указать цвет точки и координаты и записать точку в файл.

    Ответить
    • Panterr63:

      Здравствуйте, похоже в 12 версии студии данную «фишку» — сдвиг на отрицательное число (т.е. после считывания на то же место) похоронили. Я юзал 2005 для компиляции этого примера. В вашем случае вам требуется открыть файл, прочитать все заголовки, а потом сдвинутся от офсета на величину [Ширина BMP]*[Координата Y] + [Координата X]. Поподробнее? Спрашивайте, сделаем.

      Ответить
      • Алекс:

        Огромное спасибо!!!
        Это я примерно себе представляю…
        Я правильно понимаю, что ширина BMP зависит от битности картинки?
        И, поскольку картинка может сохраняться в обратном порядке, то вариантов расчета позиции тоже будет два (в зависимости от biHeight).
        1-й: при прямом расположении (biHeight меньше 0): offset+[x]*[y]*[Ширина BMP]
        2-й: при обратном (biHeight больше 0): offset+[biWidth]*abs[biHeight]*[Ширина BMP]-[x]*[y]*[Ширина BMP]
        так ли это?
        Запись цвета хранится как BRG, если не ошибаюсь… таким образом надо записывать именно в этом порядке.
        Вопрос: как записать в файл с заменой текущей информации? с этим я пока не разобрался.
        Ваша программа в VS2012 не пишет в файл совсем… с этим основная проблема.

        Ответить
        • Panterr63:

          Вполне возможно, но у меня к сожалению только VS 2005 и ставить еще горочку компонентов + 2012 не очень хочется. Так ладно, переходим непосредственно к вопросу. Вообще ссылка по теме: http://msdn.microsoft.com/en-us/library/aa930979.aspx. Ширина BMP вообще лежит в biWidth (если речь идет о размере по оси X). Цвет пикселя хранится в структуре RGBTRIPLE. Если углублятся, то в файл пишется последовательность BGR. Т.е. обратная RGB. Но если используется структура RGBTRIPLE то вроде как заботится о последовательности не надо. Со смещениями ситуация такая: я не совсем понимаю зачем вы используете умножение между [x] и [y]. а в остальном вроде похоже на правду, хотя с обратными картинками я дело не имел, но в теории должно быть так. Записать файл с заменой текущей информации: то есть вам нужно сначала считать её, а затем на её основании записать новую? Тогда могу предложить следующее: Сначала вызвать SetFilePointer(hFile,0,0,SEEK_CUR);. Таким образом мы узнаем текущую позицию. Затем вызвать ReadFile. Затем модифицировать данные, переместится на позицию, возвращаемую первым SetFilePointer и записать данные. Таким образом сдвигатся мы будем также на прежнюю позицию, и в VS2012 должно заработать. Если же знать текущую информацию не требуется, то можно просто вызывать WriteFile на том офсете, который вы вычислили. Надеюсь ответил на ваш вопрос. И да, если вы пишете что-то универсальное, то не забывайте, что у файла мб и палитра, и разрядность его может не быть 24 битной, а следовательно информация о пикселе будет хранится уже не в структуре RGBTRIPLE, а в той структуре, которая соответсвует разрядности.

          Ответить
  • Алекс:

    Полностью согласен…. спасибо…
    Буду думать…
    Кстати, о палитре — оффсет указывает на её конец или начало?
    Это у меня лаба по с++ такая… открыть bmp, взять инфу и вывести на экран, запросить координаты точки и еёё цвет и записать в файл…
    Для написания универсальной проги надо сильно попариться…
    Наверное, остановлюсь на 24-битной картинке и достаточно… :-)

    Ответить
    • Panterr63:

      Знакомо :grin: Вообще офсет указывает на само начало картинки. Т.е. на конец палитры. Да, более подробное описание BMP формата на русском: http://ru.wikipedia.org/wiki/BMP.

      Ответить

Оставить комментарий

CAPTCHA изображение
Обновить изображение
*

RSS-подписка NIG Twitter-подписка NIG

Метки
Друзья
Блог линуксоида Программы для диагностики компьютера
Супер Pixel
Убивалка флешаБэкконект шелл. Часть вторая.Бэкконект шелл. Начало.НАМ для деления чиселПростая арифметикаПередача файлов. Часть третья. Клиент.Dll injectionСканер портовКодировка текста в BMPРухнул на два дняПередача файлов. Часть вторая. Сервер.Поворот и BMPCRC32 суммаBMP форматфайловые вирусыассемблер, строкиАссемблер и матрицапримеры ассемблерМногопоточное программированиеАрхитектура клиент — серверРаздача ICQПишем шуткупишем паукаПривет от ДжеймсаОбход firewallЗагрузка картинок на Gyazo, прямо из ThunarКейлоггер 3Плюшки в контекстном меню Thunarc по сетиКейлоггер 2КейлоггерСкачать Ассемблер!VirusCheckerОтморозки мешают жить ?! Не проблемма !Cкрипты для взлома аккаунтов QIPWinAPI. Работа с файлами. Часть третья. ЧтениеDlink exploitБрут сайта etxt.ruКрасивая раскладка клавиатуры в GnomeМои безделушки на PerlКак запускать Perl скрипты под WindowsБрутфорс партнёрки ZipCoinПарсер upwap.ruИзменения в блогеЧудо ЗаливалкаСкрипт для загрузки файлов на Zalil.ruЯндекс «чоткий» поисковик!WinAPI. Работа с файлами. Часть вторая. ЗаписьWinAPI. Работа с файлами. Часть первая. ПоискПолучение MD5 хеша средствами C++Что нам стоить letitfile.com забрутить?WarCraft III запуск под LinuxЭнтропия файлаWinsock и C++. Мини прокси. Часть |\\/. Завершающая.Перепилил чекер для 4gameЧекер акаунтов YoupornCMailSend v 1.1. Отправка почы без проблемСлучайные числаWinsock и C++. Часть |||. smtp монстр.Брутфорс LetitFile.comИспользование X-Forwarded-For, для обмана веб-сервера, подмена IP подручными средствамиДело было вечером, делать было нечего…Мысли о аудио сервереПарсер ников из твиттераКонец школоло…Perl+Linux. Заметка первая (Удобный Paste bin).Бэкконект шелл.E-MAIL + winsocket + Cpp. Сложно?winsock и C++. Часть ||. Атака клоунов.C++ резолвинг адресаPerl, анализ HTML кода и определение CMSМатематика в C++winsock и C++