I have a SurfaceView to display a live camera preview image in my Android app. The SurfaceView covers the whole width of the portrait screen, and a portion of the screen height.
I tried setting various preview sizes, so the preview had a distorted aspect ratio, short and fat or tall and skinny.
I printed debugs showing the actual preview display size, and the available preview camera sizes, so I could work out the aspect ratio error.
Screen size available for preview: w*h:1200*1646; Aspect ratio: 0.73
Rotation: 0; finalCameraRotation: 90; sideways: true
Supported Preview size: w*h:1080*1920: AspectRatio: 0.56: Error: -22.84%
Supported Preview size: w*h:768*1280: AspectRatio: 0.60: Error: -17.70%
...
Usually, I would pick the preview size with the lowest error in aspect ratio, but I was experimenting.
List<Camera.Size> ss = parameters.getSupportedPreviewSizes();
...
Camera.Size s = ss.get(pickOne);
parameters.setPreviewSize(s.width, s.height);
...
camera.setParameters(parameters);
...
camera.startPreview();
I measured the aspect ratio error of the preview display by pointing the camera at a white square and screenshotting it using Eclipse, then measuring the image of the white square in the screenshot using the photoshop ruler tool:.
I measured the x
and y
size of the square in screen pixels and computed 1-(y/x)
, and that should be the same as the aspect ratio error predicted in the debug log above.
I tried this on
- Alcatel 995, Android 4.0.4
- Samsung Note 2, Android 4.4.2
- Google Nexus 7, Android 4.4.4
- Google Nexus 5, Android 5.0
with various aspect ratios, as much as 25% in error from square, and in all of these, I measured aspect ratios in the preview within about 1% of that predicted. (There are errors, the camera might be not straight-on, it might be a bit out of focus…)
Except for the Google Nexus 7.
No matter what preview size I set for the Nexus 7, the measured aspect ratio was about 2% too tall, which just happens to be the best possible aspect ratio available:
Supported Preview size: w*h:768*1024: AspectRatio: 0.75: Error: 2.87%
It’s as though some other program was coming and fixing the optimal preview size after I set it. I actually put in code to wait 10 seconds and read back the preview size from the camera, and it was the one I set, yet the display didn’t reflect that. The display is always optimal no matter what preview size I set.
Is there something weird about preview sizes on Nexus 7?
I know that my setPreviewSize
is doing something, because I can set the preview to 144*176px and then I see odd upsampling pixellation artefacts in the display. But it still has the nearly-corrrect aspect ratio!
2
Answers
I’ve just stumbled upon this problem myself. Reportedly, this happens to some devices, when camera’s picture size and preview size parameters have different aspect ratios. In my case, my Nexus 7 appeared to always use a 4:3 preview, no matter what I’d set. Also,
getPreviewSize()
reported the size I’d set, not the one really used.The solution is to find a picture size with an aspect ratio matching preview’s one. So, get a list of supported picture sizes with
getSupportedPictureSizes()
, choose one with a proper width/height ratio and thensetPictureSize(int width, int height)
. It worked for me like a charm 🙂Answer: yes, there’s something weird …
The bug of the Nexus 7 is that all its preview sizes contain 4:3 image data, stretched to the size of the preview frames. In other words: The content of Nexus 7 viewfinder frames is always what you’d get by scaling a 1920×1440 px image1 (4:3) non-proportionally to the selected live camera preview size. Which means scaling by stretching as necessary, without cropping anything off.
Now here’s why you observed the behaviour described in the question: by chance, your
1646×1200
of available screen space is nearly 4:3. So scaling the 4:3 image data provided by the Nexus 7 to that size will always result in the low aspect ratio error observed (2.87%). The intermediate step of scaling to preview size does not change anything, as it is undone by the following scaling to the available screen size.It’s a bug
You were lucky that your available screen size is 4:3, leading to proper previews from the Nexus 7. Everyone else however will fight with the fact that previews from Nexus 7 displayed in their indicated, natural size will appear distorted.
For example: the Nexus 7 reports many available preview sizes with aspect ratio that is not 4:3; let’s take 1920×1080 (aspect ratio 1.78). Displaying the 4:3 frame content in a 1920×1080 rectangle then leads to stretched images.
So definitely there is a bug in the preview behaviour of the Nexus 7. The error seems to be that the 4:3 previews are meant for still images and 19:10 previews for video images, but the frames then erroneously always contain 4:3 image data. Or the other way around: the camera reports both types of preview sizes while its current mode only allows either 4:3 or 19:10 previews.
Workaround
There must be a proper way to work around this, as the Nexus 7 native camera app in video mode provides a 19:10 preview without distortions (means, it does not contain 4:3 image data).
As reported by @fljau in the other answer, setting the camera’s still image capture resolution to match the aspect ratio of your selected preview will fix the issue. You can then display the camera live preview in its natural, reported size and will see no distortions of circular objects on the screen.
I just did this in Qt QML as follows:
It is not necessary to set
Camera { captureMode: CaptureStillImage }
for this to work. The still image resolution will affect the preview image data for anycaptureMode
as a side effect (making it more clear that this is indeed a bug).For a generic, model-independent solution in QML, you could go through the available viewfinder resolutions and then through the available still image resolutions and then set
imageCapture.resolution
as above to a value that matches the largest (resp. your desired) viewfinder resolution in aspect ratio.Alternative workarounds
When you know the actual aspect ratio of the data inside the preview frames, you could show your camera live preview non-proportionally scaled to match this aspect ratio. Basically what the question author did accidentally (within a 2.87% error margin).
I also tried setting capture mode to "video" in the hopes that it will lead to 19:10 "video style" preview frame content does not work though. (For reference, I tried it in Qt QML with
Camera { captureMode: Camera.CaptureVideo }
.) That did not work.1 A 1920×1440 px image is simply a 4:3 image large enough to cover the maximum supported preview size of 1920×1080 px. I made that size up for the sake of the argument so that scaling will not lose pixels you’d see in the preview.