devnull69
devnull69

Reputation: 16544

takePicture on SurfaceView results in small image file and wrong orientation

I have an activity layed out as a FrameLayout which serves as a container view for a fragment. This fragment contains a SurfaceView in whose SurfaceHolder I show the camera preview.

On click/tap of the SurfaceView takePicture for the camera is being called with a PhotoHandler callback which implements PictureCallback. The overridden method onPictureTaken receives a byte array containing the image data, which it stores using a FileOutputStream.

I restricted the activity to portrait orientation (in manifest) and used a method setCameraDisplayOrientation (found on stackoverflow) which will show the camera preview with portrait orientation, too.

Everything is working quite well ... except for two things:

  1. The resulting image files are very small (around 40KB)
  2. The resulting image files seem to be rotated 90 degrees to the left

So my questions are

Can I increase image quality (to full size)?

Can I make sure that the resulting image files have portrait orientation?

The following is some of my code

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.xxxxxxxxxxxxxxx" >

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".xxxxxxxxxx"
            android:label="@string/app_name"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

onPictureTaken

@Override
public void onPictureTaken(byte[] data, Camera camera) {

    File pictureFileDir = getDir();

    if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {

        Toast.makeText(context, "Can't create directory to save image.",
                Toast.LENGTH_LONG).show();
        return;

    }

    String filename = pictureFileDir.getPath() + File.separator + photoFile;

    File pictureFile = new File(filename);

    try {
        FileOutputStream fos = new FileOutputStream(pictureFile);
        fos.write(data);
        fos.close();
    } catch (Exception error) {
        Toast.makeText(context, "Image could not be saved.",
                Toast.LENGTH_LONG).show();
    }
}

Please let me know if you need anything else ...

EDIT: I found a solution for the size issue (see answer below). But I still have the issue of the rotated resulting image file. After a lot of testing I get the impression that images are always stored in landscape format even if shot in portrait mode. Maybe some EXIF information in the file marks it as portrait ... I keep testing, but you might still be able to help me here :-)

EDIT2: My idea regarding Exif information seems to be the correct starting point. The standard orientation (at least on my phone's camera) seems to be "landscape rotated 90 degrees to the left". At least, this is what the Exif information of the image file tells me: 0th row of the image is on the top, 0th column is on the left (TAG_ORIENTATION is 1)

Even though I clearly took the photo in portrait mode, the Exif information doesn't reflect that. It seems to "think" that the image is in landscape mode, so I have to rotate it 90 degrees to the right in order to display it. This seems to be wrong to me ... how can I make sure that Exif information in the image reflects the camera/app orientation?? (TAG_ORIENTATION should obviously be 6)

Don't get me wrong: I'd still need to rotate 90 degrees to the right, but TAG_ORIENTATION of the Exif information should tell me to do so. Currently I have to rely on my knowledge about how the photo was taken in the first place.

See: Android: Bitmaps loaded from gallery are rotated in ImageView

and: http://sylvana.net/jpegcrop/exif_orientation.html

EDIT3: Solution ... see below

Upvotes: 0

Views: 1982

Answers (1)

devnull69
devnull69

Reputation: 16544

Ok, I was able to at least solve the size issue. I discovered the PictureSize parameter with which I can set the desired resulting image size depending on the phone/camera capabilities

Camera.Parameters params = camera.getParameters();

List<Camera.Size> sizeList = params.getSupportedPictureSizes();
int chosenSize = Helper.getPictureSizeIndexForHeight(sizeList, 800);
params.setPictureSize(sizeList.get(chosenSize).width, sizeList.get(chosenSize).height);

camera.setParameters(params);

and the Helper method which helps me choose the picture size for the minimum height greather than the second parameter (or the maximum available size)

public static int getPictureSizeIndexForHeight(List<Camera.Size> sizeList, int height) {
    int chosenHeight = -1;
    for(int i=0; i<sizeList.size(); i++) {
        if(sizeList.get(i).height < height) {
            chosenHeight = i-1;
            if(chosenHeight==-1)
                chosenHeight = 0;
            break;
        }
    }
    return chosenHeight;
}

This works, but I sill have the issue of the rotated image result file

EDIT: I found out that I need to set another parameter called Rotation depending on the phone orientation and the UI orientation. In that case, the phone will set the correct exif information, so that the image will not be shown in landscape if taken in portrait. I don't need to rotate any more, and the image is also shown correctly in the album.

public static void setRotationParameter(Activity activity, int cameraId, Camera.Parameters param) {

    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);

    int rotation = activity.getWindowManager().getDefaultDisplay()
            .getRotation();
    rotation = (rotation + 45) / 90 * 90;

    int toRotate = (info.orientation + rotation) % 360;

    param.setRotation(toRotate);
}

Upvotes: 4

Related Questions