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

时间:2015-11-01 11:35:09

标签: java algorithm image-processing

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

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

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

黑色墨水像素设置为数字黄色(不是黑色,因为黑色很难在视觉上与墨水黑色区分开来。)

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

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

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

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

Undecoded Original image

Decoded image, almost but not good enough

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 );
}

1 个答案:

答案 0 :(得分:4)

我会使用HSV色彩空间

最好检测一下应该有很多帮助的颜色(更像是人类的感知)。您还可以使用HSV Histogram来检测您有多少种不同的颜色。

HSV histogram

如果你仍然想要RGB而不是比较

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

d=((r-r0)*(r-r0))+((g-g0)*(g-g0))+((b-b0)*(b-b0))

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

d=abs(r-r0)+abs(g-g0)+abs(b-b0)

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

  1. 形成(重新)颜色表(您将为每个找到的新笔设置不同的可见颜色的集合)
  2. 创建找到的颜色的空列表
  3. 处理输入图像的所有像素
  4. 计算列表中所有找到的颜色的距离d
  5. 如果d小于某个阈值常数,则像素属于找到的颜色列表中的该颜色。否则将其添加为新发现的颜色。
  6. 使用重新着色表中的颜色重新着色像素。
  7. 这将消除阴影和抗锯齿颜色失真。您也可以忽略重新着色表并使用找到的颜色列表中的颜色。此过程是颜色量化的形式。

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

    HSV simple recolor

    这表明您的图像具有相同的光照条件(不是渲染而是真实照片)。所以Ilumination normalization应该进一步改善这一点。另外我使用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;
        }
    
    • List<T> l;是动态数组,如std::vector<T> ...代表T l[l.num];
    • backbuffer bmp;是我的图片类... bmp.bmp包含 GDI 位图,bmp.xs,bmp.ys是分辨率
    • col拥有找到的颜色......

    [Edit1]双立方照明标准化

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

    normalized image

    正如你所看到的那样,中间的红色照明点消失了。您可以尝试使用此算法,以便了解在编码之前应用照明规范化是否有帮助(如果正确完成,则有点复杂)。这个是这样做的:

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

      每个单元格包含单元格区域的平均颜色和累积 delta (噪音)。还有单一标志,告知单元格是纸张还是墨水。单元格大小应该在最小细节大小的<0.5 - 1.5>左右(如字母或笔宽......)

    2. 将所有单元格设置为高delta作为墨水,其余部分为纸张

    3. 计算所有纸张单元格的平均颜色
    4. 每个纸质单元相邻墨盒

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

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

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

    6. 每个像素
    7. 双立方计算单元格颜色并调用它c0然后将标准化应用于像素(RGB空间中的 !!! ):

      • pixel+=global_avg_color-c0;

      这将使整个图像的纸张颜色均衡到与global_avg_color非常接近的匹配。留下非纸质细节不会失真。

    8. 可选择使用global_avg_color 重新着色所有纸质单元格区域

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