1.研究背景与意义
项目参考AAAI Association for the Advancement of Artificial Intelligence
研究背景与意义:
随着计算机视觉和图像处理技术的不断发展,图像去雾成为了一个备受关注的研究领域。在自然环境中,由于大气中的颗粒物和水汽的存在,图像中的目标物体往往会被雾霾所遮挡,导致图像质量下降,视觉效果模糊。因此,图像去雾技术的研究具有重要的理论和实际意义。
首先,图像去雾技术在军事和安全领域具有重要的应用价值。在军事侦察和监控中,由于恶劣的天气条件或远距离观测,图像质量常常受到雾霾的影响,使得目标物体无法清晰可见。通过研究和开发基于深度学习的图像去雾系统,可以提高图像的清晰度和细节,从而提高军事侦察和监控的效果。
其次,图像去雾技术在交通和安全领域也具有重要的应用前景。在交通监控和自动驾驶系统中,清晰的图像对于准确识别和判断道路和交通状况至关重要。然而,由于雾霾的存在,图像质量下降,给交通监控和自动驾驶系统带来了困难。通过研究和开发基于深度学习的图像去雾系统,可以提高交通监控和自动驾驶系统的可靠性和安全性。
此外,图像去雾技术在航空航天、环境监测和气象预测等领域也具有广泛的应用。在航空航天领域,由于大气层的存在,航天器拍摄的图像常常受到雾霾的影响,影响图像的质量和解译。通过研究和开发基于深度学习的图像去雾系统,可以提高航空航天图像的清晰度和解译能力。在环境监测和气象预测领域,通过去除图像中的雾霾,可以提高对环境和气象状况的准确监测和预测能力。
总之,基于深度学习的图像去雾系统具有广泛的应用前景和重要的研究意义。通过研究和开发高效准确的图像去雾算法,可以提高图像质量,改善视觉效果,进一步推动计算机视觉和图像处理技术的发展。此外,图像去雾技术的应用还可以带来许多实际的经济和社会效益,例如提高军事侦察和监控的效果,提高交通监控和自动驾驶系统的可靠性和安全性,提高航空航天图像的解译能力,提高环境监测和气象预测的准确性等。因此,基于深度学习的图像去雾系统的研究具有重要的现实意义和应用价值。
2.图片演示
3.视频演示
4.图像去雾相关理论概述
大气散射模型
在1.2节中,提到大气散射模型被广泛地应用于传统的去雾方法,以及早期的深度学习去雾算法也基于该模型估计中间参数来实现去雾。
在理想天气下,环境中的物体反射的光线传播到图像采集设备时,没有受到空气中悬浮介质(如粉尘、水珠等)的影响,此时采集到的图像没有任何信息损失。然而在现实场景中,大气中存在着各种各类的悬浮颗粒,如雾霾颗粒,物体反射的光线在到达成像系统时,受到不透光的介质干扰,引起光线衰减或者散射的现象,导致采集的图像退化。
针对于上述问题,研究者McCartney’l在1976年提出了大气散射模型,该模型从数学角度,简易概况了恶劣天气下设备成像过程。模型数学表达式如公式:
I代表观测到的雾图,J表示无雾图像,A代表大气光照强度,T(z)指的是介质传输图,x是像素位置。介质传输图可以具体表示为T(z)=e-Ba),其中3表示大气衰减系数,d指的是场景深度。根据大气散射模型,如果能准确地估计大气光照A和介质传输图T(az),就能通过公式(2-1)直接恢复无雾图像。
深度卷积神经网络
深度学习(Deep Learning,DL)作为机器学习方法的一个重要分支,古往今来,已经有几十年的发展历程。随着科技的发展,大规模数据集的逐渐可用以及高计算力的设备的普及力增强,深度学习方法迎来了爆发性的发展。在计算机视觉领域,深度神经网络在提取特征并进行识别、检测等任务的表现,展现了巨大的优越性,逐步取代了传统的手工方法。因此,本文基于现有深度学习技术的基础,开展图像去雾算法相关研究。
深度卷积神经网络(Deep Convolutional Neural Network, DCNN)最初是一种专为图像处理任务而设计的前馈深度学习模型,随着广泛应用,在自然语言处理等任务中也取得了巨大的成功。其中具有代表性的是,在1998年,LetNet-5结构的提出在手写数字识别任务中取得了不错的效果,同时奠定了卷积神经网络的发展基础。相比于传统的多元感知机,深度卷积神经网络以更深层的提取特征能力展现出更大的优势。
卷积层
卷积层(Convolutional Layer)是深度卷积神经网络中最核心的模块层,主要包含多维度卷积核(滤波器)和偏置项。卷积层能够对输入的三维数据中提取特征并处理。在卷积操作中,卷积核的参数是共享的,这意味着所有的输入局部区域都使用相同的参数进行计算,从而大大减少了需要学习的参数数量。卷积神经网络中,每个卷积层都包含多个卷积核,用于过滤输入像素值的子集,与核的大小相同。每个像素都乘以内核中相应的值,然后将结果相加得到一个值,以表示输出通道/特征图中的单个网格单元,即像素。这些卷积操作是一种线性变换,每个卷积都可以视为一种仿射函数。卷积核是一组小的权重参数,它们与输入数据的每个局部区域进行卷积计算,从而提取出不同的特征信息。
卷积层的输入通常是一组高维数据。例如,在计算机视觉中,通常将 RGB图像作为输入,其中包含3个通道。在卷积层中,每个卷积核都与输入数据的每个局部区域进行卷积操作,产生一个对应的特征图。例如一个3 x 3卷积核来处理高维数据,水平滑动卷积核将移动到输入数字矩阵上,从第一行开始扫描并计算特征图矩阵。
下图展示了卷积核的计算过程。
池化层
在设计神经网络的过程中,很常见地会在卷积层后面添加池化层,一次或重复地堆叠。池化层能够卷积层输出的特征图进行压缩和概括,这样可以降低网络中需要学习的参数个数和计算量。池化层通过对特征图的局部区域进行某种统计运算来提取特征的概要信息。因此,模型更关注特征的存在性而不是特征的精确位置。这使得模型对输入图像中特征位置的微小变化更加鲁棒。常见的池化层类型有平均池化层(Average pooling)、最大值池化层(Max pooling)等。
如图所示,最大值池化层从过滤器所覆盖的特征图区域中挑选出最大的元素。因此,最大值池化层的输出也是一个特征图,它包含了前一个特征图中最显著的特征。平均池化可以计算过滤器所覆盖的特征图区域中所有元素的平均值。因此,与最大值池化只保留特征图某个补丁中最显著的特征有所不同,平均池化保留了Patch中所有特征的平均水平。
激活函数
激活函数是一种数学函数,通常被用于神经网络中的每一个神经元,将输入信号转换为输出信号。激活函数的主要作用是引入非线性性质,这使得神经网络可以处理非线性的输入数据,从而提高其性能和准确性。常见的激活函数有Sigmoid、ReLU、Tanh等。图2-3展示了Sigmoid和 ReLU函数形式。
Sigmoid
Sigmoid函数,也叫 Logistic函数,是一种常见的激活函数,其定义如公式:
Sigmoid 函数的图像呈现出S形曲线,它将任意实数值映射到[0,1]的区间内,这使得它在二分类问题中非常有用。当输入值为0时,Sigmoid函数的输出值为0.5,因此,我们可以将 Sigmoid函数的输出值简单视作概率来看待。例如,在逻辑回归模型中,Sigmoid函数可以将线性模型的输出转换为一个概率值,这个概率值表示某个样本属于正类的可能性。
在神经网络中,Sigmoid函数通常被用作输出层的激活函数,也可以用作隐藏层的激活函数,但由于它存在梯度消失问题,所以在深层神经网络中往往会被其他激活函数所取代,比如ReLU函数。总的来说,Sigmoid函数是一种具有平滑性和可导性的激活函数,尤其适用于二分类问题。
5.核心代码讲解
5.1 dataset.py
class DatasetFolder(data.Dataset):
def __init__(self, root, loader, extensions, transform=None, target_transform=None):
samples = self.make_dataset(root, extensions)
if len(samples) == 0:
raise(RuntimeError("Found 0 files in subfolders of: " + root + "\n"
"Supported extensions are: " + ",".join(extensions)))
self.root = root
self.loader = loader
self.extensions = extensions
self.samples = samples
self.transform = transform
self.target_transform = target_transform
def make_dataset(self, dir, extensions):
images = []
for root, _, fnames in sorted(os.walk(dir)):
for fname in sorted(fnames):
if self.has_file_allowed_extension(fname, extensions):
path = os.path.join(root, fname)
item = (path, 0)
images.append(item)
return images
def has_file_allowed_extension(self, filename, extensions):
filename_lower = filename.lower()
return any(filename_lower.endswith(ext) for ext in extensions)
def __getitem__(self, index):
path, target = self.samples[index]
sample = self.loader(path)
if self.transform is not None:
sample = self.transform(sample)
if self.target_transform is not None:
target = self.target_transform(target)
return sample, target
def __len__(self):
return len(self.samples)
def __repr__(self):
fmt_str = 'Dataset ' + self.__class__.__name__ + '\n'
fmt_str += ' Number of datapoints: {}\n'.format(self.__len__())
fmt_str += ' Root Location: {}\n'.format(self.root)
tmp = ' Transforms (if any): '
fmt_str += '{0}{1}\n'.format(tmp, self.transform.__repr__().replace('\n', '\n' + ' ' * len(tmp)))
tmp = ' Target Transforms (if any): '
fmt_str += '{0}{1}'.format(tmp, self.target_transform.__repr__().replace('\n', '\n' + ' ' * len(tmp)))
return fmt_str
IMG_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif']
def pil_loader(path):
with open(path, 'rb') as f:
img = Image.open(f)
return img.convert('RGB')
def default_loader(path):
return pil_loader(path)
class ImageFolder(DatasetFolder):
def __init__(self, root, transform=None, target_transform=None, loader=default_loader):
super(ImageFolder, self).__init__(root, loader, IMG_EXTENSIONS, transform=transform, target_transform=target_transform)
self.imgs = self.samples
该程序文件是一个用于处理数据集的工具文件,主要包含以下几个部分:
-
导入必要的库:导入了torch.utils.data和PIL.Image库,用于数据集的处理和图像的读取。
-
函数has_file_allowed_extension:用于检查文件是否是允许的扩展名。接受一个文件名和扩展名列表作为参数,返回一个布尔值。
-
函数find_classes:用于查找数据集中的类别。接受一个目录路径作为参数,返回类别列表和类别到索引的映射字典。
-
函数make_dataset:用于创建数据集。接受一个目录路径和扩展名列表作为参数,返回一个包含图像路径和类别索引的列表。
-
类DatasetFolder:继承自torch.utils.data.Dataset类,用于表示数据集。接受数据集的根目录路径、图像加载器、扩展名列表、数据变换和目标变换作为参数。实现了__getitem__和__len__方法,用于获取数据集中的样本和样本数量。
-
常量IMG_EXTENSIONS:包含了支持的图像扩展名列表。
-
函数pil_loader:用于使用PIL库加载图像。接受图像路径作为参数,返回一个RGB格式的图像。
-
函数default_loader:默认的图像加载器,调用pil_loader函数加载图像。
-
类ImageFolder:继承自DatasetFolder类,用于表示图像数据集。接受数据集的根目录路径、数据变换、目标变换和图像加载器作为参数。初始化时调用父类的构造函数,并将IMG_EXTENSIONS作为扩展名列表传递给父类。
5.2 data_utils.py
class ImagePairFromFolders(Dataset):
def __init__(self, dataset_dirs, transform):
super(ImagePairFromFolders, self).__init__()
assert len(dataset_dirs) > 0
self.image_filenames = [sorted([join(root, x) for x in listdir(root) if is_image_file(x)]) for root in dataset_dirs]
# check file exists
for l in self.image_filenames:
for f in l:
assert isfile(f)
self.transform = transform
def __getitem__(self, index):
images = [Image.open(filenames[index]) for filenames in self.image_filenames]
if len(images) == 1:
return self.transform(images[0])
else:
seed = random.randint(0, 2**32)
l_image = []
for x in images:
random.seed(seed)
l_image += [self.transform(x)]
return l_image
def __len__(self):
return len(self.image_filenames[0])
class UnNormalize(object):
def __init__(self, mean, std):
self.mean = mean
self.std = std
def __call__(self, tensor):
out = tensor.new(*tensor.size())
for z in range(out.shape[1]):
out[:,z,:,:] = tensor