Зона кода

А давайте немного попрограммируем...

Раскраска изображений произвольными цветами

C99Графические программы

В статье, посвящённой построению изображения множества Мандельброта, я описывал процесс раскраски чёрно-белого изображения. Он весьма примитивен: каждому из 256-ти оттенков серого цвета ставился в соответствие новый цвет. Затем каждый пиксель чёрно-белого изображения закрашивался новым цветом, соответствующим тому оттенку серого, каким он был закрашен ранее.

Разумеется, можно огромным числом способов выбирать новые цвета (в количестве 256) и огромным количеством способов устанавливать взаимно однозначное соответствие между выбранными новыми цветами и "старыми" оттенками серого. Каждое такое соответствие будем называть, для определённости, "цветовой схемой". В упомянутой статье было рассмотрено построение шести таких схем.

В ходе написания той статьи мне пришло в голову попробовать точно таким же способом раскрашивать чёрно-белые фотографии. А вдруг результат получится интересным? В этой статье я решил поэкспериментировать с такой раскраской.

Мы создадим несложную программу, занимающуюся раскраской. Напишем её на языке C99. Для создания изображений будем использовать графическую библиотеку pgraph, предполагая, при этом, что читатель, хотя бы в общих чертах, с ней знаком.

Идея алгоритма

Оговорюсь, что раскрашивать мы будем, всё же, цветные изображения, предварительно их "обесцветив", т. е. преобразовав в чёрно-белые. Такими преобразованиями мы уже занимались ранее. Действовать будем следующим образом: интенсивность серого цвета i каждого пикселя цветного изображения будем вычислять по формуле

i = 0,3 r + 0,59 g + 0,11 b,

где r, g и b — это интенсивности красного, зелёного и голубого цветов соответственно. Разумеется, после вычисления полученную интенсивность будем округлять до ближайшего целого.

После такого округления мы каждый раз будем получать целое число от 0 до 255 включительно. Данное число будем рассматривать как индекс элемента массива, содержащего "новый" цвет в виде значения типа color. Этот массив будет построен заранее; его содержимое будет соответствовать конкретной цветовой схеме. Будем называть его, в дальнейшем, "цветовым массивом".

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

Как мы видим, всё предельно просто. Можно переходить к программированию.

Создание программы

Программа будет состоять из файлов, входящих в состав библиотеки pgraph, и файла main.c. Единственная функция, содержащаяся в этом файле, — это функция main(). Содержимое фала main.c приведено ниже.

 1.#include "pgraph.h"
 2.
 3.int main()
 4.{
 5.    uchar red[] = {0, 36, 73, 109, 146, 182, 219, 255};
 6.    uchar *green = red;
 7.    uchar blue[] = {0, 85, 170, 255};
 8.    color colors[256];
 9.    int m = 0;
10.    for (int i = 0; i < 8; i++)
11.        for (int j = 0; j < 8; j++)
12.            for (int k = 0; k < 4; k++)
13.                colors[m++] = (color) {red[i], green[j], blue[k]};
14.                //colors[m++] = (color) {red[i], blue[k], green[j]};
15.                //colors[m++] = (color) {blue[k], red[i], green[j]};
16.                //colors[m++] = (color) {green[j], blue[k], red[i]};
17.                //colors[m++] = (color) {blue[k], green[j], red[i]};
18.                //colors[m++] = (color) {green[j], red[i], blue[k]};
19.    image *img1 = load_from_file("in.bmp");
20.    image *img2 = create_image(img1->width, img1->height);
21.    color *pixels1 = img1->pixels;
22.    color *pixels2 = img2->pixels;
23.    for (int i = 0; i < img1->width * img1->height; i++)
24.    {
25.        color col = pixels1[i];
26.        uchar gray_col = round(0.3 * col.red + 0.59 * col.green + 0.11 * col.blue);
27.        pixels2[i] = colors[gray_col];
28.    }
29.    save_to_file(img2, "out.bmp");
30.    free(img1);
31.    free(img2);
32.    return 0;
33.}

Разумеется, к файлу main.c необходимо подключить заголовочный файл библиотеки pgraph (стр. 1).

Переходим к рассмотрению функции main().

Строки 1-13 совпадают с первыми 13-ю строками функции main(), приведённой в упомянутой статье, посвящённой множеству Мандельброта. В строках 5 и 7 формируются вспомогательные массивы red и blue, предназначенные для создания цветового массива. Можно было бы сформировать и массив green, но, поскольку он должен совпадать с red, можно объявить указатель green и присвоить ему адрес массива red (стр. 6).

Несложно догадаться о том, что массивы red и blue, а также массив, адресуемый green, содержат интенсивности красного, голубого и зелёного цветов соответственно, из которых будут формироваться новые 256 цветов, которые будут использоваться для закраски нашего изображения. Непосредственно формирование этих цветов и помещение их в цветовой массив colors, созданный в строке 8, происходит в трёх вложенных друг в друга циклах for (стр. 10-13).

Из полученной таким образом цветовой схемы можно легко получить ещё 5 цветовых схем, просто "перетасовывая" интенсивности красного, зелёного и голубого цветов. Код, выполняющий такие "перетасовки", закомментирован (стр. 14-18). Договоримся цветовые схемы, создаваемые в строках с 13-й по 18-ю нумеровать числами от 1 до 6 в порядке возрастания номеров строк.

После того, как цветовой массив заполнен, загружаем исходное изображения из графического файла in.bmp в объект типа image, адресуемый указателем img1 (стр. 19). Файл должен иметь формат BMP, содержать 24-разрядное изображение и к началу выполнения программы находиться в корневой директории исполняемого файла.

Создаём ещё один объект типа image, который, в итоге, будет содержать "раскрашенное заново" исходное изображение. Размеры второго изображения совпадают с размерами первого, а его адрес на этот раз будет содержаться в указателе img2 (стр. 20).

Адреса массивов, содержащих цвета пикселей первого и второго изображений сохраняем в переменных pixels1 и pixels2 соответственно (стр. 21, 22). Теперь нам остаётся лишь заполнить второй массив новыми цветами. Делаем это с помощью цикла for (стр. 23-28).

Перебираем в цикле все пиксели исходного изображения. Цвет каждого пикселя сохраняем сначала в переменной col (стр. 25), а затем вычисляем соответствующую ему интенсивность серого цвета, записывая её в переменную gray_col (стр. 26). Получаем в качестве нового цвета текущего пикселя значение элемента массива colors c индексом, содержащимся в gray_col, и записываем его в соответствующий элемент второго массива (стр. 27).

Итак, второе изображение окончательно сформировано. Остаётся лишь записать его в файл out.bmp (стр. 29) и удалить уже ненужные объекты, адресуемые указателями img1 и img2 (стр. 30, 31).

Программа написана.

Результаты работы программы

В качестве раскрашиваемых изображений я взял виды Санкт-Петербурга, которые уже использовал ранее для создания слайд-шоу (см. видеоролик), а также фотографии подводной лодки и горного пейзажа (обе они уже встречались на страницах данного сайта). Для каждого изображения я получал 6 других, используя 6 наших цветовых схем. Отобрал я самые интересные, с моей точки зрения, результаты.

Вот фотография Исаакиевского собора, раскрашенная с помощью первой цветовой схемы:

Исаакиевский собор

Исаакиевский собор

А вот шестая цветовая схема, "применённая" к изображению Петропавловской крепости:

Петропавловская крепость

Петропавловская крепость

Фотография Дворцовой площади, "обработанная" с применением третьей цветовой схемы:

Дворцовая площадь

Дворцовая площадь

А вот как будет выглядеть фотография Кунсткамеры, если раскрасить её с использованием пятой цветовой схемы:

Кунсткамера

Кунсткамера

Шестая схема, "применённая" к изображению прогулочного катера на участке Невы вблизи Дворцового моста:

Катер на Неве

Катер на Неве

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

Подводная лодка на Неве

Подводная лодка на Неве

А это Приморский горный пейзаж, к которому "применили" первую цветовую схему:

Горы в Приморье

Горы в Приморье

А вот как выглядит фотография Зимнего Дворца, сделанная со стрелки Васильевского острова, раскрашенная в соответствии с третьей цветовой схемой:

Зимний Дворец

Зимний Дворец

Здесь не приведены фотографии, обработанные с помощью второй и четвёртой цветовых схем, поскольку результаты такой обработки весьма неудачны: итоговые изображения содержат избыток "цветового шума", не позволяющего, зачастую, понять, что вообще изображено.

Оригиналы изображений в виде файлов формата BMP можно скачать здесь.

Заключение

Итак, мы реализовали способ "перераскраски" картинок. Результаты могут показаться кому-либо забавными. Однако большинство, как мне кажется, ничего интересного в них не найдёт и сочтёт такую обработку ни чем иным, как порчей исходных изображений. улыбка

Скачать исходный код