你可以用谷歌搜索其他算法,如中位数切割、人口、k-means 等。
我最近也需要这个,但必须好看又快(我需要这个来实时视频捕捉)所以我设法做了这样的事情:
转换为 15 位 rgb
如果您已经拥有 RGB,则可以跳过此步骤,但应用 shift/and 以匹配每通道 5 位。您也可以使用 5:6:5 方案
做一个直方图
15 位 rgb 导致 32768 个条目,因此创建 2 个数组
his[32768]
是像素数(直方图)
idx[32768]
是索引 = 颜色值
在计算颜色时,如果使用低位计数或高分辨率,请确保计数器不会溢出
重新排序数组,使零在his[]
数组的末尾
还计算非零条目his[]
并调用它hists
(index) 排序hist[],idx[]
所以hist[]
按降序排列;
创建 N 调色板
取颜色idx[i]
(i={ 0,1,2,3,...,hists-1 }
),看看你的调色板中是否没有相似的颜色。如果忽略此颜色(将其设置为找到的最接近的颜色),否则将其添加到调色板。如果你到达 N 颜色停止
创建颜色映射
所以取每种颜色并在调色板中找到最接近的颜色(这可以在步骤 5 中部分完成)我称这个表为recolor[32][32][32]
重新着色图像
这是 C++ 源代码:
BYTE db,*p;
AnsiString code;
int e,b,bits,adr;
int x0,x1,y0,y1,x,y,c;
DWORD ix,cc,cm,i0,i,mask;
union { DWORD dd; BYTE db[4]; } c0,c1;
DWORD r,g,b; int a,aa,hists;
DWORD his[32768];
DWORD idx[32768];
// 15bit histogram
for (x=0;x<32768;x++) { his[x]=0; idx[x]=x; }
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
cc=pyx[y][x];
cc=((cc>>3)&0x1F)|((cc>>6)&0x3E0)|((cc>>9)&0x7C00);
if (his[cc]<0xFFFFFFFF) his[cc]++;
}
// remove zeroes
for (x=0,y=0;y<32768;y++)
{
his[x]=his[y];
idx[x]=idx[y];
if (his[x]) x++;
} hists=x;
// sort by hist
for (i=1;i;)
for (i=0,x=0,y=1;y<hists;x++,y++)
if (his[x]<his[y])
{
i=his[x]; his[x]=his[y]; his[y]=i;
i=idx[x]; idx[x]=idx[y]; idx[y]=i; i=1;
}
// set lcolor color palete
for (i0=0,x=0;x<hists;x++) // main colors
{
cc=idx[x];
b= cc &31;
g=(cc>> 5)&31;
r=(cc>>10)&31;
c0.db[0]=b;
c0.db[1]=g;
c0.db[2]=r;
c0.dd=(c0.dd<<3)&0x00F8F8F8;
// skip if similar color already in lcolor[]
for (a=0,i=0;i<i0;i++)
{
c1.dd=lcolor[i];
aa=int(BYTE(c1.db[0]))-int(BYTE(c0.db[0])); if (aa<=0) aa=-aa; a =aa;
aa=int(BYTE(c1.db[1]))-int(BYTE(c0.db[1])); if (aa<=0) aa=-aa; a+=aa;
aa=int(BYTE(c1.db[2]))-int(BYTE(c0.db[2])); if (aa<=0) aa=-aa; a+=aa;
if (a<=16) { a=1; break; } a=0; // *** treshold ***
}
if (a) recolor[r][g][b]=i;
else{
recolor[r][g][b]=i0;
lcolor[i0]=c0.dd; i0++;
if (i0>=DWORD(lcolors)) { x++; break; }
}
} // i0 = new color table size
for (;x<hists;x++) // minor colors
{
cc=idx[x];
b= cc &31;
g=(cc>> 5)&31;
r=(cc>>10)&31;
c0.db[0]=b;
c0.db[1]=g;
c0.db[2]=r;
c0.dd=(c0.dd<<3)&0x00F8F8F8;
// find closest color
int dc=-1; DWORD ii=0;
for (a=0,i=0;i<i0;i++)
{
c1.dd=lcolor[i];
aa=int(BYTE(c1.db[0]))-int(BYTE(c0.db[0])); if (aa<=0) aa=-aa; a =aa;
aa=int(BYTE(c1.db[1]))-int(BYTE(c0.db[1])); if (aa<=0) aa=-aa; a+=aa;
aa=int(BYTE(c1.db[2]))-int(BYTE(c0.db[2])); if (aa<=0) aa=-aa; a+=aa;
if ((dc<0)||(dc>a)) { dc=a; ii=i; }
}
recolor[r][g][b]=ii;
}
所有者图像类包含以下内容:
// image data
Graphics::TBitmap *bmp,*bmp0,*bmp1; // actual and restore to 32bit frames,and 8bit input conversion frame
int xs,ys; // resolution
int *py; // interlace table
DWORD **pyx,**pyx0; // ScanLine[] of bmp,bmp0
BYTE **pyx1;
// colors (colors are computed from color_bits)
DWORD gcolor[256]; //hdr
DWORD lcolor[256]; //img
BYTE recolor[32][32][32]; //encode reduce color table
int scolors,scolor_bits; //hdr screen color depth
int gcolors,gcolor_bits; //hdr global pallete
int lcolors,lcolor_bits; //img/hdr local palette
- 包含
pyx[],bmp
源 32 位图像
- 这
pyx1[],bmp1
是用于编码的临时 8 位图像
这是重新着色的完成方式:
// recolor to lcolors
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
int r,g,b;
c0.dd=(pyx[y][x]>>3)&0x001F1F1F;
b=c0.db[0];
g=c0.db[1];
r=c0.db[2];
i=recolor[r][g][b];
// pyx [y][x]=lcolor[i]; // 32 bit output (visual)
pyx1[y][x]=i; // 8 bit output (encoding)
}
这里有一些输出示例:
这是 VCL/GDI 色彩还原、我的方法和原始图像之间的比较)

上部是调色板绘制(原始图像包含中间图像的调色板)
这是真彩色照片:

并减少到 256 种颜色:

这需要大约 185 毫秒来编码成 GIF(包括颜色减少)。我对结果非常满意,但正如您所见,图像并不相同。重新着色后的绿草簇有点不同(面积/强度较小?)
[笔记]
代码尚未优化,因此它应该是一种使其更快的方法。您可以通过以下方式提高编码速度:
- 降低最大编码字典大小
- 使用字典或三个结构的索引表来加快搜索速度
- 可以将直方图冒泡排序更改为更快的排序算法(但那部分代码远非关键)
- 要编码序列,您可以使用单个调色板(如果场景没有太多颜色变化)
- 如果您想要更快的速度,请创建静态调色板并使用抖动代替所有这些
这是 RT 捕获视频的示例(源为 50fps,因此我降低了分辨率以匹配速度):
