【问题标题】:Android Camera takes multi lined bugged picture for some usersAndroid相机为某些用户拍摄多行窃听图片
【发布时间】:2011-06-24 14:12:37
【问题描述】:
  1. 我的应用程序的一些用户正在拍摄一些看起来像这样的错误照片:

我唯一做的就是 jpegCallback 中的标准位图 API:

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 4;
bm = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
bm = Bitmap.createScaledBitmap(bm , 640, 480,  true);

然后写入磁盘

 imageFile = new File("/sdcard/app_dir/upload.jpg");
 FileOutputStream outStream = new FileOutputStream(imageFile);
 bm.compress(CompressFormat.JPEG, 75, outStream);
 outStream.flush();
 outStream.close();
  1. 编辑:我已经删除了对 setPreviewSize 的调用,就像那里解释的那样: Android: Jpeg saved from camera looks corrupted

我认为它对某些用户(Desire HD)确实有帮助,但我可以告诉其他人仍然遇到问题(Desire S)。

我真的希望有人能解释一下为什么图片看起来失真。

【问题讨论】:

  • 我的 Galaxy 标签在尝试与其他 Galaxy 标签进行 Qik 聊天时得到类似的输出,因此您不是唯一遇到此问题的人。
  • 类似的错误,可能的解决方案stackoverflow.com/questions/5540981/…
  • 我在使用 HTC Desire HD 时遇到了同样的问题。通过设置值p.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);p.setPictureSize(optimalPictureSize.width, optimalPictureSize.height);解决

标签: android android-camera


【解决方案1】:

我无法告诉您为什么您会从某些设备而不是其他设备获得乱码数据,但我可以建议一种似乎对我的应用程序有效的解决方法。

您的示例代码将相机的 JPEG 缩小到 640x480,然后将其保存到 SD 卡。所以我猜你不需要全尺寸的相机图像。

如果这个假设成立,您可以完全跳过相机的takePicture() API,只将预览帧保存到 SD 卡。最简单的方法是使用setOneShotPreviewCallback():

mCamera.setOneShotPreviewCallback( new StillPictureCallback() );

这将调用一次,并从相机返回一个数据缓冲区:

private class StillPictureCallback implements Camera.PreviewCallback {
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        mPictureTask = new SaveStillPictureTask();
        byte[] myData = null;
        if ( data != null ) {
            myData = data.clone();
        }
        mPictureTask.execute(myData);
    }
}

回调调用后台任务来压缩数据并将其保存到文件中。我唯一遗漏的代码是通过getCameraInfo() 向相机查询预览帧格式、宽度和高度的部分。另请注意,Android YUVImage 类是随 Froyo 引入的,因此如果您需要支持早期版本的 Android,则需要滚动自己的转换代码(StackOverflow 上有示例)。

/**
 * Background task to compress captured image data and save to JPEG file.
 * 
 */
private class SaveStillPictureTask extends AsyncTask<byte[], Void, Void> {

    private static final String TAG="VideoRecorder.SaveStillPictureTask";

    @Override
    protected Void doInBackground(byte[]... params) {
        byte[] data = params[0];
        FileOutputStream out = null;
        Bitmap bitmap = null;
        if ( data == null ) {
            Log.e(TAG, "doInBackground: data is null");
            return null;
        }

        try {
            out = new FileOutputStream(mSnapshotFilePath);

            // Use the preview image format, as documented in Android SDK javadoc
            if ( (mPreviewImageFormat == ImageFormat.NV21) || (mPreviewImageFormat == ImageFormat.YUY2) ) {
                saveYUVToJPEG( mCamera, out, data );
            } else if (mPreviewImageFormat == ImageFormat.JPEG) {
                Log.d(TAG, "directly write JPEG to storage");
                out.write(data);
            } else {
                Log.d(TAG, "try decoding to byte array");
                bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                if ( bitmap != null ) {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
                } else {
                    Log.e(TAG, "decodeByteArray failed, no decoded data");
                }
            }
        } 
        catch (FileNotFoundException ignore) {;} 
        catch (IOException ignore) {;}
        finally {
            if ( out != null ) {
                try {
                    out.close();
                } catch (IOException ignore) {;}
                out = null;
            }
            if ( bitmap != null ) {
                bitmap.recycle();
                bitmap = null;
            }
            data = null;
        }

        return null;
    }
}

/**
 * Save YUV image data (aka NV21 or YUV420sp) data to JPEG file.
 * 
 * @param camera
 * @param out
 * @param data
 */
protected void saveYUVToJPEG( Camera camera, FileOutputStream out, byte[] data ) {
    YuvImage yuvimg = null;
    try {
        int width = mPreviewWidth;
        int height = mPreviewHeight;

        Rect rect = new Rect();
        rect.left   = 0;
        rect.top    = 0;
        rect.right  = width  - 1;       
        rect.bottom = height - 1;       // The -1 is required, otherwise a buffer overrun occurs
        yuvimg = new YuvImage(data, mPreviewImageFormat, width, height, null);
        yuvimg.compressToJpeg(rect, 90, out);
    } finally {
        yuvimg = null;
    }
}

【讨论】:

  • 今晚结束时我会给你赏金。虽然这不是我正在等待的答案,但我感谢您教我有关 onePreview 回调的知识。在旁注中,我的一个朋友是这些问题的专家,他说这是一个常见的 JPEG 问题。我还没来得及确认呢
  • 我认为您主要使用这种方法回避 JPEG 问题。在我见过的几乎所有设备上,您都会以 ImageFormat.NV21(原始数据)的形式获取数据,然后在您自己的代码中进行 JPEG 编码。
  • NV21 是默认格式,每部手机都支持(必须)。请注意,即使相机广告某些预览尺寸并不意味着它实际上是受支持的
【解决方案2】:

嗯,看起来您在从字节数组解码位图时在图像大小方面犯了一些错误。 您可以发布您正在使用的代码: - 设置相机 - 设置解码参数 - 检索图像数据

【讨论】:

    【解决方案3】:

    我在 HTC Desire S 上遇到了完全相同的问题。

    我根据设置 -> 关于手机 -> 软件更新更新了移动系统

    我还实现了以下代码:

           Camera.Parameters parameters = mCamera.getParameters();
           parameters.setPictureFormat(PixelFormat.JPEG);
           mCamera.setParameters(parameters);
    

    为我工作。

    【讨论】:

    • 我已经将 PictureFormat 设置为 JPEG,但我无法要求每个 Desire S 用户更新他们的手机。但感谢您的信息
    猜你喜欢
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多