OpenCV 4.13.0
开源计算机视觉库 (Open Source Computer Vision)
正在加载...
正在搜索...
未找到匹配项
视网膜与真实世界视觉

目标

我在此介绍一个人类视网膜模型,该模型在图像预处理和增强方面具有一些有趣的特性。在本教程中,您将学会如何

  • 探索从视网膜输出的两个主要通道
  • 了解使用视网膜模型的基础知识
  • 探索一些参数调整

总体概述

该模型源自 Jeanny Herault 在 [141]Gipsa 的研究。它应用于图像处理领域,与 Listic(代码维护者和用户)实验室合作。这不是一个完整的模型,但它已经展现出一些有趣的特性,可用于增强图像处理体验。该模型允许使用以下人类视网膜特性:

  • 光谱白化,具有 3 个重要效果:高时空频率信号的抑制(噪声),中频细节的增强,以及低频亮度能量的降低。这种“一站式”特性直接能够净化由图像传感器和输入亮度范围引入的经典不良失真。
  • 局部对数亮度压缩可增强低光照条件下的细节。
  • 细节信息(小视网膜输出通道)和瞬态信息(事件、运动在大会视网膜输出通道可用)的解相关。

前两点在下面进行了说明。

在下图所示的 OpenEXR 图像样本 CrissyField.exr(高动态范围图像)中,为了能在网页上显示,原始输入图像被线性缩放到经典图像亮度范围 [0-255],并转换为 8 位/通道格式。这种强烈的转换由于局部对比度过大而隐藏了许多细节。此外,噪声能量也很强,污染了视觉信息。

图像

在以下图像中,应用 [26] 中提出的思想,正如您的视网膜所做的那样,局部亮度适应、空间降噪和光谱白化协同工作,并在较低范围的 8 位数据通道上传输准确信息。在这张图片中,噪声被显著去除,由强亮度对比度隐藏的局部细节得到增强。输出图像保持其自然性,视觉内容得到增强。颜色处理基于 [70] 提出的颜色复用/解复用方法。

图像

注意:图像样本可从 OpenEXR 网站下载。关于此演示,在视网膜处理之前,输入图像已线性缩放到 0-255 之间,并保持其通道浮点格式。其直方图的 5% 末端已被裁剪(主要去除错误的 HDR 像素)。有关类似处理,请查看样本 opencv/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp。以下演示将仅考虑经典的 8 位/通道图像。

视网膜模型的输出通道

视网膜模型提供两个受益于上述行为的输出。

  • 第一个称为小视网膜通道。它主要活跃在黄斑视网膜区域(高分辨率中央视觉,具有对颜色敏感的光感受器),旨在为在视网膜上保持静态的视觉细节提供准确的彩色视觉。另一方面,在视网膜上移动的物体会变得模糊。
  • 第二个众所周知的通道是大视网膜通道。它主要活跃在视网膜周边视觉,并发送与变化事件(运动、瞬态事件等)相关的信号。这些输出信号还有助于视觉系统将视网膜聚焦/居中于“瞬态”/移动区域,以进行更详细的分析,从而提高视觉场景的上下文和对象分类。

注意:关于所提出的模型,与真实视网膜不同,我们使用相同的分辨率将这两个通道应用于整个输入图像。这允许从所有考虑的图像中提取增强的视觉细节和运动信息……但请记住,这两个通道是互补的。例如,如果大视网膜通道在一个区域给出强能量,那么小视网膜通道在那里肯定会模糊,因为存在瞬态事件。

作为说明,我们在下面的一个暗视觉场景的摄像头视频流上应用了视网膜模型。在这个大学阶梯教室拍摄的视觉场景中,一些学生在与老师交谈时移动。

在这个视频序列中,由于环境昏暗,信噪比低,并且由于低质量的图像捕获工具链,视觉特征边缘存在颜色伪影。

图像

下面显示了应用于整个图像的视网膜中央视觉。在使用的视网膜配置中,全局亮度得到保留,局部对比度得到增强。此外,信噪比得到改善:由于高频时空噪声降低,增强的细节不会被任何增强的噪声所破坏。

图像

下面是视网膜模型的大视网膜输出。其信号在发生瞬态事件的地方很强。在这里,一个学生在图像底部移动,从而产生高能量。图像的其余部分是静态的,然而,它被强噪声损坏。在这里,视网膜滤除了大部分噪声,从而产生了较低的虚假运动区域“警报”。此通道可用作瞬态/移动区域检测器:它将为低成本分割工具提供相关信息,该工具将突出显示事件正在发生的区域。

图像

视网膜用例

该模型可以基本用于时空视频特效,也可用于

  • 执行纹理分析,具有增强的信噪比和增强的细节,对输入图像的亮度范围具有鲁棒性(查看小视网膜通道输出)。
  • 执行运动分析,并利用上述特性。

文献

有关更多信息,请参阅以下论文:[26]

  • 请参阅 Jeanny Herault 的参考著作,您可以在他的书中阅读:[141]

此视网膜滤波器代码包含博士/研究同事的研究贡献,作者已重新绘制了代码。

  • 查看 retinacolor.hpp 模块,了解 Brice Chaix de Lavarene 的博士论文色彩马赛克/去马赛克及其参考论文:[70]
  • 查看 imagelogpolprojection.hpp,了解源自 Barthelemy Durette 博士与 Jeanny Herault 的视网膜空间对数采样。还提出了一种视网膜/V1 皮层投影,源自 Jeanny 的讨论。更多信息请参阅上面引用的 Jeanny Heraults 的书。

代码教程

请参考原始教程源代码文件 opencv_folder/samples/cpp/tutorial_code/bioinspired/retina_tutorial.cpp

注意
请记住,视网膜模型包含在以下命名空间中:cv::bioinspired

假设 OpenCV 已正确安装,请使用以下命令进行编译。它需要 opencv_core(cv::Mat 及相关对象管理)、opencv_highgui(显示和图像/视频读取)以及 opencv_bioinspired(视网膜描述)库进行编译。

// 编译
gcc retina_tutorial.cpp -o Retina_tuto -lopencv_core -lopencv_highgui -lopencv_bioinspired -lopencv_videoio -lopencv_imgcodecs
// 运行命令:在最后一个参数“log”后面加上“log”,以应用空间对数采样(模拟视网膜采样)。
// 在摄像头上运行
./Retina_tuto -video
// 在视频文件上运行
./Retina_tuto -video myVideo.avi
// 在图像上运行
./Retina_tuto -image myPicture.jpg
// 使用对数采样在图像上运行
./Retina_tuto -image myPicture.jpg log

代码解释如下:

视网膜定义存在于 bioinspired 包中,一个简单的 include 即可使用。您也可以使用特定的头文件:opencv2/bioinspired.hpp,如果您愿意,但随后需要包含其他必需的 openv 模块:opencv2/core.hppopencv2/highgui.hpp

#include "opencv2/opencv.hpp"

为用户提供运行程序的提示,使用帮助函数。

// 帮助过程
static void help(std::string errorMessage)
{
std::cout<<"程序初始化错误:"<<errorMessage<<std::endl;
std::cout<<"\n程序调用过程:retinaDemo [处理模式] [可选:媒体目标] [可选最后一个参数:“log”激活视网膜对数采样]"<<std::endl;
std::cout<<"\t[处理模式]:"<<std::endl;
std::cout<<"\t -image :用于静态图像处理"<<std::endl;
std::cout<<"\t -video :用于视频流处理"<<std::endl;
std::cout<<"\t[可选:媒体目标]:"<<std::endl;
std::cout<<"\t 如果处理图像或视频文件,请指定目标文件的路径和文件名"<<std::endl;
std::cout<<"\t 如果处理来自已连接视频设备的视频流,则留空"<<std::endl;
std::cout<<"\t[可选:激活视网膜对数采样]:可以指定一个可选的最后一个参数用于视网膜空间对数采样"<<std::endl;
std::cout<<"\t 设置“log”而不带引号以激活此采样,输出帧大小将除以 4"<<std::endl;
std::cout<<"\n示例:"<<std::endl;
std::cout<<"\t-图像处理:./retinaDemo -image lena.jpg"<<std::endl;
std::cout<<"\t-带对数采样的图像处理:./retinaDemo -image lena.jpg log"<<std::endl;
std::cout<<"\t-视频处理:./retinaDemo -video myMovie.mp4"<<std::endl;
std::cout<<"\t-实时视频处理:./retinaDemo -video"<<std::endl;
std::cout<<"\n请使用新参数重新开始"<<std::endl;
std::cout<<"****************************************************"<<std::endl;
std::cout<<" 注意:此程序生成默认的视网膜参数文件 'RetinaDefaultParameters.xml'"<<std::endl;
std::cout<<" => 您可以使用此文件微调参数并在保存到文件 'RetinaSpecificParameters.xml' 时加载它们"<<std::endl;
}

然后,启动主程序,首先声明一个 cv::Mat 矩阵,输入图像将被加载到其中。同时,如果需要,分配一个 cv::VideoCapture 对象来加载视频流。

int main(int argc, char* argv[]) {
// 声明视网膜输入缓冲区……它将根据输入媒体以不同的方式填充
cv::Mat inputFrame;
cv::VideoCapture videoCapture; // 如果使用视频媒体,其管理器将在此声明
n 维密集数组类
定义于 mat.hpp:840
从视频文件、图像序列或摄像头捕获视频的类。
定义 videoio.hpp:786
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3

在主程序中,处理前,首先检查输入命令参数。在这里,它会加载第一个输入图像,该图像来自单个加载的图像(如果用户选择了命令 -image)或来自视频流(如果用户选择了命令 -video)。另外,如果用户在程序调用末尾添加了 log 命令,则通过布尔标志 useLogSampling 来考虑由视网膜执行的空间对数图像采样。

// 欢迎信息
std::cout<<"****************************************************"<<std::endl;
std::cout<<"* 视网膜演示:演示 Gipsa/Listic Labs 视网膜模型的包装器类的使用。"<<std::endl;
std::cout<<"* 此演示将尝试加载文件 'RetinaSpecificParameters.xml'(如果存在)。\n要创建它,请复制自动生成的模板 'RetinaDefaultParameters.xml'。\n然后用您自己的视网膜参数对其进行调整。"<<std::endl;
// 基本输入参数检查
if (argc<2)
{
help("参数数量错误");
return -1;
}
bool useLogSampling = !strcmp(argv[argc-1], "log"); // 检查用户是否需要视网膜对数采样处理
std::string inputMediaType=argv[1];
// 检查输入媒体类型(静态图像、视频文件、实时视频采集)
if (!strcmp(inputMediaType.c_str(), "-image") && argc >= 3)
{
std::cout<<"RetinaDemo: 正在处理图像 "<<argv[2]<<std::endl;
// 图像处理情况
inputFrame = cv::imread(std::string(argv[2]), 1); // 以 RGB 模式加载图像
}else
if (!strcmp(inputMediaType.c_str(), "-video"))
{
if (argc == 2 || (argc == 3 && useLogSampling)) // 尝试从视频捕获设备抓取图像
{
videoCapture.open(0);
}else// 尝试从视频文件流抓取图像
{
std::cout<<"RetinaDemo: 正在处理视频流 "<<argv[2]<<std::endl;
videoCapture.open(argv[2]);
}
// 抓取第一帧以检查一切是否正常
videoCapture>>inputFrame;
}else
{
// 命令参数错误
help("命令参数错误");
return -1;
}
virtual bool open(const String &filename, int apiPreference=CAP_ANY)
打开视频文件、捕获设备或 IP 视频流进行视频捕获。
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。

在处理完所有输入参数后,应该已经加载了第一张图像,如果没有,则显示错误并停止程序。

if (inputFrame.empty())
{
help("输入媒体无法加载,正在中止");
return -1;
}
bool empty() const
如果数组没有元素,则返回 true。

现在,一切都已准备好运行视网膜模型。我建议在此分配一个视网膜实例并管理可能的对数采样选项。视网膜构造函数至少需要一个 cv::Size 对象,该对象显示将要管理和处理的输入数据大小。可以激活其他选项,例如颜色及其相关的颜色复用策略(此处使用 enum cv::bioinspired::RETINA_COLOR_BAYER 选择拜耳马赛克)。如果使用对数采样,则可以调整图像缩减因子(较小的输出图像)和对数采样强度。

// 指向视网膜对象的指针
// 如果最后一个参数是“log”,则激活对数采样(有利于中央视野并对周边视野进行二次采样)
if (useLogSampling)
{
myRetina = cv::bioinspired::createRetina(inputFrame.size(), true, cv::bioinspired::RETINA_COLOR_BAYER, true, 2.0, 10.0);
}
else// -> 否则分配“经典”视网膜:
myRetina = cv::bioinspired::createRetina(inputFrame.size());
MatSize size
定义 mat.hpp:2226
@ RETINA_COLOR_BAYER
标准拜尔采样
定义 retina.hpp:86
std::shared_ptr< _Tp > Ptr
定义 cvstd_wrapper.hpp:23

完成后,建议的代码会写入一个默认的 xml 文件,其中包含视网膜的默认参数。这有助于您使用此模板创建自己的配置。此处生成的模板 xml 文件名为 RetinaDefaultParameters.xml

// 保存默认视网膜参数文件,以便您查看并可能修改它,然后使用方法“setup”重新加载
myRetina->write("RetinaDefaultParameters.xml");

在下一行,视网膜尝试加载另一个名为 RetinaSpecificParameters.xml 的 xml 文件。如果您创建了它并引入了自己的设置,它将被加载,否则将使用默认的视网膜参数。

// 如果文件存在则加载参数
myRetina->setup("RetinaSpecificParameters.xml");

此处不是必需的,但只是为了表明这是可能的,您可以将视网膜缓冲区重置为零,迫使其忘记过去的事件。

// 重置所有视网膜缓冲区(想象您闭眼很久)
myRetina->clearBuffers();

现在,是时候运行视网膜了!首先创建一些输出缓冲区,准备接收两个视网膜通道的输出。

// 声明视网膜输出缓冲区
cv::Mat retinaOutput_parvo;
cv::Mat retinaOutput_magno;

然后,在循环中运行视网膜,如果需要,从视频序列加载新帧,并将视网膜输出返回到专用缓冲区。

// 带有无停止条件的处理循环
while(true)
{
// 如果使用视频流,则抓取新帧,否则,输入保持不变
if (videoCapture.isOpened())
videoCapture>>inputFrame;
// 在加载的输入帧上运行视网膜滤波器
myRetina->run(inputFrame);
// 检索并显示视网膜输出
myRetina->getParvo(retinaOutput_parvo);
myRetina->getMagno(retinaOutput_magno);
cv::imshow("视网膜输入", inputFrame);
cv::imshow("视网膜小视网膜", retinaOutput_parvo);
cv::imshow("视网膜大会视网膜", retinaOutput_magno);
}
virtual bool isOpened() const
如果视频捕获已初始化,则返回 true。
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键操作。

完成了!但是,如果您想确保系统的安全性,请注意并处理异常。视网膜在遇到不相关数据时可能会抛出异常(无输入帧、设置错误等)。因此,我建议将所有视网膜代码用 try/catch 系统包围,如下所示。

try{
// 指向视网膜对象的指针
[---]
// 带有无停止条件的处理循环
while(true)
{
[---]
}
}catch(cv::Exception e)
{
std::cerr<<"使用视网膜时出错:"<<e.what()<<std::endl;
}
Class passed to an error.
定义 core.hpp:120
virtual const char * what() const noexcept override

视网膜参数,怎么办?

首先,建议阅读参考论文 [26]

完成后,打开演示生成的配置文件 RetinaDefaultParameters.xml,让我们来看一下。

<?xml version="1.0"?>
<opencv_storage>
<OPLandIPLparvo>
<colorMode>1</colorMode>
<normaliseOutput>1</normaliseOutput>
<photoreceptorsLocalAdaptationSensitivity>7.5e-01</photoreceptorsLocalAdaptationSensitivity>
<photoreceptorsTemporalConstant>9.0e-01</photoreceptorsTemporalConstant>
<photoreceptorsSpatialConstant>5.7e-01</photoreceptorsSpatialConstant>
<horizontalCellsGain>0.01</horizontalCellsGain>
<hcellsTemporalConstant>0.5</hcellsTemporalConstant>
<hcellsSpatialConstant>7.</hcellsSpatialConstant>
<ganglionCellsSensitivity>7.5e-01</ganglionCellsSensitivity></OPLandIPLparvo>
<IPLmagno>
<normaliseOutput>1</normaliseOutput>
<parasolCells_beta>0.</parasolCells_beta>
<parasolCells_tau>0.</parasolCells_tau>
<parasolCells_k>7.</parasolCells_k>
<amacrinCellsTemporalCutFrequency>2.0e+00</amacrinCellsTemporalCutFrequency>
<V0CompressionParameter>9.5e-01</V0CompressionParameter>
<localAdaptintegration_tau>0.</localAdaptintegration_tau>
<localAdaptintegration_k>7.</localAdaptintegration_k></IPLmagno>
</opencv_storage>

这里有一些建议,但实际上,最佳的参数设置取决于您想用视网膜做什么,而不是您输入视网膜的图像。除了需要更具体设置以实现特定亮度压缩目标的高动态范围图像(HDR)这一更特殊的情况外,视网膜的行为在内容之间应该相当稳定。请注意,OpenCV 凭借其 OpenEXR 图像兼容性能够管理这种 HDR 格式。

然后,如果应用程序目标需要在特定图像处理之前进行细节增强,您需要知道是否需要平均亮度信息。如果不需要,视网膜可以取消或显著降低其能量,从而为更高空间频率的细节提供更多可见性。

基本参数

最简单的参数如下:

  • colorMode:让视网膜处理彩色信息(如果为 1)或灰度图像(如果为 0)。在后一种情况下,只有输入的前几个通道会被处理。
  • normaliseOutput:每个通道都有此参数:如果值设置为 1,则考虑的通道的输出被缩放到 0 到 255 之间。此时要注意大视网膜输出的级别(运动/瞬态通道检测)。残留噪声也将被缩放!

注意:使用颜色需要颜色通道的复用/解复用,这也需要更多的处理。您可以使用灰度级预期更快的处理:所有视网膜过程每个像素大约需要 30 次乘法,并且最近已针对多核架构进行了并行化。

光感受器参数

以下参数作用于视网膜的入口点——光感受器,并影响所有后续过程。这些传感器是低通时空滤波器,可以平滑时空数据,并根据局部亮度调整其灵敏度,从而改善细节提取和高频噪声抑制。

  • photoreceptorsLocalAdaptationSensitivity:在 0 和 1 之间。接近 1 的值允许在光感受器层面实现高亮度对数压缩的效果。接近 0 的值提供更线性的灵敏度。单独增加此值可能会烧毁 小视网膜(细节通道) 输出图像。如果与 ganglionCellsSensitivity 协作调整,图像将非常对比鲜明,无论局部亮度如何……但会牺牲自然度。
  • photoreceptorsTemporalConstant:设置视网膜入口处的低通滤波器的时域常数。高值会导致强烈的时域平滑效果:移动的物体会变得模糊甚至消失,而静态物体会得到保留。但是,在视网膜处理开始时,稳定状态需要更长时间才能达到。
  • photoreceptorsSpatialConstant:指定与光感受器低通滤波器效果相关的空间常数。这些参数指定后续过程中允许的最小空间信号周期值。典型情况下,此滤波器应抑制高频噪声。另一方面,0 值不抑制任何噪声,而较高的值开始抑制高空间频率,并逐渐降低频率……如果您想看到输入图像的一些细节,请注意不要设置过高的值!对于彩色图像,一个好的折衷是 0.53 的值,因为这种选择不会过多影响彩色频谱。更高的值会导致输出图像变灰并模糊。

水平细胞参数

这个参数集调整与光感受器连接的神经网络——水平细胞。它调节光感受器的灵敏度,并完成最终的光谱白化处理(空间带通效应的一部分,从而有利于视觉细节的增强)。

  • horizontalCellsGain:这是一个关键参数!如果您不关心平均亮度,只想关注细节增强,那么将此参数设置为零。但是,如果您想保留一些环境亮度的信息,让一些低空间频率通过系统,并设置一个较高的值(<1)。
  • hcellsTemporalConstant:与光感受器类似,此参数作用于低通时域滤波器的时域常数,该滤波器用于平滑输入数据。在这里,高值会产生高度的视网膜后效,而较低的值会使视网膜反应更快。此值应低于 photoreceptorsTemporalConstant,以限制强烈的视网膜后效。
  • hcellsSpatialConstant:这是这些细胞滤波器低通滤波器的空间常数。它指定了后续过程中允许的最低空间频率。在视觉上,高值会导致非常低的(空间)频率处理,并产生明显的晕影效果。较低的值会减弱这种效果,但其限制是不能低于 photoreceptorsSpatialConstant 的值。这两个参数实际指定了视网膜的空间带通。

注意:一旦由前述参数处理完成,输入数据就会从噪声中清理干净,并且亮度已经部分增强。以下参数作用于两个输出视网膜信号的最后处理阶段。

小视网膜(细节通道)专用参数

  • ganglionCellsSensitivity:指定此细节专用通道输出的最终局部自适应强度。参数值保持在 0 和 1 之间。低值倾向于给出线性响应,而高值则增强剩余的低对比度区域。

注意:此参数可以通过偏好视觉场景中能量较低的细节来纠正潜在的烧毁图像,即使在明亮区域也是如此。

内网格层大视网膜(运动/瞬态通道)参数

在图像信息被清理后,该通道充当高通时域滤波器,仅选择与瞬态信号(事件、运动等)相关的信号。低通空间滤波器平滑提取的瞬态数据,而最终的对数压缩则增强低瞬态事件,从而增强事件灵敏度。

  • parasolCells_beta:通常设置为零,可以被视为该处理阶段入口点的放大器增益。通常设置为 0。
  • parasolCells_tau:可以添加的时域平滑效果。
  • parasolCells_k:空间滤波效果的空间常数,将其设置为一个高值以偏好低空间频率信号,这些信号受剩余噪声的影响较小。
  • amacrinCellsTemporalCutFrequency:指定高通滤波器的时域常数。高值允许选择慢速瞬态事件。
  • V0CompressionParameter:指定对数压缩的强度。行为与之前的描述类似,但在此处增强了瞬态事件的灵敏度。
  • localAdaptintegration_tau:通常设置为 0,在此处没有实际用途。
  • localAdaptintegration_k:指定进行局部自适应的区域大小。低值导致短程局部自适应(对噪声的灵敏度更高),高值保证对数压缩。