本文包含以下内容
1.简单介绍人眼彩色视觉的基本原理
2.介绍如何利用算法模拟色觉障碍用户所看到的画面
3.基于色觉障碍模拟算法,介绍几种色觉障碍辅助方法,这些方法可以帮助色觉障碍用户更好地分辨本来难以分清的颜色
4.介绍如何在Android播放器中集成色觉障碍辅助功能以及我们提供的示例库
所谓色觉辅助功能,就是帮助色盲、色弱人群更好的观看视频。比如下面的画面,是红绿两队在进行足球比赛
这里写图片描述
那么一位患有红绿色盲的用户看到的画面可能是下面这样的
这里写图片描述
可以看到,由于患有红绿色盲,这位用户将难以区分出比赛中的红绿两队,这势必会影响用户的观看体验。所谓色觉辅助功能,就是帮助这样的特殊用户更好地观看视频,经过色觉辅助优化,我们可以做到让红绿色盲看到的画面变成下面的样子
这里写图片描述
可以看到,此时患有红绿色盲的用户也可以很好地分辨出比赛中的两支队伍了。需要注意的是,因为色盲与色弱的根本成因是生物染色体层面上的,所以不管我们如何做优化,都不可能让一名红色盲用户重新看到红色,而只能在最大程度上帮助他恢复出他所看不到的画面信息。
色觉障碍辅助功能并不是我们最先提出与实现的。从Android L开始,在原生Android系统的“无障碍”选项中就提供了色彩校正的功能,可以让设备更适合色盲用户使用,不过国内手机ROM一般都没有开放这个功能;三星则推出了SeeColors应用,它可以测试出用户的色弱类型和等级,基于测试结果调整三星电视机的色彩设置,让色盲人士也能体验到丰富的色彩;腾讯视频也在安卓端全面上线了色彩辅助功能,帮助色盲用户在不破坏观看体验的同时,保持场景自然色彩、提升场景分辨能力并提高观看舒适度;一些游戏,如《战地1》,也提供了色盲模式供玩家选择。
在本系列文章中,我们将结合理论,算法与实践,介绍如何从Android视频播放器的角度,开发色觉辅助功能,最终实现多达十个档次的色弱与色盲辅助优化。希望能够抛砖引玉,为更多有兴趣开发此功能的团队提供一个思路,也希望能够多多交流,指出我们可能存在的错误与不足。
一、从人眼彩色视觉说起
我们知道,不同波长的可见光具有不同的颜色,如下图所示,短波长的光呈现为蓝色,长波长的光则呈现出红色。
对应到人眼中,有3种视锥细胞,它们具有不同的光谱敏感度(Spectral sensitivity),造就了人眼的三色视觉(Trichromacy)。一般来说,这三种视锥细胞按照它们的光谱敏感度峰值波长的顺序,分别被称为:短(S)、中(M)、和长 (L)的视锥细胞类型。S、M和L类别视锥细胞对单色光谱刺激的归一化响应光谱如下图所示
这里写图片描述
大致可以看出,L细胞对红色光更敏感,M细胞对绿色光更敏感,S细胞则对蓝色光更敏感。更具体地,这三种细胞的响应波长范围和峰值响应范围如下表所示
这里写图片描述
而色盲与色弱的成因就与这三种视锥细胞的缺失或功能缺陷有关。比如,L视锥细胞的缺失就会导致红色盲,而M视锥细胞功能的缺陷就会导致绿色弱。前面我们看到,L锥细胞和M锥细胞的敏感波段有所重叠,所以红色盲,绿色盲的症状相似,一般统称为红绿色盲。
据统计,全球约6%人口为色弱,约2%人口色盲,极少数为单色视觉(全色盲)。红绿色盲人口占全球男性人口约8%,女性人口约0.5%(因为红绿色盲是X染色体隐性遗传病)。蓝色盲患者则非常少见,一般认为是后天所得。
石原氏色盲检测图是常用于判断是否患有色盲与色弱的工具,考过驾照的朋友应该都不陌生,比如下图,从左到右,从上到下依次是数字8,29,5,3,15,74,26,42。
这里写图片描述
而红色盲患者看到的画面可能是下面这样的,可以看到,很多数字都看不清了,或者会看成别的数字
这里写图片描述
绿色盲患者看到的画面则可能是下面这样的,一样有很多数字看不清或看错,但是与红色盲患者看到的又有所不同。
这里写图片描述
再举一个有意思的例子,炒股的朋友都很熟悉的K线图,如下
这里写图片描述
那么一名红绿色盲患者所看到的K线图则可能是下面这样的,体验可以说是非常不好了。
这里写图片描述
二、算法模拟色觉障碍用户所看到的画面
研究色觉辅助算法的第一步是尝试用算法模拟色觉障碍用户所看到的画面。
色盲与色弱模拟的总体流程如下图所示
这里写图片描述
图中涉及到两个色彩空间,一个是我们熟悉的RGB色彩空间,一个是LMS色彩空间。
本文中会涉及到的各种色彩空间
RGB色彩空间:将红绿蓝三个通道作为笛卡尔坐标系中的x,y,z轴,所得到的对颜色的空间描述。
XYZ色彩空间:X,Y,Z是假想出的三原色,自然界中并不存在,而只是由RGB经过线性变换后得到的。因为基于RGB模型绘制的色度图存在着负区间,这使得计算和转换都不方便,XYZ空间就是为了让所有计算和转换都在正数区间而设计的。
LMS色彩空间:根据三种视锥细胞的刺激比例来描述各种颜色。
YUV/YCbCr色彩空间:做多媒体的朋友应该都很熟悉了,Y代表亮度,UV代表色度。
Lab色彩空间:Lab色彩空间是基于XYZ色彩空间得出的,比XYZ空间更接近人眼的感知,其中L为亮度;a的正数代表红色,负端代表绿色;b的正数代表黄色,负端代表蓝色。
RGB空间和LMS空间的相互转换都有现成的公式,即图中的U及其逆矩阵都是已知的。前面提到色盲与色弱的生理成因都与LMS三种细胞的缺失或缺陷有关,相应的,色盲与色弱模拟的关键就在于找出正常LMS空间到异常L'M'S'空间之间的转换矩阵T。
色盲模拟
这里参考以下两篇经典论文介绍色盲模拟的基本思路
- Brettel H, Viénot F, Mollon J D. Computerized simulation of color appearance for dichromats[J]. JOSA A, 1997, 14(10): 2647-2655.
- Viénot F, Brettel H, Mollon J D. Digital video colourmaps for checking the legibility of displays by dichromats[J]. Color Research & Application: Endorsed by Inter‐Society Color Council, The Colour Group (Great Britain), Canadian Society for Color, Color Science Association of Japan, Dutch Society for the Study of Color, The Swedish Colour Centre Foundation, Colour Society of Australia, Centre Français de la Couleur, 1999, 24(4): 243-252.
已知正常视觉人群能看到的颜色在LMS三维空间中,因为色盲的成因是某一种视锥细胞的缺失,所以色盲人群能看到的颜色应该在LMS三维空间中的一个二维平面上,具体到红绿蓝三种色盲,就是分别把RGB空间的颜色沿着L、M、S三个方向投射在不同的平面上,那么问题的关键是如何找到这个二维平面。
以红绿色盲的模拟为例,有下图
这里写图片描述
图中大的立方体是LMS色彩空间,小的立方体可以看做是RGB颜色在LMS色彩空间中的表示。
图中有三条直线(三个向量),它们的意义是:OE代表的是灰度颜色,即使是色盲人群也可以正常分辨,所以它一定在色盲人群所能看到的颜色面上;图中475nm这条线对应的是偏蓝色的光,实验发现红绿色盲患者可以正确的分辨这一颜色,所以这条线一定在红绿色盲人群所能观察到的颜色平面上;图中575nm这条线对应的是偏黄色的光,实验发现红绿色盲患者也可以正确的分辨这一颜色,所以这条线也一定在红绿色盲所能观察到的颜色平面上。
两条相交直线可以确定一个平面。现在我们得到了三条相交直线,即红绿色盲人群观察到的颜色集中在两个颜色平面上,分别是图中的深灰色平面和浅灰色平面。
那么对于某一颜色Q,红色盲看到的颜色相当于Q点沿L方向投射到对应的平面上,即图中Q’p点。绿色盲看到的颜色相当于Q点沿M方向投射到对应的平面上,即图中Q’d点。求Q和Q’之间的关系,也就得到了我们需要的T矩阵。
来回忆一点数学基础知识
1.向量的点乘: ,结果得到一个标量
2.向量的叉乘:
结果得到一个向量,这个向量与原来两个向量都垂直
3.如果是两向量点乘为0,则两向量垂直; 如果是两向量叉乘为0,则两向量平行
先来考虑深灰色平面,设前面图中提到的OE直线对应的向量为E,475nm可见光对应的向量为A,OQ'p直线对应的向量为Q',则得到的是深灰色平面的法向量,它与Q’垂直,即
即
其中
以红色盲模拟为例,对于已知的Q和未知的Q'p点,有和
则
因为E和A都是已知量,所以上面a,b,c都是已知量。由此就得到了Q和Q'之间的关系。
但是事情还是有点复杂,尤其是要考虑两个平面,能不能更简单一点呢?
回看上面的图,可以发现深灰色和浅灰色的两个平面的夹角很小,那么可以近似化简为一个平面。同时,475nm的单色光与蓝色光很接近,也可以直接近似为RGB空间中坐标(0, 0, 255)的蓝色光。此外,OE直线对应的灰度颜色可以直接近似为原点到RGB空间中坐标(255, 255, 255)的白色点所连接的直线。于是上面的图变成了下面这个样子,是不是简洁多了。
这里写图片描述
此时,红绿色盲所能看到的颜色面就是图中的KBWY平面,说白了就是把小立方体从对角切开所得到的R=G的颜色面。蓝色盲模拟的思路相同。
现在我们可以开始计算从LMS到L'M'S'转换的T矩阵了:
对于红色盲模拟,T=
对于绿色盲模拟,T=
对于蓝色盲模拟,T=
结合RGB空间与LMS空间之间的转换矩阵,最后得到从RGB到R'G'B'之间的色盲模拟转换矩阵如下
对于红色盲模拟,T'=
对于绿色盲模拟,T'=
对于蓝色盲模拟,T'=
观察上面的三个矩阵,会发现:对于红色盲模拟,相当于把RGB空间的颜色沿着L轴投射到R=G的颜色面;对于绿色盲模拟,相当于把RGB空间的颜色沿着M轴投射到R=G的颜色面;对于蓝色盲模拟,相当于把RGB空间的颜色沿着S轴投射到B=G的颜色面。
至此,我们就完成了色盲模拟的工作。
色弱模拟
在色弱模拟方面没有太多公认的经典论文,国内有高校论文提出可以直接基于色盲模拟矩阵加权计算得出色弱模拟矩阵,从数学上讲说得通,但是缺少人眼视觉认知的理论基础。在这里我们参考下面论文的理论介绍如何做色弱模拟
Machado G M, Oliveira M M, Fernandes L A F. A physiologically-based model for simulation of color vision deficiency[J]. IEEE Transactions on Visualization and Computer Graphics, 2009, 15(6): 1291-1298.
前面我们提到色弱的成因是视锥细胞功能的缺陷,那么这里的缺陷具体是什么?回看前面那张S、M和L类别视锥细胞对单色光谱刺激的归一化响应光谱图,我们可以定义视锥细胞功能缺陷为:视锥细胞对单色光谱刺激峰值响应的偏移。比如下图是红色弱人群的视锥细胞响应光谱图,可以看到L视锥细胞的响应曲线发生了偏移,和M细胞的响应曲线发生重叠,这就导致红色弱人群难以区分红色和绿色,偏移程度的不同即代表色弱严重程度的不同,如果偏移量到达了20nm,基本就和色盲的效果一样了。
若正常视觉的三条响应曲线可以表示为,,,λ为波长,则红色弱的响应曲线可以表示为
其中为偏移量。所以我们的关键在于求出各种下的,就得到了不同档次的色弱模拟方法,在0-20nm之间均匀取十档,就得到了十档色弱模拟方法。
先考虑极端情况,也就是的情况,此时和基本重合,直觉上应该有,但是我们仔细观察上面的图,会发现L锥细胞和M锥细胞的响应曲线并非完全重合,起码L锥细胞的曲线要“胖”一些,考虑到这一点,我们要对M锥细胞的响应曲线做一下拉伸才能得到此时的,如下
有了在极端色盲情况下的结果,那么各种档次色弱情况下的Lp(λ)也很好得出了,直接用最简单的线性加权,如下
其中
我们认为,到这一步,就和色盲模拟中的一样,得到了LMS到L'M'S'的转换方法,采用和色盲模拟一样的思路即可得到RGB到R'G'B'的转换,完成色弱模拟的工作。不过在参考论文中,基于人眼视觉认知的理论基础,以及实验数据,还做了一些额外的工作。首先是对上面的式子做了一点微调,加入一个常系数0.96,此时的为
其次参考论文中还考虑了颜色认知理论中的“阶段学说”,加入了一个从LMS空间到IPT空间的转换步骤,最终得出的结果更加精确。鉴于此,我们也直接采用了参考论文所给出的矩阵结果。
阶段学说认为颜色视觉认知过程可以分为两个阶段:第一阶段为视网膜阶段或者称为感光阶段,具有颜色叠加的性质,颜色视觉符合三色学说的规律,也就是我们前面一直说的LMS空间的事儿;第二阶段为视神经传输阶段,具有对立色的性质,所谓对立色的理论依据是颜色感觉总是以红-绿、黄-蓝、黑-白成对出现的视觉现象,同时形成了 Intensity-Protan-Tritan (IPT)颜色空间,I对应黑白,P对应红绿,T对应黄蓝。从LMS到IPT的转换矩阵是已知的。
验证模拟的结果
在上面我们分别介绍了色盲和色弱的模拟方法,那么如何验证模拟算法的准确性呢?如果使用石原氏色盲检测图来验证的话,无法得到定量的结果,而且也未必有足够的资源来找到各种患有不同程度色弱或色盲的被试来进行主观评价实验。
在这里,我们使用一款名为“Color Blind Check”的APP作为验证工具,该APP的测试界面如下图所示,被试需要从众多小方块中找出颜色不同的3x3方块区域。
在测试结束后,它可以显示如下的打分页面,图中Severity代表色盲或色弱的严重程度,分数越高越严重,PDT Score代表偏向红,绿,蓝三种色觉障碍类型中的哪一种以及偏向的程度,比如图中的0-0-3就代表轻微偏向蓝色觉障碍。
这里写图片描述
我们邀请了几位色觉障碍用户试用了该APP,通过与他们的交流和测试结果来看,该APP的打分结果可靠性很高。从应用商店的评价以及开发者网站的介绍来看也能佐证这一点。
为了验证前面提出的色盲色弱模拟矩阵的准确性,我们需要先将这些矩阵应用到Android SurfaceFlinger中,修改显示颜色,并且在该环境下利用验证APP进行测试,观察测试结果。
前面提到,从Android L开始,原生系统就支持色盲辅助功能,对应的代码实现在
/frameworks/native/services/surfaceflinger/Effects/Daltonizer.cpp
阅读代码即可发现其中包含了色盲模拟和色盲辅助的逻辑,其中色盲模拟所采用的算法和我们前面介绍的一致,而色盲辅助的部分我们在下一小节中再展开详细介绍。
把我们的色盲色弱模拟矩阵加进去之后如何生效呢?对应的代码实现在
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
case 1014: {
// daltonize
n = data.readInt32();
switch (n % 10) {
case 1: mDaltonizer.setType(Daltonizer::protanomaly); break;
case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break;
}
if (n >= 10) {
mDaltonizer.setMode(Daltonizer::correction);
} else {
mDaltonizer.setMode(Daltonizer::simulation);
}
mDaltonize = n > 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
}
也就是说,我们可以通过下面的命令让色盲模拟矩阵生效,扩展一下也可以很容易的让我们的色弱矩阵生效。
通过adb shell service call SurfaceFlinger 1014 i32 1进入红色盲模拟模式
通过adb shell service call SurfaceFlinger 1014 i32 2进入绿色盲模拟模式
通过adb shell service call SurfaceFlinger 1014 i32 3进入蓝色盲模拟模式
篇幅所限,这里贴上我们的部分验证结果。色弱级别取十级,对应level9 ~ level1,level9为严重色弱,level1为轻度色弱。结果如下表:
色觉障碍类型 | Severity | PDT Score |
---|---|---|
红色盲 | 100 | 10-0-0 |
红色弱level9 | 88 | 8-0-0 |
红色弱level8 | 63 | 10-0-0 |
红色弱level7 | 48 | 10-0-0 |
红色弱level6 | 38 | 3-0-0 |
红色弱level5 | 22 | 3-0-0 |
红色弱level4 | 10 | 3-2-2 |
红色弱level3 | 6 | 3-0-3 |
绿色盲 | 100 | 0-10-0 |
绿色弱level9 | 84 | 0-6-0 |
绿色弱level8 | 58 | 0-10-0 |
绿色弱level7 | 42 | 0-10-0 |
绿色弱level6 | 29 | 1-6-0 |
绿色弱level5 | 18 | 0-18-0 |
绿色弱level4 | 9 | 0-6-0 |
蓝色盲 | 100 | 0-0-10 |
从测试结果可以看到,我们采用的色盲与色弱模拟算法可以与人眼视觉达到较高的一致性,同时还可以看到,色弱等级低到level3左右时,色弱的症状就很轻微了。
三、色觉障碍辅助算法
色觉障碍辅助的基本思路如下图所示
这里写图片描述
拿到一张RGB原图后,利用前面计算出的色盲色弱模拟矩阵,可以计算出色觉障碍用户所看到画面R’G’B’,两者相减得到的就是色觉障碍用户所看不到的颜色,记为Error Picture, 简称为EP。
利用Transform矩阵对EP进行颜色转换,将色觉障碍用户看不到的信息映射到他们能看到的色域(颜色通道)中去,得到修正后的EP,记为Spreaded Error Picture,简称为SEP。
最后将SEP叠加到RGB原图上面去,得到优化后的画面。当色觉障碍用户观看这样的画面时,就可以分辨出他们本来无法看到的颜色信息了。
需要明确的一点是:如前所述,色觉障碍是生理原因导致的,所以我们不可能让一个红色盲患者重新看到红色。我们能做的优化是:恢复出色觉障碍用户看不到的图像信息。比如分辨球赛中的两队队员,股票k线图的涨跌等。
现在的关键问题在于如何求Transform矩阵。方法有很多。
色觉障碍辅助方法一
直接在RGB空间中操作,把色觉障碍用户感知不到的颜色全都转换为其他的颜色
以红色盲为例,得到Error Picture后,乘上如下的Transform矩阵
相当于把70%的红色分别分散到绿色和蓝色通道中。
色觉障碍辅助方法二
在Lab色彩空间中操作,Lab中的L代表亮度通道,a代表红绿通道,b代表蓝黄通道,可以看到a通道正好对应的是红绿色觉障碍用户所难以分辨的颜色,而b通道对应的是蓝色觉障碍用户所难以分辨的颜色。
还是以红色盲为例,得到Error Picture后,先转换为Lab色彩空间,然后乘上如下的Transform矩阵
相当于把50%的红绿通道色分散到亮度通道中,把100%的红绿通道色分散到蓝黄通道中。但是需要注意的是,RGB到Lab空间的转换计算非常复杂,运算量很高。
色觉障碍辅助方法三
在YUV色彩空间中操作,YUV中的Y代表亮度通道,U近似蓝绿通道,V近似红绿通道。可以看到V通道对应的是红绿色觉障碍用户所难以分辨的颜色,而U通道近似对应的是蓝色觉障碍用户所难以分辨的颜色。
以红色盲为例,得到error picture后,先转换为YUV色彩空间,再乘上如下的Transform矩阵
相当于把70%的红绿通道色分散到亮度通道中,把70%的红绿通道色分散到蓝绿通道中。相比于RGB到Lab空间的转换,RGB到YUV空间的转换就简单的多了。
色觉障碍辅助方法四
在LMS色彩空间中操作,因为LMS就是基于人眼视锥细胞类型所建立的色彩空间,所以当然可以在LMS空间中操作了。
以红色盲为例,得到error picture后,先转换为LMS色彩空间,再乘上如下的Transform矩阵
相当于把70%的L通道色分散到M通道中,把70%的L通道色分散到S通道中。Android原生就是采用这种方法的。
上面四个方法无所谓谁对谁错,并且Transform矩阵的参数可以根据测试结果进行微调,甚至可以在色盲辅助时使用一种方法,在色弱辅助时使用另一种方法,这里我们从实际效果和运行性能的角度来进行选择:
对红色盲,选择在RGB空间中进行校正,对红色弱,选择在YUV空间中进行校正;
对绿色盲和level5以上的绿色弱,选择在YUV空间中进行校正,对level5以下的绿色弱,选择在RGB空间中进行校正;
对蓝色盲和蓝色弱,选择在LMS空间中进行校正。
下面以在YUV空间中校正红色盲为例,介绍一下如何求出最终的色觉障碍辅助矩阵:
已知RGB 空间到YUV空间的转换矩阵为X;
YUV空间到RGB空间的转换矩阵为X’;
前面求出的红色盲模拟矩阵为T';
那么,假设现有一张RGB颜色空间的图片,我们只要利用如下的颜色转换矩阵,就可以将它转换为专为红色盲优化后的图片:
对于其他几种方法,也是类似的思路。在文章的最后,我们将贴出我们计算出来的所有矩阵。
验证色觉障碍辅助结果
和之前的验证一样,我们一方面通过色盲检测图获得定性结果,一方面利用检测APP获得定量结果。需要注意的是,这里我们要在色觉辅助的基础上再加上色觉障碍模拟,从而得到优化后色觉障碍用户所看到的结果。
下面的图是经过优化后红色盲所看到的色盲检测图
这里写图片描述
下图是经过优化后绿色盲所看到的色盲检测图
这里写图片描述
相比于文章开头的色盲检测图,圆圈中的数字已经变得清晰可见了。
再来看球赛的例子,下面是优化后红绿色盲所看到的图
这里写图片描述
相比于文章开头的示例图,现在色觉障碍用户可以更好地分辨两队球员了。
最后来看股票k线图的例子,下面是优化后红绿色盲所看到的图,效果也是显而易见的
这里写图片描述
下面来看一下测试结果,篇幅所限,这里贴上我们的部分验证结果。还是一样,色弱级别取十级,对应level9 ~ level1,level9为严重色弱,level1为轻度色弱。结果如下表:
色觉障碍类型 | 优化前Severity | 优化后Severity |
---|---|---|
红色盲 | 100 | 35 |
红色弱level9 | 88 | 34 |
红色弱level8 | 63 | 26 |
红色弱level7 | 48 | 23 |
红色弱level6 | 38 | 20 |
红色弱level5 | 22 | 9 |
绿色盲 | 100 | 59 |
绿色弱level9 | 84 | 53 |
绿色弱level8 | 58 | 34 |
绿色弱level7 | 42 | 30 |
绿色弱level6 | 29 | 21 |
绿色弱level5 | 18 | 3 |
蓝色盲 | 100 | 36 |
从测试结果可以看到,使用我们的色觉障碍辅助算法可以为色觉障碍用户带来更好的视频观看体验。
将色觉辅助功能集成到Android播放器中
可以看到,我们采用的色觉障碍辅助算法最后得到的其实就是几个矩阵,非常适合用OpenGL的fragment shader来实现,所以用MediaPlayer\MediaCodec + GLSurfaceView的方案是一个不错的选择。简单来说我们要做的就是一个播放器后处理模块。
对OpenGL和GLSurfaceView相关知识比较陌生的朋友可以先了解一些入门知识,受篇幅限制这里就不展开介绍了,直接贴出我们基于GPUImage等开源编写的示例库,地址如下,其中也包含了我们所计算出的所有色觉辅助矩阵。欢迎交流讨论。
https://github.com/zhanghuicuc/SpoRenderer
关注公众号,掌握更多多媒体领域知识与资讯
文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~
网友评论