I'm trying to process images in my app. The problem I'm currently facing is related to orientation of images. The thumbnail of the images selected from camera folder of Android appears 90 degrees rotated. I'm getting the thumbnail as following;
Uri thumbUri = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, uri.getLastPathSegment());
if (thumbUri != null){
try {
List<String> parts = uri.getPathSegments();
String lastPart = parts.get(parts.size() - 1);
int index = lastPart.indexOf(":");
if (index != -1)
{
lastPart = lastPart.substring(index+1);
}
long id = Long.parseLong(lastPart);
// get a thumbnail for the image
Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(
context.getContentResolver(),
id,
MediaStore.Images.Thumbnails.MINI_KIND,
null
);
if (bitmap != null)
{
return bitmap;
}
}
catch (Exception e)
{
Log.e(LOG_TAG, "Unable to generate thumbnail from thumbnail uri " + e.getMessage(), e);
}
}
Also tried to fix it by reading orientation from ExifInterface as;
ExifInterface ei = new ExifInterface(uri.getPath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
But orientation returned is always 0 ORIENTATION_UNDEFINED. Unable to get what I'm doing wrong here.
Got the solution. I used getThumbnail(ContentResolver contentResolver, long id) method from BitmapUtil which reads the metadata of image from cursor and then process accordingly.
Thanks to Jason Fry for this useful utility.
You can do a simple workaround by performing additional rotation to your bitmap, using Matrix. It's perfectly simple.
//we don't need a NullPointerException now, do we?
if (bitmap != null)
{
//ever so simply initialize a Matrix
Matrix matrix = new Matrix();
//tell the Matrix it should rotate everything it's applied to by -90 degreeds
matrix.postRotate(-90);
//create a clone of the bitmap while applying the mentioned Matrix
//for width and height attributes, you must always use old bitmap's width and height
bitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(),
matrix, true);
return bitmap;
}
EDIT:
And if you need to determine which orientation (landscape/portrait) were the photos taken in, and then decide which ones to rotate, you can use a code from this question/answer, with ExifInterface.
I see you've already applied something similar, so maybe you should try this modification with only 1 parameter, as in the linked answer:
exif.getAttribute(ExifInterface.TAG_ORIENTATION);
UPDATE:
OK, so it doesn't work either. But it should, right? That's what the mentioned method does. So I suggest you check out what uri.getPath() actually returns (using a debugger or System.out.println()), and see if it's right. Invalid path bugs happened to me plenty of times, and every time it took a while to find out what's wrong.
String path = uri.getPath();
//breakpoint somewhere below
UPDATE 2:
I've done some research and, apparently, you can't get absolute file path from URI. So I found this solution on StackOverflow.com that gives a super-simple workaround.
Link
Related
I am trying to restore the image from Native memory (using NDK,C/C++) but that returns me an Black Image.
What i am doing ::
1)get the image from Drawable
2)apply the rotation to the image
3)After rotation apply the grayscale effect to the image
4)At the end i am trying to save the grayscale image in SD Card
For all the above steps, i am referring this awesome lib,which have the native method to store and restore the images.
Please note image is being stored in the SD card but when i am trying to see the image,its totally black with no display at all.
My Java Implementation ::
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.item_rotate_90:
options.inPreferredConfig = Config.ARGB_8888;
bitmapOrig = BitmapFactory.decodeResource(this.getResources(), R.drawable.sample_cam,options);
storeBitmap(bitmapOrig);
bitmapOrig.recycle();
rotateBitmap(90,_handler);
tempBmp=getBitmapAndFree();
bitmapWip = Bitmap.createBitmap(bitmapOrig.getWidth(),bitmapOrig.getHeight(),Config.ALPHA_8);
jniConvertToGray(tempBmp,bitmapWip);
if(bitmapWip!=null)
{
try
{
Bitmap b = Bitmap.createBitmap(bitmapWip.getWidth(),bitmapWip.getHeight(),Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bitmapWip, 0, 0, paint);
storeBitmap(b);
SaveGrayScaledImage(b);
b.recycle();
tempBmp.recycle();
} catch (IOException e) {
e.printStackTrace();
}
ivDisplay.setImageBitmap(bitmapWip);
}
break;
}
}
I have not make any changes in native method(means using the same method as this lib have for storing and restoring the image).
Saving image to SD Card ::
private void SaveGrayScaledImage(Bitmap finalBitmap)throws IOException
{
String imageFileName = "Temp" + "_gray";
File albumF = new File("/mnt/sdcard/","gray_img");
if(!albumF.exists())
{
albumF.mkdirs();
}
// File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX,
// albumF);
File imageF = new File(albumF,imageFileName + ".jpeg");
if (imageF.exists()) {
imageF.delete();
imageF.createNewFile();
}
try {
FileOutputStream out = new FileOutputStream(imageF);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
imageF = null;
}
}
While googling, i found that(may be i am wrong) image which returns for Native Memory have the ALPHA_8 bitmap config,so i convert the config ALPHA_8 t0 ARGB_8888,but the result is same.
Conversion of bitmap from ALPHA_8 to ARGB_8888 ::
Bitmap b = Bitmap.createBitmap(bitmapWip.getWidth(),bitmapWip.getHeight(),Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bitmapWip, 0, 0, paint);
StoreBimap funcation ::
public void storeBitmap(final Bitmap bitmap)
{
if(_handler!=null)
freeBitmap();
_handler=jniStoreBitmapData(bitmap);
}
I have no clue about where i was wrong. i have checked the lib methods and implmentation again and again to find the issue.
I have spent my many hours on this small issue and it really frustrating me.
Let me know please if you need anything else from my side.
Please help me to resolve this issue.
Many Thanks in Advance....
EDIT ::
bitmapHolder=new JniBitmapHolder();
final Options options=new Options();
BitmapFactory.decodeFile(picPath, options);
options.inJustDecodeBounds=true;
options.inPreferredConfig=Config.ARGB_8888;
prepareForDownsampling(options,192,256);
System.gc();
bmpGrayscale=BitmapFactory.decodeFile(picPath,options);
int width = bmpGrayscale.getWidth();
int height = bmpGrayscale.getHeight();
bitmapHolder.storeBitmap(bmpGrayscale);
bmpGrayscale.recycle();
Bitmap thumbnail = null;
int rotationInDegrees = 0;
if (picPath != null) {
Uri uri = Uri.parse(picPath);
ExifInterface exif = null;
try {
exif = new ExifInterface(uri.getPath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int rotation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
rotationInDegrees = exifToDegrees(rotation);
}
rotationInDegrees = 90;
ByteBuffer _handler =null;
switch(rotationInDegrees)
{
case 90:
bitmapHolder.rotateBitmapCw90();
break;
case 180:
bitmapHolder.rotateBitmap180();
break;
}
Bitmap bitmapWip = Bitmap.createBitmap(width,height,Config.ALPHA_8);
bitmapHolder.bitmapGrayScale(bitmapWip);
if(bitmapWip!=null){
File CurrentFile = saveGrayScaledIamge(bitmapWip,
takePhotoFile);
}
I have followed your suggestion/steps but the result is same,getting black image with no display.
ok I've found multiple problems and tips for improvements:
the first createBitmap is run with width*height on a bitmap that got rotated instead of height*width. this should be as:
rotateBitmap(90,_handler);
tempBmp=getBitmapAndFree();
bitmapWip=Bitmap.createBitmap(bitmapOrig.getHeight(),bitmapOrig.getWidth(),Config.ALPHA_8);
when saving file you don't get the correct path (you use a hardcoded path, and Lint warns about it).
jniConvertToGray doesn't really need to go over arrays and can just use a pointer, as it just runs on a single pixel. you store the bitmap into JNI twice instead of once (just do: store, rotate, grayscale, restore&free).
you don't use the new bitmap after you have finished working on it, so if I call rotation multiple times, it doesn't seem to do anything.
you already have bitmapWip rotated and grayscaled. why do you need to make a new bitmap that has its content in it, do a grayscale on it, and then save it ?
functions should be named with lowercase letter in the beginning of their names.
and finally , the most important thing: you use ALPHA_8 for the image that you show and need to save to file. this configuration has no color. it's a mask. In order to see the problem, you should set a background color to the imageView :
ivDisplay.setBackgroundColor(0xFFff0000);
before choosing the rotation, you see nothing red. after choosing it, everything you think is white, has actually become red. that's because it's transparent...
If in any phase of your development you've succeeded saving the image to a file and thought it's a black image (yet the size is not 0) , try to edit it and put a background behind it. Maybe you got lucky and just got transparent pixels...
Adding the fact that you save the file to a jpg format, which doesn't support transparency, might also contribute to unexpected behaviors.
in order to solve this, you should use the same technique i've used - use a single bitmap all the time. no need to create so many. only one should exist on the java world, and it should support having colors.
Working on an application in which we capture images and upload over server. Application is in Android And I Phone. When we post image from Android, they are of quantity in Kilo Bytes but when we Post image from I Phone they are of MB size.
When we the images posted from IPHONE 5 with URL on browser, they appear good as they supposed to appear but when we download that image in Android device and show in an IMAGE VIEW, they appears 90 deg tilt to the left hand side.
I am not using, any rotation code after downloading the images in Android or in I Phone.
In I Phone the images are appearing fine.
IMages captured from Android are also visible straight. Images of low resolution capture from I Phone are also visible straight in Android.
Image uploaded from Android:
https://s3.amazonaws.com/WeddingApp/Weddingimage/933_6_stan.jpg
Image uploaded from I Phone:
https://s3.amazonaws.com/WeddingApp/Weddingimage/937_6_stan.jpg
public static boolean downloadFile(final String fileURL,File directory,Context CONTEXT){
try{
URL url = new URL(fileURL);
URLConnection ucon = url.openConnection();
ucon.setReadTimeout(35000);
ucon.setConnectTimeout(10000);
InputStream is = ucon.getInputStream();
BufferedInputStream inStream = new BufferedInputStream(is, 1024 * 5);
File file = directory;
if (file.exists())
{
file.delete();
}
file.createNewFile();
FileOutputStream outStream = new FileOutputStream(file);
byte[] buff = new byte[5 * 1024];
int len;
while ((len = inStream.read(buff)) != -1)
{
outStream.write(buff, 0, len);
}
outStream.flush();
outStream.close();
inStream.close();
}
catch (IOException e){ //IF SDCARD NOT EXIST THEN PASS RESPONSE TRUE`
e.printStackTrace();
return false;
}catch(Exception e){
e.printStackTrace();
return false;
}
return true;
}
Please suggest me.
This is because of iPhone's silly new camera implementation. I found below on research (sources given below):
1. The iPhone camera's interpretation of "up" direction is rotated 90 degrees from the actual "up" direction we know. What you and I call "up", is iphone's "left" or "right". The camera's orientation has been changed. This was done so that they could support taking pictures with the volume button.
2. But, being aware that other devices and cameras have normal orientation, and so that those devices and browsers display the images properly, iOS adds EXIF data to the uploaded images. This is added on image upload. EXIF, as mentioned in the link, is a metadata that contains all the information of how the image is actually supposed to look.
3. The target platform on which the image is downloaded is supposed to read the EXIF data, and then show the correct representation of the image. So, if the target platform reads the EXIF, it will realize that the image received is 90 degrees rotated, and that it should rely on EXIF data, and not on what's received. This is why iPhone camera apps can show the image properly.
4 However, not everybody reads EXIF. Apps and browsers that are aware of EXIF, read the EXIF data of the image, and show it properly. But those that are not aware of EXIF, don't read that data, and show the image exactly as received --> 90 degrees rotated
5 People have worked around this problem, by rotating their iPhone 90 degrees, before taking a picture (so that the camera is oriented right, before the picture is taken)
Resources:
1. Source 1
2. Source 2
Others with same problem:
1. SO Post 1
2. SO Post 2
The problem seems to be related to EXIF data found on the images. We must process it before displaying the image. It appears to be that not every camera outputs EXIF data 'cause this issues only happens to me in some android handsets.
Take a look at: http://commons.wikimedia.org/wiki/Commons:Exif#Orientation_.28rotation_and_mirroring.29
EDIT:
We could implement something like:
public Bitmap getBitmap(String path){
Bitmap tmpBitmap = BitmapFactory.decodeFile(path);
Bitmap bitmap = null;
if(path != null){
try {
ExifInterface ei = new ExifInterface(path);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Matrix mtx = new Matrix();
int w = tmpBitmap.getWidth();
int h = tmpBitmap.getHeight();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
//rotate CCW
mtx.preRotate(-90);
bitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, w, h, mtx, true);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
//rotate CW
mtx.preRotate(90);
bitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, w, h, mtx, true);
break;
//CONSIDER OTHER CASES HERE....
default:
bitmap = tmpBitmap;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return bitmap;
}
Regards.
I am using:
https://github.com/jasonpolites/gesture-imageview
on load of the app, it has a placeholder image in a GestureImageView that pinch/zooms appropriately. I have a button that when clicks fires a camera intent, saves the file, and then I wish to set that image to be the source bitmap used in the gestureimageview.
GestureImageView imageView = (GestureImageView) findViewById(R.id.imageViewOne);
ContentResolver cr = getContentResolver();
getContentResolver().notifyChange(imageUriOne, null);
try {
Bitmap mybitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUriOne);
imageView.setImageBitmap(mybitmap);
}
For a normal imageview, that works. But for the GestureImageView, the image stays as the original once returned from the camera intent, and if touched disappears.
To check it's not the bitmap that's the problem, I tried
int idTwo=getResources().getIdentifier("com.jazz.test1:drawable/second_photo", null, null);
imageView.setImageResource(idTwo);
I.e. set the imageview to an existing resource, but this has the same problem.
If I call that setImageResource code before the intent, it does work.
Any ideas how to debug? There are no errors in the logs.
Resolution is here:
https://github.com/jasonpolites/gesture-imageview/issues/21
hadn't noticed it when I initially looked at the github issues.
You have to replace your initMethod function. With this code it will work properly (GestureImageView.java file in com.polites.android package).
protected void initImage() {
if (this.drawable != null) {
this.drawable.setAlpha(alpha);
this.drawable.setFilterBitmap(true);
if (colorFilter != null) {
this.drawable.setColorFilter(colorFilter);
}
// Keppel.Cao
layout = false;
startingScale = -1.0f;
}
if (!layout) {
requestLayout();
// Keppel.Cao
// redraw();
reset();
}
}
Like Dave said. More you can find here Issue 21
fd = new FaceDetector(mFaceWidth, mFaceHeight, MAX_FACES);
count = fd.findFaces(mFaceBitmap, faces);
Using the above code I'm getting this error on some images.
return 0 faces because error exists btk_facefinder_putdcr
Can someone help me? How to get rid of this?
The same code works fine for some other images.
http://blog.csdn.net/devilkin64/article/details/8509767
传入的图片的宽度必须是偶数的
Bitmap srcImg = BitmapFactory.decodeFile(imgUrl);
Bitmap srcFace = srcImg.copy(Bitmap.Config.RGB_565, true);
srcImg = null;
int w = srcFace.getWidth();
int h = srcFace.getHeight();
if (w % 2 == 1) {
w++;
srcFace = Bitmap.createScaledBitmap(srcFace,
srcFace.getWidth()+1, srcFace.getHeight(), false);
}
if (h % 2 == 1) {
h++;
srcFace = Bitmap.createScaledBitmap(srcFace,
srcFace.getWidth(), srcFace.getHeight()+1, false);
}
As mentioned on this page, the image width needs to be even. I faced the same problem so I scaled the image width by one if the width was odd. And it started working :)
BitmapFactory.Options bitmap_options = new BitmapFactory.Options();
bitmap_options.inPreferredConfig = Bitmap.Config.RGB_565;
background_image = BitmapFactory.decodeFile(image_fn, bitmap_options);
if((1==(background_image.getWidth()%2))){
background_image = Bitmap.createScaledBitmap(background_image,
background_image.getWidth()+1, background_image.getHeight(), false);
}
FaceDetector face_detector = new FaceDetector(
background_image.getWidth(), background_image.getHeight(),
MAX_FACES);
faces = new FaceDetector.Face[MAX_FACES];
face_count = face_detector.findFaces(background_image, faces);
Log.d("Face_Detection", "Face Count: " + String.valueOf(face_count));
I had the same issue and when I created a new "drawable" folder to hold the image, without the "-hdpi" at the end of "drawable" it worked. Only problem I have now is scaling the image to fit the screen but this should be relatively straight forward.
Hope this helps.
If you are getting images from gallery and camera and this error come then check the width of bitmap
where you apply facedetection it must be 1.
As per the documentation of developers.android.com if the image width is not even then this exception occur.
for further detail check this link.
Or if u are not getting image from gallery and taken from camera then place the image in the folder name drawable.
What I'm doing seems like it should be simple, but I'm still lost after I've read every possible Stackoverflow answer I can find and Googled every article I can find.
I'm using a preview SurfaceView and capturing an image from an activity that is set for screenOrientation="landscape" in my AndroidManifest.xml.
I followed the sample Camera app code and thought things were working until I tried my app on a few Motorola devices running 1.5.
I have the OrientationEventListener running OK and I use reflection to see if set the rotation as such:
final int latchedOrientation = roundOrientation(mLastOrientation + 90);
Parameters parameters = preview.camera.getParameters();
JPLog.d("Setting camera rotation = %d", latchedOrientation);
try {
// if >= 2.0
Method method = Camera.Parameters.class.getMethod("setRotation",
int.class);
if(method != null) {
method.invoke(parameters, latchedOrientation);
}
} catch(Throwable t) {
// if < 2.0
parameters.set("rotation", latchedOrientation);
}
preview.camera.setParameters(parameters);
NexusOne (OS 2.2) - Works great. latchedOrientation = 0, picture OK without any rotation in the EXIF header.
T-Mobile G1 (OS 1.6) - Also works great. latchedOrientation = 0, picture OK.
Motorola Backflip (OS 1.5) - Image rotated. latchedOrientation = 0, picture has no EXIF rotation in it.
Motorola CLIQ (OS 1.5) - Image rotated. latchedOrientation = 0, picture has no EXIF rotation in it.
What's going on with these Motorola devices? I thought my problem was the Motorola camera driver wasn't rotating the images, so found the Sanselan EXIF reading classes for Android and was preparing to rotate them myself. Funny thing is, there is EXIF headers but no rotation element.
If I set the rotation manually to 90 degrees, the images come out perfect the Motorola devices, but now the G1 and the NexusOne have images that are rotated 90 degrees (not what I want). There has to be something I'm not getting here.
I'm doubting this is a 1.5 issue, or else someone would've posted info on it?
I had this issue and I used this method to capture the image. (Without creating a custom camera)
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(intent, 0);
and did the rest in onActivityResult(int requestCode, int resultCode, Intent data) {}
But the original image (Actual SD card image) was correct and Bitmap was rotated when I fetched like this.
Bitmap bmp = BitmapFactory.decodeStream(..
The solution:
try {
File f = new File(SD_CARD_IMAGE_PATH);
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);
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
Bitmap correctBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
}
catch (IOException e) {
Log.w("TAG", "-- Error in setting image");
}
catch(OutOfMemoryError oom) {
Log.w("TAG", "-- OOM Error in setting image");
}
This is actually a device-specific issue that mostly affects Motorola devices. The google devs included a setDisplayOrientation call in API 8 to work around the issue. The main bug is filed here.
For those that can't go to API 8, the two common solutions are:
Override onDraw
Override onDraw in a top-level ViewGroup and rotate the canvas by 90 degrees to compensate for the rotation. Note there is a caveat here as your touch events will also need to be rotated.
Use Landscape Mode
Lock the activity to landscape mode but draw assets as if they are in portrait. This means you do your layout and rotate your image assets to look like you are in portrait mode so that the view looks normal. This unfortunately makes it difficult to use the menu since the menu will open horizontally.
I have also seen people use an animation controller to rotate the view. The drawback here that I wasn't able to overcome is that the rotated view doesn't stretch to fill the screen. See the answer by Georg for a sample implementation.
Here is the code I used onActivityResult() in my activity. The intent returned was for picking an image of the type image/*. Works well for me!
Uri imageUri = intent.getData();
String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
int orientation = -1;
if (cur != null && cur.moveToFirst()) {
orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
}
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
It looks like the 'use landscape mode' suggestion is the only thing that really works. It seems that it's ok for this to be in either the manifest, or done via a call to setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) in the activity onCreate.