I have made an app which takes pictures (portrait, landscape or selfie) using the Google Camera2 sample. Everything works, I can take the pictures, they are saved, etc.
But when I open my phone's (Samsung S7) gallery then all the selfies and portrait pictures are rotated 90 degrees. The landscape pictures are fine though. I have seen a lot of post about how to read images in the correct orientation, but what about saving them properly?
I have my own gallery in my app and there the pictures are loaded correctly (using Glide) without any special rotation fixing, so I am not sure what I did wrong and most importantly, how do I fix this?
Are you using the JPEG_ORIENTATION control in your still capture request? If not, that may be the issue - that control tells the camera device how to rotate the final JPEG image to be right-side-up.
So you need to update the value in that control to describe how the image sensor currently lines up relative to the world.
To do that calculation, you'll need input from the accelerometer (which tells you which way is down), and then some basic math - reproducing this from the link above:
private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
if (deviceOrientation == android.view.OrientationEventListener.ORIENTATION_UNKNOWN) return 0;
int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Round device orientation to a multiple of 90
deviceOrientation = (deviceOrientation + 45) / 90 * 90;
// Reverse device orientation for front-facing cameras
boolean facingFront = c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;
if (facingFront) deviceOrientation = -deviceOrientation;
// Calculate desired JPEG orientation relative to camera orientation to make
// the image upright relative to the device orientation
int jpegOrientation = (sensorOrientation + deviceOrientation + 360) % 360;
return jpegOrientation;
}
where the input deviceOrientation comes from the sensors API.
An image viewer is supposed to examine the EXIF tag and rotate the image in accordance with the orientation tag. This allows camera apps and similar software to save the images in whatever orientation they come in, without having to rotate the image before saving it.
Libraries like Glide and Picasso will do this for you, if you give them access to the full JPEG (e.g., file Uri, content Uri, https URL). That is because they have access to both the EXIF tags and the resulting ImageView, and so they can tell the ImageView to rotate the image.
However, not everybody uses such libraries. ImageView on its own deals with things like Bitmap, where those EXIF tags have been lost in the decoding process.
You, as the author of a camera app, have two main options here:
You could examine the EXIF tags, see that the image needs to be rotated, rotate it yourself, and save the rotated image (removing the orientation tag but arranging to retain the others). However, this may fail with an OutOfMemoryError, particular for camera images, as these images tend to be big.
You could shrug your shoulders and move on, as it's really not your problem. As you note, other gallery apps do not exhibit the issue, because they were tested adequately, and the original gallery app that you tried did not.
If you go with option #1, it turns into option #2 if you get the OutOfMemoryError.
Related
If I use standard camera via Intent to capture image:
Open Camera:
val takePicture = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePicture.resolveActivity(packageManager) != null) {
startActivityForResult(takePicture, TAKE_PICTURE)
}
Camera Preview
Result display on ImageView
If I use Camera2 API at: git: https://github.com/googlesamples/android-Camera2Basic
Result of Image not display portrait
Camera 2 Preview
Result display on ImageView
My code display Image:
val bitmap = BitmapFactory.decodeFile(file.toString())
imagePreview.setImageBitmap(bitmap)
}
How setting Camera2 API to display result the same standard camera?
In Camera2Basic example, the ImageSaver does not rotate the captured JPEG with regards to device orientation. Instead, Camera2BasicFragment.captureStillPicture() sets CaptureRequest.JPEG_ORIENTATION, which is only a recommendation for the camera firmware.
Camera devices may either encode this value into the JPEG EXIF header, or rotate the image data to match this orientation. When the image data is rotated, the thumbnail data will also be rotated.
Most often, this recommendation 'only' sets the header, but some devices miss even that. See a recent article on this feature and its reliability.
Please note that the EXIF orientation tag is not respected by all viewer software, therefore often the stock Camera applications do rotate the actual JPEG to default orientation.
Your code that loads the captured picture to ImageView currently ignores this tag. You can use ExifInterface.getAttributeInt(TAG_ORIENTATION) to extract the orientation from the file or input stream. Or, if you capture an image and immediately display it, you can get device orientation directly from the sensor. Now it's time to decide if the camera stored the image as portrait (i.e. width is smaller than height), or as landscape, in which case it's your duty to rotate it for display. Don't rotate the bitmap according to this orientation. Instead, you can call imagePreview.setImageMatrix() to display the image correctly.
By the way, please don't decode the JPEG to full-scale bitmap in memory if you only need it to be passed to your ImageView: this may consume too much RAM. The easiest one-liner is to call setImageURI() instead.
I am building an app that relies on showing quite a few bitmap images taken from the camera. I want all of the images to show with a 90 degree (portrait) orientation, and I know how I can do this with the EXIF information provided with each image. Would it be a better idea for me to rotate the bitmap of an image to fit my needs right after it is taken and then send it to my server, or should I send the image to my server without rotating it and then whenever I pull it down, use the EXIF rotation provided with the image to rotate the image view I am displaying it in? I need a solution that is memory efficient and fast. Thanks.
Since bitmap is just a matrix with the some data in every cell it should have the same weight (in terms of mb) if it's portrait or landscape, in case you need the images to be in a 90 deg. tilt ALL the time I would probabley do it before sending it to the server since (by my logic) you would probabley pull it ALOT more than upload it (which will only happen once..) so saving it on the way you are going to display it will basically have no effect on it's weight consumption HOWEVER since you are going to display it alot (again, by my logic since you keep it in a server) saving it alreay rotated will probabley save some other clients the hassle from doing that themselves..
I've been trying to use the Android ACTION_IMAGE_CAPTURE intent and ACTION_GET_CONTENT intent to either take a photo or pick one. The problem I'm having is that when I when I try to take a photo using the Android photo intent in portrait mode, it saves it in landscape orientation.
I'm trying to save the Bitmap of the correctly orientated photo from a URI string.
I found this question: Android Camera Intent Saving Image Landscape When Taken Portrait, which is the exact same problem I'm having, but the answer is incomplete and didn't work for me. For example, what is the resizedBitmap, opts, and is file Uri.getPath()?
Well some cameras lock the landscape mode as default mode of camera(Samsung note 2) so if you take a picture in potrait mode the image is still saved in landscape mode. Most of the camera will add metadata into the image like the camera vendor, model,etc. Amongst various metadata that can be present the one we are intrested in is the rotation data. It specifies by what degrees the image is to be rotated. For knowing the rotation you can use ExifInterface class.
resizedBitmap Images are stored as bitmap objects in android. As an image can be large loading them whole into memory can lead to outofmemory error's and make your app consume more memory. So a bitmap is first resized to appropriate size and then loaded into memory.
opts By opts you must be referring to BitmapFactory.Options method. It is a class that provides methods to change the behaviour of bitmaps like making it mutable(is set to true you can apply effects like grayscale to this bitmap) , find its height and width in pixels without loading it to RAM,etc.
file Its a class used to perform CRUD operations in any file stored in the system.
Uri.getPath() this method returns the path where your image is stored or null.
I looked everywhere for a solution but couldn't use anything useful. I'm using SurfaceView and FrameLayout for custom in-app camera. I have a button that calls "mCamera.takePicture()" function. In the Callback, I save the picture as it is; the problem is that the picture is rotated and flipped, and scaled in weird ratio.
I know that it is happening mostly because I set the camera parameter's setPreviewSize to an optimal size, and rotated it using mCamera.setDisplayOrientation(). The preview looks beautiful. But when I take a picture and save it, that's where it gets messed up.
Camera.Parameters cp = mCamera.getParameters();
Size theSize = getOptimalPreviewSize(opt, w, h);
cp.setPreviewSize(theSize.width, theSize.height);
mCamera.setDisplayOrientation(90);
mCamera.setParameters(cp);
The above code is written in surfaceChanged function.
How do I make the saved picture as it's shown in the preview?
Screenshot of what is shown in the preview of the camera. Size fine, rotation fine:
This is after the picture has been taken. Look at the monitor, collection of CDs and the plug. It's squashed and the monitor looks like a wave:
The image returned in pictureTaken() is not effected by Camera.setDisplayOrientation(). It is in Jpeg compressed format, so you can set JPEG rotation via Exif header without decoding it. This is what Camera.setRotation() often does, but on some devices it actually performs rotation in hardware. This is the most efficient method, but some viewers may still show a rotated image.
Alternatively, you can use JPEG lossless rotation, using jpegtran.
On SourceForge, there is a Java open source class LLJTran. The Android port is on GitHub.
I've always been under the impression that the preview and the final output are not connected in any way; meaning that I can set the preview to be some arbitrary dimension and that the final JPG will be whatever specific resolution I set in to be in the params, but I just ran into a very odd situation where the image data coming back in the byte[] that's in the jpg callback is different, depending on what dimensions I set my preview to.
Can someone enlighten me on what actual relationship the preview has on the final JPG? (or point me to documentation on said relationship).
TIA
[Edit]
As per ravi's answer, this was my assumption as well, however, I see no alternative but to surmise that they are, in fact, directly connected based on the evidence. I'll post code if necessary (though there's a lot of it) but here's what I'm doing.
I have a preview screen where the user takes a photo of themselves. I then display the picture captured (from the jpg callback bitmap data) in a subsequent draw view and allow them to trace a shape over their photo. I then pass the points of their polygon into a class that cuts that shape out of the original image, and gives back the cut image.
All of this works, BUT depending on how I present the PREVIEW, the polygon cutting class crashes on an array out of bounds index as it tries to access pixels on the final image that simply don't exist. This effect is produced EXCLUSIVELY by altering the shape of the preview View's dimensions. I'm not altering ANYTHING else in the code, and yet, just by mis-shaping my preview view, I can reproduce this error 100% of the time.
I can't see an explanation other than that the preview and the final are directly connected somehow, since I'm never operating on the preview's data, I only display it in a SurfaceView and then move on to deal exclusively with the data from the JPG callback following the user having taken their photo.
There is no relation between the preview resolution and the final image that is captured.
They are completely independent (at least for the still image capture). The preview resolution and the aspect ratio are not interrelated with the final image resolution and the aspect ratio in anyway.
In the camera application that I have written, the preview is always VGA but the image I capture varies from 5M to VGA (depending on the device capability)
Perhaps if you can explain the situation it would be more helpful.
We are currently developing a camera application and face very similiar problems. In our case, we want to display a 16:9 preview, while capturing a 4:3 picture. On most devices this works without any problems, but on some (e.g. Galaxy Nexus, LG Optimus 3D), the output camera picture depends on the preview you've chosen. In our case the outcoming pictures on that devices are distorted when the preview ratio is different from the picture ratio.
We tried to fix this, by changing the preview resolution to a better one just before capturing the image. But this does not work on some devices and occure error while starting the preview again after capturing is finished.
We also tried to fix this, by enlarging the SurfaceView to fullscreen-width and "over fullscreen"-height to make a 16:9 preview out of a 4:3 preview. But this does not work, because SurfaceViews can not be higher then screenheight.
So there IS any connection on SOME devices, and we really want to know, how to fix/workaround this.