OpenCV  4.10.0
开源计算机视觉
正在加载...
正在搜索...
无匹配项
处理导致产生光学错觉的图像

目标

我将在这里展示生物仿生模块如何再现我们眼睛在特定光照条件下看到的一种众所周知的错觉:阿德尔森棋盘。

阿德尔森棋盘

观察下方的棋盘图像时,人眼认为“B”方块比“A”方块更亮,尽管它们以完全相同的 RGB 颜色呈现。当然,在物理世界中,棋盘的“B”方块确实比“A”方块更亮,但在这张图片中,绿色圆柱体投射到“B”方块上的阴影最终让“A”和“B”方块实际上具有相同的亮度。

阿德尔森棋盘

我们的视觉系统会“补偿”阴影,让我们觉得“B”方块更亮,仿佛阴影不存在一样。这是由于在中心凹区域进行的局部适应过程造成的。

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

证明:您可以使用图像处理程序,裁剪两格的部分,然后在没有任何背景的情况下观察它们来让自己信服。您还可以使用拾色器工具测量两个方块的 RGB 值。

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

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

中心凹视觉区域在一米处大约有一英寸(而且由于您的眼睛会持续移动,并且所谓“扫视”,所以您的大脑能够实时重建整个彩色场景)。这意味着任一字母 A 或 B 任何时候都可能击中您的凹点。

重点在于,即使您无法在单次眼睛凝视中同时看到两个字母,但当观察一个字母时,您的凹点也会考虑周围的光线信息。这意味着凹点实际上还会感知相邻的单元格。

净效果是,当观察一个区域时,你的眼睛能根据亮度进行局部适应,过滤噪音,加强轮廓等,并考虑该区域周围的情况,这能产生错觉。我们称之为视网膜“中央环绕”运作方式

因此,“A”细胞被更亮的细胞围绕起来会感觉更暗。作为比较,“B”细胞的周围区域更暗,那时会觉得“B”细胞更亮。

最后,由于阴影边缘是柔和的,视网膜便消除了这一信息。然后,阴影就不会扰乱整体棋盘观察,使得“自信地受骗”于感觉到的细胞亮度。

复现错觉

生物类比模块也模仿了小细胞视网膜过程,也就是中央视觉,它能复现我们眼睛的局部适应。

这意味着,我们可以预期小细胞通道输出真正包含类似于我们用眼睛感知到的亮度值。具体而言,在这种情况下,我们预期“B”方块 RGB 值实际上会比“A”方块更亮。

要正确模仿眼睛的动作,我们需要 opencv 在图像的右侧进行局部适应。这意味着我们必须保证 opencv 对“局部”的概念与我们图像的维度相匹配,否则局部适应将不会按预期工作。

因此,我们可能需要调整 hcellsSpatialConstant 参数(从技术上讲,它指定了低空间截止频率或对缓慢亮度变化的灵敏度),具体取决于图像分辨率。

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

为了将图像馈送到生物类比模块,你可以使用你自己的代码或生物类比模块提供的example_bioinspired_retinaDemo 示例。

运行

example_bioinspired_retinaDemo -image checkershadow_illusion4med.jpg

将导致我们的图像同时在小细胞和大细胞通道中被处理(我们只对前者感兴趣)。

如果你选择使用自己的代码,请注意小细胞(和大细胞)通道需要多次迭代(要处理的帧)才能稳定下来。

事实上,小细胞(和大细胞)通道确实关心时间信息。也就是说,当你开始馈送帧时,它类似于你闭上眼睛;然后你睁开眼睛,看到了棋盘。

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

在这个短暂的状态中,亮度信息确实很重要,你会或多或少地看到绝对亮度值。绝对亮度正是为了复现错觉而需要关注的东西。

只要稳定状态达到,您就能接收更多背景亮度信息。您的眼睛采用中央-环绕的工作方式,考虑到邻近亮度来评估感兴区域亮度等级。这就是我们的错觉产生的时间!

当处理视频时,这是您不必担心的事情,因为您在自然向虚拟视网膜输入多帧,但是要处理单帧,您必须留心这一点。

实际上,在处理单帧时您需要执行的,只需要稳定状态响应,就是用相同帧重复馈送视网膜(这是示例代码执行的操作),就像处理静止视频那样。或者,您可以将视网膜时间参数设置为 0,以立即获得稳定状态(xml 文件的 photoreceptorsTemporalConstant 和 hcellsTemporalConstant 参数);然而在这种情况下,您应该意识到您现在正在进行实验,这个实验有意降低准确性来再现真实视网膜的行为!

这里有一小段我们用于处理图像的 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)
从文件中加载图像。

无论使用哪种方法处理图像,最终都会得到类似的结果

Adelson 棋盘格的 Parvo 输出

分析结果

我们期望 parvo 通道输出中的“B”像素比“A”像素更亮。

.. 事实上,就是如此!

乍一看,查看结果的图像可能不会告诉我们太多:对我们的眼睛而言,“B”方块看起来比“A”亮,就像在输入图像中一样。不同之处在于,与输入图像相反,现在像素的 RGB 值实际上更亮;请注意,在查看输出图像时,我们实际上应用了 parvocellular 过程两次:首先在仿生模块中,然后在我们的眼睛中。我们可以通过使用图像处理程序和拾取工具来测量方块的亮度,或通过裁剪方块并将其并排放置来确信计算出的图像中出现了错觉。

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

错觉重现

显而易见的是,“B”方块实际上比“A”方块更亮!恭喜:你刚刚使用仿生模块重新制作了 Adelson 错觉!

鸣谢

我要感谢

Alexandre Benoit - 感谢他如此友善地向我解释整个工作原理,让我有机会编写本教程,并对本教程进行审阅。

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

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