4

I am trying to take a picture using the Android camera. I have a requirement to capture a 1600 (w) x 1200 (h) image (3rd party vendor requirement). My code seems to work fine for many phone cameras but the setPictureSize causes a crash on some phones (Samsung Galaxy S4, Samsung Galaxy Note) and causes a streaked picture on others (Nexus 7 Tablet). On at least the Nexus the size I desire is showing up in the getSupportPictureSizes list.

I have tried specifying the orientation but it didn't help. Taking the picture with the default picture size works fine.

Here is an example of the streaking:

enter image description here

For my image capture I have a requirement of 1600x1200, jpg, 30% compression, so I am capturing a JPG file.

I think I have three choices: 1) Figure out how to capture the 1600x1200 size without a crash or streaking, or 2) Figure out how to change the size of the default picture size to a JPG that is 1600x1200. 3) Something else that is currently unknown to me.

I have found some other postings that have similar issues but not quite the same. I am in my 2nd day of trying things but am not finding a solution. Here is one posting that got close:

Camera picture to Bitmap results in messed up image (none of the suggestions helped me)

Here is the section of my code that worked fine for until I ran into the S4/Note/Nexus 7. I have added a bit of debugging code for now:

Camera.Parameters parameters = mCamera.getParameters();
Camera.Size size = getBestPreviewSize(width, height, parameters);

if (size != null) {
    int pictureWidth = 1600;
    int pictureHeight = 1200;

    // testing
    Camera.Size test = parameters.getPictureSize();
    List<Camera.Size> testSizes = parameters.getSupportedPictureSizes();
    for ( int i = 0; i < testSizes.size(); i++ ) {
        test = testSizes.get(i);
    }

    test = testSizes.get(3);
    // get(3) is 1600 x 1200
    pictureWidth = test.width;
    pictureHeight = test.height;

    parameters.setPictureFormat(ImageFormat.JPEG);
    parameters.setPictureSize(pictureWidth, pictureHeight);
    parameters.setJpegQuality(30);
    parameters.setPreviewSize(size.width, size.height);

    // catch any exception
    try {
        // make sure the preview is stopped
        mCamera.stopPreview();

        mCamera.setParameters(parameters);
        didConfig = true;
    catch(Exception e) {
        // some error presentation was removed for brevity
        // since didConfig not set to TRUE it will fail gracefully
    }
}

Here is the section of my code that saves the JPG file:

PictureCallback jpegCallback = new PictureCallback() {
    public void onPictureTaken(byte[] data, Camera camera) {
        if ( data.length > 0 ) {

            String fileName = "image.jpg";

            File file = new File(getFilesDir(), fileName);
                String filePath = file.getAbsolutePath();

             boolean goodWrite = false;
             try {
                 OutputStream os = new FileOutputStream(file);
                 os.write(data);
                 os.close();

                goodWrite = true;
            } catch (IOException e) {
                 goodWrite = false;
             }

             if ( goodWrite ) {
                // go on to the Preview
             } else {
                // TODO return an error to the calling activity
             }

        }

        Log.d(TAG, "onPictureTaken - jpeg");
    }
};

Any suggestions on how to correctly set up the camera parameters for taking photos or how to crop or resize the resulting photo would be great. Especially if it will work with older cameras (API level 8 or later)! Based on needing the full width of the picture I can only crop off the top.

Thanks!

EDIT: Here is what I ended up doing:

I started by processing the Camera.Parameters getSupportedPictureSizes to use the first one that had the height and width both greater than my desired size, AND the same width:height ratio. I set the Camera parameters to that picture size.

Then once the picture was taken:

BitmapFactory.Options options = new BitmapFactory.Options();;
options.inPurgeable = true;

// convert the byte array to a bitmap, taking care to allow for garbage collection
Bitmap original = BitmapFactory.decodeByteArray(input , 0, input.length, options);
// resize the bitmap to my desired scale 
Bitmap resized = Bitmap.createScaledBitmap(original, 1600, 1200, true);

// create a new byte array and output the bitmap to a compressed JPG
ByteArrayOutputStream blob = new ByteArrayOutputStream();
resized.compress(Bitmap.CompressFormat.JPEG, 30, blob);

// recycle the memory since bitmaps seem to have slightly different garbage collection
original.recycle();
resized.recycle();

byte[] desired = blob.toByteArray();

Then I write out the desired jpg to a file for upload.

Community
  • 1
  • 1
Gravitoid
  • 1,294
  • 1
  • 20
  • 20

1 Answers1

4
test = testSizes.get(3);
// get(3) is 1600 x 1200

There is no requirement that the array have 4+ elements, let alone that the fourth element be 1600x1200.

1) Figure out how to capture the 1600x1200 size without a crash or streaking

There is no guarantee that every device is capable of taking a picture with that exact resolution. You cannot specify arbitrary values for the resolution -- it must be one of the supported picture sizes. Some devices support arbitrary values, while other devices will give you corrupted output (as is the case here) or will flat-out crash.

2) Figure out how to change the size of the default picture size to a JPG that is 1600x1200

I am not aware that there is a "default picture size", and, beyond that, such a size will be immutable, since it is the default. Changing the picture size is your option #1 above.

3) Something else that is currently unknown to me.

For devices that support a resolution that is bigger on both axes, take a picture in that resolution, then crop to 1600x1200.

For all other devices, where one or both axes are smaller than desired, take a picture in whatever resolution suits you (largest, closest match to 4:3 aspect ratio, etc.), and then stretch/crop to get to 1600x1200.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • What's strange to me is that the 1600x1200 size was listed as a supported picture size but I still got corrupted output. I am unable to stretch or shrink one direction because of vendor requirements (check image capture for deposit). I can crop off the top based on how the user might align the image in the preview window. Would I need to use a bitmap to do image manipulation and then convert to jpg? (By default size I meant the size from getPictureSize() before I changed any settings.) – Gravitoid Apr 04 '14 at 15:39
  • @Gravitoid: "What's strange to me is that the 1600x1200 size was listed as a supported picture size but I still got corrupted output" -- that's certainly possible. I usually focus on the highest camera resolutions, and there are definitely devices that report resolutions that give flawed output. "I am unable to stretch or shrink one direction because of vendor requirements" -- then you cannot write your app, because there is no requirement that a camera support **exactly** 1600 or 1200 pixels on any given axis. – CommonsWare Apr 04 '14 at 15:49
  • @Gravitoid: Moreover, I am puzzled as to why you are worrying about the "streaking". By your own admission, **your app crashes** on some devices. A streaked image is a positive result by comparison. – CommonsWare Apr 04 '14 at 15:50
  • Thanks for the good info. My concern is the crash happens on a Samsung Galaxy S4, a very popular phone, and I'm reluctant to say that phone isn't supported by my app. I would rather find a solution that works for the S4 as well as all the phones that already work. The streaking/corruption is on the Nexus 7 tablet, and, while better than a crash, still not a usable result (I happen to have the Nexus in my possession, but no S4). If I use the highest possible resolution, or the one currently set if it matches a 4:3 ratio, any suggestions on how to get to 1600x1200? – Gravitoid Apr 04 '14 at 16:05
  • @Gravitoid: "I'm reluctant to say that phone isn't supported by my app" -- your vendor needs to accept reality, that not every camera will bend to their will. Cropping and/or scaling are simply inevitable. "any suggestions on how to get to 1600x1200?" -- you have to crop or scale, as I covered in my answer. There are `createBitmap()` and `createScaledBitmap()` that can handle this. – CommonsWare Apr 04 '14 at 16:18
  • "Some devices support arbitrary values" -- very weird! – Matt Logan Aug 21 '14 at 22:49