【Android】二进制图片和Bitmap的getPixel方法解析

Android中Bitmap的getPixel方法解析

第一次写博客,一直想动笔,但是感觉想写的东西网上都有很详细的了。。。今天终于下定决心,写第一篇博客。感觉博客这个东西,别人的和自己的是不一样的,即使同样的问题,也还是要自己记录下来印象更深刻。但是写博客,切勿复制!!!网上一模一样复制过来的东西太多了。


  • 计算机识别的图片
  • 图片的颜色通道
  • getPixel方法的返回值

计算机识别的图片

最近想了解一下图像相关的东西,于是从图片是怎样保存在计算机上着手,简单的研究了一下。

大家都知道,计算机只能识别‘0’和‘1’。图片也一样,通过二进制保存在计算机中。图片一般分为32位(ARGB_8888)和16位(ARGB_4444/ARGB565)。前面代表颜色通道,后面代表每个颜色所占的字节。

如”ARGB_8888”有4个颜色通道,每个色值占8个bit,则ARGB_8888的图片,一像素占用 4(通道)*8(bit)=32bit内存。我们可以用代码验证一下。

Bitmap bitmap1 
    = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
Bitmap bitmap2 
    = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
Log.i(TAG,"ARGB_8888:"+bitmap1.getByteCount());
Log.i(TAG,"ARGB_565:"+bitmap2.getByteCount());

1byte=8bit,打印两张图片所占的字节,得到的值分别为4和2。对应着32位和16位。需要注意的是,如果我们打印ARGB_4444所占的字节,也会得到4byte,这是因为Android已经废弃了ARGB_4444,采用质量更好的ARGB_8888来代替它。

createBitmap部分源码:

if (config != null) {
    switch (config) {
        case ARGB_4444:
        case ARGB_8888:
        default:
            newConfig = Config.ARGB_8888;
            break;
    }
}

图片的颜色通道

知道了图片所占的内存,那么每个颜色通道占8bit是什么意思呢?我们一般表示颜色都是用16进制的ARGB,如一个不透明的红色为FFFF0000,不过计算机只能识别二进制,所以,不透明的红色由16进制转换成二进制为
1111 1111 1111 1111 0000 0000 0000 0000

相信大家已经可以看出来了,我们常用的16进制颜色,转换成二进制后,每个颜色的最大值为一个占用8bit的二进制数字。这就是每个颜色通道占8bit的意思。即每个颜色用一个8位的二进制表示。

由此我们也可以知道,颜色位数越大,所能呈现的色彩就越多,图片也就更加鲜艳。所以,目前在Android中,ARGB_8888格式的bitmap颜色是最好的。

注意,有的同学用这种方式打印图片的二进制

Bitmap bitmap 
    = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
ByteArrayOutputStream byteArrayOutputStream
    = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG
                , 100
                , byteArrayOutputStream);
byte[] bytes = byteArrayOutputStream.toByteArray();

这种方式用于传递图片之类的还可以,但是如果要看图片二进制,你会发现打印出的byte数组比你自己create的原始图片大很多,因为这毕竟是个压缩的方法,调用后会有一些压缩的信息保存在里面,所以得到的byte数组不完全是图片本身的。

具体能打印图片真实二进制的方法我也没找到,如果有同学知道的话希望请教一下,大家互相交流哈。


getPixel方法的返回值

知道了这些后,我们开始研究一下getPixel。看方法名不难看出,这应该是返回图片某个像素点的颜色。看源码的注释也确实是这样。但是我们发现,getPixel方法返回的是一个int型的值。平时用颜色的时候int型好说,都是int型的,但是返回int型咋用??有时候还有负数??

这里写图片描述

其实Color类中有alpha、red、green、blue方法,虽然不常用,但是可以提取中int型颜色中对应的ARGB色值。

Bitmap bitmap
    = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
bitmap.setPixel(0, 0, Color.RED);
int pixel = bitmap.getPixel(0, 0);
// pixel = -65536;
int a = Color.alpha(pixel);
int r = Color.red(pixel);
int g = Color.green(pixel);
int b = Color.blue(pixel);
//得到a : 255 , r : 255 , g : 0 , b : 0;

不过光会用不行,这一串int型到底是怎么表示颜色的,我们还是要看看的。

知道返回的是颜色,并且还有负数,肯定是和二进制有关。我们得到的值是-65536,转换成32位的二进制。

负数十进制转二进制等于对应二进制的绝对值的反码加1。转换成32位,不足的前面补0。

取绝对值
|-65536| = 65536。
转换成二进制:
0000 0000 0000 0001 0000 0000 0000 0000
取反:
1111 1111 1111 1110 1111 1111 1111 1111
+1:
1111 1111 1111 1111 0000 0000 0000 0000

因为我们用的颜色是红色,属于比较规则的颜色,转换成二进制后,我们一下就能看出来,这32位二进制不就是之前说的对应的颜色通道么。1111 1111 正好对应16进制的 FF

接下来就简单了,我们只要把不同位的二进制数字每8位一组提取出来就可以了。

首先保留最低的8位,我们只要把原码与
0000 0000 0000 0000 0000 0000 1111 1111进行‘&(与)’运算即可保留最低的8位。即与十进制的255或者16进制的ff等进行&运算
1111 1111 1111 1111 0000 0000 0000 0000 & 0xff

为了方便观看,我这里还使用比较长的二进制。
1111 1111 1111 1111 0000 0000 0000 0000
&
0000 0000 0000 0000 0000 0000 1111 1111
得到
0000 0000 0000 0000 0000 0000 0000 0000
则最低位也就是blue的值为0,符合我们给定的值。

同理,我们把这8位抛去,只保留9-16位也就是green的色值。
1111 1111 1111 1111 0000 0000 0000 0000
&
0000 0000 0000 0000 1111 1111 0000 0000
得到
0000 0000 0000 0000 0000 0000 0000 0000

看起来green的值是0,好像是对了,但是,如果green不是0呢?我们来用red的值验证一下。
1111 1111 1111 1111 0000 0000 0000 0000
&
0000 0000 1111 1111 0000 0000 0000 0000
得到
0000 0000 1111 1111 0000 0000 0000 0000
看起来好像也没什么问题,不过,转换成10进制是多少呢?算出来一看16711680,这都几千万了。肯定是哪出错了。

我们再看一下,这里的1111 1111虽然是255没问题,但是这些1不是从起始位开始的。我们还需要把它转换到起始位。这好说,进行>>右移操作就可以了。
green的话是0,就不写了,red的值应为
0000 0000 1111 1111 0000 0000 0000 0000
右移 >>16 位得到
0000 0000 1111 1111

这就是我们需要的red的值了。所以,getPixel方法的返回值也可以通过如下方式转换成色值:

Bitmap bitmap 
    = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
bitmap.setPixel(0, 0, Color.RED);
int pixel = bitmap.getPixel(0, 0);
int a = (pixel & 0xff000000) >> 24;
int r = (pixel & 0xff0000) >> 16;
int g = (pixel & 0xff00) >> 8;
int b = (pixel & 0xff);
/*
//也可以写成
int a = pixel >> 24 & 0xff;
int r = pixel >> 16 & 0xff;
int g = pixel >> 8 & 0xff;
int b = pixel & 0xff;
*/
//同样得到a : 255 , r : 255 , g : 0 , b : 0;

好了,就到这里了,回头一看感觉好像有点乱。以后遇到问题还是要多记录,有什么不足的地方,大佬们多提意见吧!!!感激不尽!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值