[Из песочницы] Шумоподавление путем объединения изображений на Java

Здраствуй, Хабр! Хочу поделиться кодом простой программы, которую я использую для уменьшения шума с цифровых фотограффий.Примерно восемь лет назад, рассматривая фотографии, снятые на свой первый цифровой фотоаппарат, я обнаружил, что некоторые снимки с тусклым освещением имеют какую-то странную мутность, цветные пятна, не резкость. В то время я еще не знал, что такое шум, как он зависит от параметра ISO и был очень разочарован, что фотоаппарат такой «некачественный». Однако, я обратил внимание, что на одинаковых снимках эти цветные пятна выглядят несколько по разному, меняются от кадра к кадру. Время шло, я научился снимать на ручных настройках, узнал, что такое шум, как правильно выставить светочуствительность и т.д.

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

Итак, ниже представлены 4 изображения, демонстрирующие некие фотографии одного и того-же обьекта, со случайным шумом на каждом снимке. В качестве объекта представлены красные круги, в качестве шума — белые.

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

результат обработки в adobe photoshop

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

Немного поискав в интернете, обнаружил, что программы такого типа уже существуют и активно используются в среде астрофотографов. Выдержки, на которых снимают звезды, огромны (и соответственно шум), а сам объект съемки статичен (при съемке с гидированием), что позволяет устранять шум путем объединения идентичных снимков. Однако осталось одно но: все найденные мной программы подобного типа являлись очень сложными для использования, заточенными именно для астрофото и практически все стоили больших денег. Такой функционал мне был не нужен, и я продолжил поиск.

Спустя некоторое время я нашел сайт известного в кругах панорамных фотографов немецкого математика Хельмута Дерша. На данный момент практически все ПО для склейки панорамных изображений основывается на его алгоритмах. Помимо ПО для обработки панорам на его сайте я наткнулся на программу, устраняющую шум с изображений — PTAverage. Программа была невероятно простой — просто перетягиваешь фотографии на ярлык — и получаешь результат. Как раз то, что я и искал. Однако немного поигравшись с PTAverage, я понял, что это совсем не то, что хотелось бы.

Результат обработки изображений программой PTAverage:

результат обработки в PTAverage

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

Саму программу писал на java, т.к. изучал ее к тому времени уже около года. Единственной загвоздкой была загрузка изображений в формате tiff, но позже я разобрался с библиотекой JAI. Недостатком программы является огромное потребление памяти — JAI не умеет (а может я просто не нашел) читать изображение попиксельно, не загружая всё изображение в память.

Код программы Чтобы код был понятнее, убрал все проверки (такие как разрешение изображений, бит на канал и т.д.): public class Denoise { /** * @param inputFiles массив с файлами для обработки * @param outputFile файл, в который сохранится результат обработки * @param difference максимальная разница между пикселями (0–255) * @throws IOException */ Denoise (File[] inputFiles, File outputFile, int difference) throws IOException { //Создаем массив для данных изображений Raster[] rasters = new Raster[inputFiles.length]; //В цикле читаем каждое изображение for (int i = 0; i imageReaders = ImageIO.getImageReaders (is) ; ImageReader imageReader = imageReaders.next (); imageReader.setInput (is); if (imageReader.canReadRaster ()) { rasters[i] = imageReader.readRaster (0, null); } else { rasters[i] = imageReader.readAsRenderedImage (0, null).getData (); } } } //Получаем ширину и высоту первого изображения, считая что размеры всех изображений равны int width = rasters[0].getWidth (); int height = rasters[0].getHeight (); //Создаем растр для записи результирующего изображения, используя характеристики первого изображения WritableRaster outputRaster = rasters[0].createCompatibleWritableRaster (); //В цикле обходим каждый пиксель каждого изображения, усредняя значения по каждому каналу for (int x = 0; x

//сумма значений канала всех изображений int sumBands = 0; //Общее количество изображений, не выходящих за рамки min и max int counter = 0;

//В цикле рассчитываем сумму значений канала всех изображений for (int i = 0; i=min && data[i]<= max){ sumBands = sumBands+data[i]; counter++; } } //Если отклонение от медианного значения пикселя не превышает только одно (или ни одно) //из изображений - просто усредняем все полученные значения, //в противном случае - усредняем только те, которые вошли в указанные рамки if(counter <= 1){ sumBands = 0; for(int i = 0; i

результат обработки

Практическое применение алгоритму найти сложно — объект для съемки должен быть статичен, освещение объекта не должно изменяться, ну и ничего не получится без очень устойчивого штатива.

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

Снимки до обработки; как видно по снимкам, таинственным образом перемещается банан.

874a21020837cfab5d216cdc76e6bf1e.jpg

А вот результат обработки — как видно, банан пропал не полностью. Однако, сделав большее количество кадров, при условии, что банан продолжил бы двигаться, можно было бы полностью от него избавиться.

результат обработки

А что насчет шума? Тут тоже все отлично, хватило всего трех кадров, чтобы значительно его уменьшить (астрофотографы, к примеру, используют, насколько я знаю, 15+ кадров).

сравнение шума

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

© Habrahabr.ru