主要内容

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

这个例子展示了如何使用U-Net从RAW相机数据中恢复亮的RGB图像。

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

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

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

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

下载在黑暗中看的数据集

本例使用来自摸黑(SID)数据集的索尼相机数据[1].SID数据集提供了同一场景的注册RAW图像对。在每一对图像中,一张图像曝光时间短,曝光不足,另一张图像曝光时间长,曝光良好。来自SID数据集的索尼相机数据的大小是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函数。该函数作为支持文件附加到示例中。

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

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

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

createCombinedDatastoreForLowLightRecoveryHelper函数执行以下操作:

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

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

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

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

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

dsTrainFull = 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 = (512512 4);patchesPerImage = 12;dsTrain =变换(dsTrainFull,...@(数据)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,@(数据)extractCenterPatch(数据,inputSize));

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

增加训练数据

方法扩充训练数据集变换函数和augmentPatchesForLowLightRecoveryhelper函数。helper函数包含在这个示例的末尾。的augmentPatchesForLowLightRecovery辅助函数将随机水平和垂直反射和随机90度旋转添加到成对的训练图像补丁中。

dsTrain = transform(dsTrain,@(数据)augmentPatchesForLowLightRecovery(数据)));

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

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

定义网络

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

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

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

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

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

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

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

指定网络的最后几层。

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

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

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

在训练中使用以均值为中心的归一化输入。

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

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

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

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

finalLayerName = net.Layers(结束). name;lossLayer = ssimLossLayerGray;网= addLayers(净,lossLayer);网= connectLayers(网,finalLayerName, lossLayer.Name);

指定培训选项

对于训练,使用初始学习率为1e-3的亚当求解器。训练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计算的需求(并行计算工具箱)

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

检查训练网络的结果

视觉检查训练的微光摄像机管网的结果。

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

[testPair,信息]=阅读(dsTestFull);testShortFilename = info.ShortExposureFilename;testLongFilename = info.LongExposureFilename;

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

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

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

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

显示网络预测。经过训练的网络在具有挑战性的采集条件下恢复了令人印象深刻的图像,几乎没有噪声或其他视觉假象。网络预测的颜色比场景的地面真实长曝光图像更不饱和和有活力。

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

支持功能

extractRandomPatchhelper函数从平面RAW图像中提取多个随机斑块,并从RGB图像中提取相应斑块。原始数据补丁有大小——- - - - - -n-by-4, RGB镜像补丁的大小为22n3, (n的值targetRAWSize输入参数。两个补丁有相同的场景内容。

函数dataOut = extractRandomPatch(data,targetRAWSize,patchesPerImage) dataOut = cell(patchesPerImage,2);原始数据= {1};rgb ={2}数据;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(原始windowRAW) imcrop (rgb windowRGB)};结束结束

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

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

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

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

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

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

参考文献

[1]陈,陈,陈其峰,徐佳,和Vladlen Koltun。《学会在黑暗中看》预印本,2018年5月4日提交。https://arxiv.org/abs/1805.01934

另请参阅

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

相关的例子

更多关于

Baidu
map