В этом примере показано, как аппроксимировать операцию фильтрации изображений с помощью многошкальной сети агрегации контекста (CAN).
Приближение оператора находит альтернативные способы обработать изображения, таким образом, что результат напоминает выход от обычной операции обработки изображений или трубопровода. Цель приближения оператора состоит в том, чтобы часто уменьшать время, требуемое обработать изображение.
Несколько классические и методы глубокого обучения были предложены, чтобы выполнить приближение оператора. Некоторые классические методы повышают эффективность одного алгоритма, но не могут быть обобщены к другим операциям. Другой общий метод аппроксимирует широкий спектр операций путем применения оператора к с низким разрешением копии изображения, но потеря высокочастотного содержимого ограничивает точность приближения.
Решения для глубокого обучения включают приближение более общих и комплексных операций. Например, многошкальная сеть агрегации контекста (CAN), представленная К. Ченом [1], может аппроксимировать многошкальное тональное отображение, фотографическую передачу стиля, нелокальный dehazing и карандашный эскиз. Многошкальный CAN обучается на изображениях полного разрешения для большей точности в обработке высокочастотных деталей. После того, как сеть обучена, сеть может обойти обычные изображения операции по обработке и процесса непосредственно.
Этот пример исследует, как обучить многошкальный CAN аппроксимировать двустороннюю операцию фильтрации изображений, которая уменьшает шум изображения при сохранении резкости края. Пример представляет полное обучение и рабочий процесс вывода, который включает процесс создания учебного datastore, выбора опций обучения, обучения сети и использования сети, чтобы обработать тестовые изображения.
Многошкальный CAN обучен минимизировать потеря между обычным выходом операции обработки изображений и сетевым ответом после обработки входного изображения с помощью многошкальной агрегации контекста. Многошкальная агрегация контекста ищет информацию о каждом пикселе со всех концов целого изображения, вместо того, чтобы ограничить поиск маленьким окружением, окружающим пиксель.
Чтобы помочь сети изучить глобальные свойства изображения, многошкальная архитектура CAN имеет большое восприимчивое поле. Первые и последние слои имеют тот же размер, потому что оператор не должен изменять размер изображения. Последовательные промежуточные слои расширены путем экспоненциального увеличения масштабных коэффициентов (следовательно "многошкальная" природа CAN). Расширение позволяет сети искать пространственно разделенные функции на различных пространственных частотах, не уменьшая разрешение изображения. После каждого слоя свертки сеть использует адаптивную нормализацию, чтобы сбалансировать удар нормализации партии. и единичное отображение на аппроксимированном операторе.
Загрузите Сравнительный тест IAPR TC-12, который состоит из 20 000 все еще естественных изображений [2]. Набор данных включает фотографии людей, животных, города и т.д. Размер файла данных составляет ~1.8 Гбайт. Если вы не хотите загружать обучающий набор данных, должен был обучить сеть, то можно загрузить предварительно обученный CAN путем ввода load('trainedOperatorLearning-Epoch-181.mat');
в командной строке. Затем перейдите непосредственно к разделу Perform Bilateral Filtering Approximation Using Multiscale CAN в этом примере.
imagesDir = tempdir;
url_1 = 'http://www-i6.informatik.rwth-aachen.de/imageclef/resources/iaprtc12.tgz';
downloadIAPRTC12Data(url_1,imagesDir);
Этот пример обучает сеть с небольшим подмножеством Исходных данных IAPRTC-12.
trainImagesDir = fullfile(imagesDir,'iaprtc12','images','39'); exts = {'.jpg','.bmp','.png'}; pristineImages = imageDatastore(trainImagesDir,'FileExtensions',exts);
Перечислите количество учебных изображений.
numel(pristineImages.Files)
ans = 916
Чтобы создать обучающий набор данных, читайте в нетронутых изображениях и выпишите изображения, которые были двусторонние отфильтрованный. Отфильтрованные изображения хранятся на диске в директории, заданной preprocessDataDir
.
preprocessDataDir = [trainImagesDir filesep 'preprocessedDataset'];
Используйте функцию помощника bilateralFilterDataset
предварительно обрабатывать обучающие данные. Эта функция присоединена к примеру как вспомогательный файл.
Функция помощника выполняет эти операции для каждого нетронутого изображения в inputImages
:
Вычислите степень сглаживания для двусторонней фильтрации. Сглаживание отфильтрованного изображения уменьшает шум изображения.
Выполните двустороннюю фильтрацию с помощью imbilatfilt
.
Сохраните отфильтрованный образ к диску с помощью imwrite
.
bilateralFilterDataset(pristineImages,preprocessDataDir);
Используйте случайный datastore экстракции закрашенной фигуры, чтобы накормить обучающими данными сеть. Этот datastore извлекает случайные соответствующие закрашенные фигуры из двух хранилищ данных изображений, которые содержат сетевые входные параметры и желали сетевых ответов.
В этом примере сетевые входные параметры являются нетронутыми изображениями в pristineImages
. Желаемые сетевые ответы являются обработанными изображениями после двусторонней фильтрации. Создайте datastore изображений под названием bilatFilteredImages
из набора двусторонних отфильтрованных файлов изображений.
bilatFilteredImages = imageDatastore(preprocessDataDir,'FileExtensions',exts);
Создайте randomPatchExtractionDatastore
от двух хранилищ данных изображений. Задайте размер закрашенной фигуры 256 256 пикселей. Задайте 'PatchesPerImage
'чтобы извлечь одну случайным образом расположенную закрашенную фигуру из каждой пары изображений во время обучения. Задайте мини-пакетный размер одного.
miniBatchSize = 1; patchSize = [256 256]; dsTrain = randomPatchExtractionDatastore(pristineImages,bilatFilteredImages,patchSize, .... 'PatchesPerImage',1); dsTrain.MiniBatchSize = miniBatchSize;
randomPatchExtractionDatastore
обеспечивает мини-пакеты данных к сети в каждой итерации эпохи. Выполните операцию чтения на datastore, чтобы исследовать данные.
inputBatch = read(dsTrain); disp(inputBatch)
InputImage ResponseImage _________________ _________________ {256×256×3 uint8} {256×256×3 uint8}
Этот пример задает многошкальные слои использования CAN от Deep Learning Toolbox™, включая:
imageInputLayer
(Deep Learning Toolbox) - Слой входа Image
convolution2dLayer
(Deep Learning Toolbox) - 2D слой свертки для сверточных нейронных сетей
batchNormalizationLayer
(Deep Learning Toolbox) - Слой пакетной нормировки
leakyReluLayer
(Deep Learning Toolbox) - Текучий исправленный линейный модульный слой
regressionLayer
(Deep Learning Toolbox) - Regression слой выхода для нейронной сети
Два пользовательских слоя шкалы добавляются, чтобы реализовать адаптивный слой нормализации партии. Эти слои присоединяются как вспомогательные файлы к этому примеру.
adaptiveNormalizationMu - Слой Scale, который настраивает сильные места ветви нормализации партии.
adaptiveNormalizationLambda - Слой Scale, который настраивает сильные места единичной ветви
Первый слой, imageInputLayer
, работает с закрашенными фигурами изображений. Размер закрашенной фигуры основан на сетевом восприимчивом поле, которое является пространственной областью изображений, которая влияет на ответ верхнего слоя в сети. Идеально, сетевое восприимчивое поле совпадает с размером изображения так, чтобы это видело все функции высокого уровня в изображении. Для двустороннего фильтра размер закрашенной фигуры приближения изображений фиксируется к 256 256.
networkDepth = 10; numberOfFilters = 32; firstLayer = imageInputLayer([256 256 3],'Name','InputLayer','Normalization','none');
Входной слой изображений сопровождается 2D слоем свертки, который содержит 32 фильтра 3х3 размера. Нулевая клавиатура входные параметры к каждому слою свертки так, чтобы карты функции остались тот же размер как вход после каждой свертки. Инициализируйте веса к единичной матрице.
Wgts = zeros(3,3,3,numberOfFilters); for ii = 1:3 Wgts(2,2,ii,ii) = 1; end convolutionLayer = convolution2dLayer(3,numberOfFilters,'Padding',1, ... 'Weights',Wgts,'Name','Conv1');
Каждый слой свертки сопровождается слоем нормализации партии. и адаптивным слоем шкалы нормализации, который настраивает сильные места ветви нормализации партии. Позже, этот пример создаст соответствующий адаптивный слой шкалы нормализации, который настраивает силу единичной ветви. На данный момент следуйте за adaptiveNormalizationMu
слой со слоем сложения. Наконец, задайте текучий слой ReLU со скалярным множителем 0.2
для отрицательных входных параметров.
batchNorm = batchNormalizationLayer('Name','BN1'); adaptiveMu = adaptiveNormalizationMu(numberOfFilters,'Mu1'); addLayer = additionLayer(2,'Name','add1'); leakyrelLayer = leakyReluLayer(0.2,'Name','Leaky1');
Задайте средние слои сети после того же шаблона. Последовательные слои свертки имеют коэффициент расширения, который масштабируется экспоненциально с сетевой глубиной.
middleLayers = [convolutionLayer batchNorm adaptiveMu addLayer leakyrelLayer]; Wgts = zeros(3,3,numberOfFilters,numberOfFilters); for ii = 1:numberOfFilters Wgts(2,2,ii,ii) = 1; end for layerNumber = 2:networkDepth-2 dilationFactor = 2^(layerNumber-1); padding = dilationFactor; conv2dLayer = convolution2dLayer(3,numberOfFilters, ... 'Padding',padding,'DilationFactor',dilationFactor, ... 'Weights',Wgts,'Name',['Conv' num2str(layerNumber)]); batchNorm = batchNormalizationLayer('Name',['BN' num2str(layerNumber)]); adaptiveMu = adaptiveNormalizationMu(numberOfFilters,['Mu' num2str(layerNumber)]); addLayer = additionLayer(2,'Name',['add' num2str(layerNumber)]); leakyrelLayer = leakyReluLayer(0.2, 'Name', ['Leaky' num2str(layerNumber)]); middleLayers = [middleLayers conv2dLayer batchNorm adaptiveMu addLayer leakyrelLayer]; end
Не применяйте коэффициент расширения к предпоследнему слою свертки.
conv2dLayer = convolution2dLayer(3,numberOfFilters, ... 'Padding',1,'Weights',Wgts,'Name','Conv9'); batchNorm = batchNormalizationLayer('Name','AN9'); adaptiveMu = adaptiveNormalizationMu(numberOfFilters,'Mu9'); addLayer = additionLayer(2,'Name','add9'); leakyrelLayer = leakyReluLayer(0.2,'Name','Leaky9'); middleLayers = [middleLayers conv2dLayer batchNorm adaptiveMu addLayer leakyrelLayer];
Последний слой свертки имеет один фильтр размера 1 1 32 3, который восстанавливает изображение.
Wgts = sqrt(2/(9*numberOfFilters))*randn(1,1,numberOfFilters,3); conv2dLayer = convolution2dLayer(1,3,'NumChannels',numberOfFilters, ... 'Weights',Wgts,'Name','Conv10');
Последний слой является слоем регрессии вместо текучего слоя ReLU. Слой регрессии вычисляет среднеквадратическую ошибку между двусторонне отфильтрованным изображением и сетевым предсказанием.
finalLayers = [conv2dLayer regressionLayer('Name','FinalRegressionLayer') ];
Конкатенация всех слоев.
layers = [firstLayer middleLayers finalLayers']; lgraph = layerGraph(layers);
Создайте связи пропуска, которые действуют как единичная ветвь для адаптивного уравнения нормализации. Соедините связи пропуска со слоями сложения.
skipConv1 = adaptiveNormalizationLambda(numberOfFilters,'Lambda1'); skipConv2 = adaptiveNormalizationLambda(numberOfFilters,'Lambda2'); skipConv3 = adaptiveNormalizationLambda(numberOfFilters,'Lambda3'); skipConv4 = adaptiveNormalizationLambda(numberOfFilters,'Lambda4'); skipConv5 = adaptiveNormalizationLambda(numberOfFilters,'Lambda5'); skipConv6 = adaptiveNormalizationLambda(numberOfFilters,'Lambda6'); skipConv7 = adaptiveNormalizationLambda(numberOfFilters,'Lambda7'); skipConv8 = adaptiveNormalizationLambda(numberOfFilters,'Lambda8'); skipConv9 = adaptiveNormalizationLambda(numberOfFilters,'Lambda9'); lgraph = addLayers(lgraph,skipConv1); lgraph = connectLayers(lgraph,'Conv1','Lambda1'); lgraph = connectLayers(lgraph,'Lambda1','add1/in2'); lgraph = addLayers(lgraph,skipConv2); lgraph = connectLayers(lgraph,'Conv2','Lambda2'); lgraph = connectLayers(lgraph,'Lambda2','add2/in2'); lgraph = addLayers(lgraph,skipConv3); lgraph = connectLayers(lgraph,'Conv3','Lambda3'); lgraph = connectLayers(lgraph,'Lambda3','add3/in2'); lgraph = addLayers(lgraph,skipConv4); lgraph = connectLayers(lgraph,'Conv4','Lambda4'); lgraph = connectLayers(lgraph,'Lambda4','add4/in2'); lgraph = addLayers(lgraph,skipConv5); lgraph = connectLayers(lgraph,'Conv5','Lambda5'); lgraph = connectLayers(lgraph,'Lambda5','add5/in2'); lgraph = addLayers(lgraph,skipConv6); lgraph = connectLayers(lgraph,'Conv6','Lambda6'); lgraph = connectLayers(lgraph,'Lambda6','add6/in2'); lgraph = addLayers(lgraph,skipConv7); lgraph = connectLayers(lgraph,'Conv7','Lambda7'); lgraph = connectLayers(lgraph,'Lambda7','add7/in2'); lgraph = addLayers(lgraph,skipConv8); lgraph = connectLayers(lgraph,'Conv8','Lambda8'); lgraph = connectLayers(lgraph,'Lambda8','add8/in2'); lgraph = addLayers(lgraph,skipConv9); lgraph = connectLayers(lgraph,'Conv9','Lambda9'); lgraph = connectLayers(lgraph,'Lambda9','add9/in2');
Постройте график слоев.
plot(lgraph)
Обучите сеть с помощью оптимизатора Адама. Задайте установки гиперпараметров при помощи trainingOptions
(Deep Learning Toolbox) функция. Используйте значения по умолчанию 0.9
для 'Momentum
'и 0.0001
для 'L2Regularization
'(затухание веса). Задайте постоянную скорость обучения 0.0001
. Обучайтесь в течение 181 эпохи.
maxEpochs = 181; initLearningRate = 0.0001; miniBatchSize = 1; options = trainingOptions('adam', ... 'InitialLearnRate',initLearningRate, ... 'MaxEpochs',maxEpochs, ... 'MiniBatchSize',miniBatchSize, ... 'Plots','training-progress', ... 'Verbose',false);
По умолчанию пример загружает предварительно обученный многошкальный CAN, который аппроксимирует двусторонний фильтр. Предварительно обученная сеть позволяет вам выполнить приближение двусторонней фильтрации, не ожидая обучения завершиться.
Чтобы обучить сеть, установите doTraining
переменная в следующем коде к true
. Обучите многошкальный CAN с помощью trainNetwork
(Deep Learning Toolbox) функция.
Обучайтесь на графическом процессоре, если вы доступны. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA® графический процессор. Для получения дополнительной информации смотрите Поддержку графического процессора Релизом (Parallel Computing Toolbox). Обучение занимает приблизительно 15 часов на Титане NVIDIA™ X.
doTraining = false; if doTraining modelDateTime = string(datetime('now','Format',"yyyy-MM-dd-HH-mm-ss")); net = trainNetwork(dsTrain,lgraph,options); save(strcat("trainedOperatorLearning-",modelDateTime,"-Epoch-",num2str(maxEpochs),".mat"),'net'); else load('trainedOperatorLearning-Epoch-181.mat'); end
Чтобы обработать изображение с помощью обученной многошкальной сети CAN, которая аппроксимирует двусторонний фильтр, выполните остающиеся шаги этого примера. Остаток от примера показывает как:
Создайте демонстрационное шумное входное изображение из ссылочного изображения.
Выполните обычную двустороннюю фильтрацию шумного изображения с помощью imbilatfilt
функция.
Выполните приближение к двусторонней фильтрации на шумном изображении с помощью CAN.
Визуально сравните изображения denoised от приближения оператора и обычной двусторонней фильтрации.
Оцените качество изображений denoised путем определения количества подобия изображений к нетронутому ссылочному изображению.
Создайте демонстрационное шумное изображение, которое будет использоваться, чтобы сравнить результаты приближения оператора к обычной двусторонней фильтрации. Набор тестовых данных, testImages
, содержит 21 нетронутое изображение, поставленное в Image Processing Toolbox™. Загрузите изображения в imageDatastore
.
exts = {'.jpg','.png'}; fileNames = {'sherlock.jpg','car2.jpg','fabric.png','greens.jpg','hands1.jpg','kobi.png',... 'lighthouse.png','micromarket.jpg','office_4.jpg','onion.png','pears.png','yellowlily.jpg',... 'indiancorn.jpg','flamingos.jpg','sevilla.jpg','llama.jpg','parkavenue.jpg',... 'peacock.jpg','car1.jpg','strawberries.jpg','wagon.jpg'}; filePath = [fullfile(matlabroot,'toolbox','images','imdata') filesep]; filePathNames = strcat(filePath,fileNames); testImages = imageDatastore(filePathNames,'FileExtensions',exts);
Отобразите тестовые изображения как монтаж.
montage(testImages)
Выберите одно из изображений, чтобы использовать в качестве ссылочного изображения для двусторонней фильтрации. Преобразуйте изображение в тип данных uint8
.
indx = 3; % Index of image to read from the test image datastore
Ireference = readimage(testImages,indx);
Ireference = im2uint8(Ireference);
Можно опционально использовать собственное изображение в качестве ссылочного изображения. Обратите внимание на то, что размер тестового изображения должен быть, по крайней мере, 256 256. Если тестовое изображение меньше, чем 256 256, то увеличьте размер изображения при помощи imresize
функция. Сеть также требует тестового изображения RGB. Если тестовое изображение является шкалой полутонов, то преобразуйте изображение в RGB при помощи cat
функция, чтобы конкатенировать три копии оригинального изображения по третьему измерению.
Отобразите ссылочное изображение.
imshow(Ireference)
title('Pristine Reference Image')
Используйте imnoise
функция, чтобы добавить нулевой средний Гауссов белый шум с отклонением 0,00001 к ссылочному изображению.
Inoisy = imnoise(Ireference,'gaussian',0.00001); imshow(Inoisy) title('Noisy Image')
Обычная двусторонняя фильтрация является стандартным способом уменьшать шум изображения при сохранении резкости края. Используйте imbilatfilt
функция, чтобы применить двусторонний фильтр к шумному изображению. Задайте степень сглаживания равного отклонению пиксельных значений.
degreeOfSmoothing = var(double(Inoisy(:)));
Ibilat = imbilatfilt(Inoisy,degreeOfSmoothing);
imshow(Ibilat)
title('Denoised Image Obtained Using Bilateral Filtering')
Передайте нормированное входное изображение через обучивший сеть и наблюдайте activations
(Deep Learning Toolbox) от последнего слоя (слой регрессии). Выход сети является желаемым изображением denoised.
Iapprox = activations(net,Inoisy,'FinalRegressionLayer');
Image Processing Toolbox™ требует, чтобы изображения с плавающей точкой имели пиксельные значения в области значений [0, 1]. Используйте rescale
функционируйте, чтобы масштабировать пиксельные значения к этой области значений, затем преобразовать изображение в uint8
.
Iapprox = rescale(Iapprox);
Iapprox = im2uint8(Iapprox);
imshow(Iapprox)
title('Denoised Image Obtained Using Multiscale CAN')
Чтобы получить лучшее визуальное понимание изображений denoised, исследуйте небольшую область в каждом изображении. Задайте видимую область (ROI) с помощью векторного roi
в формате [x y высота ширины]. Элементы задают x-и y-координату левого верхнего угла, и ширину и высоту ROI.
roi = [300 30 50 50];
Обрежьте изображения к этому ROI и отобразите результат как монтаж.
montage({imcrop(Ireference,roi),imcrop(Inoisy,roi), ... imcrop(Ibilat,roi),imcrop(Iapprox,roi)}, ... 'Size',[1 4]); title('Reference Image | Noisy Image | Bilateral-Filtered Image | CAN Prediction');
CAN удаляет больше шума, чем обычная двусторонняя фильтрация. Оба метода сохраняют резкость края.
Используйте метрики качества изображения, чтобы количественно сравнить шумное входное изображение, двусторонне отфильтрованное изображение и аппроксимированное оператором изображение. Ссылочное изображение является изображением исходной ссылки, Ireference
, прежде, чем добавить шум.
Измерьте пиковое отношение сигнал-шум (PSNR) каждого изображения против ссылочного изображения. Большие значения PSNR обычно указывают на лучшее качество изображения. Смотрите psnr
для получения дополнительной информации об этой метрике.
noisyPSNR = psnr(Inoisy,Ireference); bilatPSNR = psnr(Ibilat,Ireference); approxPSNR = psnr(Iapprox,Ireference); disp(['PSNR of: Noisy Image / Bilateral-Filtered Image / Operator Approximated Image = ', ... num2str([noisyPSNR bilatPSNR approxPSNR])])
PSNR of: Noisy Image / Bilateral-Filtered Image / Operator Approximated Image = 20.2857 25.7978 26.2011
Измерьте структурный индекс подобия (SSIM) каждого изображения. SSIM оценивает визуальный удар трех характеристик изображения: яркость, контраст и структура, против ссылочного изображения. Чем ближе значение SSIM к 1, тем лучше тестовое изображение соглашается со ссылочным изображением. Смотрите ssim
для получения дополнительной информации об этой метрике.
noisySSIM = ssim(Inoisy,Ireference); bilatSSIM = ssim(Ibilat,Ireference); approxSSIM = ssim(Iapprox,Ireference); disp(['SSIM of: Noisy Image / Bilateral-Filtered Image / Operator Approximated Image = ', ... num2str([noisySSIM bilatSSIM approxSSIM])])
SSIM of: Noisy Image / Bilateral-Filtered Image / Operator Approximated Image = 0.76251 0.91576 0.92663
Измерьте перцепционное качество изображения с помощью Средства анализа качества изображения естественности (NIQE). Меньшие баллы NIQE указывают на лучшее перцепционное качество. Смотрите niqe
для получения дополнительной информации об этой метрике.
noisyNIQE = niqe(Inoisy); bilatNIQE = niqe(Ibilat); approxNIQE = niqe(Iapprox); disp(['NIQE score of: Noisy Image / Bilateral-Filtered Image / Operator Approximated Image = ', ... num2str([noisyNIQE bilatNIQE approxNIQE])])
NIQE score of: Noisy Image / Bilateral-Filtered Image / Operator Approximated Image = 12.1865 7.22606 6.18105
По сравнению с обычной двусторонней фильтрацией приближение оператора производит лучшие метрические баллы.
[1] Чен, Ц. Цз. Сюй и В. Колтун. "Быстрая Обработка изображений с Полностью сверточными Сетями". В Продолжениях 2 017 Конференций по IEEE по Компьютерному зрению. Венеция, Италия, октябрь 2017, стр 2516-2525.
[2] Grubinger, M. P. Ущелье, Х. Мюллер и Т. Дезелэерс. "Сравнительный тест IAPR TC-12: Новый Ресурс Оценки для Визуальных Информационных систем". Продолжения ресурсов OntoImage 2006 Языка Для Извлечения Изображений На основе содержимого. Генуя, Италия. Издание 5, май 2006, p. 10.
randomPatchExtractionDatastore
| trainNetwork
(Deep Learning Toolbox) | trainingOptions
(Deep Learning Toolbox) | layerGraph
(Deep Learning Toolbox) | activations
(Deep Learning Toolbox) | imbilatfilt