Соляризация изображения в dlib

В этой статье я снова расскажу немного про цифровую обработку изображений с помощью языка программирования D на примере одной простой операции с изображением, которая называется соляризацией. Данная операция является частным случаем более общей операции под названием препарирование изображения и которая оперирует над цветовой плоскостью, изменяя динамический диапазон изображения и давая интересный графический эффект.

Если вам любопытно, что получилось, то добро пожаловать в эту статью.

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

Прежде всего, я должен рассказать вам, что соляризация — это очень старый графический трюк, связанный с еще нецифровыми фотографиями, которые можно было легко передержать или переэкспонировать. Именно эта особенность старых методов получения фотоизображения приводила к тому, что иногда можно было наблюдать необычное явление: при слишком большой экспозиции происходит снижение оптической плотности изображения, в результате чего происходит эффект «обращения» — светлые участки могут стать частично темными.

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

y' = y * k * (y_max - y)

где y’ — новое значение цвета, y — текущее значение цвета, k — некий коэффициент описывается весьма простой формулой: (назовем его коэффициентом соляризации), y_max — значение цвета с максимальной для изображения яркостью.

Для реализации соляризации мы воспользуемся уже знакомой нам библиотекой dlib, которая обеспечит нас необходимыми типами данных и методами работы. Создав проект dub и включив в него dlib, мы прежде всего сделаем ряд тонких «надстроек», которые помогут использовать любой арифметический тип в качестве коэффициента. Для этой цели введем шаблон allArithmetic, который позволит на этапе компиляции произвести проверку — числовой или нечисловый тип будет использован в качестве аргумента функции соляризации:

private 
{
	import std.algorithm;
	import std.range;
	import std.meta : allSatisfy;
	import std.traits : isIntegral, isFloatingPoint, Unqual;
}

// является ли тип арифметическим (числовым) ?
template allArithmetic(T...)
	if (T.length >= 1)
{
	// числовой тип  (т.е. является либо целым типом, либо с плавающей точкой)?
	template isNumberType(T)
	{
		enum bool isNumberType = isIntegral!(Unqual!T) || isFloatingPoint!(Unqual!T);

	}

	// удовлетворяет ли всем условиям ?
	enum bool allArithmetic = allSatisfy!(isNumberType, T);
}

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

// Соляризация изображения
auto solarizateImage(T)(SuperImage superImage, T coefficient)
	if (allArithmetic!T)
{
	// буфер под новую картинку 
	SuperImage newImage = image(superImage.width, superImage.height);
	// приводим тип к float (будем предполагать, что этот тип универсален)
	float modifiedCoefficient = cast(float) coefficient;

	// псевдоним для функции сравнения яркостей отдельных пикселей
	alias compareLuminance = (a, b) => a.luminance > b.luminance;
	
	// буфер под пиксели
	Color4f[] colors;
	
	// накапливаем пиксели для будущих вычислений
	foreach (x; 0..superImage.width)
	{
		foreach (y; 0..superImage.height)
		{
			colors ~= superImage[x, y];
		}
	}

	// цвет с максимальной по изображению яркостью
	auto maximalColor = colors.sort!compareLuminance.front;

	// сама соляризация изображения
	foreach (x; 0..superImage.width)
	{
		foreach (y; 0..superImage.height)
		{
			newImage[x,y] = superImage[x,y] * modifiedCoefficient * (maximalColor - superImage[x,y]);
		}
	}

	return newImage;
}

Приведенный здесь код сначала создаем новое изображение с размерами точно такими же, как и у картинки под преобразование (в общем, наш стандартный прием в делах обработки изображений), затем приводим полученный коэффициент к типу float, как наиболее удобный для расчетов (спорный момент, конечно, но в целом это почти всегда удобно). Далее, создаем псевдоним под простую лямбда-функцию для сравнения пикселей исходя из их яркости и формируем массив из всех пикселей изображения (надеюсь, в dlib когда-нибудь появится диапазон для прохода по всем пикселям изображения без необходимости формирования массива), который далее сортируем по убыванию с помощью уже созданной функции compareLuminance и с помощью метода front первый элемент, который отвечает пикселю с максимальным значением яркости по изображению. Дальнейший обход по изображению с помощью двух циклов — это стандартная версия процедуры трансформации каждого отдельно взятого пикселя, для которой в нашем случае используется формула соляризации (даже без каких-либо изменений, кроме как переменных). В совокупности, вышеописанный код выдает новое изображение, в котором соляризация уже проведена.

Для испытания эффекта необходимо просто загрузить картинку с помощью стандартной для dlib процедуры load и непосредственным применением функции solarizateImage c соответствующим коэффициентом:

void main()
{
	auto img = load("Lenna_full.png");
	
	img.solarizateImage(0.74).savePNG("Lenna_0.74.png");
	img.solarizateImage(0.5).savePNG("Lenna_0.5.png");
	img.solarizateImage(0.1).savePNG("Lenna_0.1.png");
	img.solarizateImage(1.7).savePNG("Lenna_1.7.png");
}

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

А вот оригинал:

Таким образом, мы расширили копилку интересных эффектов, реализованных с помощью D/dlib, а также рассмотрели интересную схему разработки: сначала мы разрабатываем инструменты проверки идеи, причем делаем это в самом общем виде, а затем создаем само программное описание интересующей нас проблемы.

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

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