Обработка изображений: делаем фотографию черно-белой своими руками

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

Сначала, я хотел выложить перевод жутко интересной статьи, но потом выяснилось, что меня опередили, а в этот момент была готова только часть — и это меня очень расстроило, но потом я подумал, что нет смысла огорчаться — переводами сейчас занимаются многие, но вот чтобы написать нечто уникальное по D и к тому же на русском языке — вот тут нужно гораздо больше усилий и времени, и поэтому с приподнятым настроением я сел писать эту статью…

В одном из проектов, мне потребовалось сделать следующую вещь: взять некий байт из файла и проинтерпретировав его как число в интервале от 0 до 255, отобразить его как цвет пикселя в палитре 256 цветов, а после этого преобразовать найденный цвет в оттенок серого, но так, чтобы отношения между получаемыми серыми красками на шкале цветов 256-цветной палитры сохранились. К сожалению, формул отображения палитры из 256 цветов в цвета RGB, используемые в dlib, я не нашел — и хоть когда-то я описывал способы такого преобразования, они меня не устраивали — и хотелось все-таки сделать это, но гораздо лучше и правильнее.

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

Для начала, я хотел разобраться, а можно ли обратиться к цвету в dlib (напоминаю, цвет картинки — это тип Color4f), как к некой структуре, которая содержит компоненты r, g и b соответственно, а потом выяснить, как же мне загрузить в программу изображение для экзекуции. К счастью, на эти вопросы нашлись быстрые и простые ответы: да, действительно, я могу так обратиться к цвету, и более того, это один стандартных способов работы с такими структурами; загрузка картинки вообще элементарна и интуитивно понятна: функция load из модуля dlib.image способна загрузить изображение почти любого типа, и на выходе дать объект, типом которого можно считать абстрактный класс SuperImage.

Такие простые решения меня вдохновили, и я решив воспользоваться ими, сделал простой преобразователь любой картинки в черно белую, для чего я взял стандартное уравнение приведения яркости из системы RGB в яркость в терминологии палитры оттенков серого:

Y’ = 0.2126 * R + 0.7152 * G + 0.0722 * B;

Воспользовавшись этой формулой и вышеуказанными соображениями, я сделал простой класс, который позволит легко сконвертировать почти любую картинку в черно-белую (а-ля советские фотографии на паспорт и не только):

class BlackNWhite
{
private:
	import std.string;

	string filename;
	string filetype;
	SuperImage image;
	
public:
	this(string inputFileName)
	{
		filename = inputFileName;
		filetype = filename[filename.lastIndexOf(".")..$];
		image = load(inputFileName);
	}

	void convert()
	{
		auto outputFileName = filename.replace(filetype, "") ~ "_black_and_white.png";

		for (int i = 0; i < image.width; i++)
		{
			for (int j = 0; j < image.height; j++)
			{
				auto sourcePixel = image[i, j];
				auto intensity = 0.2126 * sourcePixel.r + 0.7152 * sourcePixel.g + 0.0722 * sourcePixel.b;
				image[i, j] = Color4f(intensity, intensity, intensity);
			}
		}

		image.savePNG(outputFileName);
	}
}

Работает это хозяйство весьма просто: с помощью конструктора получаем такие важные для нас данные, как имя файла для издевательств, тип файла (который вырезается из имени файла, исходя из того факта, что последняя точка в его имени указывает на расширение — и потому сечение строки, взятое по индексу этой последней точки до конца строки — это и есть расширение, или тип файла) и само изображение (загружаем его с помощью функции load). Всю работу в классе делает метод convert, который переделывает имя файла, исключая из него тип и добавляя отличительный признак наших сконвертированных файлов — часть имени « _black_and_white »  и расширение «.png» (такая процедура неолбходима для сохранения результатов преобразований, поскольку, на текущий момент dlib умеет сохранять только в PNG), после чего и начинается вся реальная работа — берется отдельный пискель изображения и к нему применяется формула пересчета яркости, результат которой используется как новое значение для цвета того же самого пикселя.

Как видите, ничего сложного: просто двойной цикл прохода и элементарнейшее математическое преобразование, а какой результат:

Оригинал изображения
После преобразования

А запускается преобразование так:

BlackNWhite bnw = new BlackNWhite(<путь до файла>);
bmw.convert();

Также, ради интереса можно устроить массированную обработку изображений в одной папке, например, как сделал я, готовя эту статью (множество фоток, для того, чтобы выбрать самую классную):

void main()
{
	import std.algorithm : each;
	each!(a => (new BlackNWhite("/home/aquaratixc/Загрузки/tests/" ~ a)).convert())(["a.jpg", "b.jpg", "c.jpg", "d.jpg"]);
}

Благодаря Тимуру Гафарову, разработчику dlib, обработка изображений стала куда проще и занятнее, за что ему огромное спасибо — и я надеюсь, что в будущем, в dlib появится сохранение и других типов файлов изображений).

Один комментарий к “Обработка изображений: делаем фотографию черно-белой своими руками”

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

Добавить комментарий