获取颜色以使图像上的文本突出的好算法是什么?

4
例如,下面的图片是一个背景照片,中心有一个叫'Iniesta'的角色。但由于颜色不好,这个角色很难阅读。是否有好的算法可以获得颜色,使图像上的字符更加突出?

最简单的方法是使用两种不同的颜色来渲染文本 - 一种作为背景颜色,另一种作为每个字母周围的边框。 - Zohar Peled
@ZoharPeled 谢谢!但我的同事,他是设计师,告诉我“边框不够酷炫!” - ushisantoasobu
那么就让他选择完美的颜色吧...其他方法都需要对文本周围的每种颜色进行采样并进行一些繁重的计算。如果是纯色背景,那么就相对容易了,但由于你的背景包含非常对比的颜色,要找到一种足以与大多数颜色形成对比的颜色,使文本可读性更强,这将需要很多艰苦的工作。 - Zohar Peled
3个回答

2

不要使用矩形背景(看起来实际上不太好看),您可以使用以下技巧:

  • 选择两种对比鲜明的颜色(例如白色和黑色)
  • 使用第一种颜色和宽笔(例如5像素)描边文本
  • 使用第二种颜色填充文本

如果无法使用宽笔描边文本,另一种方法是多次绘制文本并进行微小偏移;例如(Python)

for dy in range(-3, 4):
    for dx in range(-3, 4):
        draw_text(color1, x+dx, y+dy, message)
draw_text(color2, x, y, message)

例如,一个像素的边框看起来像这样... 输入图像描述

1
有许多方法适用于静态和动态场景的不同处理方式。我假设这是针对静态图像的,所以我会重点关注这方面。以下是一些基本方法:
  1. 纸张背景

    使用纸张颜色清除文字后面的空白区域,并使用油墨颜色渲染文本。正如您所提到的,这对于复杂的图像并不好看。

  2. 描边、阴影、3D

    使用两种颜色渲染文本。内部使用文本颜色,外部使用对比色进行描边。如果您没有此功能,则可以先使用描边颜色以更大的字体/加粗大小渲染字符,然后再使用较小的字体大小使用内部颜色覆盖 +/- 移动位置以使字体居中。移动方法决定了是否可以通过这种方式呈现描边或阴影甚至 3D 文本。

  3. 透明度

    与其使用恒定颜色渲染文本像素,不如为原始像素颜色添加一些颜色值。您还可以减去颜色或使用 alpha 混合等方法...

  4. XOR

    使用另一种颜色或白色 XOR 原始颜色而不是渲染像素。

这是它的样子:

example

以下是VCL/C++的源代码:

void TMain::draw()
    {
    if (!_redraw) return;

    // needed variables
    AnsiString txt,a;
    TColor c0=clBlack,c1=clWhite;
    int tx=50,ty=50,tys=30,dty=tys*1.5;
    int x,y,x0,x1,y0,y1,i,b;

    // clear whole image
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));
    // copy photo
    bmp->Canvas->Draw(0,0,in);

    // render texts ...
    txt="Paper area ";
    bmp->Canvas->Font ->Size=tys;
    bmp->Canvas->Font ->Color=c1;
    bmp->Canvas->Brush->Color=c0;
    bmp->Canvas->Brush->Style=bsSolid;
    bmp->Canvas->TextOutA(tx,ty,txt); ty+=dty;

    txt="Stroke";
    bmp->Canvas->Brush->Style=bsClear;
    for (x=tx,y=ty,i=1;i<=txt.Length();i++)
        {
        a=txt[i];
        // stroked char
        bmp->Canvas->Font ->Size=tys+3;
        bmp->Canvas->Font ->Color=c0;
        bmp->Canvas->Font ->Style=TFontStyles()<<fsBold;
        x0=bmp->Canvas->TextWidth(a);
        y0=bmp->Canvas->TextHeight(a);
        bmp->Canvas->TextOutA(x,y,a);
        // inside char
        bmp->Canvas->Font ->Size=tys;
        bmp->Canvas->Font ->Color=c1;
        bmp->Canvas->Font ->Style=TFontStyles();
        x1=bmp->Canvas->TextWidth(a);
        y1=bmp->Canvas->TextHeight(a);
        bmp->Canvas->TextOutA(x+((x0-x1)>>1),y+((y0-y1)>>1),a);
        // next char position
        x+=x0;
        } ty+=dty;

    txt="Shadow/3D text";
    bmp->Canvas->Brush->Style=bsClear;
    bmp->Canvas->Font ->Size=tys;
    bmp->Canvas->Font ->Color=c0;
    for (i=0;i<5;i++)
     bmp->Canvas->TextOutA(tx-i,ty+i,txt);
    bmp->Canvas->Font ->Color=c1;
    bmp->Canvas->TextOutA(tx,ty,txt); ty+=dty;

    Graphics::TBitmap *tmp=new Graphics::TBitmap;
    tmp->PixelFormat=pf32bit;
    tmp->HandleType=bmDIB;

    txt="Transparent";
    tmp->Canvas->Brush->Style=bsSolid;
    tmp->Canvas->Brush->Color=0x00000000; // max black
    tmp->Canvas->Font ->Size=tys;
    tmp->Canvas->Font ->Color=0x00808080; // color offset
    x=bmp->Canvas->TextWidth(txt);
    y=bmp->Canvas->TextHeight(txt);
    tmp->SetSize(x,y);
    tmp->Canvas->FillRect(TRect(0,0,x,y));
    tmp->Canvas->TextOutA(0,0,txt);
    union col { DWORD dd; BYTE db[4]; } *p0,*p1;
    for (y0=0,y1=ty;y0<y;y0++,y1++)
        {
        p0=(col*)tmp->ScanLine[y0];
        p1=(col*)bmp->ScanLine[y1];
        for (x0=0,x1=tx;x0<x;x0++,x1++)
         for (i=0;i<4;i++)
            {
            b=WORD(p0[x0].db[i])+WORD(p1[x1].db[i]);
            if (b>255) b=255;
            p1[x1].db[i]=b;
            }
        } ty+=dty;

    txt="XOR";
    tmp->Canvas->Brush->Style=bsSolid;
    tmp->Canvas->Brush->Color=0x00000000; // max black
    tmp->Canvas->Font ->Size=tys;
    tmp->Canvas->Font ->Color=0x00FFFFFF; // max white
    x0=bmp->Canvas->TextWidth(txt);
    y0=bmp->Canvas->TextHeight(txt);
    tmp->SetSize(x0,y0);
    tmp->Canvas->FillRect(TRect(0,0,x0,y0));
    tmp->Canvas->TextOutA(0,0,txt);
    bmp->Canvas->CopyMode=cmSrcInvert;
    bmp->Canvas->Draw(tx,ty,tmp); ty+=dty;
    bmp->Canvas->CopyMode=cmSrcCopy;

    delete tmp;

    // render backbuffer
    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }

它仅使用封装了GDI的VCL,因此应该足够清晰... bmp 是后备缓冲图像
in 是您的图像
tmp 是用于自定义合并文本和图像的临时图像。

1
这里有另一个想法:选择文本区域周围的框,或者整个图像(如果颜色相对均匀)。现在通过将颜色值相加并除以像素数来计算出该区域的平均颜色。

现在选择一种与此平均颜色形成良好对比的颜色。例如,您可以使用简单的公式(255-r, 255-g, 255-b)来获得对比色。但是,如果平均颜色接近于128-gray,则会失败,因此您需要特殊处理。

另一种方法是将平均颜色转换为HSL或HSV颜色空间,然后玩弄色调;例如,只需将其加上180,并/或反转“亮度”(值/亮度)。同样,您需要特殊处理灰度(饱和度接近0)情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接