Медианный фильтр в D

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

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

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

Медианный фильтр — это довольно простой (в том смысле, что он легко и буквально понимается), но вместе с тем, довольно эффективный алгоритм для подавления импульсных помех, а также равномерного цифрового шума. Сам медианный фильтр действительно очень прост: берем исходное изображение и строим для него фильтр размером M*N (будем говорить, что размер окна фильтра или даже просто окна составляет M*N. Кроме того, широко употребляется и другой термин — апертура или радиус фильтра, который описывает ситуацию, когда окно фильтра является прямоугольным, т.е. размер фильтра N*N, а вот число N в такой записи и называют апертурой фильтра), представляющий собой матрицу M*N (или, как мы обычно говорим, двумерный массив), в который будут записаны значения всех пикселей находящихся вокруг некоторого пикселя, который будет проходить, в момент применения алгоритма, фильтрацию и соответственно он будет являться центральным внутри окна фильтра. После того, как создан массив для фильтра и в него помещены пиксели, происходит сортировка всего двумерного массива по возрастанию, следом за которой следует выбор среднего элемента из отсортированного массива и помещение этого элемента в качестве текущего значения пикселя. Используя такую элементарную стратегию, медианный фильтр последовательно проходит все пиксели изображения, применяя фильтрацию по медиане к каждому пикселю за исключением краевых пикселей изображения, что является некоторым недостатком фильтра, который устраняется в разных версиях алгоритма по-разному: от изменения размера изображения до степени кратности размерам окна фильтра до простого игнорирования границ изображения.

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

auto medianFilter(SuperImage source, int M = 3, int N = 3)
{
	auto imageWidth = source.width;
	auto imageHeight = source.height;
	auto edgeM = M / 2;
	auto edgeN = N / 2;
	auto simg = image(imageWidth, imageHeight);
	
	for (int i = edgeM; i < imageWidth - edgeM; i++)
	{
		for (int j = edgeN; j < imageHeight - edgeN; j++)
		{
			Color4f[] window;

			for (int w = 0; w < M; w++)
			{
				for (int h = 0; h < N; h++)
				{
					window ~= source[i + w - edgeM, j + h - edgeN];
				}
			}
			
			import std.algorithm : sort;
			import std.range : array;
			
			auto sortedWindow = (sort!"a.luminance < b.luminance"(window)).array;
			
			simg[i, j] = sortedWindow[(M + N) / 2];
		}
	}
	return simg;
}

Это в перспективе дает более производительный и эффективный алгоритм по сравнению со стандартными версиями.

Кстати, стандартный вариант по псевдокоду алгоритма отказался работать, а ведь я пытался его сделать еще в начале своей карьеры в D, да и вообще ни одно буквальное переложение медианного фильтра не захотело работать в D…

Ну и пес с ними, так как моя собственная реализация дает весьма интересные результаты!

Например, мы с Bagomot’ом для целей испытания смоделировали несколько ситуаций с зашумлением изображения вулкана Этна (взято фото его недавнего извержения):

примерно 30% обычного шума, роль которого играют простые белые точки:

медианный фильтр с апертурой 3 (т.е. размер окна фильтра 3*3):

а вот более интересный вариант с шумом разного размера:

медианный фильтр с апертурой 3:

А что будет, если шум, с которым может столкнуться фильтр, окажется цветным, да еще и случайно распределенным?

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

Исходное изображение с цветным шумом:

Результат применения фильтра с апертурой 5 * 5:

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

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

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