安卓相机对某些用户拍摄的照片存在多行错误。

7
  1. 我的应用程序的一些用户拍摄的照片出现了错误,看起来像这样:

enter image description here

我所做的唯一事情就是在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)。

我真的希望有人能够解释为什么照片一开始就看起来扭曲。


1
我的Galaxy平板电脑在尝试与其他Galaxy平板电脑进行Qik聊天时会得到类似的输出,所以你不是唯一遇到这个问题的人。 - User1578
1
可能有相似的错误和解决方案:https://dev59.com/Wm035IYBdhLWcg3wPNdk - plus-
1
我曾经在HTC Desire HD上遇到过同样的问题。通过设置值p.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);p.setPictureSize(optimalPictureSize.width, optimalPictureSize.height);解决了该问题。 - Martynas Jurkus
3个回答

1

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

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

我还实施了以下代码:

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

对我有用。


我已经将PictureFormat设置为JPEG,而且我不想让每个Desire S用户更新他们的手机。但谢谢提供信息。 - plus-

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问题。我还没有时间去确认。 - plus-
我认为你可以通过这种方法避免JPEG问题。在我看过的几乎所有设备上,你会得到ImageFormat.NV21(原始数据)格式的数据,然后在自己的代码中进行JPEG编码。 - mportuesisf
1
NV21是默认格式,并且每个手机都支持(必须支持)。请注意,即使相机宣传某些预览尺寸,也不意味着它实际上得到支持。 - Konstantin Pribluda

1

看起来你在从字节数组解码位图时出现了一些图像尺寸错误。 你能提供一下以下代码的使用情况吗? - 设置相机 - 设置解码参数 - 检索图像数据


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