[go]根据背景色计算文本颜色


在图片中添加文字是常见的操作(如何添加参见《 gg库绘图与添加文字》),怎样保证添加的文字有很好的辨识度呢?

颜色与灰度

RGB颜色空间以R(Red:红)、G(Green:绿)、B(Blue:蓝)三种基本色为基础,进行不同程度的叠加,产生丰富而广泛的颜色。

最能反映人眼感知的是灰度图像,要获取清晰的前景色,可转换为灰度图后来计算前景色。

互补色与对比色

色相环(colorcircle):是指一种圆形排列的色相光谱(SPECTRUM),色彩是按照光谱在自然中出现的顺序来排列的;暖色(WARMCOLOR)位于包含红色和黄色的半圆之内,冷色则包含在绿色和紫色的那个半圆内。
色相环

互补色:当两个颜色的色相,在色相环中的夹角为180°时,这两个颜色互为补色,也就是“互补色”。

通过RGB可方便地计算器互补色:用255减去对应的RGB后,得到的颜色即为互补色。
互补色

对比色:当两个颜色的色相,在色相环中的夹角大于130°时,这两个颜色就有明显的对比关系了,夹角越大,对比关系越强。

灰度

灰度图,每个像素点只能有一个值表示颜色,它的像素值在0到255之间,0是黑色,255是白色,中间值是一些不同等级的灰色,可以说灰度是黑与白之间的过渡色。

通过RGB,可方便计算出对应的灰度:
(299*R + 587*G + 114*B)/1000

一个图像经过上面公式计算后,即得到人眼感知的灰度图;当期望在背景色上显示清晰的前景色(文本)时,即可根据背景的灰度确定使用黑色或白色。

透明度混合

透明度混合(Alpha blending)是把透明度混合进个颜色中,方便颜色计算:
DestinationColor.rgb = (SourceColor.rgb * SourceColor.a) + (DestinationColor.rgb * (1 - SourceColor.a))

Alpha blending

最常见的像素表示格式是RGBA8888,即(r,g,b,a),每个通道8位。为了表示方便,alpha通道一般记成正规化为0~1的浮点数:
如红色60%透明度就是(255, 0, 0, 153),正规化后为(255, 0, 0, 0.6);而 Premultiplied Alpha 则是把RGB通道乘以透明度也就是(r*a, g*a, b*a, a),也就是变成(153, 0, 0, 0.6)。

透明通道在渲染的时候通过Alpha Blending产生作用,混合后的颜色计算公式(如一个透明度为a s a_sas的颜色C s C_sCs,渲染到颜色C d C_dCd上):
$ C_o = a_s*C_s + (1 − a_s)*C_d $

如果颜色以Premultiplied Alpha形式存储(C s C_sCs已经乘以透明度了),则混合公式变成了:
$ C_o = C_s^′ + (1 − a_s)*C_d $

Premultiplied Alpha

Premultiplied Alpha后的像素格式变得不直观,但混合的时候可以少一次乘法,这可以提高一些效率;除此之外最主要的是:没有Premultiplied Alph的纹理无法进行Texture Filtering(纹理过滤,除非使用最近邻插值):

Premultiplied Alpha最重要的意义是使得带透明度的图片纹理可以正常的进行线性插值;这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则在透明像素边缘附近产生奇怪的颜色。

go文本颜色

go中通过image.At获取对应位置的颜色,然后通过color.RGBA()返回对应的R,G,B值;但其值是Premultiplied Alpha的,要转换成灰度,需要:
(19595*r + 38470*g + 7471*b + 1<<15) >> 24

若只是把图片转换成黑白的,可直接使用color.GrayModel.Convert来方便实现。

要根据背景色来确定文本颜色(以黑、白为例),提取文字显示位置处的颜色,转换为灰度值后,根据其值确定是黑色或白色。

func calcTextColor(dc *gg.Context, x, y float64) color.Color {
    cur := dc.Image().At(int(x), int(y))
    //gray := color.GrayModel.Convert(cur)
    r, g, b, _ := cur.RGBA()
    lum := (19595*r + 38470*g + 7471*b + 1<<15) >> 24

    var txtColor color.Color
    if lum > 158 {
        txtColor = color.NRGBA{A: 255}
    } else {
        txtColor = color.NRGBA{R: 255, G: 255, B: 255, A: 255}
    }
    return txtColor
}

要更好地处理文本颜色,可多取几个点平均,或者每个文本处获取对应的显示颜色。


版权声明:本文为alwaysrun原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。