I have the following problem:I have an app where I'm using the camera of the android device.
I have built my own camera.The BIG problem is that when the preview starts all the image looks resized.Every object looks longer, larger...
in surfaceChanged() method I've done this:
List<Size> previews = p.getSupportedPreviewSizes();
I looped this list previews but I haven't found a better size for my preview.
The list previews has the size equal to 7 but nne of this items make my image look better!!
Here is how my method looks like:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged");
if (mPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters p = mCamera.getParameters();
List<Size> sizes = p.getSupportedPictureSizes();
List<Size> previews = p.getSupportedPreviewSizes();
Size preview = previews.get(3);
// the size of preview is 7 and looped each item....
p.setPreviewSize(preview.width,preview.height);
int f=p.getJpegQuality();
Size size = sizes.get(3);
p.setJpegQuality(f);
p.setPictureSize(size.width,size.height);
mCamera.setParameters(p);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
Anyone any idea?
Take a look at the PreviewFrameLayout class from the default Camera app. It's used to display SurfaceView using the aspect ratio it must have.
You need to maintain your aspect ratio.
Try setting your preview size to 640x480 if your device supports it. This will maintain an aspect ratio of 4:3.
If your dev doesn't support 640x480 try setting it to a different resolution which maintains an aspect ratio of 4:3.
You can set the preview size on the camera like this -
mCameraDevPara.setPreviewSize(PREVIEW_WIDTH, PREVIEW_HEIGHT);
mCameraDev.setParameters(mCameraDevPara);
Only after you set the preview size you can call the API mCameraDev.setPreviewDisplay(mSurfaceHolder); and mCameraDev.startPreview();
Related
I have a problem for my application, specifically, when I want to take a picture (from android Camera API) and send it to my server, I got some strange pictures like following example:
which contain many noise points and actually the size/resolution are very small (176*144 pixels). And this is my original code for surfaceChanged:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// start preview with new settings
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.set("orientation", "portrait");
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.e(LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
mCamera.startPreview();
}
And I tried to ask some classmates for this issue, they don't know but one of them give the following code:
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
try {
// camera.stopPreview();
// Camera.Parameters mParameters = camera.getParameters();
Camera.Parameters parameters = mCamera.getParameters();
Camera.Size optiSize = getBestPreviewSize(720, 720);
if (optiSize != null) {
parameters.setPreviewSize(optiSize.width, optiSize.height);
parameters.setPictureSize(optiSize.width, optiSize.height);
}
parameters.set("orientation", "portrait");
mCamera.setDisplayOrientation(90);
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
mCamera.setParameters(parameters);
// mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
// camera.setParameters(mParameters); // apply the changes
} catch (Exception e) {
// older phone - doesn't support these calls
}
mCamera.startPreview();
}
private Camera.Size getBestPreviewSize(int width, int height) {
List<Camera.Size> sizes = mCamera.getParameters()
.getSupportedPreviewSizes();
if (sizes == null)
return null;
Camera.Size optimalSize = null;
int tmpSize;
int minWidthDiff = 1000;
for (Camera.Size size : sizes) {
if (size.width > size.height)
tmpSize = size.height;
else
tmpSize = size.width;
if (Math.abs(tmpSize - width) < minWidthDiff) {
minWidthDiff = Math.abs(tmpSize - width);
optimalSize = size;
}
}
return optimalSize;
}
And this one works pretty well, it can store the original picture with the full resolution. Although I modify a little bit of my other codes to make the new codes compatible with my system, (add AutoFocus, for example). But I think the problem occurs because the method surfaceChanged because if I take image only with Autofocus, it still not work.
Therefore my question is: why this method will influence the quality of my image. I thought this method is only called when we "change" the surface, something like rotate the screen. But apparently it do something more than that?
Can anybody give me some help? Or some posts to explain this fact? Thank you very much in advance.
This method is called when the surface is changed, e.g. it is resized. It is common to adjust some of the camera's parameters to better fit the new configurations (such as setting an appropriate preview size).
What you are doing in the first code is you're getting the camera's Camera.Parameters, set a key-value pair (by the way, the documentation does not mention any parameter with the key "orientation" - it probably has no meaning). What you forgot to do is to apply the new parameters to the camera using Camera.setParameters(). Then again, without actually setting any valid parameters, this would have no effect.
This leads to the low-quality picture issue. You should use the Camera.Parameters object to set a desired preview and picture size. In the second block of code, this is done inside getBestPreviewSize(). A list of available preview sizes is got and the most fitting one is chosen based on the preview's size. It might be helpful to set a satisfactory picture size, too.
The thing is, different devices have different set of supported values for the parameters. If you want to provide a consistent functionality, you should check the supported values using Camera.Parameters.getSupported* methods and set them accordingly.
I have a weird problem with my Camera-SurfaceHolder.
I want to show the image of the camera in my activity.
It all works greatly on my GalaxyS1 (CyanogenMod - Android 4.4)
On my S3 (also CyanogenMod - Android 4.4) on the other hand it looks weird.
The landscape image without title bar is okay but when I show the title bar or turn it into portrait mode it looks distorted:
--- EDIT ---
Thank you Alex Cohn for your help. It looks like you are right. The preview for the portraid mode is now working nicely. But still it looks distorted on the landscape view. I checked the preview scale and it looks okay. As far as I can see I set the preview size properly, too. So what is wrong with it?
Here is the current code:
private static final String TAG = CameraView.class.getSimpleName();
private SurfaceHolder surfaceHolder;
private Camera camera;
private List<Size> mSupportedPreviewSizes;
private Size mPreviewSize;
public CameraView(Activity activity) {
super(activity);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
camera = Camera.open();
mSupportedPreviewSizes = camera.getParameters()
.getSupportedPreviewSizes();
setCameraDisplayOrientation(activity, 0, camera);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
camera.setParameters(parameters);
camera.startPreview();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
} catch (IOException exception) {
camera.release();
camera = null;
}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
camera.stopPreview();
camera.release();
camera = null;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w,
int h) {...}
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, Camera camera) {...}
--- EDIT ---
I logged the values for the onMeasure and surface Changed. They seem to be ok:
Portrait:
SURFACE CHANGED: Witdh:720 Height:1134
onMeasure: width:720 height:1134
Landscape:
SURFACE CHANGED: Witdh:1280 Height:590
onMeasure: width:1280 height:590
Preview Size: Width: 704 Height:576
I get the following possible preview sizes.
There are 8 elements in the array:
960 x 720,
1280 x 720,
1184 x 666,
960 x 640,
704 x 576,
640 x 480,
352 x 288,
320 x 240
Hopefully anyone can help!
Thanks,
Tobias
The source of your problem is the following line:
parameters.setPreviewSize(w, h);
in surfaceChanged() method. You cannot set preview size to arbitrary values in Android. You should only use the pairs of (w, h) as returned by Parameters.getSupportedPreviewSizes(). Otherwise, you hit the appearing to succeed is a valid form of undefined behavior situation. Actually, many devices will simply raise a RuntimeException on camera.setParameters(parameters), but your dev platforms chose to behave differently.
There is no requirement that the preview size be same as the surface used to display it, e.g. using 320x240 preview on a 640x480 surface is OK. But if your preview and surface have different aspect ratios, than the image will look stretched (see http://i.stack.imgur.com/VFwes.jpg). Minor distortions may be tolerable, like using 1280x720 preview on 800x400 pixel sceen, but above certain threshold, you have no choice but to only use part of the screen, leaving margins above and below the preview.
You can find what the developers community came up with: use getOptimalPreviewSize() for given width, hight (this is just one example).
I'm using camera on my app. Camera is working perfect on all devices upto Samsung S3 even. Image is correct from all other devices.
While taking image from S4 , image gets corrupted and image gets saved with some lines in horizontal.
I tried changing resolution and everything but still issue is there .
Any help
I've been pulling my hair out over this and I think I found the issue, at least with regards to my app - it's got something to do with the aspect ratio of the preview image versus the captured image.
In my case, my code was sniffing out the ideal preview size based on the aspect ratio of the screen. The S4 is a 1080p phone, so the preview image was 1920x1080, which is a 16:9 aspect ratio. But my code was hardcoded to capturing a 1600x1200 image, which is 4:3, because that's all I needed. But 1600x1200 is not one of the valid sizes the S4 supports.
Without setting the size, the S4 captured 4128x3096, which is the maximum size, and is 4:3, but the lines still appeared. Once I told the camera to capture a 16:9 photo, the lines went away. In your case, you might want to adjust the preview's aspect ratio.
Here's some code which can tell you the available sizes.
List<Camera.Size> previewSizes = p.getSupportedPreviewSizes();
int i = 1;
for (Size previewSize : previewSizes) {
Log.v("DebugCamera", "previewSize " + i++ + " width: " + previewSize.width + " height: " + previewSize.height);
}
Just tried this code on S4 and it works. Try it:
private Camera.Size getBestPreviewSize(int width, int height)
{
Camera.Size result=null;
Camera.Parameters p = camera.getParameters();
for (Camera.Size size : p.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;
}
public void surfaceCreated(SurfaceHolder holder) {
if(myCamera == null){
myCamera = getCameraInstance();
try {
myCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
// Surface will be destroyed when we return, so stop the preview.
if (myCamera != null)
{
myCamera.stopPreview();
myCamera.setPreviewCallback(null);
myCamera.release();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//This line helped me set the preview Display Orientation to Portrait
//Only works API Level 8 and higher unfortunately.
camera.setDisplayOrientation(90);
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = getBestPreviewSize(width, height);
parameters.setPreviewSize(size.width, size.height);
camera.setParameters(parameters);
camera.startPreview();
}
I have built a custom Camera App and I am trying to change the resoloution of the image that is took. I have read around that this could depend on the phone or version of Android?
I know they are set using the setParameters but just dont know how to set the actuall resoloution to work on all phones. I am wanting it to be kind of small as my app force closes otherwise. When I use a test picture at 640x348 this works so around that size/resoloution would be perfect.
It may be easier to use setPictureSize?
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
camera = Camera.open();
try {
Camera.Parameters parameters = camera.getParameters();
if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
parameters.set("orientation", "portrait");
camera.setDisplayOrientation(90);
// Uncomment for Android 2.0 and above
parameters.setRotation(90);
} else {
parameters.set("orientation", "landscape");
camera.setDisplayOrientation(0);
// Uncomment for Android 2.0 and above
parameters.setRotation(0);
}
camera.setParameters(parameters);
camera.setPreviewDisplay(holder);
} catch (IOException exception) {
camera.release();
}
camera.startPreview();
}
There is no setResolution(), only setPictureSize(). Use getSupportedPictureSizes() on Camera.Parameters to find the size you want, or use that information to populate a ListView or Spinner or something for the user to choose the desired size. Here is a sample project recently updated to use getSupportedPictureSizes() to find the smallest supported resolution and use that.
It's too easy to capture image with high quality, here you can set your own resolution:
mCamera = Camera.open();
Camera.Parameters params = mCamera.getParameters();
// Check what resolutions are supported by your camera
List<Size> sizes = params.getSupportedPictureSizes();
// Iterate through all available resolutions and choose one.
// The chosen resolution will be stored in mSize.
Size mSize;
for (Size size : sizes) {
Log.i(TAG, "Available resolution: "+size.width+" "+size.height);
mSize = size;
}
}
Log.i(TAG, "Chosen resolution: "+mSize.width+" "+mSize.height);
params.setPictureSize(mSize.width, mSize.height);
mCamera.setParameters(params);
Hope this will help you all.
I copied the code from the answer here and I still am getting a RuntimeException: setParameters failed error on my nexus one. My manifest file has camera and wake_lock permissions. This works on the emulator, and on the droid I don't get the error but it does have a rotation problem.
You're most likely requsting an invalid preview size. If you check the results of adb logcat you'll probably see something like this:
E/QualcommCameraHardware(22732): Invalid preview size requested: 480x724
The solution is to request the closest available preview size to the one you'd like; you can get a list of available preview sizes by calling getSupportedPreviewSizes in the Camera.Parameters object returned by Camera.getParameters.
I corrected this by doing what Roman said, with the code:
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size cs = sizes.get(0);
parameters.setPreviewSize(cs.width, cs.height);
camera.setParameters(parameters);
For what it's worth, the source of my issue ended up being that I was trying to call parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); without first verifying that flash modes were supported by checking that parameters.getFlashMode() != null.
There's more than one cause for this poorly documented exception, so check all of your parameters and not just that you're using a supportedPreviewSize.
None of the above solved this for me. Adding this code before setting the parameters did though.
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
//now set your parameters
For me this would happen after taking a photo and the preview would freeze, until I updated my call for parameters to be the following. It is always important with this error to make sure you check all of the parameters that the camera is asking to set to make sure that every parameter you are asking the camera to set itself to is possible for the camera.
Camera.Parameters parameters = myCamera.getParameters();
With the preview size:
if (myCamera.getParameters().getSupportedPreviewSizes() != null){
Camera.Size previewSize = getOptimalPreviewSize(myCamera.getParameters().getSupportedPreviewSizes(), width, height);;
parameters.setPreviewSize(previewSize.width, previewSize.height);
}
With the flash/focus modes:
if(parameters.getSupportedFocusModes() != null && parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)){
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
if (parameters.getSupportedFlashModes() != null && parameters.getSupportedFlashModes().contains(Camera.Parameters.FLASH_MODE_AUTO)){
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
}
myCamera.setParameters(parameters);
etc. All of this wrapped in a nice try{}catch(){} works great. Good luck.
Here is the getOptimalPreview Size from this great tutorial:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height)
{
// Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails
Camera.Size optimalSize = null;
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) height / width;
// Try to find a size match which suits the whole screen minus the menu on the left.
for (Camera.Size size : sizes){
if (size.height != width) continue;
double ratio = (double) size.width / size.height;
if (ratio <= targetRatio + ASPECT_TOLERANCE && ratio >= targetRatio - ASPECT_TOLERANCE){
optimalSize = size;
}
}
// If we cannot find the one that matches the aspect ratio, ignore the requirement.
if (optimalSize == null) {
// TODO : Backup in case we don't get a size.
}
return optimalSize;
}
the solution from Sam is correct but the output image is still zoomed a little bit on several tablet devices. One of the best practices that I found on Internet, we should set in Camera host so that the properties will be re-used each time the camera is resumed. Here is implemented method in CameraHost:
#Override
public Parameters adjustPreviewParameters(Parameters parameters) {
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size cs = sizes.get(0);
parameters.setPreviewSize(cs.width, cs.height);
return super.adjustPreviewParameters(parameters);
}
Some open source camera project like opencamera always use try-catch to call Camera.setParameters:
private void setCameraParameters(Camera.Parameters parameters) {
if( MyDebug.LOG )
Log.d(TAG, "setCameraParameters");
try {
camera.setParameters(parameters);
if( MyDebug.LOG )
Log.d(TAG, "done");
} catch (RuntimeException e) {
// just in case something has gone wrong
if( MyDebug.LOG )
Log.d(TAG, "failed to set parameters");
e.printStackTrace();
count_camera_parameters_exception++;
}
}
in addition,always get the latest getParameters before you call setParameters like this:
void setRotation(int rotation) {
Camera.Parameters parameters = this.getParameters();
parameters.setRotation(rotation);
setCameraParameters(parameters);
}