OpenCV  4.10.0
开源计算机视觉
正在加载...
正在搜索...
无匹配项
视网膜和真实世界视觉

目标

我在此提供一个人类视网膜模型,该模型显示了一些图像预处理和增强方面的有趣特性。在本教程中,你将学习如何

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

概述

提出的模型源自 Jeanny Herault 在Gipsa进行的研究[136]。它与Listic(代码维护者和用户)实验室一起参与图像处理应用。这不是一个完整的模型,但它已经显示出一些有趣的特性,可以参与到增强的图像处理体验中。该模型允许使用以下人类视网膜特性

  • 具有 3 个重要效果的光谱白化:高时空频率信号消除(噪声)、中频细节增强和低频亮度能量降低。这种“多合一”特性直接允许视觉信号消除图像传感器和输入亮度范围引入的传统不希望的失真。
  • 局部对数亮度压缩允许在弱光条件下增强细节。
  • 细节信息(小细胞输出通道)和瞬态信息(Magnocellular 输出通道提供的事件、动作)的去相关。

以下说明了前两点

在下图中,显示了 OpenEXR 图像示例CrissyField.exr,这是一个高动态范围图像。为了使其在此网页上可见,将原始输入图像线性重新缩放为经典图像亮度范围 [0-255],并转换为 8bit/通道格式。如此强大的转换隐藏了许多细节,因为局部对比度太强。此外,噪声能量也很强,污染了视觉信息。

图片

在下图中,应用了在 [24] 中提出的理念,就像你的视网膜所做的那样,局部亮度适应、空间噪声去除和光谱漂白共同发挥作用,并在较低范围的 8bit 数据通道上传输准确的信息。在这张图片上,噪声被明显去除,被强烈的亮度对比所掩盖的局部细节得到增强。输出图像保持其自然度,视觉内容得到增强。颜色处理基于在 [67] 中提出的颜色复用/解复用方法。

图片

注意:图像样本可以从OpenEXR 网站下载。关于此演示,在视网膜处理之前,输入图像在 0-255 内线性重新缩放,同时保持其通道为浮点格式。其直方图的 5% 结尾已被剪切(主要去除错误的 HDR 像素)。查看示例 opencv/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp 以进行类似处理。以下演示仅考虑经典的 8bit/通道图像。

视网膜模型输出通道

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

  • 第一个称为小细胞通道。它主要活跃在中央视网膜区域(具有对颜色敏感的光感受器的清晰中央视觉),其目的是为视网膜上保持静止的视觉细节提供准确的色彩视觉。另一方面,视网膜投射上移动的对象是模糊的。
  • 第二个众所周知的通道是大细胞通道。它主要活跃在视网膜周边视觉中,并发送与变化事件(运动、瞬态事件等)有关的信号。这些输出信号还有助于视觉系统对“瞬态”/移动区域进行聚焦/集中注意力,以便进行更详细的分析,从而改善视觉场景上下文和对象分类。

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

作为示例,我们接下来对一个黑暗视觉场景的网络摄像头视频流应用视网膜模型。在这个视觉场景中,在大学的一个圆形剧场中捕捉到,一些学生在与老师交谈时走动。

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

图片

以下显示应用在整幅图像上的视网膜中央凹视觉。在所使用的视网膜配置中,总体亮度得以保留,局部对比度得以增强。此外,信噪比也得到改善:由于已减少高频时空间噪声,因此增强后的细节不会因任何增强的噪声而损坏。

图片

以下为视网膜模型的巨细胞层输出。其信号在瞬态事件发生时比较强。此处有一名学生正在图像底部移动,因而产生高能量。图像的其余部分是静态的,然而它因强噪音而损坏。在此,视网膜滤出大部分噪音,因此产生低误报运动区域“警报”。此通道可用作瞬态/移动区域探测器:它将为低成本分割工具提供相关信息,该工具将突出显示正在发生事件的区域。

图片

视网膜用例

此模型基本上可以用于时空间视频效果,但也可以用于

  • 执行纹理分析,其中信噪比增强并且细节增强,针对输入图像亮度范围有抵抗力(检查视网膜细胞层通道输出)
  • 执行运动分析,同时利用先前引用的属性。

参考资料

如需了解更多信息,请参考以下论文:[24]

  • 请查阅 Jeanny Herault 的参考著作,您可以在其著作 [136] 中阅读该著作。

此视网膜滤镜代码包括博士/研究同事的研究成果,代码由作者重新编写

  • 查看 retinacolor.hpp 模块,了解 Brice Chaix de Lavarene 博士的彩色镶嵌/反镶嵌以及他的参考论文 [67]
  • 查看 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 and friends objects management)*、opencv_highgui *(display and image/video read)* 和 opencv_bioinspired *(Retina description)* 库来编译。

// 编译
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
// 在图片上运行,带 log 采样
./Retina_tuto -image myPicture.jpg log

下面是代码说明

视网膜定义存在于 bioinspired 包中,通过简单的 include 可以使用它。你也可以使用特定头文件:opencv2/bioinspired.hpp,如果你更喜欢则可以 include 其他必需的 opencv 模块: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:812
用于从视频文件、图像序列或相机捕捉视频的类。
定义 videoio.hpp:731
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3

在执行处理之前,先检查输入命令参数。此处将从单个已加载图像(如果用户选择了命令 -image)或从视频流(如果用户选择了命令 -video)加载第一幅输入图像。此外,如果用户在其程序调用的末尾添加了 log 命令,则布尔标志 useLogSampling 会考虑视网膜执行的空间对数图像采样。

// 欢迎消息
std::cout<<"****************************************************"<<std::endl;
std::cout<<"* 视网膜演示:演示了 Gipsa/Listic 实验室视网膜模型的包装器类的用法。"<<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 视频流以便进行视频捕获。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR)
从文件加载图像。

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

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

现在,一切都已准备好运行视网膜模型。我建议在此处分配一个视网膜实例并管理最终对数采样选项。视网膜构造函数至少需要一个cv::Size对象来显示要管理的输入数据大小。可以激活其他选项,如颜色及其相关的颜色复用策略(此处使用枚举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// -> else allocate "classical" retina :
myRetina = cv::bioinspired::createRetina(inputFrame.size());
MatSize size
定义 mat.hpp:2160
@ RETINA_COLOR_BAYER
标准 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("retina input", inputFrame);
cv::imshow("Retina Parvo", retinaOutput_parvo);
cv::imshow("Retina Magno", 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<<"Error using Retina : "<<e.what()<<std::endl;
}
传递给 error 的类。
定义 core.hpp:115
virtual const char * what() const CV_OVERRIDE

视网膜参数,该做什么?

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

完成后打开演示生成的配置文件 *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) 的更特殊情况外,视网膜行为对于不同内容应当相对稳定。请注意,借助与 OpenEXR 图像的兼容性,OpenCV 能够管理此类 HDR 格式。

然后,如果应用程序目标需要在具体图像处理之前进行细节增强,则需要知道是否需要平均亮度信息。如果没有,则视网膜可以取消或显著减少其能量,从而使较高空间频率细节更易于识别。

基本参数

最简单的参数如下

  • colorMode:让视网膜处理彩色信息(如果为 1)或灰度图像(如果为 0)。在后一种情况下,将只处理输入的第一个通道。
  • normalizeOutput:每个通道都有这样的参数:如果将该值设为 1,则视为通道的输出在 0 到 255 之间重新缩放。在这种情况下,注意大细胞输出等级(运动/瞬态通道检测)。残留噪声也将被重新缩放!

注意:使用颜色需要颜色通道的多路复用/解复用,这也要求进行更多处理。你可以期待使用灰度级进行更快的处理:对于视网膜的所有处理,它大约需要每个像素 30 个产品,并且最近已针对多核架构进行了并行化处理。

光感受器参数

下列参数作用于视网膜的入口点 - 光感受器 - 并且对所有下列过程产生影响。这些传感器是低通时空滤波器,可以平滑时间和空间数据,并且还可以调节其对局部亮度的敏感性,从而改善细节提取和高频噪声消除。

  • photoreceptorsLocalAdaptationSensitivity介于 0 到 1 之间。接近 1 的值允许在光感受器级别进行高亮度对数压缩的效果。更接近 0 的值提供更线性的灵敏度。单独增加它可以烧毁视锥(细节通道)输出图像。如果与 ganglionCellsSensitivity 进行协同调整,则无论局部亮度如何,图像都可以形成强烈反差...但自然度会降低。
  • photoreceptorsTemporalConstant 这设置了视网膜入口处的低通滤波效应的时间常数。高值导致较强的时态平滑效应:移动的物体变得模糊并可能消失,而静止的物体则受到青睐。但在开始视网膜处理时,后面才达到稳定的状态。
  • photoreceptorsSpatialConstant指定与光感受器的低通滤波器效应相关的空间常数。这些参数指定了下文中允许的最小空间信号周期值。通常,此滤波器应切断高频噪声。另一方面,0 值不切断任何噪声,而较高值开始切断高空间频率,并且逐渐降低频率...如果你想看到输入图像的某些细节,请注意不要达到高电平!对于彩色图像,一个很好的折衷方案是 0.53 的值,因为这样的选择不会太多地影响色谱。较高值会导致灰度和模糊的输出图像。

水平细胞参数

此参数集对连接到感光细胞的水平细胞神经网络进行调整。它调节感光细胞敏感性并完成最终光谱白化处理(空间频带通滤效应的一部分,从而有利于视觉细节增强)。

  • horizontalCellsGain 这里是一个关键参数!如果您不关注平均亮度并且只想集中于细节增强,那么将此参数设置为零。但是,如果您要保留一些环境亮度数据,请让一些较低的 spatial 频率进入系统并设置较高的值(<1)。
  • hcellsTemporalConstant 类似于感光细胞,此参数作用于低通时域滤波器的时域常数,该滤波器平滑输入数据。在此,高值会产生高视网膜后效,而较低的值则使视网膜更具反应性。此值应低于 **photoreceptorsTemporalConstant** 以限制强视网膜后效。
  • hcellsSpatialConstant 是这些细胞滤波器低通滤波器的 spatial 常数。它指定以下允许的最低 spatial 频率。在视觉上,高值会导致非常低的 spatial 频率处理,并导致明显的晕圈效应。较低的值会降低此效果,但不能低于 photoreceptorsSpatialConstant** 的值。这两个参数实际上指定了视网膜的 spatial 带通。

注意 一旦完成由前面参数管理的处理,输入数据将从噪声中清除,并且亮度已经得到部分增强。以下参数用于两个输出视网膜信号的最后处理阶段。

专用小细胞(细节通道)参数

  • ganglionCellsSensitivity 指定此细节专用通道输出端发生的最终局部适应的强度。参数值介于 0 和 1 之间。低值倾向于给出线性响应,而高值会加强剩余的低对比度区域。

注意:此参数可以通过提高视觉场景中低能量细节(即使在明亮区域)来纠正最终的图像灼烧。

IPL 大细胞(运动/瞬态通道)参数

一旦图像信息被清除,此通道将充当高通时域滤波器,仅选择与瞬态信号(事件、运动等)相关的信号。低通 spatial 滤波器平滑提取的瞬态数据,而最终的 logarithmic 压缩增强低瞬态事件,从而增强事件敏感性。

  • parasolCells_beta 通常设置为零,可以被视为此处理阶段入口点的放大器增益。通常设置为 0。
  • parasolCells_tau 可以添加的时间平滑效果
  • parasolCells_k 空间滤波效果的空间常数,将其设置为高值以支持低空间频率信号,低空间频率信号的残留噪声较低。
  • amacrinCellsTemporalCutFrequency 指定高通滤波器的时域常数。高值可让缓慢的瞬态事件被选中。
  • V0CompressionParameter 指定 log 压缩的强度。类似于先前的说明中的行为,但此处强制执行瞬态事件的敏感性。
  • localAdaptintegration_tau 一般设为 0,实际上在此处没有真正用处。
  • localAdaptintegration_k 指定执行局部适应的区域大小。低值导致短程局部适应(对噪声更加敏感),高值保护 log 压缩。