4

I have an issue when saving images in my application. I am using Android camera api 1 (pre api 21) on android version 4.4.4. Device is a OnePlus One.

When I take a picture my in-built camera in my app in seems to save the images in poor quality and also rotated 90 degrees counter-clockwise (-90).

Here is an example with images.

Portrait view with default android camera app (saved image):

enter image description here

Portrait view with in-built app camera:

enter image description here

Picture when saved with in-built app camera (saved image):

enter image description here

First problem, rotation orientation

Now the rotation I am guessing is due to this (if I don't change the setDisplayOrientation the camera is skewed in my app):

public void refreshCamera(Camera camera) {
    if (holder.getSurface() == null) {
        // preview surface does not exist
        return;
    }
    // stop preview before making changes
    try {
        camera.stopPreview();
    } catch (Exception e) {
        // ignore: tried to stop a non-existent preview
    }

    int rotation = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
    int degrees = 0;

    // specifically for back facing camera
    switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 90;
            break;
        case Surface.ROTATION_90:
            degrees = 0;
            break;
        case Surface.ROTATION_180:
            degrees = 270;
            break;
        case Surface.ROTATION_270:
            degrees = 180;
            break;
    }

    camera.setDisplayOrientation(degrees);
    setCamera(camera);
    try {
        camera.setPreviewDisplay(holder);
        camera.startPreview();
    } catch (Exception e) {
        Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
    }
}

To fix this I guess I could rotate the images when I have saved the image, seems like a waste of code writing such a method though.

Second problem, the quality

This I am clueless as to why the quality is so bad, I'm guessing it has to do with this:

private PictureCallback getPictureCallback() {
    return new PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            // save picture on seperate thread so camera can refresh quicker
            new Thread(new SavePicThread(data)).start();
            // refresh camera to continue preview
            cameraPreview.refreshCamera(camera);
        }
    };
}

public class SavePicThread implements Runnable {
    byte[] data;
    public SavePicThread(byte[] data) {
        this.data = data;
    }
    public void run() {
        // make a new picture file
        File pictureFile = getOutputMediaFile();

        if (pictureFile == null) {
            return;
        }
        try {
            // write to the file
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.flush();
            fos.close();
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast toast = Toast.makeText(getActivity(), "Picture saved", Toast.LENGTH_SHORT);
                    toast.show();
                }
            });
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // make the picture visible to the rest of the device
        galleryAddPic(pictureFile);
    }
}

// make picture and save to a folder
private File getOutputMediaFile() {
    // make a new file directory inside the "sdcard" folder
    // File mediaStorageDir = new File("/sdcard/", "fela"); // private pic for app

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "fela");

    // if the directory does not exist
    if (!mediaStorageDir.exists()) {
        // if you cannot make this directory return
        if (!mediaStorageDir.mkdirs()) {
            return null;
        }
    }

    // take the current timeStamp
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    // and make a media file:
    mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");

    return mediaFile;
}

/**
 * makes the image visible for the device (gallery)
 * @param pic file
 */
private void galleryAddPic(File file) {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri contentUri = Uri.fromFile(file);
    mediaScanIntent.setData(contentUri);
    getActivity().sendBroadcast(mediaScanIntent);
}

Been looking at tutorials, this is pretty much what they cover!

basickarl
  • 37,187
  • 64
  • 214
  • 335
  • 3
    1. What device are you using. 2. Are you comparing your image to the ODM camera app? 3. What camera parameters are you using; http://developer.android.com/reference/android/hardware/Camera.Parameters.html – Morrison Chang May 10 '15 at 01:38
  • @MorrisonChang 1. OnePlus One 2. With ODM I suppose you mean the normal smartphone camera app, yes 3. I haven't set any apparently... (maybe that is the issue? Never knew it existed) – basickarl May 10 '15 at 01:43
  • Note that the ODM/Normal smartphone app has access to things that isn't available in the older Camera API which would explain the difference. The Camera2 API was released to help create better 3rd party camera apps. See http://source.android.com/devices/camera/camera3.html for hardware implementor details. – Morrison Chang May 10 '15 at 02:07
  • @MorrisonChang I updated the question with how my application looks. - So there is no better way to take an image with the old api? It's weird as my smartphone (OnePlus One) does not support api 21 as it is not Lollipop. – basickarl May 10 '15 at 02:09
  • Your image looks like it isn't use the AutoFocus callback. http://developer.android.com/reference/android/hardware/Camera.html#autoFocus(android.hardware.Camera.AutoFocusCallback) – Morrison Chang May 10 '15 at 02:13
  • Or this might be easier: http://stackoverflow.com/a/23267547/295004 – Morrison Chang May 10 '15 at 02:20
  • @MorrisonChang I did include it `params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)`, it however did not change the quality of the saved image! – basickarl May 10 '15 at 02:23
  • 1
    @MorrisonChang I fixed it! I needed to set the parameter `setPictureSize` to the highest supported width/height, you got +1 for getting me on the right track. :) – basickarl May 10 '15 at 02:43

1 Answers1

0

I understand that you found the solution for image quality. Indeed, you cannot assume that the default PictureSize is the best quality available on the device; you should use getSupportedPictureSizes() and after that call setPictureSize().

Regarding rotation, the situation is more cumbersome. Camera parameters support the setRotation() method which

Sets the clockwise rotation angle in degrees relative to the orientation of the camera. This affects the pictures returned from JPEG Camera.PictureCallback. The camera driver may set orientation in the EXIF header without rotating the picture. Or the driver may rotate the picture and the EXIF thumbnail. If the Jpeg picture is rotated, the orientation in the EXIF header will be missing or 1 (row #0 is top and column #0 is left side).

As you can see from the description, some devices will actually save the JPEG image oriented correctly, but other devices will only save the EXIF header.

Unfortunately, the orientation flag is not enough for many image viewer apps, for example - for Windows built-in image preview. Performing rotation of the full-size image programmatically is possible, but it is a time- and (more importantly) memory-consuming task. The easiest way is to create a bitmap and rotate it.

Many people use scaled bitmap to make rotation faster and use less memory. But this will inevitably defeat your purpose of getting the best picture quality.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307