Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to take a photo of the same aspect ratio as in preview size?

99.9% of all answers on a question in title are as follows:

When you searching through the List<Camera.Size>, provided by Camera.Parameters#getSupportedPictureSizes() try to find the size of the same aspect ratio as the camera's preview size which you set via Camera.Parameters.html#setPreviewSize(int,int), or as close as possible.

That's fine, but what if I can't find the picture size of the same aspect ratio as in preview size (Android gives you no guarantee that for every preview size supported by the camera there is a picture size of the same aspect ratio), and all other picture sizes (which are as close as possible) just stretch my photo as in this and this questions? How I can make a photo of the exactly same aspect ratio as in preview, even if picture size and preview size set in Camera.Parameters have different aspect ratios?

like image 465
aga Avatar asked Jun 24 '14 19:06

aga


1 Answers

We can learn about general approach to this issue in this answer. Short quote:

My solution was to first scale the preview selection rectangle to the native camera picture size. Then, now that I know which area of the native resolution contains the content I want, I can do a similar operation to then scale that rectangle on the native resolution to the smaller picture that was actually captured per Camera.Parameters.setPictureSize.

Now, to the actual code. The easiest way to perform scaling is to use Matrix. It has the method Matrix#setRectToRect(android.graphics.RectF, android.graphics.RectF, android.graphics.Matrix.ScaleToFit) which we can use like so:

// Here previewRect is a rectangle which holds the camera's preview size,
// pictureRect and nativeResRect hold the camera's picture size and its 
// native resolution, respectively.
RectF previewRect = new RectF(0, 0, 480, 800),
      pictureRect = new RectF(0, 0, 1080, 1920),
      nativeResRect = new RectF(0, 0, 1952, 2592),
      resultRect = new RectF(0, 0, 480, 800);

final Matrix scaleMatrix = new Matrix();

// create a matrix which scales coordinates of preview size rectangle into the 
// camera's native resolution.
scaleMatrix.setRectToRect(previewRect, nativeResRect, Matrix.ScaleToFit.CENTER);

// map the result rectangle to the new coordinates
scaleMatrix.mapRect(resultRect);

// create a matrix which scales coordinates of picture size rectangle into the 
// camera's native resolution.
scaleMatrix.setRectToRect(pictureRect, nativeResRect, Matrix.ScaleToFit.CENTER);

// invert it, so that we get the matrix which downscales the rectangle from 
// the native resolution to the actual picture size
scaleMatrix.invert(scaleMatrix);

// and map the result rectangle to the coordinates in the picture size rectangle
scaleMatrix.mapRect(resultRect);

After all these manipulations the resultRect will hold the coordinates of area inside the picture taken by the camera which correspond to the exactly the same image you've seen in the preview of your app. You can cut this area from the picture by the BitmapRegionDecoder.html#decodeRegion(android.graphics.Rect, android.graphics.BitmapFactory.Options) method.

And that's it.

like image 138
aga Avatar answered Nov 15 '22 15:11

aga