I'm working on Camera 2 API recently and my device has a 16:9 screen ratio but my camera sensor is 4:3. So all the preview size I got is 4:3. I' wondering is there any way I get a crop the size and only display the 16:9 part? I tried a long time and didn't find any help for camera 2.
My current camera code is similar to the camera 2 basic sample.
So how should I crop the preview and only display the 16:9 part on the texture view?
Thanks!!
You could create a SurfaceTexture with your ratio 4:3 instead of 16:9.
You could use the custom view from that sample project or create your own with the new ConstraintLayout and setting the ratio of height from 16:9 of your width (or inverse).
Then when you set the size of your texture:
texture.setDefaultBufferSize(width, height)
You will get not problems streching, because your texture is the same ratio as your camera output.
Hope this helps
I answered a similar question after having this same problem, and not being able to change the aspect ratio of the actual view to match the camera output (as Sulfkain's answer suggests).
Tl;dr: the answer lies in the configureTransform in Camera2BasicFragment. Although their function is mostly implemented for orientation, the scaling matrix can resolve scaling/aspect ratio issues with views that are the wrong shape for the camera output.
You have to do some debug on
setUpCameraOutputs(int width, int height)
and check the part that you are selecting the size of the output
// 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.
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), rotatedPreviewWidth,
rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, largest);
maybe you have to hard select something there.
Related
I am working on a camera app.The camera preview show is with 4:3 ratio,When i take a photo i get the complete image or full screen image.
Since i am showing the user a Camera Preview with 4:3 ratio, i want the user to also get the image with 4:3 ratio only.
I have seen many libraries which gives the user an option to crop, but here as i give path of the image and then it should crop the image and display it to user.
How can i achieve this ?
I don't think android has an out of the box cropper - but you can crop your own with Bitmap.createBitmap(originalBitmap, x1, y1, x2, y2)
Each Camera on each Android device has List of Preview Sizes and Picture Sizes.
Camera.Parameters parameters = camera.getParameters();
List<Size> preview_sizes = parameters.getSupportedPreviewSizes();
List<Size> pictures_sizes = parameters.getSupportedPictureSizes();
Try to loop all this Sizes and pick 4:3. So your result will be 4:3 without cropping.
Read more here.
Does anybody know how to modify the picture size using OpenCV for Android ?
It seems that sizes are set to a maximum that I didn't managed to change.
Using the tutorial ImageManipulations which is based on JavaCameraView, here are the maximum resolutions that I can get:
camera Preview Size. Width: 960 Height : 720
camera Picture Size. Width: 640 Height : 480
The problem is that I need a much higher resolution for the picture (I don't care about the preview size).
Maybe there's an answer in the opencv forum but I can't access to this answer since it seems there are works over there (OpenCVForum)
You can resize a Mat as follows:
Size szSource = new Size(640,480);
Size szResized = new Size(2592,1944);
Mat mSource= new Mat(szSource, CvType.CV_8U);// Read or Fill Something in your mSource
Mat mResised = new Mat();
Imgproc.resize( mSource, mResised, szResized,0,0,INTER_NEAREST);//mSource-> Your Source image
interpolation – interpolation method can be any of the above
INTER_NEAREST - a nearest-neighbor interpolation
INTER_LINEAR - a bilinear interpolation (used by default)
INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood
INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood
For further reference please see this.
My application needs to capture some pictures of a given size (lets say WxH) in a portrait orientation mode.
In a general case the size WxH I want is not supported by the camera therefore I will need to crop the captured picture in order to match my specifications.
This apparently simple program is driving me crazy for the problem of "good" corrispondence among preview and picture sizes and format.
Let me explain:
I need a format (let's give some numbers: 800x600) for the output image, and I have to take pictures in a portrait screen orientation. My camera by default is in Landscape mode therefore it takes pictures with a Width much larger than the height. But since I want a portrait preview I need to rotate the image and as a consequence I get images with an height much larger than the width (the transpose of the original image I guess).
In this scenario I need to cut a horizontally extended rectangle from a bigger vertically extended rectangle and I would like to do that by having an accettable large preview.
the problem of cropping the out image from the picture does not scare me (for the moment), the mean problem is the matching among what the user sees into the preview and what the camera actually captures.
for each possible phone I need to:
- chose a suitable camera picture size with respect the desired image format
- chose a suitable camera preview size with respect to the picture size and format.
- hide the preview parts that will be cropped.
And with the constraints of no distortion and large preview.
How to do it in general?
What I thought and tried:
the main algorithm steps are:
- get the optimal picture size once known the desired format
- get the optimal preview size once known the picture size
- hide the parts not capturable of the preview
- crop the image
tried method 1)
A) I get the optimal picture size by minimizing the area difference (I could also check the aspect ratio affinity is not very important). (Size is a custom type different from Camera.Size)
public Size getOptimalPictureSize(List<Camera.Size> sizes) {
Size opt = new Size();
float objf = Float.MAX_VALUE;
float v;
for(Camera.Size s : sizes){
if(s.height<target_size.width || s.width<target_size.height)
continue;
v = (s.height-target_size.width)*s.width + (s.width-target_size.height)*target_size.width;
if(v<objf){
opt.width=s.width;
opt.height=s.height;
objf=v;
}
}
return opt;
}
B) I get the optimal preview size by finding the best compromise among different aspect ratio (with respect to the picture size) :
#Override
public Size getOptimalPreviewSize(Size picSize,List<android.hardware.Camera.Size> sizes) {
Size opt = new Size();
double objf = Double.MAX_VALUE;
double aspratio = picSize.getAspectRatio();
double v;
for(Camera.Size s : sizes){
v = Math.abs( ((double)s.width)/((double)s.height) - aspratio )/(Math.max(((double)s.width)/((double)s.height), aspratio));
if(v<objf){
objf=v;
opt.width=s.width;
opt.height=s.height;
}
}
return opt;
}
C) hiding methods for displaying only capturable parts....(discussed later)
** Trial 2) **
A) I get the picture and preview sizes by minimizing an optimality functions that weights at the same time the misfit among camera image aspect ratio and desired one and the misfit among the preview and picture aspect ratio.
public void setOptimalCameraSizes(List<Camera.Size> preview_sizes,List<Camera.Size> picture_sizes,Size preview_out, Size picture_out) {
double objf=Double.MAX_VALUE;
double tmp;
for(Camera.Size pts : picture_sizes){
for(Camera.Size pws : preview_sizes){
tmp = percv(((double)pws.height)/((double)pws.width),target_size.getAspectRatio())
+ percv(((double)pws.width)/((double)pws.height),((double)pts.width)/((double)pts.height));
if(tmp<objf){
preview_out.set(pws.width, pws.height);
picture_out.set(pts.width, pts.height);
objf=tmp;
}
}
}
}
where
percv(a,b) = |a-b|/max(|a|,|b|) measures the relative deviation (and thus is dimensionless).
C) some hiding methods...
Ok this two sizes selection methods are the best I found and chose good sizes, but they have a physiological problem that comes from the camera landscape orientation... they can only produce vertical rectangular images and this implies that when I draw the preview I can get two cases:
1. I set the surface dimensions so as not distort the preview image -> due to the huge height this reflects in a very small valid area in which the image is visible (so the user experience is hurted)
2. I set the maximum possible width -> I can obtain (it depends on the preview aspect ratio) distorted previews but much bigger than in case 1.
How to avoid these problems??
What I though is work on phase C) of the algorithm (hiding phase) and I tried to:
trial 1: make the camera preview go beyond the screen sizes. This will allow me to make an arbitrary zoom in the area of interest and make the screen crop the preview. I tried using a scrollview but it didn't work and I don't know why. The topology was simple a root scrollview and inside a FrameLayout with the attached surfaceview but the surface always filled the screen leading to horrible distortions.
trial 2: capture the camera frame and manipulate them directly by overriding the onPreviewFrame(.) method: I got a misteryous error in locking the canvas (IllegalArgumentException)
How can I solve this?
I just setup a very basic camera preview that displays the camera full screen. I compared the smoothness of both my app and the android camera and figured that the android camera seems a lot smoother.
Why is that the case? Are there any special tricks to improve the camera preview performance?
I faced the same issue a time ago, and it turned out to be related with the camera resolution. Chances are that your Camera is initializing with the maximum available resolution, which may slow down performance during preview. Try and set a lower picture size with something like this.-
Camera.Parameters params = camera.getParameters();
params.setPictureSize(1280, 960);
camera.setParameters(params);
Notice that you'll need to set an available picture size. You can check available sizes with
camera.getParameters().getSupportedPictureSizes();
Hope it helps.
EDIT
It seems using a picture size with different aspect ratio than the default one, slows down performance as well. This is how I choose my pictureSize.-
First off, I get the aspect ratio of the camera default pictureSize
Camera.Parameters params = camera.getParameters();
defaultCameraRatio = (float) params.getPictureSize().width / (float) params.getPictureSize().height;
And then I get a lower pictureSize that fits the same ratio.-
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 PHOTO_HEIGHT_THRESHOLD is the max height you want to allow.
This answer is a bit late, but after struggling with the same problem, I figured I'd share what I found.
Not only was I unable to match the stock camera's "smoothness", but I was also unable to match the metering of the stock camera. My live preview was much darker compared to the stock camera, especially in low light situations.
My solution was ultimately an FPS issue, as originally proposed by Robin in an early comment.
The default preview FPS on the Nexus 5 is a static 15 FPS. The stock android app detects and sets a preview FPS that uses a dynamic range (as long as that range includes 30fps). On a nexus 5, that range is 7fps -> 30fps. In low light the camera drops to a lower FPS to keep the preview bright, while in bright conditions it jumps to a smooth 30 fps.
Relevant code from the stock android photo app:
The call to set the FPS starts on line 1560 of com.android.camera.PhotoModule (https://android.googlesource.com/platform/packages/apps/Camera2/+/android-4.4.4_r2.0.1/src/com/android/camera/PhotoModule.java).
The utility it uses to identify the ideal FPS starts on line 833 of com.android.camera.util.CameraUtil (https://android.googlesource.com/platform/packages/apps/Camera2/+/android-4.4.4_r2.0.1/src/com/android/camera/util/CameraUtil.java)
Here's are the camera api calls to set the FPS using hard coded values (for the sake of simplicity) specific to a Nexus 5.
public void setFps(Camera camera) {
Camera.Parameters params = camera.getParameters();
params.setPreviewFpsRange(7000, 30000);
camera.setParameters(params);
}
I have a game what I made in 480x320 resolution (I have set it in the build settings) in Unity. But I would like to publish my game for every Android device with every resolution. How can I do it, to tell Unity to scale my game up to the device's resolution? Is it possible to do?
Thanks in advance!
The answer to your question largely depends on how you've implemented the game. If you've created it using GUI textures, then it largely depends on how you've placed/sized your objects versus screen size, which makes things a little tricky.
If the majority of your game is done using objects (such as planes, cubes, etc) then there's two methods I usually choose to use.
1) First method is very easy to implement, though doesn't always look too good. You can simply change the camera's aspect ratio to match the one you've designed your game around. So in your case, since you've designed your game at 4:3, you'd do something like this:
Camera.aspect = 4f/3f;
However, if someone's playing on a screen meant for 16:9, the game will end up looking distorted and stretched.
2) The second method isn't as easy, requiring quite a bit of work and calculations, but will give a much cleaner looking result for you. If you're using an orthographic camera, one important thing to keep in mind is that regardless of what screen resolution is being used, the orthographic camera keeps the height at a set height and only changes the width. For example, with an orthographic camera at a size of 10, the height will be set to 2. With this in mind what you'd need to do is compensate for the widest possible camera within each level (for example, have a wide background) or dynamically change the Orthographic Size of the camera until its width matches what you've created.
If you've done a 3d game with a stereoscopic camera , screen resolution shouldn't really affect how it looks, but I guess that depends on the game, so more info would be required
The way i did is to change camera viewport according to device aspect ratio
Consider you made the game for 800x1280
The you can do this in any one of the script
float xFactor = Screen.width / 800f;
float yFactor = Screen.height / 1280f;
Camera.main.rect=new Rect(0,0,1,xFactor/yFactor);
and this works like magic
A easy way to do this is considering your target, I mean if you're doing a game for Iphone 5 then the aspect ratio is 9:16 v or 16:9 h.
public float targetRatio = 9f/16f; //The aspect ratio you did for the game.
void Start()
{
Camera cam = GetComponent<Camera>();
cam.aspect = targetRatio;
}
Here is my script for scaling the ortographic camera in 2D games
public float screenHeight = 1920f;
public float screenWidth = 1080f;
public float targetAspect = 9f / 16f;
public float orthographicSize;
private Camera mainCamera;
// Use this for initialization
void Start () {
// Initialize variables
mainCamera = Camera.main;
orthographicSize = mainCamera.orthographicSize;
// Calculating ortographic width
float orthoWidth = orthographicSize / screenHeight * screenWidth;
// Setting aspect ration
orthoWidth = orthoWidth / (targetAspect / mainCamera.aspect);
// Setting Size
Camera.main.orthographicSize = (orthoWidth / Screen.width * Screen.height);
}
I assume it's 2D instead of 3D, this what I do:
Create a Canvas object
Set the Canvas Scaler to Scale with Screen Size
Set the Reference Resolution to for example: 480x320
Set the Screen Match Mode to match width or height
Set the match to 1 if your current screen width is smaller (0 if height is smaller)
Create an Image as background inside the Canvas
Add Aspect Ratio Fitter script
Set the Aspect Mode to Fit in Parent (so the UI anchor can be anywhere)
Set the Aspect Ratio to 480/320 = 1.5
And add this snippet on main Canvas' Awake method:
var canvasScaler = GetComponent<CanvasScaler>();
var ratio = Screen.height / (float) Screen.width;
var rr = canvasScaler.referenceResolution;
canvasScaler.matchWidthOrHeight = (ratio < rr.x / rr.y) ? 1 : 0;
//Make sure to add Using Unity.UI on top of your Aspect Ratio Script!
For 3D objects you can use any of the answers above
The best solution for me is to use the theorem of intersecting lines so that there is neither a cut-off on the sides nor a distortion of the game view. That means that you have to step back or forward depending on the different aspect ratio.
If you like, I have an asset on the Unity asset store which automatically corrects the camera distance so you never have a distortion or a cut off no matter which handheld device you are using.