В этом примере показано, как восстановить украшенные изображения RGB с НЕОБРАБОТАННЫХ данных о камере, собранных при экстремальном слабом освещении с помощью U-Net.
Восстановление образа недостаточной освещенности в камерах является сложной проблемой. Стандартное решение должно увеличить выдержку, которая позволяет более легкий в сцене поражать датчик и увеличивает яркость изображения. Однако более длительные времена воздействия могут привести к артефактам размытости изображения движущегося объекта, когда объекты в сцене перемещаются или когда камера встревожена во время захвата.
Глубокое обучение предлагает решения, которые восстанавливают разумные изображения для Необработанных данных, собранных от цифровых однообъективных зеркальных фотоаппаратов и многих современных телефонных камер несмотря на слабое освещение и короткие времена воздействия. Эти решения используют в своих интересах полную информацию, существующую в Необработанных данных, чтобы превзойти по характеристикам проясняющиеся методы, выполняемые в постобработанных данных о RGB [1].
Изображение недостаточной освещенности (слева) и восстановленное изображение (справа)
В этом примере показано, как обучить сеть, чтобы реализовать трубопровод камеры недостаточной освещенности с помощью данных из конкретного датчика камеры. В этом примере показано, как восстановить хорошо отсоединенные изображения RGB с очень недостаточной освещенности, недоэкспонируемые Необработанные данные от того же типа датчика камеры.
Этот пример использует данные о камере Sony из набора данных Смотрите в темноте (SID) [1]. Набор данных SID обеспечивает указанные пары НЕОБРАБОТАННЫХ изображений той же сцены. В каждой паре одно изображение имеет короткую выдержку и недоэкспонируется, и другое изображение имеет более длительную выдержку и хорошо отсоединено. Размер данных о камере Sony из набора данных SID составляет 25 Гбайт.
Установите dataDir
как желаемое местоположение набора данных.
dataDir = fullfile(tempdir,"SID"); if ~exist(dataDir,"dir") mkdir(dataDir); end
Чтобы загрузить набор данных, перейдите к этой ссылке: https://storage.googleapis.com/isl-datasets/SID/Sony.zip. Извлеките данные в директорию, заданную dataDir
переменная. Когда экстракция успешна, dataDir
содержит директорию Sony
с двумя подкаталогами: long
и short
. Файлы в long
подкаталог имеет длинную выдержку и хорошо отсоединен. Файлы в short
подкаталог имеет короткое воздействие и вполне недоэкспонируется и темный.
Набор данных также предоставляет текстовые файлы, которые описывают, как разделить файлы в обучение, валидацию и наборы тестовых данных. Переместите файлы Sony_train_list.txt
, Sony_val_list.txt
, и Sony_test_list.txt
к директории, заданной dataDir
переменная.
Импортируйте список файлов, чтобы включать в обучение, валидацию и наборы тестовых данных с помощью importSonyFileInfo
функция помощника. Эта функция присоединена к примеру как вспомогательный файл.
trainInfo = importSonyFileInfo(fullfile(dataDir,"Sony_train_list.txt")); valInfo = importSonyFileInfo(fullfile(dataDir,"Sony_val_list.txt")); testInfo = importSonyFileInfo(fullfile(dataDir,"Sony_test_list.txt"));
Создайте объединенные хранилища данных, которые читают и предварительно обрабатывают пары недоэкспонированных и хорошо отсоединили НЕОБРАБОТАННЫЕ изображения с помощью createCombinedDatastoreForLowLightRecovery
функция помощника. Эта функция присоединена к примеру как вспомогательный файл.
createCombinedDatastoreForLowLightRecovery
функция помощника выполняет эти операции:
Создайте imageDatastore
это читает короткие изображения СЫРЫХ ДАННЫХ воздействия с помощью пользовательской функции чтения. Функция чтения читает НЕОБРАБОТАННОЕ изображение с помощью rawread
функция, затем разделяет НЕОБРАБОТАННЫЙ шаблон Байера на отдельные каналы для каждого из этих четырех датчиков с помощью raw2planar
функция. Нормируйте данные к области значений [0, 1] путем преобразования imageDatastore
объект.
Создайте imageDatastore
возразите, что СЫРЫЕ ДАННЫЕ длинной выдержки чтений отображают и преобразуют данные в изображение RGB за один шаг использование raw2rgb
функция. Нормируйте данные к области значений [0, 1] путем преобразования imageDatastore
объект.
Объедините imageDatastore
объекты с помощью combine
функция.
Примените простое мультипликативное усиление к парам изображений. Усиление корректирует для различия выдержки между более короткой выдержкой темных входных параметров и более длительная выдержка выходных изображений. Это усиление задано путем взятия отношения долгих и коротких времен воздействия, обеспеченных в именах файла образа.
Сопоставьте изображения с метаданными, такие как выдержка, ISO и апертура.
dsTrainFull = createCombinedDatastoreForLowLightRecovery(dataDir,trainInfo); dsValFull = createCombinedDatastoreForLowLightRecovery(dataDir,valInfo); dsTestFull = createCombinedDatastoreForLowLightRecovery(dataDir,testInfo);
Используйте подмножество изображений валидации, чтобы сделать расчет метрик валидации более быстрым. Не применяйте дополнительное увеличение.
numVal = 30; dsValFull = shuffle(dsValFull); dsVal = subset(dsValFull,1:numVal);
Предварительно обработайте обучающий набор данных с помощью transform
функционируйте и extractRandomPatch
функция помощника. Функция помощника присоединена к примеру как к вспомогательному файлу. extractRandomPatch
обрезки функции помощника несколько случайных закрашенных фигур размера 512 512 на 4 пикселя от плоского НЕОБРАБОТАННОГО изображения и соответствующих закрашенных фигур размера 1024 1 024 на 3 пикселя от изображения RGB. Содержимое сцены в соответствиях закрашенных фигур. Извлеките 12 закрашенных фигур на учебное изображение.
inputSize = [512,512,4]; patchesPerImage = 12; dsTrain = transform(dsTrainFull,@(data) extractRandomPatch(data,inputSize,patchesPerImage));
Предварительно просмотрите исходное полноразмерное изображение и случайную учебную закрашенную фигуру.
previewFull = preview(dsTrainFull);
previewPatch = preview(dsTrain);
montage({previewFull{1,2},previewPatch{1,2}},BackgroundColor="w");
Предварительно обработайте набор данных валидации с помощью transform
функционируйте и extractCenterPatch
функция помощника. Функция помощника присоединена к примеру как к вспомогательному файлу. extractCenterPatch
обрезки функции помощника одна закрашенная фигура размера 512 512 на 4 пикселя от центра плоского НЕОБРАБОТАННОГО изображения и соответствующих закрашенных фигур размера 1024 1 024 на 3 пикселя от изображения RGB. Содержимое сцены в соответствиях закрашенных фигур.
dsVal = transform(dsVal,@(data) extractCenterPatch(data,inputSize));
Набор данных тестирования не требует предварительной обработки. Тестовые изображения питаются в полном размере в сеть.
Увеличьте обучающие данные путем добавления случайного горизонтального и вертикального отражения, и рандомизировал вращения на 90 градусов.
dsTrain = transform(dsTrain,@(data) augmentPatchesForLowLightRecovery(data));
Проверьте, что операции предварительной обработки и увеличения работают как ожидалось путем предварительного просмотра одного канала от плоской НЕОБРАБОТАННОЙ закрашенной фигуры изображений, и соответствующий RGB декодировал закрашенную фигуру. Плоские Необработанные данные и целевые данные о RGB изображают закрашенные фигуры той же сцены, случайным образом извлеченной из изображения первоисточника. Значительный шум отображается в НЕОБРАБОТАННОЙ закрашенной фигуре из-за короткого времени захвата Необработанных данных, вызывая низкое отношение сигнал-шум.
imagePairs = read(dsTrain); rawImage = imagePairs{1,1}; rgbPatch = imagePairs{1,2}; montage({rawImage(:,:,1),rgbPatch});
Используйте сетевую архитектуру, похожую на U-Net. Пример создает подсети энкодера и декодера с помощью blockedNetwork
функция. Эта функция создает подсети энкодера и декодера программно использование buildEncoderBlock
и buildDecoderBlock
помощник функционирует, соответственно. Функции помощника заданы в конце этого примера. Пример использует нормализацию экземпляра между сверткой и слоями активации во всех сетевых блоках кроме первого и последнего, и использует текучий слой ReLU в качестве слоя активации.
Создайте подсеть энкодера, которая состоит из четырех модулей энкодера. Первый модуль энкодера имеет 32 канала или карты функции. Каждый последующий модуль удваивает количество карт функции от предыдущего модуля энкодера.
numModules = 4; numChannelsEncoder = 2.^(5:8); encoder = blockedNetwork(@(block) buildEncoderBlock(block,numChannelsEncoder), ... numModules,NamePrefix="encoder");
Создайте подсеть декодера, которая состоит из четырех модулей декодера. Первый модуль декодера имеет 256 каналов или карты функции. Каждый последующий модуль декодера половины количество функции сопоставляет от предыдущего модуля декодера.
numChannelsDecoder = fliplr(numChannelsEncoder); decoder = blockedNetwork(@(block) buildDecoderBlock(block,numChannelsDecoder), ... numModules,NamePrefix="decoder");
Задайте мостоукладчики, которые соединяют подсети энкодера и декодера.
bridgeLayers = [ convolution2dLayer(3,512,Padding="same",PaddingValue="replicate") groupNormalizationLayer("channel-wise") leakyReluLayer(0.2) convolution2dLayer(3,512,Padding="same",PaddingValue="replicate") groupNormalizationLayer("channel-wise") leakyReluLayer(0.2)];
Задайте последние слои сети.
finalLayers = [ convolution2dLayer(1,12) depthToSpace2dLayer(2)];
Объедините подсеть энкодера, мостоукладчики, подсеть декодера и последние слои с помощью encoderDecoderNetwork
функция.
net = encoderDecoderNetwork(inputSize,encoder,decoder, ... LatentNetwork=bridgeLayers, ... SkipConnections="concatenate", ... FinalNetwork=finalLayers); net = layerGraph(net);
Используйте среднюю нормализацию центрирования на входе как часть обучения.
net = replaceLayer(net,"encoderImageInputLayer",imageInputLayer(inputSize,Normalization="zerocenter"));
Задайте полную потерю с помощью пользовательского слоя ssimLossLayerGray
. Это определение слоя присоединено к этому примеру как к вспомогательному файлу. ssimLossLayerGray
слой использует потерю формы
Слой вычисляет многошкальное структурное подобие (SSIM) потеря для полутоновых представлений предсказанных и целевых изображений RGB с помощью multissim
функция. Слой задает фактор взвешивания как 7/8 и использование пять шкал.
finalLayerName = net.Layers(end).Name; lossLayer = ssimLossLayerGray; net = addLayers(net,lossLayer); net = connectLayers(net,finalLayerName,lossLayer.Name);
Для обучения используйте решатель Адама с начальной скоростью обучения 1e-3. Обучайтесь в течение 30 эпох.
miniBatchSize = 12; maxEpochs = 30; options = trainingOptions("adam", ... Plots="training-progress", ... MiniBatchSize=miniBatchSize, ... InitialLearnRate=1e-3, ... MaxEpochs=maxEpochs, ... ValidationFrequency=400);
По умолчанию пример загружает предварительно обученную версию сети восстановления недостаточной освещенности. Предварительно обученная сеть позволяет вам запустить целый пример, не ожидая обучения завершиться.
Чтобы обучить сеть, установите doTraining
переменная в следующем коде к true
. Обучите модель с помощью trainNetwork
(Deep Learning Toolbox) функция.
Обучайтесь на графическом процессоре, если вы доступны. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA® графический процессор. Для получения дополнительной информации смотрите Поддержку графического процессора Релизом (Parallel Computing Toolbox).
doTraining = false; if doTraining checkpointsDir = fullfile(dataDir,"checkpoints"); if ~exist(checkpointsDir,"dir") mkdir(checkpointsDir); end options.CheckpointPath=checkpointsDir; netTrained = trainNetwork(dsTrain,net,options); modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss")); save(dataDir+"trainedLowLightCameraPipelineNet-"+modelDateTime+".mat",'netTrained'); else trainedNet_url = "https://ssd.mathworks.com/supportfiles/vision/data/trainedLowLightCameraPipelineNet.zip"; trainedNet_filename = "trainedLowLightCameraPipelineNet.mat"; downloadTrainedLowLightRecoveryNet(trainedNet_url,dataDir); load(fullfile(dataDir,trainedNet_filename)); end
Визуально исследуйте результаты обученной сети трубопровода камеры недостаточной освещенности.
Считайте пару изображений и сопроводительных метаданных от набора тестов. Получите имена файлов коротких и изображений с большой выдержкой от метаданных.
[testPair,info] = read(dsTestFull); testShortFilename = info.ShortExposureFilename; testLongFilename = info.LongExposureFilename;
Преобразуйте исходное недоэкспонированное НЕОБРАБОТАННОЕ изображение в изображение RGB за один шаг использование raw2rgb
функция. Отобразите результат, масштабируя область значений отображения к области значений пиксельных значений. Изображение выглядит почти абсолютно черным только с несколькими яркими пикселями.
testShortImage = raw2rgb(testShortFilename); testShortTime = info.ShortExposureTime; imshow(testShortImage,[]) title(["Short Exposure Test Image";"Exposure Time = "+num2str(testShortTime)]+" s")
Преобразуйте исходное хорошо отсоединенное НЕОБРАБОТАННОЕ изображение в изображение RGB за один шаг использование raw2rgb
функция. Отобразите результат.
testLongImage = raw2rgb(testLongFilename); testLongTime = info.LongExposureTime; imshow(testLongImage) title(["Long Exposure Target Image";"Exposure Time = "+num2str(testLongTime)]+" s")
Отобразите сетевое предсказание. Обучивший сеть восстанавливает впечатляющее изображение при сложных условиях захвата с очень небольшим количеством шума или другими визуальными артефактами. Цвета сетевого предсказания менее насыщаются и вибрирующие, чем в изображении длинной выдержки основной истины сцены.
outputFromNetwork = im2uint8(activations(netTrained,testPair{1},'FinalNetworkLayer2')); imshow(outputFromNetwork) title("Low-Light Recovery Network Prediction")
extractRandomPatch
обрезки функции помощника несколько случайных закрашенных фигур от плоского НЕОБРАБОТАННОГО изображения и соответствующих закрашенных фигур от изображения RGB. Закрашенная фигура Необработанных данных имеет размер m n 4, и закрашенная фигура RGB изображений имеет размер 2m 2n 3, где [m n] значение targetRAWSize
входной параметр. Обе закрашенных фигуры имеют то же содержимое сцены.
function dataOut = extractRandomPatch(data,targetRAWSize,patchesPerImage) dataOut = cell(patchesPerImage,2); raw = data{1}; rgb = data{2}; for idx = 1:patchesPerImage windowRAW = randomCropWindow3d(size(raw),targetRAWSize); windowRGB = images.spatialref.Rectangle(2*windowRAW.XLimits+[-1,0],2*windowRAW.YLimits+[-1,0]); dataOut(idx,:) = {imcrop3(raw,windowRAW),imcrop(rgb,windowRGB)}; end end
extractCenterPatch
обрезки функции помощника одна закрашенная фигура от центра плоского НЕОБРАБОТАННОГО изображения и соответствующая закрашенная фигура от изображения RGB. Закрашенная фигура Необработанных данных имеет размер m n 4, и закрашенная фигура RGB изображений имеет размер 2m 2n 3, где [m n] значение targetRAWSize
входной параметр. Обе закрашенных фигуры имеют то же содержимое сцены.
function dataOut = extractCenterPatch(data,targetRAWSize) raw = data{1}; rgb = data{2}; windowRAW = centerCropWindow3d(size(raw),targetRAWSize); windowRGB = images.spatialref.Rectangle(2*windowRAW.XLimits+[-1,0],2*windowRAW.YLimits+[-1,0]); dataOut = {imcrop3(raw,windowRAW),imcrop(rgb,windowRGB)}; end
buildEncoderBlock
функция помощника задает слои одного модуля энкодера в подсети энкодера.
function block = buildEncoderBlock(blockIdx,numChannelsEncoder) if blockIdx < 2 instanceNorm = []; else instanceNorm = instanceNormalizationLayer; end filterSize = 3; numFilters = numChannelsEncoder(blockIdx); block = [ convolution2dLayer(filterSize,numFilters,Padding="same",PaddingValue="replicate",WeightsInitializer="he") instanceNorm leakyReluLayer(0.2) convolution2dLayer(filterSize,numFilters,Padding="same",PaddingValue="replicate",WeightsInitializer="he") instanceNorm leakyReluLayer(0.2) maxPooling2dLayer(2,Stride=2,Padding="same")]; end
buildDecoderBlock
функция помощника задает слои одного модуля энкодера в подсети декодера.
function block = buildDecoderBlock(blockIdx,numChannelsDecoder) if blockIdx < 4 instanceNorm = instanceNormalizationLayer; else instanceNorm = []; end filterSize = 3; numFilters = numChannelsDecoder(blockIdx); block = [ transposedConv2dLayer(filterSize,numFilters,Stride=2,WeightsInitializer="he",Cropping="same") convolution2dLayer(filterSize,numFilters,Padding="same",PaddingValue="replicate",WeightsInitializer="he") instanceNorm leakyReluLayer(0.2) convolution2dLayer(filterSize,numFilters,Padding="same",PaddingValue="replicate",WeightsInitializer="he") instanceNorm leakyReluLayer(0.2)]; end
[1] Чэнь, Чэнь, Цифэн Чэнь, Цзя Сюй и Владлен Кольтун. "Учась Видеть в темноте". Предварительно распечатайте, представленный 4 мая 2018. https://arxiv.org/abs/1805.01934.
imageDatastore
| trainingOptions
(Deep Learning Toolbox) | trainNetwork
(Deep Learning Toolbox) | transform
| combine