高分急求24位色到16(15)位色的转换

horsehorse 2001-12-12 11:47:04
已知24位DIB,要求将颜色数减少到R、G、B各5位。
左移位的方法失真太大,不用想了。希望能够达到ACDSee所带的Editor中24位色转16位色的效果。
最好有源码,不行的话算法也行,多谢!
...全文
239 点赞 收藏 16
写回复
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
horsehorse 2001-12-31
9494
回复
casobug 2001-12-30
看来只能针对你的图像进行单独处理了。
统计各颜色分量的颜色位的变化频率(0或1为图像象素一半时最大),取出变化最大的五位,其他位数用1或0代替(根据统计结果来)。上述过程实际上是主分量分析过程。
对于通常的恢复24位的各分量有y=x*8,上述变换为y=f(x'),利用关系导出各颜色分量x。
上述方法速度可能比较慢,没试过,不知是否可行?
回复
horsehorse 2001-12-30
SaintNiya(来自远方),仔细拜读了你贴的长文,发现他说的方法主要是针对颜色量化,而16位色没有调色板,只能用抖动的方法。
lyzcom(初学者,什么都不懂),你说的方法玩过两天图像的想不知道都难,没必要这么大声嚷嚷吧?这种方法对于某些图像可能好用,速度也很快,但至少对我的图像没用。
现在想想,好像没有哪个软件的抖动效果可以达到ACDSee的效果,连Photoshop都比它差一点。
回复
chenlee 2001-12-30
to lyzcom :
即使是24位到16位的转换,抖动有时也是必要的。

找一张渐变色比较多的24位图片用acdsee打开(在16位显示模式下),
比较一下打开和关闭抖动的画面效果就可以发现十分明显的差别。
回复
lyzcom 2001-12-30
这种算法在游戏中常用,并且失真并不像你所说的太大了,不想再用了那么严重!
回复
lyzcom 2001-12-30
有必要这么复杂吗?24位色到16、15位还要抖动?开玩笑!
24位色分解出R、G、B,然后R>>3,G>>2,B>>3就直接得到了16位色的R、G、B值了,R>>3,G>>3,B>>3,就得到了15位色的R、G、B值了,然后只要再混合一下就行了!
回复
horsehorse 2001-12-30
casobug(虫虫),能否说得更详细点?多谢!
回复
horsehorse 2001-12-29
多谢!太长了,先存起来慢慢看。
回复
SaintNiya 2001-12-28
给你看一篇文章


发信人: is80001@cis.nctu (William Yeh), 信区: programming
标 题: 最接近色 (新)
发信站: 交大资科计算机中心 (Mon Jun 12 22:38:53 1995)
转信站: cis_nctu!news.cis.nctu!is80001

[ Article crossposted from tw.90net.c ]
[ Author was Chi'u I-Nan ]
[ Posted on 09 Jun 95 09:54:00 ]

上次公布之最接近色搜寻法, 目前另有突破. 而原其中之程式一, 原使用
sentinel的技巧加速搜寻, 但後来发现速度并未增快多少, 但程式却变得
较不易看懂, 因此也将之修正回来. 以下重新公布目前定版的
"最接近色搜寻方法研究" 一文如下:

**********************************************************************
* 最接近色搜寻方法研究 - 球壳□围逼近法 *
* 作者:邱奕南 (Chi'u I-Nan) *
* 版权声明:以下文章内容本人仅同意供BBS 站上流传学习,但必须完整流传 *
* (含版权声明及程式),其馀权利一概保留。任何未经本人同意 *
* ,将本文贩卖、刊登、节录、或其他一切侵害本人著作权之行为 *
* 者,皆需负担刑事责任及民事赔偿责任。 *
**********************************************************************

对於彩色影像的处理上,不管是处理抖色、更换色盘资料、或是全彩影像
转色盘影像时,最接近色搜寻的速度往往决定这些影像处理的速度。因此本文
的目的,便是要探讨如何快速搜寻到最接近色。首先将问题定义如下:

对於一个在RGB色彩空间、具有N个颜色的集合S ={Ci},i=0~N-1,现给
定任一颜色C,请找出集合S中的一个颜色C',使得C与C'在RGB色彩空间上
的距离最接近。

由於RGB色彩空间可视为一个立方体的三度空间,因此我们可将整个问题转换成
三度空间几何上的问题:

假设三度空间上有N个点Pi,i=0~N-1。现给定任一点P,请在这N个点中找
出另一点P',使得P与P'的距离最小。

解决这个问题最简单的方法,便是循序搜寻法(Sequential Search),也就是
算出每一个点Pi和点P的距离,然後一个点一个点的比较,自然便可找出和点P
距离最接近的点P'。但是可想而知的是,这种方法的效率必然很差,因此我们
必须想办法略去一些不必比较的点。而要略去不必比较的点,首先便必须从目
前已算出最接近点的资讯上著手,由该点已算得的资讯略去一些必定较远的点。
至於目前已算出最接近点中,有那些与距离相关的资讯可用来判断某些未比较
点较远呢?我们先看下面的定理:

对於空间上点P和点Q,若其与原点O的距离为Rp和Rq,且点P和点Q的距离为
D,则 D >=│Rp-Rq│。

证明:

三点O、P、Q成一三角形。现已知OP=Rp,OQ=Rq,假设OP与OQ夹角为θ,则:

PQ = Sqrt(Rp^2 + Rq^2 - 2RpRq*cosθ)

因此当θ=0(也就是OP与OQ重叠)时,PQ值最小,故

PQ >= Sqrt(Rp^2 + Rq^2 - 2RpRq*cos0) = │Rp-Rq│

现在我们先假设已存在的N个点Pi,若其与原点距离为Ri,而给定点P,其与原
点距离为R,与点Pi距离为Di。则当目前最接近点P'与点P的距离为D'时,任何
一点Pj,只要符合Dj >= D'时,由於其距离不会比较近,因此便不需去比较这
个点Pj了。然而我们并未算出Dj的值呀?这时候便可用上面的定理了。当下式
成立时:

D' <= abs(R-Rj)

由於abs(R-Rj) <= Dj,因此D' <= Dj。也就是说,只要任何一点Pj,其与原点
距离Rj代入上式成立时,便不需去比较了。从另外一个角度来看,若我们已算
出目前最接近点与点P的距离D'时,则所有与原点相距在(R-D',R+D')□围以外
的其他点都不必再去比较了。

接下来我们再考虑距离的计算方式。一般距离的定义为sqrt(x^2+y^2+z^2),
但由於必须有三个乘法且必须开根号,其速度会慢很多。因此,我们可以将色
彩距离的定义改为:

D' = │R-Rj│+│G-Gj│+│B-Bj│

由於

│R-Rj│+│G-Gj│+│B-Bj│ >= │(R-Rj) + (G-Gj) + (B-Bj)│

因此D' <= abs(R-Rj)的式子依然成立。如此在距离的计算上速度会比较快些。
至於采用这种距离定义,效果是否会变得太差?这点倒不用担心。因为不管是
在影像处理或地貌比对上,绝对误差和常用来取代平方误差和,其效果虽较差
些,但差异并不大。这点作者也实际验证过。最後我们将整个搜寻最接近色的
演算法列出如下:

1.算出颜色集合S中,每个颜色Ci与原点的距离Ri,并依Ri值大小排序好。
这个部份可事先算好。
2.算出给定颜色C与原点的距离R。
3.取出颜色集合中与原点距离亦为R(或附近)的颜色C',并计算与颜色C
的距离为D',得到搜寻□围(R-D',R+D')。
4.取出搜寻□围(R-D',R+D')中尚未比较过的点C",并计算与颜色C的距离
为D"。若D"<D',则令C'=C",D'=D",得到另一更小的搜寻□围(R-D',R+D')。
5.重复步骤4,直到搜寻□围(R-D',R+D')中已无尚未比较过的点。此时,
C'即为所要搜寻的最接近色。

现在我们已得到一种快速搜寻最接近色的方法了,但是我们要如何写成程
式呢?对於搜寻□围的逼近,以及已比较点的略去,我们可以从已排序好的色
盘资料中,由初始计算(距离R)的颜色索引值开始往两方向逐点计算,直到两
方向所计算的颜色原点距均超出搜寻□围为止。至於颜色索引值的更新方式有
两种,一种是依目前比较的颜色索引值渐增及渐减,一种是对目前搜寻距离值
渐增及渐减(利用距离区间的方式记录应比较的颜色索引值)。直觉上,前者
的速度应比後者快。以下是这两种方法的程式部份:


(一) 颜色索引值增减逼近法

static int Color_No; /* 颜色数 */
static unsigned char Use_Palette[3][256]; /* 改变索引方式,以加速取值 */
static unsigned char Palette_Index[256]; /* 与原调色盘对应之索引值 */
static unsigned int RGB_Distance[256]; /* 各颜色与原点距离 */
static unsigned char Distance_Index[765+1]; /* 相同原点距离索引值 */

void SetPalette(int color_no,unsigned char* palette)
{
/* ------------------------------------------------------------
作用:设定使用的调色盘资料
参数:color_no = 色彩数,1~256。
palette = 色盘资料,依次为RGB,每色3 byte。
------------------------------------------------------------ */
int i, j, min_index, min_distance, cur_distance, temp;

/* 计算与原点距离 */
Color_No = color_no;
for (i=0; i<color_no; i++)
{
Palette_Index[i] = i;
RGB_Distance[i] = palette[i*3] + palette[i*3+1] + palette[i*3+2];
}
/* 依原点距离排序 - 使用Minimax Sort */
cur_distance = 0;
Distance_Index[0] = 0;
for (i=0; i<color_no; i++)
{
/* 找出最小原点距离者 */
min_distance = RGB_Distance[i];
min_index = i;
for (j=i+1; j<color_no; j++)
if (RGB_Distance[j] < min_distance)
{
min_distance = RGB_Distance[j];
min_index = j;
}
/* 将最小者调上来 */
j = Palette_Index[min_index];
Palette_Index[min_index] = Palette_Index[i];
Palette_Index[i] = j;
temp = RGB_Distance[min_index];
RGB_Distance[min_index] = RGB_Distance[i];
RGB_Distance[i] = temp;
/* 记录调色盘资料 */
Use_Palette[0][i] = palette[j*3];
Use_Palette[1][i] = palette[j*3+1];
Use_Palette[2][i] = palette[j*3+2];
/* 更新距离索引□围 */
for (j=cur_distance+1; j<=min_distance; j++) Distance_Index[j] = i;
cur_distance = min_distance;
}
for (j=cur_distance+1; j<=765; j++) Distance_Index[j] = color_no-1;
}

int GetNearestColor(int r,int g,int b)
{
/* ------------------------------------------------------------
作用:取得最接近色之索引值
参数:r,g,b = RGB三色分量值
传回:最接近色在色盘资料上的索引值
限制:呼叫前必须呼叫SetPalette设定使用之调色盘资料
------------------------------------------------------------ */
int min_index, min_distance, distance, left_bound, right_bound;
int cur_distance, left_index, right_index;
int left_distance, right_distance, left_ok, right_ok;

/* 初始化 : 先比较相同距离之色彩
色彩索引值可由Distance_Index中直接取得 */
distance = r+g+b;
left_index = right_index = min_index = Distance_Index[distance];
min_distance = abs(r-Use_Palette[0][min_index]) +
abs(g-Use_Palette[1][min_index]) +
abs(b-Use_Palette[2][min_index]);
/* 搜寻至超出□围为止
搜寻□围 : distance - min_distance ~ distance + min_distance
left_index, right_index : 两方向目前比较的颜色索引值
left_distance, right_distance : 两方向目前已搜寻到的原点距
left_bound, right_bound : 两方向的搜寻□围
left_ok, right_ok : 两方向是否已超出搜寻□围旗标 */
left_ok = right_ok = false;
left_distance = right_distance = distance;
left_bound = distance-min_distance;
right_bound = distance+min_distance;
for (;;)
{
/* 往右搜寻 */
if (! right_ok)
if ((++right_index > Color_No) ||
((right_distance = RGB_Distance[right_index]) > right_bound))
right_ok = true;
else
{
cur_distance = abs(r-Use_Palette[0][right_index]) +
abs(g-Use_Palette[1][right_index]) +
abs(b-Use_Palette[2][right_index]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = right_index;
left_bound = distance-min_distance;
right_bound = distance+min_distance;
if (left_distance < left_bound) left_ok = true;
}
}
/* 往左搜寻 */
if (! left_ok)
if ((--left_index < 0) ||
((left_distance = RGB_Distance[left_index]) < left_bound))
left_ok = true;
else
{
cur_distance = abs(r-Use_Palette[0][left_index]) +
abs(g-Use_Palette[1][left_index]) +
abs(b-Use_Palette[2][left_index]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = left_index;
left_bound = distance-min_distance;
right_bound = distance+min_distance;
if (right_distance > right_bound) right_ok = true;
}
}
/* 两边都超出□围时, 即结束搜寻 */
if (left_ok && right_ok) break;
}
return Palette_Index[min_index];
}


(二) 搜寻距离值增减逼近法

static unsigned char Use_Palette[3][256]; /* 改变索引方式,以加速取值 */
static unsigned char Palette_Index[256]; /* 与原调色盘对应之索引值 */
static unsigned char Distance_Index[765/9+2]; /* 相同原点距离索引值 */
static unsigned int Distance_Divide; /* 区间分割距离 */
static unsigned int Max_Divide; /* 最大的区间索引值 */

void SetPalette(int color_no,unsigned char* palette)
{
/* ------------------------------------------------------------
作用:设定使用的调色盘资料
参数:color_no = 色彩数,1~256。
palette = 色盘资料,依次为RGB,每色3 byte。
------------------------------------------------------------ */
int i, j, min_index, min_distance, cur_distance;
unsigned char distance[256];

/* 决定分割区间数,目前尽量使得每个区间约有3个颜色
除数:16色=144,256色=9 */
Distance_Divide = 2304/color_no;
Max_Divide = 765/Distance_Divide;
/* 计算与原点距离 */
for (i=0; i<color_no; i++)
{
Palette_Index[i] = i;
distance[i] = (palette[i*3] + palette[i*3+1] + palette[i*3+2]) /
Distance_Divide;
}
/* 依原点距离排序 - 使用Minimax Sort */
cur_distance = 0;
Distance_Index[0] = 0;
for (i=0; i<color_no; i++)
{
/* 找出最小原点距离者 */
min_distance = distance[i];
min_index = i;
for (j=i+1; j<color_no; j++)
if (distance[j] < min_distance)
{
min_distance = distance[j];
min_index = j;
}
/* 将最小者调上来 */
j = Palette_Index[min_index];
Palette_Index[min_index] = Palette_Index[i];
Palette_Index[i] = j;
distance[min_index] = distance[i];
/* 记录调色盘资料 */
Use_Palette[0][i] = palette[j*3];
Use_Palette[1][i] = palette[j*3+1];
Use_Palette[2][i] = palette[j*3+2];
/* 更新距离索引□围 */
for (j=cur_distance+1; j<=min_distance; j++) Distance_Index[j] = i;
cur_distance = min_distance;
}
for (j=cur_distance+1; j<=Max_Divide+1; j++) Distance_Index[j] = color_no;
}

int GetNearestColor(int r,int g,int b)
{
/* ------------------------------------------------------------
作用:取得最接近色之索引值
参数:r,g,b = RGB三色分量值
传回:最接近色在色盘资料上的索引值
限制:呼叫前必须呼叫SetPalette设定使用之调色盘资料
------------------------------------------------------------ */
int i, min_index, min_distance, cur_range, distance, end_index;
int cur_distance, search_range;

/* 初始化 : 搜寻相同距离区间之色彩 */
distance = (r+g+b)/Distance_Divide;
end_index = Distance_Index[distance+1];
min_distance = 768;
for (i=Distance_Index[distance]; i<end_index; i++)
{
cur_distance = abs(r-Use_Palette[0][i]) + abs(g-Use_Palette[1][i]) +
abs(b-Use_Palette[2][i]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = i;
}
}
/* 搜寻至超出□围为止
搜寻□围 : distance - search_range ~ distance + search_range */
cur_range = 1;
search_range = min_distance/Distance_Divide + 1;
while (cur_range <= search_range)
{
/* 往左搜寻一个区间 */
if (distance-cur_range >= 0)
{
end_index = Distance_Index[distance-cur_range+1];
for (i=Distance_Index[distance-cur_range]; i<end_index; i++)
{
cur_distance = abs(r-Use_Palette[0][i]) +
abs(g-Use_Palette[1][i]) +
abs(b-Use_Palette[2][i]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = i;
search_range = min_distance/Distance_Divide+1;
}
}
}
/* 往右搜寻一个区间 */
if (distance+cur_range <= Max_Divide)
{
end_index = Distance_Index[distance+cur_range+1];
for (i=Distance_Index[distance+cur_range]; i<end_index; i++)
{
cur_distance = abs(r-Use_Palette[0][i]) +
abs(g-Use_Palette[1][i]) +
abs(b-Use_Palette[2][i]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = i;
search_range = min_distance/Distance_Divide+1;
}
}
}
cur_range++;
}
return Palette_Index[min_index];
}


上面两个程式,我们以标准调色盘资料为准,R、G、B各以4倍增,全部呼叫次
数为262144次来做分析。首先测得两种方法的平均比较次数:

│循序搜寻法│ 程式一 │ 倍差 │ 程式二 │ 倍差
———┼—————┼—————┼———┼—————┼———
16色│ 16 次 │ 5.5 次 │ 2.9 │ 10.0 次 │ 1.6
256色│ 256 次 │ 39.9 次 │ 6.4 │ 48.1 次 │ 5.3

因此在理论上来讲,程式一的速度应较快。但是实际在486DX2-66测试而得的速
度如下:

│循序搜寻法│ 程式一 │ 倍差 │ 程式二 │ 倍差
———┼—————┼—————┼———┼—————┼———
16色│ 2.9 sec │ 2.1 sec │ 1.4 │ 2.7 sec │ 1.1
256色│ 42.9 sec │ 11.6 sec │ 3.7 │ 10.0 sec │ 4.3

在256色的情况下,程式二反比程式一好,而且实际上的倍差和理论上的倍差相
差甚多。这是因为两个程式因处理上较复杂,而使得花了不少时间在额外的控
制上。在测试完上面两个程式之後,作者一直在想,为什麽程式一的Overhead
(额外处理的时间)会如此之高呢?於是将两个程式翻成组合语言来看,发现
程式一因控制较复杂,导致编译器的最佳化处理比程式二差,因此速度可能是
因此而减慢。至於若将两者均做组合语言最佳化的处理时,那个程式速度会比
较快?这点作者并未详细测试过,留给读者自行试验好了。

接下我们想到一个疑问:如果我们把原点平移到RGB色彩空间的中心点,搜
寻速度是否较快?经作者实际测试的结果,这项改变并不会加快搜寻速度,反
而会因处理平移的计算而减慢速度。因此不需要去考虑这个问题。

最後我们再想想看,是否有方法能够更进一步去除不必比较的点?前述的
公式,其几何意义是这样子的。假设目前已算得的最接近点与点P距离D',则以
点P为球心,D'为半径可得到一个圆球A,而在这圆球以外的点都不必再比较。
利用原点距离缩减比较□围的方法,只能去除以原点为球心,在半径R-D'的圆
球B以内,以及半径R+D'的圆球C以外的点,所有在B、C这两个圆球之间球壳的
点仍然要比较。但是实际应比较的,只是位於球壳中的圆球A内部的点。因此如
果我们再以另一点做新的原点,然後使用同样的方法造出另一个限制比较的球
壳□围,由这两个球壳的交集,便可得到更精确的应搜寻□围。当然,使用的
球壳□围愈多,应搜寻□围便愈精确,但所需的额外处理时间也就愈多,速度
反而不增反减。因此我们建议只使用两个球壳□围就好了。至於另一原点应取
在那里呢?我们取RGB色彩立方体空间的另7个角点做测试,发现除了
(255,255,255)的对角点外,其馀6个角点均可得到较佳的搜寻□围,且结果均
相近。因此以下我们取(255,0,0)为新的原点,然後修改程式一和程式二後再进
行测试,其结果如下:

│循序搜寻法│ 程式三 │ 倍差 │ 程式四 │ 倍差
———┼—————┼—————┼———┼—————┼———
16色│ 16 次 │ 3.5 次 │ 4.6 │ 6.0 次 │ 2.7
256色│ 256 次 │ 11.5 次 │ 22.3 │ 14.3 次 │ 17.9
———┼—————┼—————┼———┼—————┼———
16色│ 2.9 sec │ 2.3 sec │ 1.3 │ 2.6 sec │ 1.1
256色│ 42.9 sec │ 10.2 sec │ 4.2 │ 7.3 sec │ 5.9

读者可以看到,在理论上的速度倍差可达到22倍!但因额外处理的时间,其实
际速度也只能增进到近6倍左右。由上面的测试结果,似乎程式四比程式三好些,
但事实上程式三的编译最佳化结果颇差,尤其是距离值的计算上,是否会因此
而严重影响其执行速度?然而根据作者对程式三的距离值计算方式,使用组合
语言将之最佳化後,其执行速度依然比不上程式四。但这项测试也不能完全证
实当完全写成组合语言之後,程式四一定比程式三好,这些我们都留给读者自
行去验证。至少在C语言的情况下,程式四是比程式三好些。以下便是这两个程
式部份:


(程式三) 双球壳颜色索引值增减逼近法

static int Color_No; /* 颜色数 */
static unsigned char Use_Palette[3][256]; /* 改变索引方式,以加速取值 */
static unsigned char Palette_Index[256]; /* 与原调色盘对应之索引值 */
static unsigned int RGB_Distance[256]; /* 各颜色与原点距离 */
static unsigned int RGB_Distance2[256]; /* 第二原点距 */
static unsigned char Distance_Index[765+1]; /* 相同原点距离索引值 */

void SetPalette(int color_no,unsigned char* palette)
{
/* ------------------------------------------------------------
作用:设定使用的调色盘资料
参数:color_no = 色彩数,1~256。
palette = 色盘资料,依次为RGB,每色3 byte。
------------------------------------------------------------ */
int i, j, min_index, min_distance, cur_distance, temp;

/* 计算与原点距离 */
Color_No = color_no;
for (i=0; i<color_no; i++)
{
Palette_Index[i] = i;
RGB_Distance[i] = palette[i*3] + palette[i*3+1] + palette[i*3+2];
}
/* 依原点距离排序 - 使用Minimax Sort */
cur_distance = 0;
Distance_Index[0] = 0;
for (i=0; i<color_no; i++)
{
/* 找出最小原点距离者 */
min_distance = RGB_Distance[i];
min_index = i;
for (j=i+1; j<color_no; j++)
if (RGB_Distance[j] < min_distance)
{
min_distance = RGB_Distance[j];
min_index = j;
}
/* 将最小者调上来 */
j = Palette_Index[min_index];
Palette_Index[min_index] = Palette_Index[i];
Palette_Index[i] = j;
temp = RGB_Distance[min_index];
RGB_Distance[min_index] = RGB_Distance[i];
RGB_Distance[i] = temp;
/* 记录第二原点距离 */
RGB_Distance2[i] = 255-palette[j*3] + palette[j*3+1] + palette[j*3+2];
/* 记录调色盘资料 */
Use_Palette[0][i] = palette[j*3];
Use_Palette[1][i] = palette[j*3+1];
Use_Palette[2][i] = palette[j*3+2];
/* 更新距离索引□围 */
for (j=cur_distance+1; j<=min_distance; j++) Distance_Index[j] = i;
cur_distance = min_distance;
}
for (j=cur_distance+1; j<=765; j++) Distance_Index[j] = color_no-1;
}

int GetNearestColor(int r,int g,int b)
{
/* ------------------------------------------------------------
作用:取得最接近色之索引值
参数:r,g,b = RGB三色分量值
传回:最接近色在色盘资料上的索引值
限制:呼叫前必须呼叫SetPalette设定使用之调色盘资料
------------------------------------------------------------ */
int min_index, min_distance, distance, left_bound, right_bound;
int cur_distance, left_index, right_index, distance2;
int left_distance, right_distance, left_ok, right_ok;
int left_bound2, right_bound2;

/* 初始化 : 先比较相同距离之色彩
色彩索引值可由Distance_Index中直接取得 */
distance = r+g+b;
distance2 = 255-r+g+b;
left_index = right_index = min_index = Distance_Index[distance];
min_distance = abs(r-Use_Palette[0][min_index]) +
abs(g-Use_Palette[1][min_index]) +
abs(b-Use_Palette[2][min_index]);
/* 搜寻至超出□围为止
搜寻□围 : distance - min_distance ~ distance + min_distance
left_index, right_index : 两方向目前比较的颜色索引值
left_distance, right_distance : 两方向目前已搜寻到的原点距
left_bound, right_bound : 两方向的搜寻□围
left_bound2, right_bound2 : 第二原点距搜寻□围
left_ok, right_ok : 两方向是否已超出搜寻□围旗标 */
left_ok = right_ok = false;
left_distance = right_distance = distance;
left_bound = distance-min_distance;
right_bound = distance+min_distance;
left_bound2 = distance2-min_distance;
right_bound2 = distance2+min_distance;
for (;;)
{
/* 往右搜寻 */
if (! right_ok)
if ((++right_index > Color_No) ||
((right_distance = RGB_Distance[right_index]) > right_bound))
right_ok = true;
else
{
_AX = RGB_Distance2[right_index];
if (((int)_AX >= left_bound2) && ((int)_AX <= right_bound2))
{
cur_distance = abs(r-Use_Palette[0][right_index]) +
abs(g-Use_Palette[1][right_index]) +
abs(b-Use_Palette[2][right_index]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = right_index;
left_bound = distance-min_distance;
right_bound = distance+min_distance;
left_bound2 = distance2-min_distance;
right_bound2 = distance2+min_distance;
if (left_distance < left_bound) left_ok = true;
}
}
}
/* 往左搜寻 */
if (! left_ok)
if ((--left_index < 0) ||
((left_distance = RGB_Distance[left_index]) < left_bound))
left_ok = true;
else
{
_AX = RGB_Distance2[left_index];
if (((int)_AX >= left_bound2) && ((int)_AX <= right_bound2))
{
cur_distance = abs(r-Use_Palette[0][left_index]) +
abs(g-Use_Palette[1][left_index]) +
abs(b-Use_Palette[2][left_index]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = left_index;
left_bound = distance-min_distance;
right_bound = distance+min_distance;
left_bound2 = distance2-min_distance;
right_bound2 = distance2+min_distance;
if (right_distance > right_bound) right_ok = true;
}
}
}
/* 两边都超出□围时, 即结束搜寻 */
if (left_ok && right_ok) break;
}
return Palette_Index[min_index];
}


(程式四) 双球壳搜寻距离值增减逼近法

static unsigned char Use_Palette[3][256]; /* 改变索引方式,以加速取值 */
static unsigned char Palette_Index[256]; /* 与原调色盘对应之索引值 */
static unsigned char Distance_Index[765/9+1]; /* 相同原点距离索引值 */
static unsigned char Distance_No[765/9+1]; /* 相同原点距离颜色数 */
static unsigned int RGB_Distance2[256]; /* 与第二原点距离 */
static unsigned int Distance_Divide; /* 区间分割距离 */
static unsigned int Max_Divide; /* 最大的区间索引值 */

void SetPalette(int color_no,unsigned char* palette)
{
/* ------------------------------------------------------------
作用:设定使用的调色盘资料
参数:color_no = 色彩数,1~256。
palette = 色盘资料,依次为RGB,每色3 byte。
------------------------------------------------------------ */
int i, j, k, min_index, min_distance, cur_distance, start_index;
int min_distance2, temp;
unsigned char distance[256];

/* 决定分割区间数,目前尽量使得每个区间约有3个颜色
除数:16色=144,256色=9 */
Distance_Divide = 2304/color_no;
Max_Divide = 765/Distance_Divide;
/* 计算与原点距离 */
for (i=0; i<=Max_Divide; i++) Distance_No[i] = 0;
for (i=0; i<color_no; i++)
{
Palette_Index[i] = i;
distance[i] = (palette[i*3] + palette[i*3+1] + palette[i*3+2]) /
Distance_Divide;
}
/* 依原点距离排序 - 使用Minimax Sort */
cur_distance = 0;
start_index = 0;
for (i=0; i<color_no; i++)
{
/* 找出最小原点距离者 */
min_distance = distance[i];
min_index = i;
for (j=i+1; j<color_no; j++)
if (distance[j] < min_distance)
{
min_distance = distance[j];
min_index = j;
}
/* 将最小者调上来 */
j = Palette_Index[min_index];
if (min_index != i)
{
Palette_Index[min_index] = Palette_Index[i];
Palette_Index[i] = j;
distance[min_index] = distance[i];
}
/* 记录第二原点距离 */
RGB_Distance2[i] = 255-palette[j*3] + palette[j*3+1] + palette[j*3+2];
if ((min_distance > cur_distance) || (i == color_no-1))
{
/* 处理同原点距离资讯 */
Distance_Index[cur_distance] = start_index;
Distance_No[cur_distance] = i - start_index;
if (i == color_no-1)
if (min_distance <= cur_distance) Distance_No[cur_distance]++;
else
{
/* 最後一个颜色自成一组时 */
Distance_Index[min_distance] = i;
Distance_No[min_distance] = 1;
}
/* 同原点距离者, 依第二原点距离做排序 */
j = start_index;
start_index = i;
if (min_distance <= cur_distance) start_index++;
for (; j<start_index; j++)
{
/* 找出最小第二原点距离者 */
min_distance2 = RGB_Distance2[j];
min_index = j;
for (k=j+1; k<start_index; k++)
if (RGB_Distance2[k] < min_distance2)
{
min_distance2 = RGB_Distance2[k];
min_index = k;
}
/* 将最小者调上来 */
if (min_index != j)
{
temp = Palette_Index[min_index];
Palette_Index[min_index] = Palette_Index[j];
Palette_Index[j] = temp;
temp = RGB_Distance2[min_index];
RGB_Distance2[min_index] = RGB_Distance2[j];
RGB_Distance2[j] = temp;
}
}
cur_distance = min_distance;
}
}
/* 记录调色盘资料 */
for (i=0; i<color_no; i++)
{
j = Palette_Index[i];
Use_Palette[0][i] = palette[j*3];
Use_Palette[1][i] = palette[j*3+1];
Use_Palette[2][i] = palette[j*3+2];
}
}

int GetNearestColor(int r,int g,int b)
{
/* ------------------------------------------------------------
作用:取得最接近色之索引值
参数:r,g,b = RGB三色分量值
传回:最接近色在色盘资料上的索引值
限制:呼叫前必须呼叫SetPalette设定使用之调色盘资料
------------------------------------------------------------ */
int i, min_index, min_distance, distance, end_index, cur_range, index;
int top, down, cur_distance, search_range, distance2, no;

/* 初始化 : 搜寻相同距离区间之色彩 */
distance = (r+g+b)/Distance_Divide;
distance2 = 255-r+g+b;
min_distance = 768;
if ((no=Distance_No[distance]) != 0)
{
end_index = (i = Distance_Index[distance]) + no;
for (; i<end_index; i++)
{
cur_distance = abs(r-Use_Palette[0][i]) +
abs(g-Use_Palette[1][i]) +
abs(b-Use_Palette[2][i]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = i;
}
}
}
/* 搜寻至超出□围为止
搜寻□围: distance - min_distance ~ distance + min_distance */
cur_range = 1;
search_range = min_distance/Distance_Divide+1;
top = distance2 + min_distance;
down = distance2 - min_distance;
while (cur_range <= search_range)
{
/* 往左搜寻一个区间 */
if (((index=distance-cur_range) >= 0) &&
((no=Distance_No[index]) != 0))
{
end_index = (i = Distance_Index[index]) + no;
for (; i<end_index; i++)
{
_AX = RGB_Distance2[i];
if ((int)_AX >= top) break;
if ((int)_AX <= down) continue;
cur_distance = abs(r-Use_Palette[0][i]) +
abs(g-Use_Palette[1][i]) +
abs(b-Use_Palette[2][i]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = i;
search_range = min_distance/Distance_Divide+1;
top = distance2 + min_distance;
down = distance2 - min_distance;
}
}
}
/* 往右搜寻一个区间 */
if (((index=distance+cur_range) <= Max_Divide) &&
((no=Distance_No[index]) != 0))
{
end_index = (i = Distance_Index[index]) + no;
for (; i<end_index; i++)
{
_AX = RGB_Distance2[i];
if ((int)_AX >= top) break;
if ((int)_AX <= down) continue;
cur_distance = abs(r-Use_Palette[0][i]) +
abs(g-Use_Palette[1][i]) +
abs(b-Use_Palette[2][i]);
if (cur_distance < min_distance)
{
min_distance = cur_distance;
min_index = i;
search_range = min_distance/Distance_Divide+1;
top = distance2 + min_distance;
down = distance2 - min_distance;
}
}
}
cur_range++;
}
return Palette_Index[min_index];
}

* 青衫诗客 -- 小邱 *

... 错拾玉蟾碣石,误入红尘千仞。 - 青衫
回复
emmai 2001-12-13
学习
回复
horsehorse 2001-12-13
pfans007(corefans),多谢,不过你的代码应该是
dw16bitColor=((r/255)*31<<11)¦¦(g/255)*63<<5¦¦(b/255)*31;
dw15bitColor=((r/255)*31<<10)¦¦(g/255)*31<<5¦¦(b/255)*31;
吧?这个方法和向右移3位差不多,效果不是很理想。

我试了一下Intel Image Process Lib提供的转换函数,抖动算法试了它提供的
IPL_DITHER_FS The Floid-Steinberg error diffusion dithering algorithm is used.
IPL_DITHER_JJH The Jarvice-Judice-Ninke error diffusion dithering algorithm is used.
IPL_DITHER_STUCKEY The Stucki dithering algorithm is used.
IPL_DITHER_BAYER The Bayer threshold dithering algorithm is used.

但是抖出来的图像颜色发暗,比ACDSee的Photo Enhancer抖出来的差多了,不知谁还有更好的方法?
回复
horsehorse 2001-12-13
没人知道吗?
回复
asptoe 2001-12-12
用PHOTOSHOP转换一下啊!
回复
pfans007 2001-12-12
DWORD dw24bitColor,dw16bitColor,dw15bitColor;
DWORD r,g,b;
r=(dw24bitColor&0xff0000)>>16;
g=(dw24bitColor&0xff00)>>8;
b=(dw24bitColor&0xff);
dw16bitColor=((r/256)*32<<11)||(g/256)*64<<5||(b/256)*32;
dw15bitColor=((r/256)*32<<10)||(g/256)*32<<5||(b/256)*32;
回复
vertex 2001-12-12
不用抖动的话,直角转换不就行了?
回复
horsehorse 2001-12-12
asptoe(雪中飞),你真的用过Photoshop吗?Photoshop是不支持16位色Bitmap的。
我在这里想找的是源码或算法啊。
回复
发动态
发帖子
游戏开发
创建于2007-08-27

7842

社区成员

游戏开发相关内容讨论专区
申请成为版主
社区公告
暂无公告