В этом примере показано, как аппроксимировать операцию фильтрации изображений с помощью многомасштабной сети агрегирования контекста (CAN).
Приближение оператора находит альтернативные способы обработки изображений таким образом, чтобы результат напоминал выход от обычной операции обработки изображений или конвейера. Цель приближения часто состоит в том, чтобы уменьшить время, необходимое для обработки изображения.
Для выполнения операторских приближений было предложено несколько классических и глубокое обучение техник. Некоторые классические методы улучшают эффективность одного алгоритма, но не могут быть обобщены на другие операции. Другой распространенный метод аппроксимирует широкую область значений операций путем применения оператора к копии изображения с низким разрешением, но потеря высокочастотного содержимого ограничивает точность приближения.
Решения для глубокого обучения позволяют аппроксимировать более общие и сложные операции. Например, многомасштабная сеть агрегирования контекста (CAN), представленная Q. Chen [1], может аппроксимировать многомасштабное отображение тонов, передачу фотографического стиля, нелокальное обезвреживание и рисунок карандаша. Multiscale CAN обучает на изображениях с полным разрешением для большей точности при обработке высокочастотных деталей. После обучения сети сеть может обойти обычную операцию обработки и обрабатывать изображения непосредственно.
Этот пример исследует, как обучить multiscale CAN аппроксимировать операцию двусторонней фильтрации изображений, которая уменьшает шум изображения при сохранении резкости ребра. В примере представлен полный рабочий процесс обучения и вывода, который включает процесс создания обучающего datastore, выбора опций обучения, обучения сети и использования сети для обработки тестовых изображений.
Многомасштабный CAN обучен минимизации потеря между обычным выходом операции обработки изображений и сетевым откликом после обработки входа изображения с помощью многомасштабной контекстной агрегации. Многомасштабная контекстная агрегация ищет информацию о каждом пикселе со всего изображения, а не ограничивает поиск небольшим соседством, окружающим пиксель.
Чтобы помочь сети узнать глобальные свойства изображений, многомасштабная архитектура CAN имеет большое восприимчивое поле. Первый и последний слои имеют одинаковый размер, потому что оператор не должен изменять размер изображения. Последующие промежуточные слои расширены экспоненциально увеличивающимися масштабными факторами (отсюда и «многомасштабная» природа CAN). Расширение позволяет сети искать пространственно разделённые функции на различных пространственных частотах, не снижая разрешение изображения. После каждого слоя свертки сеть использует адаптивную нормализацию, чтобы сбалансировать влияние нормализации партии . и единичного отображения на аппроксимированный оператор.
Загрузите IAPR TC-12 Benchmark, который состоит из 20 000 все еще естественных изображений [2]. Набор данных включает фотографии людей, животных, городов и многое другое. Размер файла данных составляет ~ 1,8 ГБ. Если вы не хотите загружать обучающий набор обучающих данных, необходимый для обучения сети, то можно загрузить предварительно обученный CAN, набрав load('trainedOperatorLearning-Epoch-181.mat');
в командной строке. Затем перейдите непосредственно к разделу «Выполнение двухсторонней фильтрации приближения с использованием многомасштабного CAN» в этом примере.
imagesDir = tempdir;
url_1 = 'http://www-i6.informatik.rwth-aachen.de/imageclef/resources/iaprtc12.tgz';
downloadIAPRTC12Data(url_1,imagesDir);
Этот пример обучает сеть с небольшим подмножеством данных IAPRTC-12 Benchmark.
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'];
Используйте функцию helper bilateralFilterDataset
для предварительной обработки обучающих данных. Эта функция присоединена к примеру как вспомогательный файл.
Функция helper выполняет эти операции для каждого первозданного изображения в 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;
The randomPatchExtractionDatastore
предоставляет мини-пакеты данных в сеть в каждую итерацию эпохи. Выполните операцию read на datastore, чтобы исследовать данные.
inputBatch = read(dsTrain); disp(inputBatch)
InputImage ResponseImage _________________ _________________ {256×256×3 uint8} {256×256×3 uint8}
Этот пример задает многомасштабный CAN с использованием слоев из Deep Learning Toolbox™, включая:
imageInputLayer
(Deep Learning Toolbox) - Входной слой изображения
convolution2dLayer
(Deep Learning Toolbox) - 2D слой свертки для сверточных нейронных сетей
batchNormalizationLayer
(Deep Learning Toolbox) - слой нормализации партии .
leakyReluLayer
(Deep Learning Toolbox) - Утечка выпрямленного линейного единичного слоя
regressionLayer
(Deep Learning Toolbox) - Выходной слой регрессии для нейронной сети
Для реализации адаптивного слоя нормализации партии . добавляются два пользовательских слоя шкалы. Эти слои присоединены как вспомогательные файлы к этому примеру.
adaptiveNormalizationMu - Масштабный слой, который корректирует сильные стороны ветви нормализации партии .
adaptiveNormalizationLambda - Шкала слой, который корректирует сильные стороны тождеств ветви
Первый слой, imageInputLayer
, работает с закрашенными фигурами изображений. Размер закрашенной фигуры основан на сетевом приемном поле, которое является пространственной областью изображения, которая влияет на ответ самого верхнего слоя в сети. В идеале сетевое поле приема совпадает с размером изображения, так что оно может видеть все функции высокого уровня в изображении. Для двустороннего фильтра размер закрашенной фигуры изображения приближения прикреплен к 256 на 256.
networkDepth = 10; numberOfFilters = 32; firstLayer = imageInputLayer([256 256 3],'Name','InputLayer','Normalization','none');
За входным слоем изображения следует слой свертки 2-D, который содержит 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)
Обучите сеть с помощью оптимизатора Adam. Задайте установки гиперпараметров при помощи trainingOptions
(Deep Learning Toolbox) функция. Используйте значения по умолчанию 0.9
для 'Momentum
'и 0.0001
для 'L2Regularization
'(массовый распад). Задайте постоянную скорость обучения 0.0001
. Train на 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) функция.
Обучите на графическом процессоре, если он доступен. Для использования GPU требуется Parallel Computing Toolbox™ и графический процессор с поддержкой CUDA ® NVIDIA ®. Для получения дополнительной информации смотрите Поддержку GPU by Release (Parallel Computing Toolbox). Обучение занимает около 15 часов на NVIDIA™ Titan 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.
Визуально сравните деноизированные изображения из операторного приближения и обычной двусторонней фильтрации.
Оцените качество деноцированных изображений путем количественной оценки подобия изображений к первозданному эталонному изображению.
Создайте выборку шумное изображение, которое будет использоваться для сравнения результатов оператора приближения с обычной двусторонней фильтрацией. Набор тестовых данных, 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.
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) из последнего слоя (слоя регрессии). Выходы сети являются желаемым деноизированным изображением.
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')
Чтобы получить лучшее визуальное понимание деноизированных изображений, исследуйте небольшую область внутри каждого изображения. Задайте видимую область (ROI) с помощью вектора roi
в формате [x y ширина высота]. Элементы определяют координаты x и y верхнего левого угла, ширину и высоту информация только для чтения.
roi = [300 30 50 50];
Обрезайте изображения в этот информация только для чтения и отобразите результат как монтаж.
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
Измерьте качество перцептивного изображения с помощью Naturalness Image Quality Evaluator (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] Chen, Q. J. Xu, and V. Koltun. «Быстрая обработка изображений с полностью сверточными сетями». В трудах конференции IEEE 2017 по компьютерному зрению. Венеция, Италия, октябрь 2017, стр. 2516-2525.
[2] Грубингер, М., П. Клаф, Х. Мюллер и Т. Дезелаерс. «IAPR TC-12 Benchmark: A New Evaluation Resource for Visual Information Systems». Сведения о языковых ресурсах OntoImage 2006 для поиска изображений на основе содержимого. Генуя, Италия. Том 5, май 2006, стр. 10.
imbilatfilt
| randomPatchExtractionDatastore
| activations
(Deep Learning Toolbox) | layerGraph
(Deep Learning Toolbox) | trainingOptions
(Deep Learning Toolbox) | trainNetwork
(Deep Learning Toolbox)