Reputation: 1300
I am using CameraX for developing my android application in which when I take the pricture in landscape mode or portrait mode the image captured and saved is mirror image.
I know that FRONT camera works in the same way. But what to do if I want to save the picture in the same way it was taken?
Here is the buildUseCase()
Code I am using:
private fun buildUseCases() {
val screenAspectRatio = Rational(width, height)
val screenTargetRotation = display.rotation
//Preview
val previewConfig = PreviewConfig.Builder().apply {
setTargetAspectRatio(screenAspectRatio)
setTargetRotation(screenTargetRotation)
setLensFacing(lensFacing)
}.build()
preview = AutoFitPreviewBuilder.build(previewConfig, this)
//End - Preview
// Set up the capture use case to allow users to take photos
val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
setTargetAspectRatio(screenAspectRatio)
setTargetRotation(screenTargetRotation)
setLensFacing(lensFacing)
setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY)
}.build()
imageCapture = ImageCapture(imageCaptureConfig)
}
Please help me with what to change to get correct image captured.
Note: The camera facing is FRONT and it is in Landscape mode.
Upvotes: 3
Views: 8019
Reputation: 1154
Received advice from someone on the CameraX team about the same issue in my own code.
The thing missing from your code is reading the EXIF rotation from the saved Jpeg so you can set the rotation properly on the
ImageView
. You can do this withExifInterface
, or alternatively, just use a library that handles this for you, like Glide. The official sample, CameraXBasic, uses the Glide approach.
First, their proposal means you don't need to define setTargetRotation(screenTargetRotation)
for Preview
or ImageCapture
to yield an image that displays with the correct rotation.
In case the official sample changes, here is how it used Glide:
Glide.with().load().into()
load()
receives a reference to the captured image. If you saved it to a File
, provide a reference to that File
.
Upvotes: 1
Reputation: 372
To avoid the mirror effect of photos when taking a photo with the front camera, it is necessary to pass the metadata to the
ImageCapture.OutputFileOptions
Depending on which camera is in use, here is an example of what I do
val metadata = ImageCapture.Metadata()
metadata.isReversedHorizontal = cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
.setMetadata(metadata)
.build()
imageCapture.takePicture(outputOptions, executor, object: ImageCapture.OnImageSavedCallback {})
Upvotes: 2
Reputation: 6414
Not really an answer, but might come in very handy if you need to read EXIF information out of a byte[] instead of a already decompressed bitmap.
/**
* Returns the degrees, counted clockwise, from a byte[] instead of a already saved file.<br>
*
* @param jpeg the JPEG image byte[]
* @return Exif orientation as either <b>0</b>, <b>90</b>, <b>180</b> or <b>270</b>
*/
public static int getExifOrientation(byte[] jpeg) {
if (jpeg == null) {
return 0;
}
int offset = 0;
int length = 0;
// ISO/IEC 10918-1:1993(E)
while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
int marker = jpeg[offset] & 0xFF;
// Check if the marker is a padding.
if (marker == 0xFF) {
continue;
}
offset++;
// Check if the marker is SOI or TEM.
if (marker == 0xD8 || marker == 0x01) {
continue;
}
// Check if the marker is EOI or SOS.
if (marker == 0xD9 || marker == 0xDA) {
break;
}
// Get the length and check if it is reasonable.
length = pack(jpeg, offset, 2, false);
if (length < 2 || offset + length > jpeg.length) {
Log.e(TAG, "Invalid JPEG length");
return 0;
}
// Break if the marker is EXIF in APP1.
if (marker == 0xE1 && length >= 8 &&
pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
pack(jpeg, offset + 6, 2, false) == 0) {
offset += 8;
length -= 8;
break;
}
// Skip other markers.
offset += length;
length = 0;
}
// JEITA CP-3451 Exif Version 2.2
if (length > 8) {
// Identify the byte order.
int tag = pack(jpeg, offset, 4, false);
if (tag != 0x49492A00 && tag != 0x4D4D002A) {
Log.e(TAG, "Invalid JPEG EXIF byte order");
return 0;
}
boolean littleEndian = (tag == 0x49492A00);
// Get the offset and check if it is reasonable.
int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
if (count < 10 || count > length) {
Log.e(TAG, "Invalid JPEG EXIF offset");
return 0;
}
offset += count;
length -= count;
// Get the count and go through all the elements.
count = pack(jpeg, offset - 2, 2, littleEndian);
while (count-- > 0 && length >= 12) {
// Get the tag and check if it is orientation.
tag = pack(jpeg, offset, 2, littleEndian);
if (tag == 0x0112) {
// We do not really care about type and count, do we?
int orientation = pack(jpeg, offset + 8, 2, littleEndian);
switch (orientation) {
case 1:
return 0;
case 3:
return 180;
case 6:
return 90;
case 8:
return 270;
}
Log.i(TAG, "Unsupported EXIF orientation");
return 0;
}
offset += 12;
length -= 12;
}
}
Log.i(TAG, "EXIF Orientation not found");
return 0;
}
private static int pack(byte[] bytes, int offset, int length,
boolean littleEndian) {
int step = 1;
if (littleEndian) {
offset += length - 1;
step = -1;
}
int value = 0;
while (length-- > 0) {
value = (value << 8) | (bytes[offset] & 0xFF);
offset += step;
}
return value;
}
Upvotes: 0
Reputation: 863
You need to read EXIF data of the image created & have to write your own custom controllers as per requirements & needs. It's very normal in most Android & iOS devices that the captured images get rotated & it must be handled accordingly. In most of the devices, the default orientation of the camera is set to landscape mode, so even if you take a pic in portrait mode it gets rotated to 90degrees.
From EXIF data, you can get the degree of the image rotated or if its mirrored & then you can handle it in the backend.
To rotate your image you can try
private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException
{
ExifInterface ei = new ExifInterface(selectedImage.getPath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
private static Bitmap rotateImage(Bitmap img, int degree)
{
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
For the image flipping issue you can try this
public static Bitmap flip(Bitmap src, int type)
{
// create new matrix for transformation
Matrix matrix = new Matrix();
matrix.preScale(-1.0f, 1.0f);
// return transformed image
return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
}
And then set the image to ImageView as
imgPreview.setImageBitmap(flip(bitmap));
Upvotes: 7