技术开发 频道

J2ME开发中彩色转灰度算法的应用

  【IT168 技术】一、基础

  对于彩色转灰度,有一个很著名的心理学公式:

Gray = R*0.299
+ G*0.587
+ B*0.114

  二、整数算法

  而实际应用时,希望避免低速的浮点运算,所以需要整数算法。

  注意到系数都是3位精度的没有,我们可以将它们缩放1000倍来实现整数运算算法:

Gray = (
R
*299
+ G*587
+ B*114
+ 500
)
/ 1000

  RGB一般是8位精度,现在缩放1000倍,所以上面的运算是32位整型的运算。注意后面那个除法是整数除法,所以需要加上500来实现四舍五入。

  就是由于该算法需要32位运算,所以该公式的另一个变种很流行:

Gray = (
R
*30
+ G*59
+ B*11
+ 50
)
/ 100

  但是,虽说上一个公式是32位整数运算,但是根据80x86体系的整数乘除指令的特点,是可以用16位整数乘除指令来运算的。而且现在32位早普及了(AMD64都出来了),所以推荐使用上一个公式。

  三、整数移位算法

  上面的整数算法已经很快了,但是有一点仍制约速度,就是最后的那个除法。移位比除法快多了,所以可以将系数缩放成 2的整数幂。

  习惯上使用16位精度,2的16次幂是65536,所以这样计算系数:

0.299
* 65536
= 19595.264
19595

0.587
* 65536
+ (
0.264
)
= 38469.632
+ 0.264
= 38469.896
38469

0.114
* 65536
+ (
0.896
)
= 7471.104
+ 0.896
= 7472

  可能很多人看见了,我所使用的舍入方式不是四舍五入。四舍五入会有较大的误差,应该将以前的计算结果的误差一起计算进去,舍入方式是去尾法:

  写成表达式是:

Gray = (
R
*19595
+ G*38469
+ B*7472
)
>> 16

  2至20位精度的系数:

Gray = (
R
*1
+ G*2
+ B*1
)
>> 2

Gray
= (
R
*2
+ G*5
+ B*1
)
>> 3

Gray
= (
R
*4
+ G*10
+ B*2
)
>> 4

Gray
= (
R
*9
+ G*19
+ B*4
)
>> 5

Gray
= (
R
*19
+ G*37
+ B*8
)
>> 6

Gray
= (
R
*38
+ G*75
+ B*15
)
>> 7


Gray
= (
R
*76
+ G*150
+ B*30
)
>> 8

Gray
= (
R
*153
+ G*300
+ B*59
)
>> 9

Gray
= (
R
*306
+ G*601
+ B*117
)
>> 10

Gray
= (
R
*612
+ G*1202
+ B*234
)
>> 11

Gray
= (
R
*1224
+ G*2405
+ B*467
)
>> 12

Gray
= (
R
*2449
+ G*4809
+ B*934
)
>> 13

Gray
= (
R
*4898
+ G*9618
+ B*1868
)
>> 14

Gray
= (
R
*9797
+ G*19235
+ B*3736
)
>> 15

Gray
= (
R
*19595
+ G*38469
+ B*7472
)
>> 16

Gray
= (
R
*39190
+ G*76939
+ B*14943
)
>> 17

Gray
= (
R
*78381
+ G*153878
+ B*29885
)
>> 18

Gray
= (
R
*156762
+ G*307757
+ B*59769
)
>> 19

Gray
= (
R
*313524
+ G*615514
+ B*119538
)
>> 20

 

  仔细观察上面的表格,这些精度实际上是一样的:3与4、7与8、10与11、13与14、19与20

 

  所以16位运算下最好的计算公式是使用7位精度,比先前那个系数缩放100倍的精度高,而且速度快:

Gray = (
R
*38
+ G*75
+ B*15
)
>> 7

  其实最有意思的还是那个2位精度的,完全可以移位优化:

Gray = (
R
+ (
WORD)
G
<<1
+ B)
>> 2

  由于误差很大,所以做图像处理绝不用该公式(最常用的是16位精度)。但对于游戏编程,场景经常变化,用户一般不可能观察到颜色的细微差别,所以最常用的是2位精度。

public
static
int
[
]
Turngrey(
Image

image)
{

    
int
rgx[
]
;
    rgx
= new
int
[
image.getWidth
(
)
* image.getHeight
(
)
]
;
    image.getRGB
(
rgx,
0
, image.getWidth
(
)
,
0
,
0
, image.getWidth
(
)
,
                 image.getHeight
(
)
)
;
//获得图片的ARGB值

    
int
r, g, b;
    
for
(
int
j
= 0
; j
< rgx.length
; j
++)
{

        r
= (
rgx[
j]
& 0x00ff0000)
>> 16
;
        g
= (
rgx[
j]
& 0x0000ff00)
>> 8
;
        b
= rgx[
j]
& 0x000000ff;
        
if
(
(
rgx[
j]
== 0x00FFFFFF)
)
{

            continue
;
        }

        r
= g;
        b
= g;

        rgx[
j]
= (
(
r
<< 16
)
| (
g
<< 8
)
| b)
| 0xff000000;
    }

    return
rgx;
}

  以上是别人写的算法,我把这个算法用到游戏中处理角色死亡的画面,整个屏幕变灰,刷新也变慢,这个效果是很COOL的~~

 

  在我的项目中我自己写了个函数,以便于我的游戏引擎调度:

/**
* @todo 灰度处理
*/

public
static
int
[
]
Turngrey(
Image

image)
{

    
int
rgx[
]
;
    rgx
= new
int
[
image.getWidth
(
)
* image.getHeight
(
)
]
;
    image.getRGB
(
rgx,
0
, image.getWidth
(
)
,
0
,
0
, image.getWidth
(
)
,
                 image.getHeight
(
)
)
;
//获得图片的ARGB值

    
int
r, g, b;
    
for
(
int
j
= 0
; j
< rgx.length
; j
++)
{

        r
= (
rgx[
j]
& 0x00ff0000)
>> 16
;
        g
= (
rgx[
j]
& 0x0000ff00)
>> 8
;
        b
= rgx[
j]
& 0x000000ff;
        
if
(
(
rgx[
j]
== 0x00FFFFFF)
)
{

            continue
;
        }

        r
= g;
        b
= g;

        
//r = g*3/10;

        
//r = (g << 1 + r) >> 3 + (g << 1 + r);

        
//b = g * 6 / 10;

        
//b = ((g << 1) << 1 + (g << 1)) >> 3 + ((g << 1) << 1 + (g << 1));


        
//g = g >> 3 + g;


        rgx[
j]
= (
(
r
<< 16
)
| (
g
<< 8
)
| b)
| 0xff000000;
    }

    return
rgx;
}

  游戏中的效果在下面(当然引擎可以随时控制颜色恢复彩色):

0
相关文章