[Из песочницы] Шумоподавление путем объединения изображений на Java
Здраствуй, Хабр! Хочу поделиться кодом простой программы, которую я использую для уменьшения шума с цифровых фотограффий.Примерно восемь лет назад, рассматривая фотографии, снятые на свой первый цифровой фотоаппарат, я обнаружил, что некоторые снимки с тусклым освещением имеют какую-то странную мутность, цветные пятна, не резкость. В то время я еще не знал, что такое шум, как он зависит от параметра ISO и был очень разочарован, что фотоаппарат такой «некачественный». Однако, я обратил внимание, что на одинаковых снимках эти цветные пятна выглядят несколько по разному, меняются от кадра к кадру. Время шло, я научился снимать на ручных настройках, узнал, что такое шум, как правильно выставить светочуствительность и т.д.
Спустя несколько лет, когда уже начал заниматься программированием, снова обратил внимание на то, что шум на изображениях не является статичным. В голове возникла идея:, а что если взять, снять несколько абсолютно одинаковых изображений, а потом неким образом объединить их, устранив разность между снимками, т.е. шум?
Итак, ниже представлены 4 изображения, демонстрирующие некие фотографии одного и того-же обьекта, со случайным шумом на каждом снимке. В качестве объекта представлены красные круги, в качестве шума — белые.
Первым делом, я решил попробовать банальным способом совместить эти изображения в фотошопе, установив каждому из изображений прозрачность = 50%. Конечно, ничего хорошего из этого не вышло.

Такой результат вполне логичен — пиксели не усредняются, а просто прибавляются один к другому, а так же каждый последующий слой имеет больший «вес» над нижестоящими.
Немного поискав в интернете, обнаружил, что программы такого типа уже существуют и активно используются в среде астрофотографов. Выдержки, на которых снимают звезды, огромны (и соответственно шум), а сам объект съемки статичен (при съемке с гидированием), что позволяет устранять шум путем объединения идентичных снимков. Однако осталось одно но: все найденные мной программы подобного типа являлись очень сложными для использования, заточенными именно для астрофото и практически все стоили больших денег. Такой функционал мне был не нужен, и я продолжил поиск.
Спустя некоторое время я нашел сайт известного в кругах панорамных фотографов немецкого математика Хельмута Дерша. На данный момент практически все ПО для склейки панорамных изображений основывается на его алгоритмах. Помимо ПО для обработки панорам на его сайте я наткнулся на программу, устраняющую шум с изображений — 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 //сумма значений канала всех изображений
int sumBands = 0;
//Общее количество изображений, не выходящих за рамки min и max
int counter = 0; //В цикле рассчитываем сумму значений канала всех изображений
for (int i = 0; i Практическое применение алгоритму найти сложно — объект для съемки должен быть статичен, освещение объекта не должно изменяться, ну и ничего не получится без очень устойчивого штатива. Кстати, у программы есть некий «побочный» эффект — удаляется не только шум, а любой не статичный объект. Например, сделав большое количество кадров с оживленной площади, теоретически можно «удалить» всех людей. Ниже небольшой пример. Снимки до обработки; как видно по снимкам, таинственным образом перемещается банан. А вот результат обработки — как видно, банан пропал не полностью. Однако, сделав большее количество кадров, при условии, что банан продолжил бы двигаться, можно было бы полностью от него избавиться. А что насчет шума? Тут тоже все отлично, хватило всего трех кадров, чтобы значительно его уменьшить (астрофотографы, к примеру, используют, насколько я знаю, 15+ кадров). Конечно, описанный мною алгоритм невероятно прост, и мало применим в реальной практике, но, я надеюсь, возможно кто-то будет пользоваться им, или почерпнет из статьи что-то полезное.




