Android 相机为某些用户拍摄多行错误图片

发布于 2024-11-17 12:39:10 字数 997 浏览 4 评论 0原文

  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 看起来已损坏

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

我真的希望有人能够解释为什么照片看起来扭曲的原因。

  1. Some users of my app are taking some bugged pictures that look like that:

enter image description here

The only thing I do is standard bitmap API in jpegCallback:

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

and then write it on the disk

 imageFile = new File("/sdcard/app_dir/upload.jpg");
 FileOutputStream outStream = new FileOutputStream(imageFile);
 bm.compress(CompressFormat.JPEG, 75, outStream);
 outStream.flush();
 outStream.close();
  1. edit: I've removed a call to setPreviewSize like explain there:
    Android: Jpeg saved from camera looks corrupted

I think it did help for some users (Desire HD), but I can tell others still got the issue (Desire S).

I really wish someone could explain the reason why pics looks distorded in the first place.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

傲影 2024-11-24 12:39:11

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

。我按照“设置”->“更新了移动系统”关于电话->软件更新

我还实现了以下代码:

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

为我工作。

I had the exact same problem on an HTC Desire S.

I updated the mobile system per Settings -> About phone -> software updates

I also implemented the following code:

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

Worked for me.

ぽ尐不点ル 2024-11-24 12:39:10

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

您的示例代码将相机的 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;
    }
}

I can't tell you why you're getting garbled data from some devices and not others, but I can suggest a workaround that seems to be working successfully for my app.

Your example code scales the camera's JPEG down to 640x480 before saving it off to the SD card. So I'm guessing you don't require the full-sized camera image.

If this assumption is true, you can skip Camera's takePicture() API entirely, and just save a preview frame to SD card. The easiest way to do this is with setOneShotPreviewCallback():

mCamera.setOneShotPreviewCallback( new StillPictureCallback() );

This will invoke once, and hand you back a buffer of data from the camera:

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);
    }
}

The callback invokes a background task to compress the data and save it to the file. The only bit of code I'm leaving out is the part that queries the camera for the preview frame format, width and height via getCameraInfo(). Note also that the Android YUVImage class was introduced with Froyo, so if you need to support earlier versions of Android, you will need to roll your own conversion code (there are examples here on 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;
    }
}
淡淡绿茶香 2024-11-24 12:39:10

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

Well, it looks like you made some mistake with image size while decoding bitmap from byte array.
Can you post code you are using for:
- setting up camera
- setting up decode parameters
- retrieval of image data

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文