검색어 : exifinterface 값이 null
출처:http://blog.naver.com/free2824/60199479410
카메라 촬영후 특정 단말의 경우 (예 : 넥서스S, 갤S, 베가 단말 등.)
onActivityResult 의 data.getData() 값이 null 인경우가 있다.
이런경우 보통은 Bitmap 으로 data값이 넘어오기 때문에
Bitmap captureBitmap = (Bitmap) data.getExtras().get("data");
와 같이 호출하여 사용할수 있다.
문제는 단말에따라 bitmap으로 데이터가 넘어오면서 썸네일이 오는경우도 있고
원본 데이터가 넘어오는경우가 있다.
그래서 썸네일의 경우 너무 작아서 문제이고 원본데이터의 경우 OutofMemory 가 발생하는경우가 종종 생길수 있다.
또한가 지 방법은 마지막에 저장된 이미지Uri을 가져오는것이다.
=======================================================================
private Uri getLastCaptureImageUri(){
Uri uri =null;
String[] IMAGE_PROJECTION = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns._ID,
};
try {
Cursor cursorImages = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
IMAGE_PROJECTION, null, null,null);
if (cursorImages != null && cursorImages.moveToLast()) {
uri = Uri.parse(cursorImages.getString(0)); //경로
int id = cursorImages.getInt(1); //아이디
cursorImages.close(); // 커서 사용이 끝나면 꼭 닫아준다.
}
} catch(Exception e) {
e.printStackTrace();
}
return uri;
}
=======================================================================
위의 코드로 가장 최근에 찍은 사진의 Uri를 가져올수도 있다...
하지만 특정 단말에서는 카메라를호출시에 특정 경로를 지정해주 지 않을경우 bitmap데이터만 넘길분 저장 하지 않는 경우가 있었다.(베가/sky 단말)
그래서 아래와 같이 카메라 호출될때 이미지를 저장할 경로를 지정해줘서 해결했다.
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String url = "tmp_" + String.valueOf(System.currentTimeMillis()) + ".jpg";
Uri mImageCaptureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), url));
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
startActivityForResult(intent, 0);
mImageCaptureUri 변수를 전역으로 지정해서 사용하면 되겠다.
========================== 실적용 코드 ===========================
>>카메라 호출 영역.
public Uri mImageCaptureUri = null;
public void showCapture(){
//카메라 호출.
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mImageCaptureUri = FileUtils.createCacheFile(this);
cameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
startActivityForResult(cameraIntent, CODE_IMAGE_CAPTURE);
}
>> onActivityResult
if(requestCode == CODE_IMAGE_CAPTURE || requestCode == CODE_PICK_PICTURE){
if(resultCode == RESULT_OK){
Uri mPicImageURI = null;
if(mImageCaptureUri != null)
mPicImageURI = mImageCaptureUri ;
else
mPicImageURI = data.getData();
Intent intent = new Intent("com.android.camera.action.CROP");
if( mPicImageURI == null){
//******비트맵으로 넘어오는경우.******
Log.d(TAG, "RESULT_DATA_BITMAP");
//Uri 리턴이 아닌 Bitmap 리턴일경우 크롭 적용이 않된다.
captureBitmap = (Bitmap) data.getExtras().get("data");
//크롭할 임시 파일생성.
ResultUri = FileUtils.createCacheFile(this);
//생성한 임시 파일에 Bitmap 이미지 저장.
BitmapUtil.BitmapSaveToFileCach(this, ResultUri, captureBitmap, 100);
intent.setDataAndType(ResultUri, "image/*");
}else{
//*******URI로 넘어오는경우.*******
Log.d(TAG, "RESULT_DATA_URI");
//크롭할 임시 파일생성.
ResultUri = FileUtils.createCacheFile(this);
intent.setDataAndType(mPicImageURI, "image/*");
}
//크롭할 해상도 설정.
intent.putExtra("outputX", 300);
intent.putExtra("outputY", 300);
//크롭 비율 설정.
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
//출력할 Uri
intent.putExtra("output", ResultUri);
startActivityForResult(intent, CODE_CROP_FROM_CAMERA);
}
}
else if(requestCode == CODE_CROP_FROM_CAMERA){
//크롭 처리
if(resultCode == RESULT_OK){
if(ResultUri != null){
BitmapFactory.Options options = new BitmapFactory.Options();
int size =1;
//OutOfMemoryError 날경우 이미지 사이즈 축소
while (true) {
try {
options.inSampleSize = size;
captureBitmap = BitmapFactory.decodeFile(ResultUri.getPath(), options);
break;
} catch (OutOfMemoryError e) {
e.printStackTrace();
size++;
}
}
captureFile = new File(ResultUri.getPath());
//Crop 이미지 처리
}
}
else{
setResult(RESULT_CANCELED);
finish();
}
=================================================================
data.getData() == null 일경우 (Bitmap) data.getExtras().get("data") 를 이용하여
Bitmap으로 처리를 했었다가 이부분이 단말마다 다르게 동작해서
카메라 호출시 EXTRA_OUTPUT 를 지정하므로서 Bitmap을 이요한 분기는 쓰이지 않게되었지만
EXTRA_OUTPUT를 쓰지 않는경우가 있을수 있으니 그대로 적용 해놨다.
크롭후 이미지를 가져오는 부분인데... 크롭후에도 OutOfMemory가 나는경우가 있어서
저리 처리를 해봤는데... 코드가 조금 그렇긴하다..(참고만...^^;)
==================================================================
>> onActivityResult 에서 쓰인 메소드.
/**
* 저장소는 내부 임시 저장 getExternalCacheDir()
* @return Uri
*/
public static Uri createCacheFile(Context _context){
Uri uri;
String url = "tmp_" + String.valueOf(System.currentTimeMillis()+".jpg");
uri = Uri.fromFile(new File(_context.getExternalCacheDir(), url));
return uri;
}
//특정 Uri 경로에 Bitmap 저장.
public static File BitmapSaveToFileCach(Context context, Uri uri, Bitmap bitmap, int quality){
//Bitmap 을 지정된 uri로 저장 한다.
FileOutputStream fos = null;
File file = null;
try {
file = new File(uri.getPath());
fos = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, quality, fos);
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
fos.close();
} catch (Exception e) {
}
}
return file;
}
===================================================================
디바이스 마다 다르게 동작하는 카메라 부분 때문에 많이 당황하셨죠 ㅠㅠ...
조금이나마 도움이 됬으면 좋겠네요...
생성된 이미지 파일은 onDestroy() 같은곳에서..
적절하게 삭제를 해주자.
참조 : http://ondestroy.tistory.com/entry/%EC%B9%B4%EB%A9%94%EB%9D%BC-%ED%98%B8%EC%B6%9C-%ED%9B%84-%EB%84%98%EC%96%B4%EC%98%A4%EB%8A%94-intentgetData%EA%B0%80-null-%EC%9D%BC%EB%95%8C-%ED%95%B4%EA%B2%B0%EB%B2%95
=====================================================================
위와 같이 적용후 사용하자 뜻하지 않는 문제가 하나 발생했다...
가로로 사진을 찍을때는 무제가 되지 않으나 세로로 사진을 찍을때 크롭 하려고 하면 가로 화면이 나타나는것이다...
이것은 카메라를 촬영할때 가로 세로를 체크 하여 크롭하기전에 찍은 방향으로 다시 저장을 하는것이다. (순서 : 카메라 촬영 -> Uri -> Bitmap 변경 -> 사진 촬영 방향 체크 -> Bitmap 변경 -> Uri 경로에 파일로 저장)
사용된 코드는 아래와 같다. 크롭하기 Intent를 호출하기전에 적절한 위치에 추가시켜주면 되겠다.
=====================================================================
private void captureOrientation(Uri resultUri) {
// TODO 이미지 회전을 체크하여 크롭할때 출력할 이미지를 재생성한다.
try {
// 비트맵 이미지로 가져온다
String imagePath = resultUri.getPath();
Bitmap image = BitmapFactory.decodeFile(imagePath);
// 이미지를 상황에 맞게 회전시킨다
ExifInterface exif = new ExifInterface(imagePath);
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int exifDegree = exifOrientationToDegrees(exifOrientation);
image = rotate(image, exifDegree);
//원본 경로에 수정된 bitmap다시 저장한다.
BitmapUtil.BitmapSaveToFileCach(this, resultUri, image, 100);
}
catch(OutOfMemoryError ex) {
// 메모리 부족으로 에러날경우 무시.
ex.printStackTrace();
}
catch (Exception e) {
// 오류시 무시.
e.printStackTrace();
}
}
/**
* EXIF정보를 회전각도로 변환하는 메서드
*
* @param exifOrientation EXIF 회전각
* @return 실제 각도
*/
public int exifOrientationToDegrees(int exifOrientation){
if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
}
else if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
}
else if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}
/**
* 이미지를 회전시킵니다.
*
* @param bitmap 비트맵 이미지
* @param degrees 회전 각도
* @return 회전된 이미지
*/
public Bitmap rotate(Bitmap bitmap, int degrees){
if(degrees != 0 && bitmap != null){
Matrix m = new Matrix();
m.setRotate(degrees, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
try {
Bitmap converted = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), m, true);
if(bitmap != converted) {
bitmap.recycle();
bitmap = converted;
}
}
catch(OutOfMemoryError ex) {
// 메모리가 부족하여 회전을 시키지 못할 경우 그냥 원본을 반환합니다.
}
}
return bitmap;
}
참조 : http://theeye.pe.kr/entry/simple-way-of-handling-bitmap-camera-with-rotate-fix-on-android
더보기 접기
조금 순서가 반대로 되는 포스팅일것 같습니다만 이전에 [ 카메라 호출후 이미지 크롭하기 ] 글을 작성한 적이 있습니다. 간단하게 카메라만 임시로 호출하기 위해 사용하기엔 너무 복잡한 면이 있는듯 하니 이번에는 간단하게 카메라 사용에 관련된 포스팅을 하나 해보겠습니다.
하지만 여기서 중요한 기능을 하나 추가했는데요, 대부분의 기기가 사진 촬영시에 기기의 회전율을 고려하지 않고 바로 저장해 버린다는 문제를 재회전을 통해 복구하는 로직을 추가해보았습니다.
1. 카메라 호출하기 [code]// 임시로 사용할 파일 생성 File photo = new File(Environment.getExternalStorageDirectory(), “.camera.jpg”); imageUri = Uri.fromFile(photo);
// 카메라를 호출합니다. Intent i = new Intent(“android.media.action.IMAGE_CAPTURE”); i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); MainActivity.this.startActivityForResult(i,REQUEST_CAMERA);[/code]
2. 결과 처리 (onActivityResult) [code]if(requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) { try { // 비트맵 이미지로 가져온다 String imagePath = imageUri.getPath(); Bitmap image = BitmapFactory.decodeFile(imagePath); // 이미지를 상황에 맞게 회전시킨다 ExifInterface exif = new ExifInterface(imagePath); int exifOrientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); int exifDegree = exifOrientationToDegrees(exifOrientation); image = rotate(image, exifDegree); // 변환된 이미지 사용 imageView.setImageBitmap(image); } catch(Exception e) { Toast.makeText(this, “오류발생: ” + e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); } }[/code]
3. 회전에 사용되는 추가 함수들 [code]/** * EXIF정보를 회전각도로 변환하는 메서드 * * @param exifOrientation EXIF 회전각 * @return 실제 각도 */ public int exifOrientationToDegrees(int exifOrientation) { if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; } else if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) { return 180; } else if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) { return 270; } return 0; }
/** * 이미지를 회전시킵니다. * * @param bitmap 비트맵 이미지 * @param degrees 회전 각도 * @return 회전된 이미지 */ public Bitmap rotate(Bitmap bitmap, int degrees) { if(degrees != 0 && bitmap != null) { Matrix m = new Matrix(); m.setRotate(degrees, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2); try { Bitmap converted = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); if(bitmap != converted) { bitmap.recycle(); bitmap = converted; } } catch(OutOfMemoryError ex) { // 메모리가 부족하여 회전을 시키지 못할 경우 그냥 원본을 반환합니다. } } return bitmap; }[/code]
4. AndroidManifest.xml 추가 설정 - 카메라 회전시에도 기존의 엑티비티를 제거하지 않도록 방지하기 위해 <activity..에 추가 [code]android:configChanges=”keyboardHidden|orientation”[/code] - 카메라를 사용하기 위한 퍼미션 추가 [code]<uses-permission android:name=”android.permission.CAMERA”/>[/code]
* 결론 카메라를 이용하여 사진을 찍을때는 EXIF라는 메타 데이터가 이미지 파일에 추가로 기록이 됩니다. 이곳에서는 회전상태등이 저장이 되는데 이 값을 읽어온 후 회전된 상태만큼을 다시 원상복귀를 시키게 됩니다. exifOrientationToDegrees 메서드를 사용하여 일반적으로 우리가 사용하는 정수형 회전각도값을 알아온후에 rotate 메서드를 사용하여 이미지를 실제로 회전시킵니다.
접기