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.
Related
I have searched about fitting camera preview and surface view but I couldn't find about not stretching image while saving it in android studio.
The camera works well on showing on the surface view of the devices. The ratios of the supported screen sizes and surface view are ok.
The problem is that after capturing the image it takes more from all sides of the surface view of the screen. It just does not take the view that is shown on the surface view but more in all sides of the devices while saving the image.
The problem is only solved when height of the surface view and supported screen sizes are equal. Such as:
Supported size is:
1280/720
Surface view is:
405/720
But this leads to a problem which limits the height and width of the surface view. I want height of surface view to be longer and width to be match parent.
So, the problem is not solved without having the heights equal. I want height of the surface view to be longer and width to be match parent.
Can anyone help me with this,
Thanks everyone,
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public int measurewidth, measureheigh;
public CameraPreview(Context context, Camera camera) {
super(context);
mContext = context;
mCamera = camera;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
for(Camera.Size str: mSupportedPreviewSizes)
Log.e(TAG, str.width + "/" + str.height);
// 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// empty. surfaceChanged will take care of stuff
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + h);
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or reformatting changes here
// start preview with new settings
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width ,mPreviewSize.height); //B
// parameters.setPreviewSize(1280,720 );
mCamera.setParameters(parameters);
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
Log.e(TAG, "REAL SCREEN SIZE => w=" + width + ", h=" + height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
Log.e(TAG, "MPREVIEW SIZE OPTIMAL => w=" + mPreviewSize.width + ", h=" + mPreviewSize.height);
float ratio;
if(mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
Log.e(TAG, "Ration => R=" + ratio);
// One of these methods should be used, second method squishes preview slightly
//setMeasuredDimension(1080, 1200);
//setMeasuredDimension(mPreviewSize.height, mPreviewSize.width); //B
setMeasuredDimension(width, (int)(width*ratio)); //B
// setMeasuredDimension(720, 1280);
// setMeasuredDimension((int) (width ), height);
measurewidth = width ; // this is for bitmap width
measureheigh = (int)(width*ratio); // this is for bitmap height
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { // long screen
final double ASPECT_TOLERANCE = 0.1;
double targetRatio=(double)h / w;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.width - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.width - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.width - targetHeight) < minDiff) {// size.height was changed with size.width
optimalSize = size;
minDiff = Math.abs(size.width - targetHeight); // size.height was changed with size.width
}
}
}
return optimalSize;
}
}
After checking through your code (and the problem you are facing), I have decided to not focus on the coding part. Instead, I will give you an overview of what's happening between a preview and a snap (take picture).
Overview
In legacy camera API, there are 2 very important Camera.Size that you would need to take care - preview size and picture size.
From Camera.Parameters API, you are able to get 2 separate list of supported sizes by calling Parameters.getSupportedPreviewSizes() and Parameters.getSupportedPicturSizes().
As the names imply, each size list is dedicated for different purpose - preview and take picture.
Sample case
Imagine you have the list like this:
Preview size: 1920x1080 (16:9), 1280x960 (4:3)
Picture size: 3840x2160 (16:9), 1920x1440 (4:3)
Say, you want to take a picture in 3840x2160 (16:9), you call Parameters.setPictureSize(3840, 2160) to tell the camera you want to take a picture in this resolution when you call takePicture(...).
Solve equation
Now, how are you going to show a preview with the same resolution on the screen (or viewfinder)?
Yes, you guessed it - a preview size with the same aspect ratio (AR).
We need to find out the AR of the (target) picture size and find a matching preview size with the same AR and call Parameters.setPreviewSize(width, height) when we are ready.
In our case, we will select the preview size 1920x1080 as it has the same AR (16:9) as the picture size.
Different AR?
What happened if you want to take a picture of 16:9 but is setting the preview size to 4:3?
The answer is obvious, user will not be able to snap a picture in WYSIWYG style.
Points to note
Selecting a picture size and deciding on the preview size is not really difficult. Still, there are more you need to take care of to use the camera properly.
The orientation of the camera (CameraInfo.orientation) as opposed to the device natural orientation. Remember to call `Parameters.setRotation(rotation) when necessary.
In cases where the camera supports the same resolution in different orientation (eg. 1920x1080 and 1080x1920), call `Parameters.setPreviewSize(width, height) with the orientation you wish to take the picture in.
Hope this helps.
I have found out that when using a textureview instead of a surfaceview as a camera preview (both hooked up to the camera via a mediarecorder) then the preview is much more fuzzy.
What I mean by fuzzy is that in a texture view you can see the pixels, especially when zooming. That is not the case when using a surfaceview. Why is that the case?
UPD:
Sorry,but after I re-write my shit code, the key is the preview size too small that caused "fuzziness", so you should set a reasonable preview Size,not the reason strikeout below, but auto-focus is suggested ...
Size size = getBestSupportSize(parameters.getSupportedPreviewSizes(), width, height);
parameters.setPreviewSize(size.width, size.height);
As to the method getBestSupportSize(), how to get the bestSize for your project needs, in this case, it is as large as the screen width andhe ratio is 4/3 your's may be some other, I calculate the ration dividing width/height.
private Size getBestSupportSize(List<Size> sizes, int width, int height) {
Size bestsize = sizes.get(0);
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int dt = Integer.MAX_VALUE;
for (int i = sizes.size() - 1; i >= 0; i--) {
Log.d(TAG, "-index : " + i);
Size s = sizes.get(i);
if (s.width * 3.0f / 4 == s.height) {
int newDT = Math.abs(screenWidth - s.width);
if (newDT < dt && screenWidth < s.width) {
dt = newDT;
bestsize = s;
}
}
}
return bestsize;//note that if no "4/3" size supported,default return size[0]
}
So this "fuzziness" was caused by a small previewSize calcualate a best size for the camera using this getSupportedPreviewSizes() method
And I will keep the autoFocus snippet below, strikeout though, FYR if is needed.
Well i got the solution for this "fuzzy" problem,and my case is just using TextureView andsurfaceTexture to take a pic instead of old surfaceView withsurfaceHolderway.
The key is set this mCamera.autofocus(), why the pic is"fuzzy" is bacause we lack of this autoFocus setting.
like below :
mCamera.setPreviewTexture(surface);
//enable autoFocus if moving
mCamera.setAutoFocusMoveCallback(new AutoFocusMoveCallback() {
#Override
public void onAutoFocusMoving(boolean start, Camera camera) {
if (start) { //true means you are moving the camera
mCamera.autoFocus(myAutoFocus);
}
}
});
mCamera.startPreview();
The autoFocusCallback like this:
AutoFocusCallback myAutoFocus = new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
}
};
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.
For my application I am using android native camera and previewing the image using surface view. in my case everything is working except the camera orientation. When I open the camera by setting screenOrientation="landscape on manifest file I am getting the preview without any problem in landscape mode. But I need to take image in portrait mode, for this I changed my manifest like android:screenOrientation="portrait" and change my code like mCamera.setDisplayOrientation(90), params.set("orientation", "landscape"),params.set("rotation", 90), but still I am getting 90 degree rotated image.
And my code is
public void setupCamera(int width, int height) {
Log.i(TAG, "setupCamera");
synchronized (this) {
if (mCamera != null) {
Camera.Parameters params = mCamera.getParameters();
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
List<Camera.Size> imgsize=params.getSupportedPictureSizes();
mFrameWidth = width;
mFrameHeight = height;
// mCamera.setDisplayOrientation(90);
params.set("orientation", "landscape");
params.set("rotation", 90);
// selecting optimal camera preview size
{
int minDiff = Integer.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - height) < minDiff) {
mFrameWidth = size.width;
mFrameHeight = size.height;
minDiff = Math.abs(size.height - height);
}
}
}
params.setPreviewSize(getFrameWidth(), getFrameHeight());
List<String> FocusModes = params.getSupportedFocusModes();
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
{
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
mCamera.setParameters(params);
mCamera.startPreview();
}
}
}
I am using Micromax A 52 model...
Any one please help.....
If your application runs on v2.2 or above you can rotate camera orientation to portrait using camera.setDisplayOrientation(90).
For others:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//After opening camera - call via reflection
Method rotateMethod = android.hardware.Camera.class.getMethod("setDisplayOrientation", int.class);
rotateMethod.invoke(mCamera, 90);
for more details please refer this link and this Hope this will be helpful
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);
}