OpenCV 4.11.0
开源计算机视觉
加载中…
搜索中…
无匹配项
处理产生光学错觉的图像

目标

我将在此展示仿生模块如何重现一种众所周知的视觉错觉,这种错觉在特定光照条件下会被人眼感知到:阿德尔森棋盘格。

阿德尔森棋盘格

观察下面的棋盘格图像,人眼会感知到“B”方块比“A”方块亮,尽管它们实际上具有相同的RGB颜色。当然,在物理世界中,“B”方块比“A”方块亮,但在该图像中,绿色圆柱体的阴影投射到“B”方块上,最终导致“A”和“B”方块的亮度实际上相同。

阿德尔森棋盘格

我们的视觉系统会对阴影进行“补偿”,使我们感知到“B”方块更亮,就好像没有阴影一样。这是由于在中央凹区域进行的局部适应过程造成的。

您可以在此处找到阿德尔森的原始解释。

证明:您可以使用图像处理程序剪切这两个方块的一部分,然后在没有任何背景的情况下观察它们来验证这一点。您也可以使用取色工具测量这两个方块的RGB值。

在这张图片中,我已经剪裁了A和B方块的一小部分,并将它们并排放置。很明显,它们的亮度相同。

值得注意的是,这种错觉之所以有效,是因为棋盘格图像(正如您在笔记本电脑上看到的那样)投射到您的视网膜上,其尺寸会导致视网膜局部适应同时考虑这两个方块。

中央凹视觉区域大约是一米距离上一英寸大小(并且由于您的眼睛不断移动,即所谓的“扫视”,您的大脑能够实时重建整个色彩场景)。这意味着“A”或“B”中的任何一个字母都可能随时落在您的中央凹上。

关键在于,即使您无法在一次注视中同时看到两个字母,当您注视一个字母时,您的中央凹也会考虑到周围的光线信息。这意味着中央凹实际上也感知到了相邻的细胞。

最终效果是,当注视某个区域时,您的眼睛会根据该区域的周围环境进行局部亮度适应,过滤噪声,增强轮廓等。这就是错觉产生的原因。我们说视网膜以“中心-周边”的方式工作

因此,“A”单元被较亮的单元包围,因此看起来更暗。相比之下,“B”单元的周围环境较暗,“B”单元看起来就更亮。

最后,由于阴影边缘比较柔和,视网膜会消除这些信息。因此,阴影不会破坏整体棋盘的观察,从而使我们能够“自信地被”感知到的单元亮度所愚弄。

重现错觉

仿生模块也模拟了视网膜的视细部细胞通路过程,即我们的中央凹视觉,并且它重现了我们眼睛的局部适应能力。

这意味着我们可以预期视细部细胞通路输出将包含与我们用眼睛感知到的亮度值相似的值。具体来说,在这种情况下,我们预期“B”方块的RGB值实际上比“A”方块的RGB值更亮。

为了正确模拟我们眼睛所做的工作,我们需要OpenCV对图像的正确部分进行局部适应。这意味着我们必须确保OpenCV的“局部”概念与我们图像的尺寸相匹配,否则局部适应将无法按预期工作。

因此,我们可能需要根据图像分辨率调整hcellsSpatialConstant参数(从技术上讲,它指定低空间截止频率或慢速亮度变化灵敏度)。

对于本教程中的图像,默认的视网膜参数应该没问题。

为了将图像馈送到仿生模块,您可以使用您自己的代码或仿生模块附带的example_bioinspired_retinaDemo示例。

运行

example_bioinspired_retinaDemo -image checkershadow_illusion4med.jpg

将导致我们的图像在视细部细胞通路和视广阔细胞通路中进行处理(我们只对第一个感兴趣)。

如果您选择使用您自己的代码,请注意,视细部细胞通路(和视广阔细胞通路)需要一些迭代(要处理的帧)才能真正达到稳定状态。

实际上,视细部细胞通路(和视广阔细胞通路)关心的是时间信息。也就是说,当您开始馈送帧时,这类似于您闭上眼睛;然后您睁开眼睛,看到棋盘。

这是一张静态图像,但您的视网膜刚刚开始转移到新的环境(睁开眼睛),并且必须适应。

在这个瞬态状态下,亮度信息很重要,您或多或少地看到绝对亮度值。绝对亮度正是您为了重现错觉而不需要查看的内容。

一旦达到稳定状态,您就会接收到更多上下文亮度信息。您的眼睛以中心-周边的方式工作,并考虑邻域亮度来评估感兴趣区域的亮度水平。这就是我们的错觉产生的原因!

在处理视频时,您不需要担心这个问题,因为您自然会向虚拟视网膜馈送多个帧,但是您必须注意才能处理单个帧。

当处理单个帧时,您实际上只需要稳定状态响应,需要重复地向视网膜馈送相同的帧(这就是示例代码所做的),就像处理静态视频一样。或者,您可以将视网膜时间参数设置为0以立即获得稳定状态(xml 文件的photoreceptorsTemporalConstanthcellsTemporalConstant参数);但是,在这种情况下,您应该意识到您现在正在进行实验,这种实验在重现真实视网膜的行为方面故意不太准确!

这里有一小段我们用来处理图像的python代码片段。它进行了20次迭代。这是一个我们通过实验发现的(超过)足够的任意数字。

import cv2 as cv
inputImage = cv.imread('checkershadow_illusion4med.jpg', 1)
retina = cv.bioinspired.createRetina((inputImage.shape[1], inputImage.shape[0]))
# 使用默认参数创建视网膜对象。如果要从外部XML文件读取
# 参数,请取消注释下一行
#retina.setup('MyRetinaParameters.xml')
# 馈送视网膜多个帧,以达到“稳定”状态
for i in range(20)
retina.run(inputImage)
# 获取我们处理过的图像 :)
retinaOut_parvo = retina.getParvo()
# 显示原始图像和处理后的图像
cv.imshow('image', inputImage)
cv.imshow('retina parvo out', retinaOut_parvo)
# 等待按键并退出
# 将输出图像写入文件
cv.imwrite('checkershadow_parvo.png', retinaOut_parvo)
void imshow(const String &winname, InputArray mat)
在指定的窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void destroyAllWindows()
销毁所有HighGUI窗口。
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > &params=std::vector< int >())
将图像保存到指定文件。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件中加载图像。

无论你使用何种方法处理图像,最终结果都应该类似于:

Adelson棋盘的Parvo输出

分析结果

我们预期Parvo通道输出中“B”像素比“A”像素更亮。

事实的确如此!

乍一看结果图像可能不会给我们太多信息:“B”方块看起来比“A”方块更亮,就像输入图像一样。不同之处在于,与输入图像相反,现在像素的RGB值实际上更亮了;需要注意的是,在查看输出图像时,我们实际上应用了两次parvocellular过程:第一次在仿生模块中,第二次在我们的眼睛里。我们可以通过使用图像处理程序和拾色器工具测量方块的亮度,或者剪切方块的一部分并将它们并排放置,来说服自己这种错觉出现在计算图像中。

在下图中,我剪切了“A”方块的一部分和“B”方块的一部分,并将它们并排放置,就像我处理原始Adelson图像一样。

错觉再现

应该很明显,“B”方块确实比“A”方块更亮!恭喜你:你刚刚使用仿生模块重现了Adelson错觉!

致谢

我要感谢

Alexandre Benoit - 感谢他如此耐心地向我解释整个过程,感谢他给我撰写本教程的机会,以及感谢他的审阅。

Edward Adelson - 感谢他允许我自由使用他的棋盘图像。

Antonio Cuni - 感谢他审阅本教程并编写Python代码。