Reliable method for getting properly rotated bitmap from camera/gallery - android

This is an issue that has plagued me for MONTHS. I've seen all the SO posts there are about rotating an image chosen from the gallery or taken via image capture intent, but none have worked. The big offender is, of course, Samsung devices, but I've even seen some funky behavior on my Nexus.
I'm using intents to choose images from gallery and camera, but it seems that landscape photos always seem to come in rotated 90 degrees. The first step is always ExifInterface, something like this:
ExifInterface exif = new ExifInterface(imageUri.getLastPathSegment();
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationNeeded = getRotationFromExifData(orientation);
Matrix matrix = new Matrix();
matrix.setRotate(rotationNeeded);
Where getRotationFromExifData(orientation) is:
private static int getRotationFromExifData(int orientation) {
Log.d(LOG_TAG, "Orientation: " + orientation);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
Log.d(LOG_TAG, "Exif returned 90");
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
Log.d(LOG_TAG, "Exif returned 180");
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
Log.d(LOG_TAG, "Exif returned 270");
return 270;
default: // Normal or 0
Log.d(LOG_TAG, "Exif returned 0");
return 0;
}
}
This doesn't work, ExifInterface (from what I'm seeing) always returns 0 (or normal), especially on Samsung devices.
Other steps include querying the MediaStore and other junk that doesn't work. Can somebody please tell me what the exact way to get proper image rotation using all native Intents, so all images are displayed correctly? Thanks in advance.

Not sure about the photos selected from gallery but for that taken directly from camera, you can try this:
In your activity/fragment to capture a photo, try adding and enabling an OrientationEventListener in onResume(), and disabling it in onPause().
Here is the onResume()
#Override
public void onResume() {
super.onResume();
orientationListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int angle) {
if (angle > 315 || angle <= 45) {
rotation = 90;
} else if (angle > 45 && angle <= 135) {
rotation = 180;
} else if (angle > 135 && angle <= 225) {
rotation = 270;
} else if (angle > 225 && angle <= 315) {
rotation = 0;
} else {
rotation = -1;// UNKNOWN
}
if (currentFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
rotation = (360 - rotation) % 360;
}
Log.i("Orientation", "rotation = " + rotation);
}
};
if (orientationListener.canDetectOrientation()) {
orientationListener.enable();
}
}
Here is the onPause()
#Override
public void onPause() {
//...
orientationListener.disable();
super.onPause();
}
When you take the photo using your camera, use that rotation to rotate the bitmap accordingly, as following:
camera.takePicture(null, null, null, new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Bitmap theBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if (rotation != -1) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
theBitmap = Bitmap.createBitmap(theBitmap, 0, 0, theBitmap.getWidth(), theBitmap.getHeight(), matrix, false);
}
// Save the bitmap here...
}
}
Hope this helps.

Related

Camera Source (Google Mobile Vision) returns rotated image on some devices

I have the open sourced code for the Google Mobile Vision - CameraSource and this is the method I call to click a photo : cameraSource.takePicture();
In the open sourced version of CameraSource.java, the method for determining screen orientation is the stock one:
private void setRotation(Camera camera, Camera.Parameters parameters, int cameraId) {
WindowManager windowManager =
(WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
int degrees = 0;
int rotation = windowManager.getDefaultDisplay().getRotation();
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
default:
Log.e(TAG, "Bad rotation value: " + rotation);
}
CameraInfo cameraInfo = new CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
int angle;
int displayAngle;
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
angle = (cameraInfo.orientation + degrees) % 360;
displayAngle = (360 - angle) % 360; // compensate for it being mirrored
} else { // back-facing
angle = (cameraInfo.orientation - degrees + 360) % 360;
displayAngle = angle;
}
// This corresponds to the rotation constants in {#link Frame}.
mRotation = angle / 90;
camera.setDisplayOrientation(displayAngle);
parameters.setRotation(angle);
}
Here, the displayAngle and the angle are the same for Samsung, Lenovo and Yuntab H8. But the bitmap returned for backCamera is rotated differently in each of the device. I have to manually rotate the bitmap for each of the devices (Samsung : 90, Lenovo : 0 and Yuntab : 180)
My requirement is that onPictureTaken should return a bitmap which matches the current display orientation. I am looking into this, since a long time but yet have to figure a way to the solution. Here below is my onPicturetaken() (called after taking picture):
#Override
public void onPictureTaken(byte[] bytes) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, currentCameraId == 0 ? options : null);
}catch (Exception ex){
ex.printStackTrace();
Log.e("PictureTaken",ex.toString());
}
};
You should rotate the image when it is already saved in the device.
Then you can rotate it to match the position it was when the photo was taken.
Example code (It might need some cleanup and improvement, but it works...):
Method do calculate the rotation an image has:
private static int rotationNeeded(String path) {
try {
File file = new File(path);
if (!file.getName().contains(".jpg")) {
return 0;
}
ExifInterface exif = new ExifInterface(file.getAbsolutePath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
}
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
Apply the rotation needed to the image:
public static void rotateImage(String filePath) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
//check image rotation
int rotate = rotationNeeded(filePath);
if (rotate != 0) {
//rotate image if needed
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
Bitmap rotatedImage = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
bitmap.recycle();
bitmap = rotatedImage;
//save image
byte[] dataPicture = bao.toByteArray();
FileOutputStream fos = new FileOutputStream(filePath);
fos.write(dataPicture);
fos.flush();
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}

How to get the Android device's physical orientation in degrees.

I know that I have to use the class OrientationListener to get the angle from the device. I want to get the angle between -90° and 90°. I don't know how to do it.
picture on the left: 90 degree, picture in the middle: 0 degree and picture on the right: -90 degree
Code
class OrientationListener implements SensorEventListener
{
#Override
public void onSensorChanged(SensorEvent event)
{
angle = Math.round(event.values[2]);
if (angle < 0)
{
angle = angle * -1;
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
}
You can use this code for simple 0, 90, 180 degrees.
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.getRotation();
Surface.ROTATION_0 is 0 degrees, Surface,ROTATION_90 is 90 degrees etc.
You can also use SensorEventListener interface if you want the degrees other than 0, 90, etc:
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float rawX = event.values[0];
}
}
You'd need to get use this code to get the degrees:
double k = 90/9.8;
double degrees = rawX * k; // this is a rough estimate of degrees, converted from gravity
This is a working example.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
setContentView(tv);
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.getRotation();
String rotString="";
switch(rotation) {
case Surface.ROTATION_0:
rotString="portrait";
break;
case Surface.ROTATION_90:
rotString="landscape left";
break;
case Surface.ROTATION_180:
rotString="flipped portrait";
break;
case Surface.ROTATION_270:
rotString="landscape right";
break;
}
tv.setText(rotString);
}
This is an old question, but there is an OrientationEventListener() I found while trying to figure out the rotation for a camera in degrees.
public void onOrientationChanged(int orientation) {
if (orientation == ORIENTATION_UNKNOWN) return;
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
orientation = (orientation + 45) / 90 * 90;
int rotation = 0;
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + orientation) % 360;
}
mParameters.setRotation(rotation);
}
The answers here actually help me as I am not capturing the onConfigurationChanged() as discussed in the Handling Runtime Changes Yourself topic in the developer guide.
I am simply going to use the display.getRotation() method presented by Elduderino here in onSurfaceCreated() of MyActivity to set the rotation of the camera appropriately. This will work as the surface gets recreated by default whenever the device orientation changes.

issue with image rotation on android devices

I created a custom camera activity in my Android app but am having issues with images being rotated 90 degrees based on which device it's taken on. This only happens on particular devices and I can't seem to figure out why.
What I do in the app is
Update the variable "mOrientation" which saves the current orientation of the phone in degrees (0-360)
When the user taps the shutter button, I check what the last saved orientation was and rotate the image accordingly before saving it.
I've tested on two different devices, a Huawei phone and a Samsung Galaxy S II. What I'm finding is that while I'm able to determine that the orientation of the phone is being updated correctly and that both have a default portrait orientation, they require different "mOrientation" values, offset by 90 degrees, in order to be oriented correctly when saved.
Here's how I check the current orientation:
public void onOrientationChanged(int orientation) {
if(cameraPreview.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT){
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
}else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
}else { // orientation <135 && orientation > 45
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
}else{
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
}else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
}else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}else { // orientation <135 && orientation > 45
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
}
And here's how I save the image:
private PictureCallback myPictureCallback_JPG = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera cam) {
Log.d(TAG, "saving picture");
Log.d(TAG, "portrait image? " + Boolean.toString(cameraPreview.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT));
Log.d(TAG, "mOrientaiton: " + mOrientation);
//Degrees to rotate image by when saving
int degree = 0;
// do not rotate image, just put rotation info in
if(cameraPreview.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT){
switch (mOrientation) {
case ORIENTATION_LANDSCAPE_INVERTED:
Log.d(TAG, "orientation landscape inverted");
degree = 90;
break;
case ORIENTATION_PORTRAIT_NORMAL:
Log.d(TAG, "orientation portrait normal");
degree = 0;
break;
case ORIENTATION_LANDSCAPE_NORMAL:
Log.d(TAG, "orientation landscape normal");
degree = 270;
break;
case ORIENTATION_PORTRAIT_INVERTED:
Log.d(TAG, "orientation portrait inverted");
degree = 180;
break;
}
}else{
switch (mOrientation) {
case ORIENTATION_LANDSCAPE_INVERTED:
degree = 270;
break;
case ORIENTATION_PORTRAIT_NORMAL:
degree = 180;
break;
case ORIENTATION_LANDSCAPE_NORMAL:
degree = 0;
break;
case ORIENTATION_PORTRAIT_INVERTED:
degree = 90;
break;
}
}
Bitmap bMap;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 6;
options.inDither = false; // Disable Dithering mode
options.inPurgeable = true; // Tell to gc that whether it needs free
// memory, the Bitmap can be cleared
options.inInputShareable = true; // Which kind of reference will be
// used to recover the Bitmap
// data after being clear, when
// it will be used in the future
options.inTempStorage = new byte[32 * 1024];
options.inPreferredConfig = Bitmap.Config.RGB_565;
bMap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
if(degree != 0){
bMap = rotate(bMap, degree);
}
//Getting the picture's unique file to be added to the folder
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
/*
* This catches errors when creating the output file to put the picture in
*/
if (pictureFile == null) {
Log.d(TAG,"Error creating media file, check storage permissions");
//After the camera has finished taking the picture
handler.post(new Runnable(){
#Override
public void run() {
//Remove white border from preview
Log.d(TAG, "removing white background from preview");
cameraPreview.setBackgroundResource(0);
}
});
//Setting the onclicklistener back on the shutter
shutter.setOnClickListener(shutterClickListener);
//Restarting the preview as soon as picture is done
camera.startPreview();
Toast.makeText(getApplicationContext(), "Error saving file. Try restarting the camera.", Toast.LENGTH_LONG).show();
return;
}
FileOutputStream out = null;
try {
//Saving the image
out = new FileOutputStream(pictureFile);
bMap.compress(Bitmap.CompressFormat.JPEG, 90, out);
if (bMap != null) {
bMap.recycle();
bMap = null;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
try {
//Assuring we always close the output stream
if(out!=null){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
myHorizontalLayout.add(Uri.parse(pictureFile.getPath()));
MediaScannerConnection.scanFile(getApplicationContext(), new String[] { Environment.getExternalStorageDirectory().toString() }, null, new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri)
{
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
camera.startPreview();
if(myHorizontalLayout.getItemList().size() == 1){
//Have added the first item to the pictures set check to green
MenuItem check = menu.findItem(R.id.save);
}
//After the camera has finished taking the picture
handler.post(new Runnable(){
#Override
public void run() {
//Remove white border from preview
Log.d(TAG, "removing white background");
cameraPreview.setBackgroundResource(0);
}
});
//Setting the onclicklistener back on the shutter
shutter.setOnClickListener(shutterClickListener);
}
});
}
};
I would be really grateful if anyone could help me with this. Also, if anyone has general advice on how to test camera configurations across Android devices without having to own many different makes of phones, I would appreciate that too.
am having issues with images being rotated 90 degrees based on which device it's taken on
Your pictures are probably technically fine. Some devices do not rotate the image themselves, but instead set an EXIF header telling the image viewer to rotate the image. Not all image viewers do this.
Also, if anyone has general advice on how to test camera configurations across Android devices without having to own many different makes of phones, I would appreciate that too.
Rely on somebody else to do that work for you. :-)

Android camera resulted image should be rotated after the capture?

I'm writing an Android application that uses the camera.
I'm setting camera display orientation to 90, my activity is in a portrait orientation:
camera.setDisplayOrientation(90);
I'm getting a well oriented preview picture, but the resulting image is rotated to -90 degrees (counter clockwise) and
exif.getAttribute(ExifInterface.TAG_ORIENTATION)
returns ORIENTATION_NORMAL
Is it expected behavior? Should I rotate resulted image after the capture?
Device - Nexus S, API - 10
Try this
try {
File f = new File(imagePath);
ExifInterface exif = new ExifInterface(f.getPath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
int angle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
angle = 90;
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
angle = 180;
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
angle = 270;
}
Matrix mat = new Matrix();
mat.postRotate(angle);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f),
null, options);
bitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(),
bmp.getHeight(), mat, true);
ByteArrayOutputStream outstudentstreamOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100,
outstudentstreamOutputStream);
imageView.setImageBitmap(bitmap);
} catch (IOException e) {
Log.w("TAG", "-- Error in setting image");
} catch (OutOfMemoryError oom) {
Log.w("TAG", "-- OOM Error in setting image");
}
It will work
Problem is the camera orientation is a complete disaster (as is capturing an image) because OEMs do not adhere to the standard. HTC phones do things one way, Samsung phones do it a different way, the Nexus line seems to adhere no matter which vendor, CM7 based ROMs I think follow the standard no matter which hardware, but you get the idea. You sort of have to determine what to do based on the phone/ROM. See discussion here: Android camera unexplainable rotation on capture for some devices (not in EXIF)
I had the same problem like you, but i've fix it.
You should use the same code:
Camera.Parameters parameters = camera.getParameters();
parameters.setRotation(90);
camera.setParameters(parameters);
I hope you can use this code too.
camera.setDisplayOrientation(90);
I have coded the app for only Portrait Mode.
Will make the Camera to rotate to 90 degree and This may result in not suitable for all devices in android
In order to get the Correct Preview for all android devices use the following code which is refereed in developers site.
Below you have to send your activity, cameraId = back is 0 and for Front camera is 1
public static void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
//int currentapiVersion = android.os.Build.VERSION.SDK_INT;
// do something for phones running an SDK before lollipop
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
This is how to set the setDisplayOrientation for camera
Now you may have trouble is saving the captured Image in Correct Orientation, which is bug in Camera API to support all devices in android .you can overcome using the Steps below
PLS NOTE EXIF VALUE WILL NOT GIVE YOU CORRECT VALUE IN ALL DEVICES , So this would help you
int CameraEyeValue = setPhotoOrientation(CameraActivity.this, cameraFront==true ? 1:0); // CameraID = 1 : front 0:back
By using the same concept we used before for DisplayOrientation
public int setPhotoOrientation(Activity activity, int cameraId) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
// do something for phones running an SDK before lollipop
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
So your final PictureCallBack method should look like
private PictureCallback getPictureCallback() {
PictureCallback picture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//make a new picture file
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
return;
}
try {
//write the file
FileOutputStream fos = new FileOutputStream(pictureFile);
Bitmap bm=null;
// COnverting ByteArray to Bitmap - >Rotate and Convert back to Data
if (data != null) {
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int screenHeight = getResources().getDisplayMetrics().heightPixels;
bm = BitmapFactory.decodeByteArray(data, 0, (data != null) ? data.length : 0);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
// Notice that width and height are reversed
Bitmap scaled = Bitmap.createScaledBitmap(bm, screenHeight, screenWidth, true);
int w = scaled.getWidth();
int h = scaled.getHeight();
// Setting post rotate to 90
Matrix mtx = new Matrix();
int CameraEyeValue = setPhotoOrientation(AndroidCameraExample.this, cameraFront==true ? 1:0); // CameraID = 1 : front 0:back
if(cameraFront) { // As Front camera is Mirrored so Fliping the Orientation
if (CameraEyeValue == 270) {
mtx.postRotate(90);
} else if (CameraEyeValue == 90) {
mtx.postRotate(270);
}
}else{
mtx.postRotate(CameraEyeValue); // CameraEyeValue is default to Display Rotation
}
bm = Bitmap.createBitmap(scaled, 0, 0, w, h, mtx, true);
}else{// LANDSCAPE MODE
//No need to reverse width and height
Bitmap scaled = Bitmap.createScaledBitmap(bm, screenWidth, screenHeight, true);
bm=scaled;
}
}
// COnverting the Die photo to Bitmap
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
fos.write(byteArray);
//fos.write(data);
fos.close();
Toast toast = Toast.makeText(myContext, "Picture saved: " + pictureFile.getName(), Toast.LENGTH_LONG);
toast.show();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
//refresh camera to continue preview
mPreview.refreshCamera(mCamera);
mPreview.setCameraDisplayOrientation(CameraActivity.this,GlobalCameraId,mCamera);
}
};
return picture;
}
As the Works only for Portrait mode using Front and Back camera The Picture is rotated to only portrait mode with correct portrait Orientation in all android devices .
For LandScape you can Make this as reference and make changes in the below block
if(cameraFront) { // As Front camera is Mirrored so Fliping the Orientation
if (CameraEyeValue == 270) {
mtx.postRotate(90); //change Here
} else if (CameraEyeValue == 90) {
mtx.postRotate(270);//change Here
}
}else{
mtx.postRotate(CameraEyeValue); // CameraEyeValue is default to Display Rotation //change Here
}

android camera surfaceview orientation

Ok so I have a class that extends SurfaceView and overrides
surfaceChanged - just calls startPreview
surfaceCreated - opens camera, edits params *, sets surfaceHolder
surfaceDestroyed - calls stopPreview, release camera
this all work great because when the orientation is Portrait:
from surfaceCreated *
m_camera = Camera.open();
Camera.Parameters p = m_camera.getParameters();
if (getResources().getConfiguration().orientation !=
Configuration.ORIENTATION_LANDSCAPE)
{
p.set("orientation", "portrait");
// CameraApi is a wrapper to check for backwards compatibility
if (CameraApi.isSetRotationSupported())
{
CameraApi.setRotation(p, 90);
}
}
However, everytime the orientation changes it calls Camera.open()... which as you may know is quite an expensive operation, causing the transitions to be not so smooth.
When i force the orientation to landscape, the preview is great. Create only gets called once which works because the preview is in landscape the camera is always what the user sees. However, I need a way to set the orientation of the actual picture taken when in portrait. When I force landscape though, the surface never gets recreated and the parameters are never set when the camera is held in portrait.
So how can I do one of the following (exclusively)?
Hold onto m_camera between onDestroy and onCreate when orientation changes so that the transition is smooth
Force landscape and detect orientation changes another way... rotating the final snaped picture if held in portrait.
Also, if I am off base can someone point me in a better direction? Thank you.
The way I implemented it:
private Camera mCamera;
private OrientationEventListener mOrientationEventListener;
private int mOrientation = -1;
private static final int ORIENTATION_PORTRAIT_NORMAL = 1;
private static final int ORIENTATION_PORTRAIT_INVERTED = 2;
private static final int ORIENTATION_LANDSCAPE_NORMAL = 3;
private static final int ORIENTATION_LANDSCAPE_INVERTED = 4;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// force Landscape layout
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
/*
Your other initialization code here
*/
}
#Override
protected void onResume() {
super.onResume();
if (mOrientationEventListener == null) {
mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
// determine our orientation based on sensor response
int lastOrientation = mOrientation;
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
}
else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
}
else { // orientation <135 && orientation > 45
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
if (lastOrientation != mOrientation) {
changeRotation(mOrientation, lastOrientation);
}
}
};
}
if (mOrientationEventListener.canDetectOrientation()) {
mOrientationEventListener.enable();
}
}
#Override protected void onPause() {
super.onPause();
mOrientationEventListener.disable();
}
/**
* Performs required action to accommodate new orientation
* #param orientation
* #param lastOrientation
*/
private void changeRotation(int orientation, int lastOrientation) {
switch (orientation) {
case ORIENTATION_PORTRAIT_NORMAL:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 270));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 270));
Log.v("CameraActivity", "Orientation = 90");
break;
case ORIENTATION_LANDSCAPE_NORMAL:
mSnapButton.setImageResource(android.R.drawable.ic_menu_camera);
mBackButton.setImageResource(android.R.drawable.ic_menu_revert);
Log.v("CameraActivity", "Orientation = 0");
break;
case ORIENTATION_PORTRAIT_INVERTED:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 90));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 90));
Log.v("CameraActivity", "Orientation = 270");
break;
case ORIENTATION_LANDSCAPE_INVERTED:
mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 180));
mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 180));
Log.v("CameraActivity", "Orientation = 180");
break;
}
}
/**
* Rotates given Drawable
* #param drawableId Drawable Id to rotate
* #param degrees Rotate drawable by Degrees
* #return Rotated Drawable
*/
private Drawable getRotatedImage(int drawableId, int degrees) {
Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId);
Matrix matrix = new Matrix();
matrix.postRotate(degrees);
Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
return new BitmapDrawable(rotated);
}
And then in your PictureCallback set metadata to indicate rotation level:
private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
// Populate image metadata
ContentValues image = new ContentValues();
// additional picture metadata
image.put(Media.DISPLAY_NAME, [picture name]);
image.put(Media.MIME_TYPE, "image/jpg");
image.put(Media.TITLE, [picture title]);
image.put(Media.DESCRIPTION, [picture description]);
image.put(Media.DATE_ADDED, [some time]);
image.put(Media.DATE_TAKEN, [some time]);
image.put(Media.DATE_MODIFIED, [some time]);
// do not rotate image, just put rotation info in
switch (mOrientation) {
case ORIENTATION_PORTRAIT_NORMAL:
image.put(Media.ORIENTATION, 90);
break;
case ORIENTATION_LANDSCAPE_NORMAL:
image.put(Media.ORIENTATION, 0);
break;
case ORIENTATION_PORTRAIT_INVERTED:
image.put(Media.ORIENTATION, 270);
break;
case ORIENTATION_LANDSCAPE_INVERTED:
image.put(Media.ORIENTATION, 180);
break;
}
// store the picture
Uri uri = getContentResolver().insert(
Media.EXTERNAL_CONTENT_URI, image);
try {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
data.length);
OutputStream out = getContentResolver().openOutputStream(
uri);
boolean success = bitmap.compress(
Bitmap.CompressFormat.JPEG, 75, out);
out.close();
if (!success) {
finish(); // image output failed without any error,
// silently finish
}
} catch (Exception e) {
e.printStackTrace();
// handle exceptions
}
mResultIntent = new Intent();
mResultIntent.setData(uri);
} catch (Exception e) {
e.printStackTrace();
}
finish();
}
};
I hope it helps.
UPDATE Now when landscape based devices are appearing an additional check for it is required in OrientationEventListener.
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0) {
// landscape oriented devices
} else {
// portrait oriented device
}
Full code (a bit wasteful by LC, but easily demonstrates the approach)
#Override
public void onOrientationChanged(int orientation) {
// determine our orientation based on sensor response
int lastOrientation = mOrientation;
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
}
} else { // portrait oriented devices
if (orientation >= 315 || orientation < 45) {
if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
mOrientation = ORIENTATION_PORTRAIT_NORMAL;
}
} else if (orientation < 315 && orientation >= 225) {
if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
}
} else if (orientation < 225 && orientation >= 135) {
if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
mOrientation = ORIENTATION_PORTRAIT_INVERTED;
}
} else if (orientation <135 && orientation > 45) {
if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
}
}
}
if (lastOrientation != mOrientation) {
changeRotation(mOrientation, lastOrientation);
}
}
Have you considered using the standard method thats provided in the API doc, which you can call on surfaceChanged? You could store the degrees in a global variable to later use when saving the picture. Also could do a simple null checker on your camera variable, so you don't create it again in surfaceCreated.
public void setCameraDisplayOrientation()
{
if (mCamera == null)
{
Log.d(TAG,"setCameraDisplayOrientation - camera null");
return;
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(CAM_ID, info);
WindowManager winManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
int rotation = winManager.getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation)
{
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
{
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
As you've seen from the other answers, this code gets very complicated. You may want to investigate using a library to help you provide this feature, for example, CWAC-Camera supports OS 2.3 and up (hopefully you can drop OS 2.1 and OS 2.2 support now):
https://github.com/commonsguy/cwac-camera
CWAC-Camera supports locking the camera preview to landscape, and will auto-rotate images into the correction orientation for you. Browse the project issues if you want a taste of all the device specific problems that need to be solved, which IMO are more reasons for trying to use a library instead of maintaining all this code and testing yourself.

Categories

Resources