BitmapFactory.decodeByteArray() 返回了 NULL

21

我正在使用相机的previewCallback来尝试抓取图像。这是我使用的代码:

private Camera.PreviewCallback mPrevCallback = new Camera.PreviewCallback() 
{
        public void onPreviewFrame( byte[] data, Camera Cam ) {
                Log.d("CombineTestActivity", "Preview started");
                Log.d("CombineTestActivity", "Data length = " 
                        + data.length );
                currentprev = BitmapFactory.decodeByteArray( data, 0, 
                        data.length );

               if( currentprev == null )
                   Log.d("CombineTestActivity", "currentprev is null" );

                Log.d("CombineTestActivity", "Preview Finished" );

        }
};

数据的长度始终为576000。

另外,我尝试更改相机的参数,以便以不同格式返回图像。当我这样做时,它看起来像这样。

mCamera = Camera.open();
camParam = mCamera.getParameters();
camParam.setPreviewFormat( ImageFormat.RGB_565 );
mCamera.setParameters( camParam );
    mCamera.setPreviewCallback( mPrevCallback );
然而,无论我更改预览格式还是保留其默认值NV21时,BitmapFactory.decodeByteArray都返回null。我还尝试将预览格式更改为JPEG类型。即使我在ddms中获取了调试语句,也是这样。"D/skia (14391): --- SkImageDecoder::Factory returned null"

我回来了,点击了这里得到的一些答案旁边的复选框。感谢提供信息。 - RyoxSinfar
可能是 从Android中获取视频图像帧 的重复问题。 - Tim
6个回答

43

好的,希望这能有所帮助。

在网上搜寻了许久,最终找到了完美的解决方案。

截止至Android 2.1版本,此方法有效。

感谢来自这个页面的off3nsiv3。

// Convert to JPG
Size previewSize = camera.getParameters().getPreviewSize(); 
YuvImage yuvimage=new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 80, baos);
byte[] jdata = baos.toByteArray();

// Convert to Bitmap
Bitmap bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length);

只需稍作修改 off3nsiv3 的代码即可,FPS 与手动解码相比仍然非常高。

对于上述代码,80 是 JPEG 质量(0 到 100,100 最好)。


有趣;你可以发布一下帧率的比较吗? - Chinasaur
我可以这样做,但在LG Optimus C上FPS基本相同,在HTC上FPS也是一样的。没有明显的延迟。 - Qix - MONICA WAS MISTREATED
需要将图像编码为JPEG格式(相对较慢的操作),然后再解码回来吗? - Violet Giraffe
@VioletGiraffe 在本回答撰写时,实际上没有更快的方法。此外,如果你尝试一下,它是一个相对较快的操作 ;) 没有其他方法可以将其转换为位图。 - Qix - MONICA WAS MISTREATED
@Qix 我的图像变得更蓝了.. 我正在使用三星S4平板电脑。 - Chetan Chaudhari

7

你可以尝试这个方法,它是有效的...

mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    Camera.Parameters parameters = camera.getParameters();
                    int format = parameters.getPreviewFormat();
                    //YUV formats require more conversion
                    if (format == ImageFormat.NV21 || format == ImageFormat.YUY2 || format == ImageFormat.NV16) {
                        int w = parameters.getPreviewSize().width;
                        int h = parameters.getPreviewSize().height;
                        // Get the YuV image
                        YuvImage yuv_image = new YuvImage(data, format, w, h, null);
                        // Convert YuV to Jpeg
                        Rect rect = new Rect(0, 0, w, h);
                        ByteArrayOutputStream output_stream = new ByteArrayOutputStream();
                        yuv_image.compressToJpeg(rect, 100, output_stream);
                        byte[] byt = output_stream.toByteArray();
                        FileOutputStream outStream = null;
                        try {
                            // Write to SD Card
                            File file = createFileInSDCard(FOLDER_PATH, "Image_"+System.currentTimeMillis()+".jpg");
                            //Uri uriSavedImage = Uri.fromFile(file);
                            outStream = new FileOutputStream(file);
                            outStream.write(byt);
                            outStream.close();
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                        }
                    }
                }

3
我正在尝试做同样的事情。根据这里这里的讨论,似乎人们在Android 2.1 / 2.2中使用decodeByteArray()处理NV21格式没有成功。虽然我认为这会调用本机代码,所以它可能适用于某些具有不同驱动程序的手机?但它在我的模拟器或Droid Incredible上明显不起作用。
作为替代方案,您可以尝试在Java中自己解码NV21(请参见上面的链接示例),但是这对于大多数情况来说速度太慢而无用。我也没能让CameraPreview发送不同格式,并且我希望这对于编写跨不同硬件可移植的代码会有问题。如果你在NDK中编写了NV21解码方法,你可能会让帧率提高一点。
显然,由于竞争条件而存在稳定性问题,以尝试处理CameraPreview,虽然我自己还没有确认这个问题。我认为您可以通过使用Android 2.1 / 2.2中添加的缓冲预览回调方法setPreviewCallbackWithBuffer()避免这个问题,同时也可以提高您的帧率。这是在2.1中添加的,但被隐藏起来直到2.2。要在2.1中使用它,您需要绕过隐藏。请点击此处了解更多信息。 一些人建议使用MediaRecorder而不是CameraPreview。不幸的是,MediaRecorder似乎比CameraPreview提供更少有关获取预览帧的支持,因此我无法推荐该方案。

通过上面链接中的NV21解码,我能够获取像素,但在HTC Droid Incredible上的帧速率约为1帧/ 10秒。然而,我相信对像素进行降采样可以将此帧速率提高到可用于某些应用程序的速度。 - Chinasaur

2

这对我很有效...

 private Camera.PreviewCallback getPreviewCallback() {
    Log.d("TAG", "previewCallBack ==> " + "getPreviewCallback");
    Camera.PreviewCallback previewBitmap = new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            Camera.Size previewSize = camera.getParameters().getPreviewSize();
            YuvImage yuvimage=new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 80, baos);
            byte[] jdata = baos.toByteArray();
            // Convert to Bitmap
            Bitmap bitmap = BitmapFactory.decodeByteArray(jdata, 0, jdata.length);
            Log.d("TAG", "BITMAP ==> " + bitmap);
            runModelInference(bitmap);
        }
    };

    return previewBitmap;
}

1

更新自我的早期答案,但请注意 Qix 的答案,看起来更简单

实际上,我在纯 Java 中解码方面取得了不错的结果。只要预览大小不太大,帧速率可以达到 10-15 帧每秒。不幸的是,我的 Droid Inc 的 Android 2.3 更新似乎去掉了一些较小的预览大小选项 :(。我还尝试使用从另一个项目中提取的本机代码来执行此操作,但这有缺陷,并且在我进行的相对简单的处理方面似乎没有更快,因此我没有进一步追求它。请参见 我的 Github 获取源代码(Java 和本机)。


-1

请按照以下方式尝试:

    public Bitmap stringtoBitmap(String string) {
    Bitmap bitmap = null;
    try {
        YuvImage yuvimage = new YuvImage(base64.getBytes(),ImageFormat.YUY2, 120, 30, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuvimage.compressToJpeg(new Rect(0, 0, 20, 20), 100, baos);
        byte[] jdata = baos.toByteArray();
        bitmap = BitmapFactory.decodeByteArray(jdata, 0, jdata.length);
    } catch (Exception e) {

    }
    return bitmap;
}

1
你可以详细解释一下你的方法吗? - Volker E.
为什么要吞噬异常?不要这样做。 - Qix - MONICA WAS MISTREATED

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