需要有关调色板颜色量化的帮助:java - Android

use*_*666 5 java algorithm image-processing

我正在Android上开发一个项目,这要求我最多可以过滤5种不同的颜色 - 包括背景颜色(近似白色).我不能使用高级数学算法,因为我不太了解数学(我会完全迷失),但是我使用了一些简单的逻辑算法,这些算法部分是通过反复试验得出的,部分是逻辑的,我已经能够结果可以说是70%.但我需要有关如何使其100%工作的建议.

为了测试算法,我在普通白(第5色)纸上用4种不同颜色的笔潦草地写下随机字/字母,然后写了一些代码来解码颜色.所以在代码中算法......

  • 如果红笔墨水像素被解码,则像素被设置为数字红色
  • 如果蓝色笔墨水像素被解码,则像素被设置为数字蓝色
  • 如果绿色笔墨像素被解码,则像素被设置为数字绿色
  • 如果黑色笔墨水像素被解码,则像素被设置为数字黄色

黑色墨水像素设置为数字黄色(非黑色,因为黑色很难在视觉上与墨水黑色区分).

我的代码如下,但首先是我的最佳结果之一.如您所见,黑色(用数字黄色表示)和红色(用数字红色表示)之间的边缘有重叠.就像我说我的代码是部分逻辑部分试错.

我的第一个问题是如何改进这个算法,以便完美地解码/过滤颜色(我之所以选择这种方法是因为我很快就会因为非常复杂的数学而迷路)?

其次,我怎样才能使它适用于不同的光线(白光,黄光......)?

编辑:经过进一步的评论和讨论,我意识到我需要的帮助是调色板颜色量化Adroid java代码示例,谢谢

未解码的原始图像

解码图像,差不多但还不够好

if(  (Blue > Green) &&(Red > Blue) && (Red - Green) > 25 )
    copyOfBM.setPixel(x,  y, Color.RED); //red
else if(  (Blue > Red) && ( (Blue > Green)) )
    copyOfBM.setPixel(x,  y, Color.BLUE);
else if( (Green >= 82) && ((Green - Blue) >= 12)  && 
  ((DecodeLuminaceColor( x, y, copyOfBM )>= 82 ) && 
   (DecodeLuminaceColor( x, y, copyOfBM )< 124))  )
    copyOfBM.setPixel(x,  y, Color.GREEN);
else if( ((Red - Green) > 6) &&((Red - Green) < 17) &&((Green - Blue) < 8) 
  && (DecodeLuminaceColor( x, y, copyOfBM )< 118 ) )
    copyOfBM.setPixel(x,  y, Color.YELLOW);



void DecodeLuminaceColor( int _x, int _y, Bitmap cBM  ){
    float avrLum = 0;
    Red = 0; Green = 0; Blue = 0;  //Color.green(green_encoded_color);

    red_encoded_color = cBM.getPixel(_x, _y);
    green_encoded_color = cBM.getPixel(_x, _y);
    blue_encoded_color  = cBM.getPixel(_x, _y);

    Red   = ( (red_encoded_color >> 16) & 0xff );
    Green = ( (green_encoded_color >> 8) & 0xff);
    Blue  = ( (blue_encoded_color >> 0) & 0xff );
}
Run Code Online (Sandbox Code Playgroud)

Spe*_*tre 4

我会使用HSV色彩空间

最好检测颜色(更像人类的感知),这应该会有很大帮助。您还可以使用HSV 直方图来检测您有多少种不同的颜色。

HSV直方图

如果您仍然想要 RGB,请进行不同的比较

您有笔color0=(r0,g0,b0)和像素,color=(r,g,b)因此计算它们之间的距离:

d=((r-r0)*(r-r0))+((g-g0)*(g-g0))+((b-b0)*(b-b0))
Run Code Online (Sandbox Code Playgroud)

不需要sqrt。现在你只需计算d你拥有的每种颜色(笔)并选择最小的d......你也可以使用不太精确的:

d=abs(r-r0)+abs(g-g0)+abs(b-b0)
Run Code Online (Sandbox Code Playgroud)

如果您之前不知道颜色并且不想使用直方图

  1. 形成(重新)颜色表(您将为每支找到的新笔设置一组不同的可见颜色)
  2. 创建找到的颜色的空列表
  3. 处理输入图像的所有像素
  4. 计算d到列表中所有找到的颜色的距离
  5. 如果d小于某个阈值常数,则像素属于找到的颜色列表中的该颜色。否则将其添加为新发现的颜色。
  6. 使用重新着色表中的颜色重新着色像素。

这将消除阴影和抗锯齿颜色失真。您还可以忽略重新着色表并使用找到的颜色列表中的颜色。这个过程是颜色量化的形式。

[编辑1]使用HSV颜色并重新着色找到颜色列表(无直方图)后,我得到了这个结果:

HSV 简单重新着色

这表明您的图像具有不同的照明条件(不是渲染,而是真实照片)。因此,照明归一化应该可以进一步改善这一点。我还使用 2 个阈值,一个用于灰度,一个用于颜色...来区分这两个...您也可以通过以下方式检测背景颜色:

  • 像素数(应该比文本颜色大得多)
  • 沿图像的分散(应覆盖大面积且密度相对较高的均匀分散...文本是本地化的)

这里是 C++/VCL 源代码:

backbuffer bmp; // source and target image
struct _color { DWORD rgb; int h,s,v; };    // color entry in (re)color table
_color ld_rgb(DWORD rgb)                    // just RGB -> HSV conversion
    {
    const int _b=0;
    const int _g=1;
    const int _r=2;
    const int _a=3;
    union { DWORD dd; BYTE db[4]; } c;
    double r,g,b,min,max,del,h,s,v,dr,dg,db;
    c.dd=rgb;
    r=c.db[_r]; r/=255.0;
    g=c.db[_g]; g/=255.0;
    b=c.db[_b]; b/=255.0;
    min=r; if (min>g) min=g; if(min>b) min=b;
    max=r; if (max<g) max=g; if(max<b) max=b;
    del=max-min;
    v=max;
    if (del<=0.1) { h=0; s=0; } // grayscale
    else{
        s=del/max;
        dr=(((max-r)/6.0)+(del/2.0))/del;
        dg=(((max-g)/6.0)+(del/2.0))/del;
        db=(((max-b)/6.0)+(del/2.0))/del;
        if      (fabs(r-max)<1e-10) h=db-dg;
        else if (fabs(g-max)<1e-10) h=(1.0/3.0)+dr-db;
        else if (fabs(b-max)<1e-10) h=(2.0/3.0)+dg-dr;
        if (h<0.0) h+=1.0;
        if (h>1.0) h-=1.0;
        }
    _color ccc;
    ccc.rgb=rgb;
    ccc.h=255.0*h;
    ccc.s=255.0*s;
    ccc.v=255.0*v;
    return ccc;
    }
void recolor() // this is the recolor you want
    {
    // load input jpg file to bmp image
    TJPEGImage *jpg=new TJPEGImage();
    jpg->LoadFromFile("in.jpg");
    bmp.bmp->Assign(jpg);
    bmp.resize(bmp.bmp->Width,bmp.bmp->Height);
    delete jpg;

    // recolor bmp
    int i,x,y,d;
    _color c0,c1;
    List<_color> col;                   // color list
    col.num=0;                          // clear colro list
    for (y=0;y<bmp.ys;y++)              // process all pixels
     for (x=0;x<bmp.xs;x++)
        {
        c0=ld_rgb(bmp.pyx[y][x]);       // pixel color -> hsv

        if ((c0.h==0)&&(c0.s==0))       // compare it to found colors (grayscales)
         for (i=0;i<col.num;i++)
            {
//          i=-1; c1.rgb=0x00202020; break;
            c1=col[i];
            if ((c1.h!=0)||(c1.s!=0)) continue;
            d=abs(c1.v-c0.v);
            if (d<32) { i=-1; break; }  // match found ?
            }
        else                            // compare it to found colors
         for (i=0;i<col.num;i++)
            {
//          i=-1; c1.rgb=0x0000FF00; break;
            c1=col[i];
            if ((c1.h==0)&&(c1.s==0)) continue;
            d=(abs(c1.h-c0.h))+(abs(c1.s-c0.s));
            if (d<50) { i=-1; break; }  // match found ?
            }
        if (i>=0) { c1=c0; col.add(c1); }   // if not add new color
        bmp.pyx[y][x]=c1.rgb;               // recolor;
        }
    bmp.bmp->Canvas->Brush->Style=bsClear;
    bmp.bmp->Canvas->Font->Color=0x00802040;
    bmp.bmp->Canvas->TextOutA(5,0,"Found colors: "+AnsiString(col.num));
    bmp.bmp->Canvas->Brush->Style=bsSolid;
    for (d=16,i=0;i<col.num;i++)
     for (y=d;y<=d+d;y++)
      for (x=d*i+1;(x<d*i+d)&&(x<bmp.xs);x++)
       bmp.pyx[y][x]=col[i].rgb;
    }
Run Code Online (Sandbox Code Playgroud)
  • List<T> l; 是动态数组,如std::vector<T>... 代表T l[l.num];
  • backbuffer bmp;是我的图像类...bmp.bmp保存GDI位图并且bmp.xs,bmp.ys 是分辨率
  • col保存发现的颜色...

[Edit1] 双三次照明标准化

我最近正在重写我的DIP库,升级我的照明标准化,因此我对您的输入图像(作为许多测试图像之一)进行了测试,这里是结果(强制(检测到的)空白空间重新着色):

归一化图像

正如你所看到的,中间的红色光点消失了。您可以尝试一下您的算法,这样您就知道在编码之前应用照明归一化是否有帮助(如果做得正确,会有点复杂)。这个是这样完成的:

  1. 为您的图像创建网格(表格)

    每个单元格包含单元格区域的平均颜色和累积增量(噪声)。还有单个标志告诉单元格是还是墨水。单元格大小应约为<0.5 - 1.5>最小细节大小(例如字母或笔宽度......)

  2. 将所有具有高增量的单元格设置为墨水,其余单元格设置为纸张

  3. 计算所有纸细胞组合的平均颜色
  4. 每个纸单元相邻的墨单元

    如果其平均颜色与全局平均纸张颜色相差太远,则设置为墨水。注意不要将那些新的墨水设置单元格作为此步骤的邻居条件。(使用临时标志或不同的标志位并在完成后恢复...

  5. 找到沿图像均匀分布的 16 个控制点(仅使用纸单元)

    它们应该在0%,33%,66%,100%图像分辨率的坐标附近,因此双三次插值是有效的。

  6. 对于每个像素

    双三次计算单元格颜色并调用它c0然后将归一化应用于像素(在 RGB 空间!!!):

    • pixel+=global_avg_color-c0;

    这将使整个图像的纸张颜色均衡,非常接近匹配global_avg_color。保持非纸张细节不失真。

  7. 可选择将所有纸单元区域重新着色global_avg_color

    这不是必需的,但它会消除背景中的大部分噪音。像纸张的质感...