I use Camera.Parameters#setPictureSize() in my application to get the higher resolution image for Camera#takePicture().
I use Camera.Parameters#getSupportedPictureSizes to get this size.
This works fine, except on a Sony Xperia S : in Logcat, the only thing I get after calling Camera#takePicture() is a line saying :
Error -2147483648
I finally managed to discover that the best picture size of this phone was 4000x3000, which seemed far too big for me.
Si I tried by setting the picture size manually at 1920x1080, and takePicture worked, then.
Here is my code for getting best picture size :
private Camera.Size getBestPictureSize(int width, int height,
Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
Log.d(this.getClass().getName(), "getBestPictureSize() - s(n) = "
+ size.width + "x" + size.height);
if (result == null) {
result = size;
} else {
if (size.width > result.width) {
result = size;
}
}
}
return result;
}
So, do you know a way to get the best picture size of a device, and being sure that takePicture will work as expected ?
Maybe not the best solution, but this is what I use in a project I'm working on.-
private Size getPreferredPictureSize() {
Size res = null;
List<Size> sizes = camera.getParameters().getSupportedPictureSizes();
for (Size s : sizes) {
float ratio = (float) s.width / (float) s.height;
if (ratio == defaultCameraRatio && s.height <= PHOTO_HEIGHT_THRESHOLD) {
res = s;
break;
}
}
return res;
}
Where defaultCameraRatio is the aspect ratio of the default camera resolution; you can get it like this.-
Camera.Parameters params = camera.getParameters();
defaultCameraRatio = (float) params.getPictureSize().width / (float) params.getPictureSize().height;
And PHOTO_HEIGHT_THRESHOLD is a 'reasonable' max height, such as 960. So, in summary, you will get a smaller resolution keeping the same aspect ratio as the default one.
Related
I cannot help myself anymore, I have read every thread about this on stackoverflow, but nothing would fix my problem.
I try to set up my camera preview in a FrameLayout, everything works fine. I determine the correct size for the preview with this code:
private Camera.Size getBestPreviewSize(int width, int height,
Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
return (result);
}
Afterwards I apply it to my camera:
Camera.Parameters params = mCamera.getParameters();
Camera.Size size = getBestPreviewSize(width, height, params);
params.setPreviewSize(size.width, size.height);
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(params);
The Preview is still distorted afterwards, and my FrameLayout, which I expected to have the same size as the Preview Size I calculated, remains Fullscreen.
Fullscreen means 1920x1200
Preview Size means 1920x1080
So what I did is I set my Size of the FrameLayout manually to the calculated Preview Size. Then, however, my Preview looks even more skewed.
I have no idea what I am doing wrong. I thought when I use a supported Preview Size, this should not happen.
UPDATE:
I ran my application on another device, there everything works fine. Can this be a hardware bug? The device that is not working for me is the Nexus 7 Tablet.
I have finally come to a solution, this is really related to the hardware. There is a bug with some devices:
Bug-Report
The workaround:
This is a known low-level issue with some devices; they require that the still picture size and the preview size have matching aspect ratios, to avoid stretching artifacts.
If possible for your application, match the aspect ratios for setPreviewSize and setPictureSize.
Hope this helps you as well!
I'm having an issue with the camera on Android, basically when I take a photo with the Camera class, the taken output picture actually shows more than the preview. The best way to put it would be the preview is cropped. How would I fix this?
I've run the app on Samsung Galaxy S4, Samsung Galaxy S3, HTC One M8 and Nexus One and all exhibit this same issue. (All running Android 4.0 ICS)
The camera preview is full screen in an Activity with no Action Bar and the status bar hidden.
The camera i'm trying to create is portrait so I've set displayOrientation to 90.
The code I use for determining preview size and output photo size is as follows (I believe the problem is most likely here somewhere):
public static Camera.Size getBestAspectPreviewSize(int displayOrientation,
int width,
int height,
Camera.Parameters parameters,
double closeEnough) {
double targetRatio=(double)width / height;
Camera.Size optimalSize=null;
double minDiff=Double.MAX_VALUE;
if (displayOrientation == 90 || displayOrientation == 270) {
targetRatio=(double)height / width;
}
List<Size> sizes=parameters.getSupportedPreviewSizes();
Collections.sort(sizes,
Collections.reverseOrder(new SizeComparator()));
for (Size size : sizes) {
double ratio=(double)size.width / size.height;
if (Math.abs(ratio - targetRatio) < minDiff) {
optimalSize=size;
minDiff=Math.abs(ratio - targetRatio);
}
if (minDiff < closeEnough) {
break;
}
}
return(optimalSize);
}
public static Camera.Size getLargestPictureSize(CameraHost host,
Camera.Parameters parameters,
boolean enforceProfile) {
Camera.Size result=null;
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
// android.util.Log.d("CWAC-Camera",
// String.format("%d x %d", size.width, size.height));
if (!enforceProfile
|| (size.height <= host.getDeviceProfile()
.getMaxPictureHeight() && size.height >= host.getDeviceProfile()
.getMinPictureHeight())) {
if (result == null) {
result=size;
}
else {
int resultArea=result.width * result.height;
int newArea=size.width * size.height;
if (newArea > resultArea) {
result=size;
}
}
}
}
if (result == null && enforceProfile) {
result=getLargestPictureSize(host, parameters, false);
}
return(result);
}
Size comparator class:
private static class SizeComparator implements
Comparator<Camera.Size> {
#Override
public int compare(Size lhs, Size rhs) {
int left=lhs.width * lhs.height;
int right=rhs.width * rhs.height;
if (left < right) {
return(-1);
}
else if (left > right) {
return(1);
}
return(0);
}
}
This is using cwac-camera and using SimpleCameraHost with no changes.
Any help is appreciated!
How would I fix this?
If you are using the full-bleed preview, the preview will be cropped in most cases, as the aspect ratio of the CameraView is unlikely to match the aspect ratio of the preview frames.
Quoting the documentation:
The original default behavior of CameraFragment and CameraView was to show the entire
preview, as supplied by the underlying Camera API. Since the aspect ratio of
the preview frames may be different than the aspect ratio of the CameraView,
this results in a "letterbox" effect, where the background will show through on
one axis on the sides.
The new default behavior is to completely fill the CameraView, at the cost of
cropping off some of the actual preview frame, what is known as "full-bleed preview"
(stealing some terminology from the world of print media).
To control this behavior:
Have your CameraHost
return true or false from useFullBleedPreview()
Or, call useFullBleedPreview()
on your SimpleCameraHost.Builder, passing in a boolean value to use by default.
Note that the pictures and videos taken by this library are unaffected by
useFullBleedPreview(). Hence, if useFullBleedPreview() returns true, the
picture or video may contain additional content on the edges that was not
visible in the preview.
I am developing an android camera application. The device for which I'm developing the app has the following supported Preview sizes in descending order
1920 x 1080
1280 x 720
1088 x 1920
960 x 720
...
And the supported picture sizes are
2592 – 1944
2592 – 1728
2304 – 1296
...
and so on. I am supposed to take the best picture possible from the camera hardware. I have set camera the params as follows
private void setCameraParams(Parameters params) {
params.set("s3d-prv-frame-layout", "none");
params.set("s3d-cap-frame-layout", "none");
params.set("iso", "auto");
params.set("contrast", 100);
params.set("brightness", 50);
params.set("saturation", 100);
params.set("sharpness", 100);
params.setAntibanding("auto");
params.setPictureFormat(ImageFormat.JPEG);
params.set("jpeg-quality", 100);
// params.setJpegQuality(CameraProfile.getJpegEncodingQualityParameter(0, CameraProfile.QUALITY_HIGH));
if (params.isZoomSupported())
params.setZoom(0);
setPreviewSize(params);
setPictureSize(params);
camera.setParameters(params);
}
The methods setPreviewSize() and setPictureSize() sort the supported sizes in descending order and choose the top one (maximum, best one). Still when I take the picture and pull that file on my system the dimension that I see is 1920 x 1080 which is the same as the previewSize that I am setting. The problem is when I take the photo from the default camera and pull the file on my system, the size is 2592 x 1944 which is the max picture size. How can I take a picture from my camera with that size of the image? Any params that need to be added, I have tried with various permutations and combinations of the params but in vain. Any help would be greatly appreciated.
UPDATE: Adding code for setPictureSize() method
private void setPictureSize(Camera.Parameters params) {
if (sizes == null || resStrings == null) {
sizes = params.getSupportedPictureSizes();
if (sizes != null && !sizes.isEmpty()) {
Collections.sort(sizes, new Comparator<Camera.Size>() {
#Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
if ((lhs.width > rhs.width) && (lhs.height >= rhs.height))
return -1;
else if ((rhs.width > lhs.width) && (rhs.height >= lhs.height))
return 1;
else if ((lhs.width == rhs.width)) {
if (lhs.height > rhs.height)
return -1;
else if (rhs.height > lhs.height)
return 1;
else
return 0;
} else
return 0;
}
});
int i = 0;
resStrings = new String[sizes.size()];
for (Camera.Size temp : sizes) {
resStrings[i] = temp.width + " x " + temp.height;
i++;
Log.d("PICRES", temp.width + " x " + temp.height);
}
params.setPictureSize(sizes.get(getCameraResAtPosition(cameraResSelected)).width,
sizes.get(getCameraResAtPosition(cameraResSelected)).height);
}
} else {
params.setPictureSize(sizes.get(getCameraResAtPosition(cameraResSelected)).width,
sizes.get(getCameraResAtPosition(cameraResSelected)).height);
}
}
Try the below code,
List<Size> sizes = param.getSupportedPictureSizes();
Camera.Size size = sizes.get(0);
for (int i = 0; i < sizes.size(); i++) {
if (sizes.get(i).width > size.width)
size = sizes.get(i);
}
param.setPictureSize(size.width, size.height);
it will give the maximum picture size available.
In portrait mode, the images look vertically stretched and in landscape mode it looks horizontally stretched.
Although after capturing the image appears in proper size.
How to resolve this issue?
You need to choose a preview size that matches your display size. I would suggest changing the preview size setting to match your SurfaceView rather than the other way around. While the preview data is just fine, it's not distorted, it will look distorted when flung onto a surface with different aspect ratio.
If you have a full-screen view, then you should find the camera has a preview size matching that size -- at the least there will be one with the same aspect ratio. For example if your screen is 640x480 then a 320x240 preview size will not appear stretched on a full-screen SurfaceView.
You have to constrain your preview size based on (1) available preview sizes (2) your view. Here is my solution if you still need it:
private class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
private void startPreview() {
try {
/**
* Orientation should be adjusted, see http://stackoverflow.com/questions/20064793/how-to-fix-camera-orientation/26979987#26979987
*/
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
Camera.Size previewSize = null;
float closestRatio = Float.MAX_VALUE;
int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;
Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
for (Camera.Size candidateSize : previewSizes) {
float whRatio = candidateSize.width / (float) candidateSize.height;
if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
closestRatio = whRatio;
previewSize = candidateSize;
}
}
Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
parameters.setPreviewSize(previewSize.width, previewSize.height);
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
}
I fixed this by adding this before calling camera.startPreview():
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(yourSurfaceView.getWidth(), yourSurfaceView.getHeight());
camera.setParameters(parameters);
It might help someone.
just add the following function to set the aspect ratio and preview size
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio=(double)h / w;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Size size : sizes) {
double ratio = (double) size.getWidth() / size.getHeight();
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.getHeight() - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.getHeight() - targetHeight);
}
}
}
return optimalSize;
}
use like this
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// For still image captures, we use the largest available size.
final Size largest =
Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)),
new CompareSizesByArea());
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
/* previewSize =
chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
inputSize.getWidth(),
inputSize.getHeight());*/
previewSize = getOptimalPreviewSize(Arrays.asList(map.getOutputSizes(SurfaceTexture.class)),textureView.getWidth(),textureView.getHeight());
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
} catch (final NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
// TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and
// reuse throughout app.
ErrorDialog.newInstance(getString(R.string.camera_error))
.show(getChildFragmentManager(), FRAGMENT_DIALOG);
throw new RuntimeException(getString(R.string.camera_error));
}
The solution is simple! If you use surfaceview under actionbar this maybe the problem. I use this line and i can fix, but i not test using action bar.
use this:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
If you are talking about preview, try setting the SurfaceView's size same as Camera's preview size. That way the preview shouldn't be scaled.
Here is my surface-changed event handling code:
public void surfaceChanged(SurfaceHolder holder,
int format, int width,
int height) {
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = getBestPreviewSize(width, height,
parameters);
//...
}
private Camera.Size getBestPreviewSize(int width, int height,
Camera.Parameters parameters) {
Camera.Size result = null;
// it fails with NullPointerExceptiopn here,
// when accessing "getSupportedPreviewSizes" method:
// that means "parameters" is null
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
///...
}
}
I initialize camera like this:
#Override
public void onResume() {
super.onResume();
camera = Camera.open();
}
This problem doesn't occur on my Galaxy S Plus neither it happen on LG Optimus Black phone. Has anyone thoughts what's wrong here?
I've solved this.
parameters.getSupportedPreviewSizes()
Returns NULL on Galaxy Tab. So I just make a check if it is null and don't set new preview size in such case. To this conclusion I've came after looking into standard Camera application sources.
Looks like the camera variable was never initialized so you are calling getParameters() on null. Try calling camera = Camera.open(); first
camera initialization depends a lot on the specific device. For instance a specific Samsung device GT5500 is reporting null (width = 0, height = 0) as a valid resolution for preview, but crashes the whole phone ("hard" reboot) if you try to use it. We experienced it with mixare augmented reality engine (http://www.mixare.org) and it was PITA to debug (since we didn't have the phone and could not reproduce the bug on any other hardware).
However, about getting the "right" preview size you can take a look at our code (it's a free and open source app) on github. In the file: https://github.com/mixare/mixare/blob/master/src/org/mixare/MixView.java (row 871 and onwards)
List<Camera.Size> supportedSizes = null;
//On older devices (<1.6) the following will fail
//the camera will work nevertheless
supportedSizes = Compatibility.getSupportedPreviewSizes(parameters);
//preview form factor
float ff = (float)w/h;
Log.d("Mixare", "Screen res: w:"+ w + " h:" + h + " aspect ratio:" + ff);
//holder for the best form factor and size
float bff = 0;
int bestw = 0;
int besth = 0;
Iterator<Camera.Size> itr = supportedSizes.iterator();
//we look for the best preview size, it has to be the closest to the
//screen form factor, and be less wide than the screen itself
//the latter requirement is because the HTC Hero with update 2.1 will
//report camera preview sizes larger than the screen, and it will fail
//to initialize the camera
//other devices could work with previews larger than the screen though
while(itr.hasNext()) {
Camera.Size element = itr.next();
//current form factor
float cff = (float)element.width/element.height;
//check if the current element is a candidate to replace the best match so far
//current form factor should be closer to the bff
//preview width should be less than screen width
//preview width should be more than current bestw
//this combination will ensure that the highest resolution will win
Log.d("Mixare", "Candidate camera element: w:"+ element.width + " h:" + element.height + " aspect ratio:" + cff);
if ((ff-cff <= ff-bff) && (element.width <= w) && (element.width >= bestw)) {
bff=cff;
bestw = element.width;
besth = element.height;
}
}
Log.d("Mixare", "Chosen camera element: w:"+ bestw + " h:" + besth + " aspect ratio:" + bff);
//Some Samsung phones will end up with bestw and besth = 0 because their minimum preview size is bigger then the screen size.
//In this case, we use the default values: 480x320
if ((bestw == 0) || (besth == 0)){
Log.d("Mixare", "Using default camera parameters!");
bestw = 480;
besth = 320;
}
parameters.setPreviewSize(bestw, besth);
As you see we're not using directly the call to getSupportedPreviewSizes of the Camera class, but instead added a compatibility layer (the code is here: https://github.com/mixare/mixare/blob/master/src/org/mixare/Compatibility.java ) because we needed compatibility with older phones. If you don't want to support older android releases you can use the method of the Camera class directly.
HTH
Daniele