如何从原始图像获取位图

7

我正在从网络读取原始图像。这个图像是通过图像传感器读取的,而不是通过文件读取。

以下是我了解到的关于图像的信息:
~ 高度和宽度
~ 总大小(以字节为单位)
~ 8位灰度
~ 每个像素1个字节

我试图将这个图像转换成位图以在ImageView中显示。

这是我尝试过的:

BitmapFactory.Options opt = new BitmapFactory.Options();
opt.outHeight = shortHeight; //360
opt.outWidth = shortWidth;//248
imageBitmap = BitmapFactory.decodeByteArray(imageArray, 0, imageSize, opt);

decodeByteArray无法解码我的图片,因此返回null

我还尝试直接从输入流中读取它,而不是先将其转换为字节数组:

imageBitmap = BitmapFactory.decodeStream(imageInputStream, null, opt);

这也会返回null

我在这个论坛和其他论坛上搜索过,但无法找到实现这一点的方法。

有什么想法吗?

编辑:我应该补充一点,我首先检查了流是否实际包含原始图像。我使用其他应用程序(iPhone / Windows MFC)进行了检查,它们能够正确读取并显示图像。我只需要找出在Java / Android中实现这一点的方法。

4个回答

14

Android不支持灰度位图。所以首先,您需要将每个字节扩展为32位ARGB整数。 Alpha是0xff,R、G和B字节是源图像字节像素值的副本。然后在该数组上创建位图。

此外(请参见注释),似乎设备认为0是白色,1是黑色-我们必须反转源位。

因此,让我们假设源图像在名为Src的字节数组中。以下是代码:

byte [] src; //Comes from somewhere...
byte [] bits = new byte[src.length*4]; //That's where the RGBA array goes.
int i;
for(i=0;i<src.length;i++)
{
    bits[i*4] =
        bits[i*4+1] =
        bits[i*4+2] = ~src[i]; //Invert the source bits
    bits[i*4+3] = 0xff; // the alpha.
}

//Now put these nice RGBA pixels into a Bitmap object

Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bm.copyPixelsFromBuffer(ByteBuffer.wrap(bits));

@Seva Alekseyev:感谢您的回答。我尝试了您的建议,但出现了运行时异常。根据logcat的显示,“缓冲区对于像素来说不够大”。我是使用图像的宽度和高度创建Bitmap的。 - OceanBlue
@OceanBlue:该消息表明Bits缓冲区的长度小于4 * Width * Height字节。请检查一下。ARGB_888是每像素4个字节的格式。 - Seva Alekseyev
@Seva Alekseyev:感谢您一直以来的支持。我确实看到了一张图片(Yoohoo!!!)。但出现了一个奇怪的问题,它绘制的是青色背景上的白线而不是白色背景上的黑线。如果您知道原因,我将不胜感激。 - OceanBlue
哦,还有一点:似乎图像源设备认为0是白色,1是黑色。Android则不是这样认为的。因此,如果您发现图像颜色被倒置 - 白色变成了黑色,反之亦然 - 只需反转源字节。请参见编辑2。 - Seva Alekseyev
就是这样(RGBA)!现在它显示得非常完美。非常感谢您一直陪伴我解决问题! - OceanBlue
显示剩余5条评论

1
for(i=0;i<src.length;i++)
{
    bits[i*4] = bits[i*4+1] = bits[i*4+2] = ~src[i]; //Invert the source bits
    bits[i*4+3] = 0xff; // the alpha.
}

转换循环将花费大量时间将8位图像转换为RGBA,一个640x800的图像可能需要超过500毫秒...更快的解决方案是使用ALPHA8格式的位图并使用颜色过滤器:
//setup color filter to inverse alpha, in my case it was needed
float[] mx = new float[]{
    1.0f, 0, 0, 0, 0, //red
    0, 1.0f, 0, 0, 0, //green
    0, 0, 1.0f, 0, 0, //blue
    0, 0, 0, -1.0f, 255 //alpha
};

ColorMatrixColorFilter cf = new ColorMatrixColorFilter(mx);
imageView.setColorFilter(cf);

// after set only the alpha channel of the image, it should be a lot faster without the conversion step

Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
bm.copyPixelsFromBuffer(ByteBuffer.wrap(src));  //src is not modified, it's just an 8bit grayscale array
imageview.setImageBitmap(bm);

1

曾经我做过类似这样的事情,来解码从相机预览回调获得的字节流:

    Bitmap.createBitmap(imageBytes, previewWidth, previewHeight, 
                        Bitmap.Config.ARGB_8888);

试一试。


3
谢谢你的提问。第一个参数“imageBytes”包含什么内容?根据我在在线Javadocs中看到的所有重载版本的Bitmap.createBitmap(...)函数,没有一个将字节数组作为参数。 - OceanBlue

0
使用Drawable从流中创建。以下是使用HttpResponse的方法,但您可以以任何方式获取输入流。

使用Drawable从流中创建。以下是使用HttpResponse的方法,但您可以以任何方式获取输入流。

  InputStream stream = response.getEntity().getContent();

  Drawable drawable = Drawable.createFromStream(stream, "Get Full Image Task");

谢谢你的回答。不幸的是,这也返回了 null :-( 。我查看了关于 Drawable.createFromStream 为什么会返回 null 的在线 JavaDocs http://developer.android.com/reference/android/graphics/drawable/Drawable.html#createFromStream%28java.io.InputStream,%20java.lang.String%29 ,但它并没有详细说明。我应该补充一下,我知道流本身是有效的,因为另一个非 Android 应用程序能够读取它并显示图像。(我没有那个应用程序的源代码)。 - OceanBlue
当然,这似乎是最有可能的情况,但我已经测试过了,服务返回图像正确(请参见我在问题中的编辑)。所以我现在感到困惑和沮丧 :-( - OceanBlue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接