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.
Related
We can open android camera by the specify preview size, such as:
mParameter.setPreviewSize(640, 480);
mCamera.setParameter(mParameter);
The ratio above is 1.3333333333333333
Is there any way to exchange the width and height? That is to say the effect like this:
mParameter.setPreviewSize(480, 640);
mCamera.setParameter(mParameter);
The ratio is 0.75
All the video preview sizes are not support in all devices. You can only do it programmatically. If the size is not applicable then throw the media recorder stop -19 error
int width = 320;
int height = 240;
try {
//get the available sizes of the video
List<Size> tmpList = getSupportedVideoSizes();
final List<Size> sizeList = new Vector<Size>();
// compare the apsect ratio of the candidate sizes against the
// real ratio
Double aspectRatio = (Double.valueOf(getWindowManager()
.getDefaultDisplay().getHeight()) / getWindowManager()
.getDefaultDisplay().getWidth());
for (int i = tmpList.size() - 1; i > 0; i--) {
Double tmpRatio = Double.valueOf(tmpList.get(i).height)
/ tmpList.get(i).width;
if (EnableLog.LOG_TAG) {
Log.e("Width & height", tmpList.get(i).width + " x "
+ tmpList.get(i).height);
}
if (Math.abs(aspectRatio - tmpRatio) < .15) {
width = tmpList.get(i).width;
height = tmpList.get(i).height;
sizeList.add(tmpList.get(i));
}
}
if (EnableLog.LOG_TAG) {
Log.e("tmpList", tmpList + "*");
}
} catch (Exception e) {
e.printStackTrace();
}
// set the size of video.
// If the size is not applicable then throw the media recorder stop
// -19 error
mrec.setVideoSize(width, height);
/**
* This method will return the size of the video
*
* if the getSupportedVideoSizes() is return null
*
* then it is possible to get the size of video from
* getSupportedPreviewSizes()
*
* #return list of size
*/
public List<Size> getSupportedVideoSizes() {
if (params.getSupportedVideoSizes() != null) {
return params.getSupportedVideoSizes();
} else {
// Video sizes may be null, which indicates that all the supported
// preview sizes are supported for video recording.
return params.getSupportedPreviewSizes();
}
}
Try this code maybe it will help.
EDIT:
As per your comment you want to set the best preview size so you need to change the for loop. i will show you the example
change this for loop
for (int i = tmpList.size() - 1; i > 0; i--) {
}
to
for (int i = 0 ; i < tmpList.size()-1; i++) {
}
This will give you the better preview size.
I have built a module similar to vine app video recording. But I am not able to make the video size to 480x480 px . Is there any way to do that. Thanks
The Android camera has a limited list of available sizes for the camera. So we need to select best camera size and select sub image(480x480) from the original camera image.
For example on my HTC one m8 I have this sizes for the camera:
1920x1088
1920x1080
1808x1080
....
720x480
640x360
640x480
576x432
480x320
384x288
352x288
320x240
240x160
176x144
You can retrieve list of available size by use getSupportedPreviewSizes() method.
public Camera mCamera;//Your camera instance
public List<Camera.Size> cameraSizes;
private final int CAMERA_IMAGE_WIDTH = 480;
private final int CAMERA_IMAGE_HEIGHT = 480;
...
cameraSizes = mCamera.getParameters().getSupportedPreviewSizes()
After that, you need to find most suitable camera size and set preview size for the camera.
Camera.Size findBestCameraSize(int width, int height){
Camera.Size bestSize = cameraSizes.get(0);
int minimalArea = bestSize.height * bestSize.width;
for(int i = 1;i < cameraSizes.size();i++){
Camera.Size size = cameraSizes.get(i);
int area = size.height * size.width;
if(size.width < width || size.height < height){
continue;
}
if(area < minimalArea){
bestSize = size;
minimalArea = area;
}
}
return bestSize;
}
...
SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
//Do something
}
public void surfaceChanged(SurfaceHolder holder,
int format, int width,
int height) {
Camera.Parameters params = mCamera.getParameters();
Camera.Size size = findBestCameraSize(CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT);
params.setPreviewSize(size.width, size.height);
camera.setParameters(params);
if(mCamera != null){
mCamera.startPreview();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Do something
}
};
After setup camera size, we need to get sub image from result bitmap, from the camera. You need put this code where you receive a bitmap picture(Usually I use an OpenCV library and matrixes for better performance).
Bitmap imageFromCamera = //here ve receive image from camera.
Camera.Size size = mCamera.getParameters().getPreviewSize();
int x = (size.width - CAMERA_IMAGE_WIDTH)/2;
int y = (size.height - CAMERA_IMAGE_HEIGHT)/2;
Bitmap resultBitmap = null;
if(x < 0 || y < 0){
resultBitmap = imageFromCamera;
}else{
resultBitmap = Bitmap.createBitmap(imageFromCamera, x, y, CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT);
}
The video capture resolution for android are limited to the native resolutions supported by the camera.
You can try using a 3rd party library for video post processing. So you can crop or re-scale the video captured by the camera.
I am using this one
android-gpuimage-videorecording
and it works quite well.
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.
I want to take a photo using SurfaceView and PictureCallBack with the highest resolution supported.
Here is the configuration that works for my Galaxy Nexus:
Camera.Parameters params = camera.getParameters();
List<Camera.Size> sizes = params.getSupportedPictureSizes();
Toast.makeText(MainActivity.this,"Supported Sizes: " + sizes,Toast.LENGTH_LONG).show();
params.setPictureSize(2592, 1944);
params.setJpegQuality(100);
camera.setParameters(params);
The 2592 x 1944, is the best resolution for my device, but how I get from the var sizes the highest resolution for any devices?
Thank you for the help!
Loop through your list, and multiply the height and width to get the pixel count and keep a variable of the largest index
int max = 0;
int index = 0;
for (int i = 0; i < List.size(); i++){
Size s = List.get(i);
int size = s.height * s.width;
if (size > max) {
index = i;
max = size;
}
}
params.setPictureSize(List.get(index).width, List.get(index).height);
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