Android-камера делает многослойное изображение с ошибками для некоторых пользователей - PullRequest
7 голосов
/ 24 июня 2011

1) Некоторые пользователи моего приложения делают фотографии с ошибками, которые выглядят так:

http://lh3.ggpht.com/i_VKS_Z1Ike5V8gEySiscQRRNkLwZMvv1a6u9diJrkWWGgYXUS-kqqxvAylhLIEJ1gs3MMZSEYIJJ4hX

Единственное, что я делаю, это стандартный растровый API в 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);

и затем записать его на диск

 imageFile = new File("/sdcard/app_dir/upload.jpg");
 FileOutputStream outStream = new FileOutputStream(imageFile);
 bm.compress(CompressFormat.JPEG, 75, outStream);
 outStream.flush();
 outStream.close();

2) редактировать: я удалил вызов setPreviewSize, как объяснить там: Android: JPEG, сохраненный с камеры, выглядит поврежденным

Я думаю, что это помогло некоторым пользователям (Desire HD), но я могу сказать, что другие все еще имеют проблему (Desire S).

Мне бы очень хотелось, чтобы кто-то мог объяснить причину, по которой картинки выглядят искаженными.

Ответы [ 3 ]

1 голос
/ 14 июля 2011

Я не могу сказать вам, почему вы получаете искаженные данные с одних устройств, а не с других, но я могу предложить обходной путь, который, похоже, успешно работает для моего приложения.

Ваш пример кода масштабирует JPEG камеры до 640x480, прежде чем сохранить его на SD-карту. Поэтому я предполагаю, что вам не нужно полноразмерное изображение с камеры.

Если это предположение верно, вы можете полностью пропустить API takePicture() камеры и просто сохранить предварительный просмотр на 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;
    }
}
1 голос
/ 02 сентября 2011

У меня была точно такая же проблема на HTC Desire S.

Я обновил мобильную систему в Настройки -> О телефоне -> Обновления программного обеспечения

Я также реализовал следующий код:

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

работал на меня.

1 голос
/ 14 июля 2011

Ну, похоже, вы допустили некоторую ошибку с размером изображения при декодировании растрового изображения из байтового массива.Можете ли вы опубликовать код, который вы используете для: - настройки камеры - настройки параметров декодирования - извлечения данных изображения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...