OpenCV 4.12.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 文件的 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)
从文件加载图像。

无论您使用什么方法来处理图像,您都应该得到类似的结果

阿德尔森棋盘的细小输出

分析结果

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

.. 事实上就是这样!

乍一看,观察结果图像可能无法告诉我们太多:在我们看来,“B”方块比“A”方块更亮,就像在输入图像中一样。不同之处在于,与输入图像相反,现在像素的 RGB 值实际上更亮;请注意,在查看输出图像时,我们实际上应用了两次细小细胞过程:第一次在仿生模块中,第二次在我们的眼睛中。我们可以通过使用图像处理程序和取色器工具测量正方形的亮度,或者通过裁剪正方形的一部分并将它们并排放置,从而说服自己错觉出现在计算出的图像中。

在下面的图像中,我裁剪了“A”方块的一部分和“B”方块的一部分,并将它们并排放置,就像我对原始阿德尔森图像所做的那样。

重现错觉

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

致谢

我要感谢

Alexandre Benoit - 非常友善地向我解释了整个过程的工作原理,给了我撰写本教程的机会,并对其进行了审核。

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

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