В этом примере показано, как применить стилистический внешний вид одного изображения к содержимому сцены второго изображения с использованием предварительно обученной VGG-19 сети [1].
Загрузите изображение стиля и изображение содержимого. Этот пример использует отличительную картину Ван Гога «Звёздная ночь» в качестве изображения стиля и фотографию маяка в качестве изображения содержания.
styleImage = im2double(imread('starryNight.jpg')); contentImage = imread('lighthouse.png');
Отображение изображения стиля и изображения содержимого в качестве монтажа.
imshow(imtile({styleImage,contentImage},'BackgroundColor','w'));
В этом примере для извлечения элементов содержимого и изображения стиля на различных слоях используется модифицированная предварительно подготовленная VGG-19 глубокая нейронная сеть. Эти многослойные элементы используются для вычисления соответствующих потерь содержимого и стиля. Сеть генерирует стилизованное изображение переноса с использованием комбинированной потери.
Чтобы получить предварительно обученную сеть VGG-19, установите vgg19 (инструментарий глубокого обучения). Если необходимые пакеты поддержки не установлены, программа предоставляет ссылку для загрузки.
net = vgg19;
Чтобы сделать сеть VGG-19 подходящей для извлечения элементов, удалите все полностью подключенные слои из сети.
lastFeatureLayerIdx = 38; layers = net.Layers; layers = layers(1:lastFeatureLayerIdx);
Максимальные уровни пула VGG-19 сети вызывают эффект затухания. Чтобы уменьшить эффект затухания и увеличить градиентный поток, замените все макс. слои пула средними слоями пула [1].
for l = 1:lastFeatureLayerIdx layer = layers(l); if isa(layer,'nnet.cnn.layer.MaxPooling2DLayer') layers(l) = averagePooling2dLayer(layer.PoolSize,'Stride',layer.Stride,'Name',layer.Name); end end
Создайте график слоев с измененными слоями.
lgraph = layerGraph(layers);
Визуализация сети извлечения элементов на графике.
plot(lgraph)
title('Feature Extraction Network')
Чтобы обучить сеть с помощью пользовательского цикла обучения и включить автоматическое дифференцирование, преобразуйте график слоев в dlnetwork объект.
dlnet = dlnetwork(lgraph);
Измените размер изображения стиля и содержимого на меньший размер для ускорения обработки.
imageSize = [384,512]; styleImg = imresize(styleImage,imageSize); contentImg = imresize(contentImage,imageSize);
Предварительно обученная сеть VGG-19 выполняет классификацию по среднеканальному вычитаемому изображению. Получите среднее значение по каналу из уровня ввода изображения, который является первым уровнем в сети.
imgInputLayer = lgraph.Layers(1); meanVggNet = imgInputLayer.Mean(1,1,:);
Значения среднего по каналу соответствуют изображениям типа данных с плавающей запятой со значениями пикселей в диапазоне [0, 255]. Преобразование изображения стиля и изображения содержимого в тип данных single с диапазоном [0, 255]. Затем вычитают среднее по каналу из изображения стиля и изображения содержимого.
styleImg = rescale(single(styleImg),0,255) - meanVggNet; contentImg = rescale(single(contentImg),0,255) - meanVggNet;
Изображение переноса является выходным изображением в результате переноса стиля. Можно инициализировать изображение переноса с изображением стиля, изображением содержимого или любым случайным изображением. Инициализация изображения стиля или изображения содержимого смещает процесс передачи стиля и создает изображение переноса, более похожее на входное изображение. Напротив, инициализация с белым шумом устраняет смещение, но требует больше времени, чтобы сойтись на стилизованном изображении. Для лучшей стилизации и более быстрой сходимости этот пример инициализирует выходное передающее изображение как взвешенную комбинацию изображения содержимого и изображения белого шума.
noiseRatio = 0.7; randImage = randi([-20,20],[imageSize 3]); transferImage = noiseRatio.*randImage + (1-noiseRatio).*contentImg;
Цель потери содержимого состоит в том, чтобы сделать признаки передаваемого изображения соответствующими признакам изображения содержимого. Потери содержимого вычисляются как среднеквадратичная разница между признаками изображения содержимого и признаками изображения переноса для каждого слоя [1] признаков содержимого. - это прогнозируемая карта признаков для передаваемого изображения, а - это прогнозируемая карта признаков для изображения содержимого. - вес слоя содержания для слоя. Забота о высоте, ширине и каналах карт элементов соответственно.
2
Укажите имена слоев извлечения элементов содержимого. Элементы, извлеченные из этих слоев, используются для вычисления потери содержимого. В сети VGG-19 обучение более эффективно с использованием элементов из более глубоких слоев, а не элементов из неглубоких слоев. Поэтому укажите слой извлечения элемента содержимого как четвертый сверточный слой.
styleTransferOptions.contentFeatureLayerNames = {'conv4_2'};Укажите веса слоев извлечения элемента содержимого.
styleTransferOptions.contentFeatureLayerWeights = 1;
Цель потери стиля состоит в том, чтобы текстура передаваемого изображения соответствовала текстуре изображения стиля. Представление стиля изображения представляется в виде матрицы Gram. Поэтому потери стиля вычисляются как средняя квадратичная разность между матрицей Gram изображения стиля и матрицей Gram изображения переноса [1]. и - это прогнозируемые карты элементов для стиля и передаваемого изображения соответственно. и - это матрицы Gram для элементов стиля и элементов переноса соответственно. - вес слоя стиля для слоя стиля.
GZˆl-GZl) 2
Укажите имена слоев извлечения элементов стиля. Элементы, извлеченные из этих слоев, используются для вычисления потери стиля.
styleTransferOptions.styleFeatureLayerNames = {'conv1_1','conv2_1','conv3_1','conv4_1','conv5_1'};Задайте веса слоев извлечения элементов стиля. Укажите малые веса для простых изображений стиля и увеличьте веса для сложных изображений стиля.
styleTransferOptions.styleFeatureLayerWeights = [0.5,1.0,1.5,3.0,4.0];
Общая потеря - это взвешенная комбинация потери содержимого и потери стиля. и являются весовыми факторами для потери содержимого и потери стиля соответственно.
× Lstyle
Укажите весовые коэффициенты alpha и beta для потери содержимого и стиля. Соотношение alpha кому beta должно быть около 1e-3 или 1e-4 [1].
styleTransferOptions.alpha = 1; styleTransferOptions.beta = 1e3;
Обучение на 2500 итераций.
numIterations = 2500;
Укажите параметры оптимизации Adam. Установите для скорости обучения значение 2 для ускорения сходимости. Вы можете поэкспериментировать с частотой обучения, наблюдая ваше выходное изображение и потери. Инициализируйте среднюю градиентную скорость и среднюю градиентно-квадратную скорость затухания с помощью [].
learningRate = 2; trailingAvg = []; trailingAvgSq = [];
Преобразование изображения стиля, изображения содержимого и переноса изображения в dlarray (Deep Learning Toolbox) объекты с базовым типом single и метки размеров 'SSC'.
dlStyle = dlarray(styleImg,'SSC'); dlContent = dlarray(contentImg,'SSC'); dlTransfer = dlarray(transferImage,'SSC');
Обучение на GPU, если он доступен. Для использования графического процессора требуются параллельные вычислительные Toolbox™ и графический процессор NVIDIA ® с поддержкой CUDA ®. Дополнительные сведения см. в разделе Поддержка графического процессора по выпуску (Панель инструментов параллельных вычислений). Для обучения GPU преобразуйте данные в gpuArray.
if canUseGPU dlContent = gpuArray(dlContent); dlStyle = gpuArray(dlStyle); dlTransfer = gpuArray(dlTransfer); end
Извлеките элементы содержимого из изображения содержимого.
numContentFeatureLayers = numel(styleTransferOptions.contentFeatureLayerNames);
contentFeatures = cell(1,numContentFeatureLayers);
[contentFeatures{:}] = forward(dlnet,dlContent,'Outputs',styleTransferOptions.contentFeatureLayerNames);Извлеките элементы стиля из изображения стиля.
numStyleFeatureLayers = numel(styleTransferOptions.styleFeatureLayerNames);
styleFeatures = cell(1,numStyleFeatureLayers);
[styleFeatures{:}] = forward(dlnet,dlStyle,'Outputs',styleTransferOptions.styleFeatureLayerNames);Обучение модели с помощью пользовательского цикла обучения. Для каждой итерации:
Вычислите потери содержимого и потери стиля с помощью функций изображения содержимого, изображения стиля и изображения переноса. Для вычисления потерь и градиентов используйте вспомогательную функцию imageGradients (определено в разделе «Вспомогательные функции» данного примера).
Обновите изображение переноса с помощью adamupdate (Deep Learning Toolbox).
Выберите передающее изображение наилучшего стиля в качестве конечного выходного изображения.
figure minimumLoss = inf; for iteration = 1:numIterations % Evaluate the transfer image gradients and state using dlfeval and the % imageGradients function listed at the end of the example. [grad,losses] = dlfeval(@imageGradients,dlnet,dlTransfer,contentFeatures,styleFeatures,styleTransferOptions); [dlTransfer,trailingAvg,trailingAvgSq] = adamupdate(dlTransfer,grad,trailingAvg,trailingAvgSq,iteration,learningRate); if losses.totalLoss < minimumLoss minimumLoss = losses.totalLoss; dlOutput = dlTransfer; end % Display the transfer image on the first iteration and after every 50 % iterations. The postprocessing steps are described in the "Postprocess % Transfer Image for Display" section of this example. if mod(iteration,50) == 0 || (iteration == 1) transferImage = gather(extractdata(dlTransfer)); transferImage = transferImage + meanVggNet; transferImage = uint8(transferImage); transferImage = imresize(transferImage,size(contentImage,[1 2])); image(transferImage) title(['Transfer Image After Iteration ',num2str(iteration)]) axis off image drawnow end end

Получить обновленное изображение переноса.
transferImage = gather(extractdata(dlOutput));
Добавьте обучаемое по сети среднее значение к образу передачи.
transferImage = transferImage + meanVggNet;
Некоторые значения пикселей могут превышать исходный диапазон [0, 255] содержимого и стиля изображения. Можно подрезать значения к диапазону [0, 255], преобразовав тип данных в uint8.
transferImage = uint8(transferImage);
Измените размер изображения переноса до исходного размера изображения содержимого.
transferImage = imresize(transferImage,size(contentImage,[1 2]));
Отображение изображения содержимого, изображения переноса и изображения стиля в монтаже.
imshow(imtile({contentImage,transferImage,styleImage}, ...
'GridSize',[1 3],'BackgroundColor','w'));
imageGradients вспомогательная функция возвращает потери и градиенты с помощью элементов изображения содержимого, изображения стиля и изображения переноса.
function [gradients,losses] = imageGradients(dlnet,dlTransfer,contentFeatures,styleFeatures,params) % Initialize transfer image feature containers. numContentFeatureLayers = numel(params.contentFeatureLayerNames); numStyleFeatureLayers = numel(params.styleFeatureLayerNames); transferContentFeatures = cell(1,numContentFeatureLayers); transferStyleFeatures = cell(1,numStyleFeatureLayers); % Extract content features of transfer image. [transferContentFeatures{:}] = forward(dlnet,dlTransfer,'Outputs',params.contentFeatureLayerNames); % Extract style features of transfer image. [transferStyleFeatures{:}] = forward(dlnet,dlTransfer,'Outputs',params.styleFeatureLayerNames); % Compute content loss. cLoss = contentLoss(transferContentFeatures,contentFeatures,params.contentFeatureLayerWeights); % Compute style loss. sLoss = styleLoss(transferStyleFeatures,styleFeatures,params.styleFeatureLayerWeights); % Compute final loss as weighted combination of content and style loss. loss = (params.alpha * cLoss) + (params.beta * sLoss); % Calculate gradient with respect to transfer image. gradients = dlgradient(loss,dlTransfer); % Extract various losses. losses.totalLoss = gather(extractdata(loss)); losses.contentLoss = gather(extractdata(cLoss)); losses.styleLoss = gather(extractdata(sLoss)); end
contentLoss вспомогательная функция вычисляет средневзвешенную квадратичную разницу между признаками изображения содержимого и признаками изображения переноса.
function loss = contentLoss(transferContentFeatures,contentFeatures,contentWeights) loss = 0; for i=1:numel(contentFeatures) temp = 0.5 .* mean((transferContentFeatures{1,i} - contentFeatures{1,i}).^2,'all'); loss = loss + (contentWeights(i)*temp); end end
styleLoss вспомогательная функция вычисляет средневзвешенную квадратичную разницу между матрицей Gram признаков изображения стиля и матрицей Gram признаков изображения переноса.
function loss = styleLoss(transferStyleFeatures,styleFeatures,styleWeights) loss = 0; for i=1:numel(styleFeatures) tsf = transferStyleFeatures{1,i}; sf = styleFeatures{1,i}; [h,w,c] = size(sf); gramStyle = computeGramMatrix(sf); gramTransfer = computeGramMatrix(tsf); sLoss = mean((gramTransfer - gramStyle).^2,'all') / ((h*w*c)^2); loss = loss + (styleWeights(i)*sLoss); end end
computeGramMatrix вспомогательная функция используется styleLoss вспомогательная функция для вычисления матрицы Gram карты элементов.
function gramMatrix = computeGramMatrix(featureMap) [H,W,C] = size(featureMap); reshapedFeatures = reshape(featureMap,H*W,C); gramMatrix = reshapedFeatures' * reshapedFeatures; end
[1] Леон А. Гэтис, Александр С. Эккер и Маттиас Бетж. «Нейронный алгоритм художественного стиля». Препринт, представлен 2 сентября 2015 года. https://arxiv.org/abs/1508.06576
dlarray (инструментарий для глубокого обучения) | trainingOptions (инструментарий для глубокого обучения) | trainNetwork (инструментарий для глубокого обучения) | vgg19 (инструментарий для глубокого обучения)