主要内容

使用深度学习点亮极暗的图像

这个例子展示了如何从使用U-Net在极端低光条件下收集的RAW相机数据中恢复增强的RGB图像。

相机的微光图像恢复是一个具有挑战性的问题。一个典型的解决方案是增加曝光时间,这允许更多的光线在场景中击中传感器,并增加图像的亮度。然而,较长的曝光时间会导致在场景中的物体移动或相机在采集过程中受到扰动时产生运动模糊伪影。

深度学习提供的解决方案可以恢复从单反相机和金宝搏官方网站许多现代手机相机收集的原始数据的合理图像,尽管光照条件低,曝光时间短。这些解决方案金宝搏官方网站充分利用了原始数据中存在的全部信息,优于后处理RGB数据中执行的增光技术[1].

弱光图像(左)和复原图像(右)

这个例子展示了如何使用来自特定相机传感器的数据来训练网络来实现低光相机管道。这个例子展示了如何从非常低的光线中恢复曝光良好的RGB图像,来自同一类型的相机传感器的曝光不足的RAW数据。

下载在黑暗中看到的数据集

本例使用来自See-in-the-Dark (SID)数据集的Sony相机数据[1].SID数据集提供了同一场景的注册RAW图像对。在每一对图像中,一张图像曝光时间短,曝光不足,另一张图像曝光时间长,曝光良好。来自SID数据集的Sony相机数据的大小为25 GB。

dataDir作为数据集的期望位置。

dataDir = fullfile(tempdir,“席德”);

要下载数据集,请转到此链接:https://storage.googleapis.com/isl-datasets/SID/Sony.zip.方法指定的目录中提取数据dataDir变量。提取成功后,dataDir包含目录索尼有两个子目录:而且.文件在子目录曝光时间长,曝光效果好。文件在子目录有一个短曝光和相当不足曝光和黑暗。

数据集还提供了描述如何将文件划分为训练、验证和测试数据集的文本文件。移动文件Sony_train_list.txtSony_val_list.txt,Sony_test_list.txt属性指定的目录dataDir变量。

为培训、验证和测试创建数据存储

方法导入要包含在训练、验证和测试数据集中的文件列表importSonyFileInfohelper函数。该函数作为支持文件附加到示例中。金宝app

trainInfo = importSonyFileInfo(fullfile(dataDir, dataDir)“Sony_train_list.txt”));valInfo = importSonyFileInfo(fullfile(dataDir, dataDir)“Sony_val_list.txt”));testInfo = importSonyFileInfo(fullfile(dataDir, dataDir)“Sony_test_list.txt”));

使用数据存储组合和预处理RAW和RGB数据

创建组合数据存储,用于读取和预处理曝光不足和曝光良好的RAW图像对createCombinedDatastoreForLowLightRecoveryhelper函数。该函数作为支持文件附加到示例中。金宝app

createCombinedDatastoreForLowLightRecoveryHelper函数执行以下操作:

  • 创建一个imageDatastore使用自定义读取功能读取短曝光RAW图像。方法读取RAW图像rawread函数,然后将RAW拜耳模式分离为四个传感器中的每个单独的通道raw2planar函数。类型将数据归一化到范围[0,1]imageDatastore对象。

  • 创建一个imageDatastore对象,该对象读取长时间曝光的RAW图像并将数据转换为RGB图像raw2rgb函数。类型将数据归一化到范围[0,1]imageDatastore对象。

  • 结合imageDatastore对象使用结合函数。

  • 对图像对应用简单的乘法增益。增益校正了暗输入的较短曝光时间和输出图像的较长曝光时间之间的曝光时间差。这个增益是通过取图像文件名中提供的长曝光时间和短曝光时间的比值来定义的。

  • 将图像与曝光时间、ISO和光圈等元数据关联起来。

dstraainfull = createCombinedDatastoreForLowLightRecovery(dataDir,trainInfo);dsValFull = createCombinedDatastoreForLowLightRecovery(dataDir,valInfo);dsTestFull = createCombinedDatastoreForLowLightRecovery(dataDir,testInfo);

使用验证图像的子集使验证度量的计算更快。不要应用额外的增强。

numVal = 30;dsValFull = shuffle(dsValFull);dsVal =子集(dsValFull,1:numVal);

预处理训练和验证数据

对训练数据集进行预处理变换功能和extractRandomPatchhelper函数。helper函数在本例的最后定义。的extractRandomPatchhelper函数从平面RAW图像中裁剪多个大小为512 × 512 × 4像素的随机补丁,并从RGB图像中裁剪大小为1024 × 1024 × 3像素的相应补丁。补丁中的场景内容匹配。每个训练图像提取12个补丁。

inputSize = [512,512,4];patchesPerImage = 12;dsTrain = transform(dsTrain full,...@(数据)extractRandomPatch(数据、inputSize patchesPerImage));

预览原始全尺寸图像和随机训练补丁。

previewFull =预览(dsTrainFull);previewPatch =预览(dsTrain);蒙太奇({previewFull {1,2}, previewPatch{1,2}},写成BackgroundColor =“w”);

对验证数据集进行预处理变换功能和extractCenterPatchhelper函数。helper函数在本例的最后定义。的extractCenterPatchhelper函数从平面RAW图像的中心裁剪一个512 × 512 × 4像素的单个补丁,并从RGB图像中裁剪相应的1024 × 1024 × 3像素的补丁。补丁中的场景内容匹配。

dsVal = transform(dsVal,@(data) extractCenterPatch(data,inputSize));

测试数据集不需要预处理。测试图像以全尺寸输入网络。

增强训练数据

增强训练数据集使用变换功能和augmentPatchesForLowLightRecoveryhelper函数。helper函数包含在本例的末尾。的augmentPatchesForLowLightRecoveryHelper函数为成对训练图像补丁添加随机水平和垂直反射以及随机90度旋转。

dsTrain = transform(dsTrain,@(data) augmentPatchesForLowLightRecovery(data));

通过预览平面RAW图像补丁和相应的RGB解码补丁中的一个通道,验证预处理和增强操作按预期工作。平面RAW数据和目标RGB数据描述的是从原始源图像中随机提取的同一场景的斑块。由于RAW数据采集时间较短,在RAW patch中可见明显的噪声,导致信噪比较低。

imagePairs = read(dsTrain);rawImage = imagePairs{1,1};rgbPatch = imagePairs{1,2};蒙太奇({rawImage (:,: 1), rgbPatch});

定义网络

使用类似于U-Net的网络架构。类创建编码器和解码器子网络blockedNetwork函数。类以编程方式创建编码器和解码器子网络buildEncoderBlock而且buildDecoderBlock分别为辅助函数。helper函数在本例的最后定义。该示例在除第一个和最后一个网络块外的所有网络块的卷积层和激活层之间使用实例规范化,并使用泄漏的ReLU层作为激活层。

创建一个编码器子网,由四个编码器模块组成。第一个编码器模块有32个通道或特征映射。每个后续模块的特征映射数量都是前一个编码器模块的两倍。

numModules = 4;numChannelsEncoder = 2.^(5:8);encoder = blockedNetwork(@(block) buildEncoderBlock(block,numChannelsEncoder),...numModules NamePrefix =“编码器”);

创建一个由四个解码器模块组成的解码器子网。第一个解码器模块有256个通道或特征映射。每个后续的解码器模块都将前一个解码器模块的特征映射数量减半。

numChannelsDecoder = fliplr(numChannelsEncoder);decoder = blockedNetwork(@(block) buildDecoderBlock(block,numChannelsDecoder),...numModules NamePrefix =“解码”);

指定连接编码器和解码器子网络的桥接层。

bridgeLayers = [convolution2dLayer(3,512,Padding= .“相同”PaddingValue =“复制”) groupNormalizationLayer (“channel-wise”) leakyReluLayer(0.2)卷积2dlayer(3,512,填充=“相同”PaddingValue =“复制”) groupNormalizationLayer (“channel-wise”) leakyReluLayer (0.2)];

指定网络的最后一层。

finalLayers = [convolution2dLayer(1,12) depthToSpace2dLayer(2)];

组合编码器子网、桥接层、解码器子网和最终层encoderDecoderNetwork函数。

net = encoderDecoderNetwork(inputSize,编码器,解码器,...LatentNetwork = bridgeLayers,...SkipConnections =“连接”...FinalNetwork = finalLayers);net = layerGraph(net);

对输入使用均值居中归一化作为训练的一部分。

net = replaceLayer(net,“encoderImageInputLayer”...imageInputLayer (inputSize正常化=“zerocenter”));

使用自定义层定义总体损失ssimLossLayerGray.这个层定义作为支持文件附加到这个示例中。金宝app的ssimLossLayerGray层使用丢失的形式

lossOverall α × lossSSIM + 1 - α × 损失 l 1

该层计算预测和目标RGB图像的灰度表示的多尺度结构相似性(SSIM)损失multissim函数。该层指定权重因子 α 如7/8,使用五个音阶。

finalLayerName = net.Layers(end).Name;lossLayer = ssimLossLayerGray;net = addLayers(net,lossLayer);net = connectLayers(net,finalLayerName,lossLayer.Name);

指定培训项目

对于训练,使用初始学习率为1e-3的Adam求解器。训练30次。

miniBatchSize = 12;maxEpochs = 30;选项= trainingOptions(“亚当”...情节=“训练进步”...MiniBatchSize = MiniBatchSize,...InitialLearnRate = 1 e - 3,...MaxEpochs = MaxEpochs,...ValidationFrequency = 400);

训练网络或下载预训练网络

默认情况下,该示例加载预训练的微光恢复网络版本。预训练的网络使您可以运行整个示例,而无需等待训练完成。

为了训练网络,设置doTraining变量转换为真正的.训练模型使用trainNetwork(深度学习工具箱)函数。

如果有GPU,可以在GPU上进行训练。使用GPU需要并行计算工具箱™和支持CUDA®的NVIDIA®GPU。有关更多信息,请参见GPU支金宝app持版本(并行计算工具箱)

doTraining = false;如果doTraining checkpointtsdir = fullfile(dataDir,“检查点”);如果~存在(checkpointsDir“dir”mkdir (checkpointsDir);结束options.CheckpointPath = checkpointsDir;netTrained = trainNetwork(dsTrain,net,options);modelDateTime = string(datetime(“现在”格式=“yyyy-MM-dd-HH-mm-ss”));保存(fullfile (dataDir“trainedLowLightCameraPipelineNet——”+ modelDateTime +“.mat”),“netTrained”);其他的trainedNet_url =“https://ssd.mathworks.com/金宝appsupportfiles/vision/data/trainedLowLightCameraPipelineNet.zip”;downloadTrainedNetwork (trainedNet_url dataDir);负载(fullfile (dataDir“trainedLowLightCameraPipelineNet.mat”));结束

检查训练网络的结果

目视检查训练的低光摄像机管道网络的结果。

从测试集中读取一对图像和相应的元数据。从元数据中获取短曝光和长曝光图像的文件名。

[testPair,info] = read(dsTestFull);testShortFilename = info.ShortExposureFilename;testLongFilename = info.LongExposureFilename;

将原始曝光不足的RAW图像转换为RGB图像,只需一步raw2rgb函数。显示结果,将显示范围缩放到像素值的范围。图像看起来几乎完全是黑色的,只有几个明亮的像素。

testshorttimage = raw2rgb(testShortFilename);testShortTime = info.ShortExposureTime;imshow (testShortImage[])标题([“短曝光测试图像”“曝光时间=”+ + num2str (testShortTime))“s”

将原始曝光良好的RAW图像转换为RGB图像,只需一步raw2rgb函数。显示结果。

testLongImage = raw2rgb(testLongFilename);testlong = info.LongExposureTime;imshow (testLongImage)标题(“长曝光目标图像”“曝光时间=”+ + num2str (testLongTime))“s”

显示网络预测。经过训练的网络在具有挑战性的采集条件下恢复了令人印象深刻的图像,噪音或其他视觉伪影很少。网络预测的颜色饱和度和活力低于地面真实长曝光图像的场景。

outputFromNetwork = im2uint8(激活(netTrained,testPair{1},“FinalNetworkLayer2”));imshow (outputFromNetwork)标题(“微光恢复网络预测”

金宝app支持功能

extractRandomPatchhelper函数从平面RAW图像中裁剪多个随机补丁,并从RGB图像中裁剪相应的补丁。原始数据补丁有大小——- - - - - -n-by-4, RGB图像补丁大小为22n-by-3,其中[n的值targetRAWSize输入参数。两个补丁都有相同的场景内容。

函数dataOut = extractRandomPatch(data,targetRAWSize,patchesPerImage) dataOut = cell(patchesPerImage,2);Raw =数据{1};RGB =数据{2};idx = 1:patchesPerImage windowRAW = randomCropWindow3d(大小(raw),targetRAWSize);windowRGB = images.spatialref.Rectangle(...2 * windowRAW.XLimits + (1,0), 2 * windowRAW.YLimits + (1,0));dataOut(idx,:) = {imcrop3(raw,windowRAW),imcrop(rgb, windowwrgb)};结束结束

extractCenterPatchhelper函数从平面RAW图像的中心提取单个补丁,并从RGB图像中提取相应的补丁。原始数据补丁有大小——- - - - - -n-by-4, RGB图像补丁大小为22n-by-3,其中[n的值targetRAWSize输入参数。两个补丁都有相同的场景内容。

函数dataOut = extractCenterPatch(data,targetRAWSize) raw = data{1};RGB =数据{2};windowRAW = centerCropWindow3d(大小(raw),targetRAWSize);windowRGB = images.spatialref.Rectangle(...2 * windowRAW.XLimits + (1,0), 2 * windowRAW.YLimits + (1,0));dataOut = {imcrop3(raw,windowRAW),imcrop(rgb, windowwrgb)};结束

buildEncoderBlockHelper函数定义编码器子网内单个编码器模块的层。

函数block = buildEncoderBlock(blockIdx,numChannelsEncoder)如果blockIdx < 2 instanceNorm = [];其他的instanceNorm = instanceNormalizationLayer;结束filterSize = 3;numFilters = numChannelsEncoder(blockIdx);block = [convolution2dLayer(filterSize,numFilters,Padding= .“相同”...PaddingValue =“复制”WeightsInitializer =“他”) instanceNorm leakyReluLayer(0.2)卷积2dlayer (filterSize,numFilters,Padding=“相同”...PaddingValue =“复制”WeightsInitializer =“他”) instanceNorm leakyReluLayer(0.2) maxPooling2dLayer(2,Stride=2,Padding=“相同”));结束

buildDecoderBlockHelper函数定义解码器子网内单个编码器模块的层。

函数block = buildDecoderBlock(blockIdx,numChannelsDecoder)如果blockIdx < 4 instanceNorm = instanceNormalizationLayer;其他的instanceNorm = [];结束filterSize = 3;numFilters = numChannelsDecoder(blockIdx);block =[转置conv2dlayer (filterSize,numFilters,Stride=2,...WeightsInitializer =“他”裁剪=“相同”) convolution2dLayer (filterSize numFilters填充=“相同”...PaddingValue =“复制”WeightsInitializer =“他”) instanceNorm leakyReluLayer(0.2)卷积2dlayer (filterSize,numFilters,Padding=“相同”...PaddingValue =“复制”WeightsInitializer =“他”) instanceNorm leakyReluLayer(0.2)];结束

参考文献

[1]陈,陈,陈其峰,徐佳,弗拉德伦·科尔顿。“学会在黑暗中看东西。”预印本,2018年5月4日提交。https://arxiv.org/abs/1805.01934

另请参阅

|(深度学习工具箱)|(深度学习工具箱)||

相关的例子

更多关于