Android 图片显示

时间:2023-08-29 12:10:20

一、Android手机显示图片

若R、G、B每种颜色使用一个字节(8bit)表示,每幅图像可以有1670万种颜色;若R、G、B每种颜色使用两个字节(16bit)表示,每幅图像可以有10的12次方种颜色;

如果是灰度图像,每个象素用一个字节(8bit)表示,一幅图像可以有256级灰度;若每个象素用两个字节(16bit)表示,一幅图像可以有65536级灰度。

但是在Android手机上只能显示0-255的图像,所以在显示之前要做一下处理。

以pmd的模组为例,获取的灰度图片数据范围是 uint16_t grayValue;      //!< 16-bit gray value,显示的时候,采取的方法是通过JNI直接上传int数组,然后通过imageview.setbitmap的方法进行显示。

 for (int i = ; i < width * height; i++)
{
fillir[i]= data->points.at (i).grayValue/4;
if(fillir[i]>) fillir[i]=;
fillir[i] = fillir[i] | fillir[i] << | fillir[i] << | << ;
}

因为最后显示的只是8bit数据,所以需要对数据进行处理,比如

  fillir[i] = fillir[i] | fillir[i] << 8 | fillir[i] << 16 | 255 << 24;
就是把四个八位二进制数(颜色的RGB三个值和透明度A)拼接成一个二进制数32位)。
前八位表示透明度,往后的三个八位分别代表RGB。有点像字符串相连。 “<<”和“|”分别是按位左移和按位或。 255<<24:11111111变成111111110000...(24个0)
四个数移动完后,按位或运算,相同位有1就为1,全为0则为0。

但是输入的是16bit所以图像有些数值大于255,在右移之前需要/4,降低图像尺度。

fillir[i]= data->points.at (i).grayValue/4;
而且定义,大于255 的为255.这样输入的就是8bit图像了。
注意:为了Android能够显示图像,一定要做的就是
 fillir[i] = fillir[i] | fillir[i] << 8 | fillir[i] << 16 | 255 << 24;将图像转换成ARGB,这样在Java层创建图像。

irImageBmp = Bitmap.createBitmap(width, height,Bitmap.Config.ARGB_8888);

升级讨论,图像转换问题:

16bit灰度图像映射到8bit显示

图像显示和打印面临的一个问题是:图像的亮度和对比度能否充分突出关键部分。这里所指的“关键部分”在   CT   里的例子有软组织、骨头、脑组织、肺、腹部等等。  
   
  技术问题:    
  o     显示器往往只有   8-bit,   而数据有   12-   至   16-bits。  
  o     如果将数据的   min   和   max   间   (dynamic   range)   的之间转换到   8-bit   0-255   去,过程是个有损转换,   而且出来的图像往往突出的是些噪音。  
   
  针对这些问题,研究人员先提出一些要求   (requirements),然后根据这些要求提出了一些算法。这些算法现在都很成熟。  
   
  要求一:充分利用   0-255   间的显示有效值域  
  要求二:尽量减少值域压缩带来的损失  
  要求三:不能损失应该突出的组织部分  
   
  算法分析:  
  A.     16-bit   到   8-bit   直接转换:    
   

computeMinMax(pixel_val,   min,   max);   //   先算图像的最大和最小值
for (i = ; i < nNumPixels; i++)
disp_pixel_val[i] = (pixel_val[i] - min)*255.0/(double)(max - min);

这个算法必须有,对不少种类的图像是很有效的:如   8-bit   图像,MRI,   ECT,   CR   等等。  
   
  B.   Window-leveling   算法:   W/L   是专门为   CT   设计的。

原理很简单:CT   图像里不同组织的密度   (用   Hounsfield   单位)   是在固定的值域,   与具体设备和成像软件没有关系。因此,要看头颅时,   我们只需将头颅的值域转换到   0-255   就行了。  
   
  CT   W/L   不讲头颅值域的   min   和   max,   而说   max   -   min   (即  window_width)   和   (max+min)/2   (即   window_center)。  
   
  我们还可以用原来的公式,只是   min   和   max   的算法不一样。

 //   先算图像的最大和最小值
min = (*window_center - window_width)/2.0 + 0.5;
max = (*window_center + window_width)/2.0 + 0.5;
for (i = ; i < nNumPixels; i++)
disp_pixel_val[i] = (pixel_val[i] - min)*255.0/(double)(max - min);

请注意,CT   图像必须先转换成   Hounsfield   值再做   window-level。   这个转换包括将多余高位   bits   变成   0   (clipping),   和用   recale   slope   和   rescale   intercept   来做单位转换。  
   
  HU[i]   =   pixel_val[i]*rescale_slope   +   rescale_intercept  
   
  C.非线性转换  
   
  我刚刚说的是将   min   和   max   间的数值线性转换到   0-255   之间。   如果   max   -   min   出来是个很大的数值,比如说   25500,   那就说每   100   原始密度会压缩成一个显示灰度。   这样的损失可能会很大。  
   
  因为人眼对灰度地反应式是非线性的,非线性转换可以解决一些问题。   常用算法有   log   和  gamma   两种。gamma   比较好调   gamma   值,因此用得比较多。

 for   (i   =   ;   i   <   nNumPixels;   i++)
disp_pixel_val[i] = 255.0 * pow(pixel_value[i]/(max-min), 1.0/gamma);

  D.   有效值域:CT   的   Window-level   有标准的定义,请参看   “Practical   CT  Techniques",   by   Wladyslaw   Gedroyc   and   Sheila   Rankin,   Springer-Verlag。最常用到的有   WW   =   400,   WL   =   40   (实用许多部位);   WW   =   100,   WL   =  36   (头);WW   =   3200,   WL   =   200   (骨头),等等。  
   
  补充几点:  
   
  o       在做任何转换时要注意有效灰度域外的数值的处理。    
        最好先用   int   而非   unsigned   char   来算,再转入矩阵,以避免   overflow  和   underflow。

     double   dFactor   =   255.0/(double)(max   -   min);
int nPixelVal;
for (i = ; i < nNumPixels; i++)
{
nPixelVal = (int) ((pixel_val[i] - min)*dFactor);
if (nPixelVal < )
disp_pixel_val[i] = ;
else if (nPixelVal > )
disp_pixel_val[i] = ;
else
disp_pixel_val[i] = nPixelVal;
}

o       做   window-level   时要注意   min   和   max   之外原始数据的处理

        double   dFactor,   min,   max;
int nPixelVal;
min = (*window_center - window_width)/2.0 + 0.5;
max = (*window_center + window_width)/2.0 + 0.5;
dFactor = 255.0/(double)(max - min);

for (i = ; i < nNumPixels; i++)
{
if (pixel_val[i] < min)
{
disp_pixel_val[i] = ;
continue;
}
if (pixel_val[i] > max)
{
disp_pixel_val[i] = ;
continue;
}
nPixelVal = (int)((pixel_val[i] - min)*dFactor); if (nPixelVal < )
disp_pixel_val[i] = ;
else if (nPixelVal > )
disp_pixel_val[i] = ;
else
disp_pixel_val[i] = nPixelVal;
}

二、色彩深度

  色彩深度(Depth of Color),色彩深度又叫色彩位数。视频画面中红、绿、蓝三个颜色通道中每种颜色为N位,总的色彩位数则为3N,色彩深度也就是视频设备所能辨析的色彩范围。目前有18bit、24bit、30bit、36bit、42bit和48bit位等多种。24位色被称为真彩色,R、G、B各8bit,常说的8bit,色彩总数为1670万,如诺基亚手机参数,多少万色素就这个概念。
灰阶
  什么又是灰阶呢?通常来说,液晶屏幕上人们肉眼所见的一个点,即一个像素,它是由红、绿、蓝(RGB)三原色组成的。每一个基色,其背后的光源都可以显现出不同的亮度级别。而灰阶代表了由最暗到最亮之间不同亮度的层次级别。把三基色每一个颜色从纯色(如纯红)不断变暗到黑的过程中的变化级别划分成为色彩的灰阶,并用数字表示,就是最常见的色彩存储原理。这中间层级越多,所能够呈现的画面效果也就越细腻。以8bit 为例,我们就称之为256灰阶。
8bit 10bit 12bit 14bit 16bit
    在数字信息存贮中,计算设备用2进制数来表示,每个0或1就是一个位(bit)。 假设1代表黑、0代表白,在黑白双色系统中最少有2bit。单基色为nbit,画面位数就为2 ⁿbit,位数越大,灰度越多,颜色也越多,彩色系统中同理。视频画面10bit含义就是画面能以10为二进制数的数量控制色彩层次(即灰阶)。通常8bit相当于256级灰阶——即常说得24位真彩色;而10bit就相当于1024级灰阶。三基色混合成彩色,增加1 bit就意味色彩数增加8倍。10bit就相当于1024的三次方——1073741824,约为10.7亿色。远大于8bit的1670万色。
三、
        选择一个极端的例子,找个蓝天占据大部分画面的例子。Raw在CS调整完后,存储Tiff,下面有位数选择,8bit/16bit,各选一次,分开两个文件。各自打开,然后调整,色阶,把暗部和高光压缩尽量小的可见范围。调完相同的参数,大家再看看各自的直方图,注意蓝天部分的区别。。
       有人就说了,我们讨论的是12bit和14bit,你讨论8bit和16bit,这靠谱吗?
       这个例子是说明色深位数的区别。8-12-14-16的区别就在连续性和宽容度上。靠不靠谱自己琢磨吧。不要再说这个是数字游戏了,这个bit不是这么傻瓜地自动把你的文件增大,是件很靠谱的事情。
四、
       14bit意义重大。
       将RAW转换为JPG时,14bit比12bit有更大的“裁剪空间”。大家都知道像素数多便于几何裁剪,同样的,灰度级多便于“灰度裁剪”。