Android系统Camera图片反转的一个问题

一、问题提出

  目前遇到项目问题,Camera预览图像是反的,于是考虑设置180度反转以便正常。

  通过如下两种方式:

  params.setRotation(180); //java部分

  p.set(CameraParameters::KEY_ROTATION,180); //C部分

  发现应用部分takepicture出来数据都没有变化。

代码片段如下:

Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
/*              
Matrix matrix = new Matrix();  
matrix.setRotate(180);
int width = bitmap.getWidth();  
int height = bitmap.getHeight(); 
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
*/               
File out = new File("/data/");
if (!out.exists()) {
  out.mkdirs();
}
out = new File("/data/", "1.jpg");
try {
  FileOutputStream outStream = new FileOutputStream(out);
  bitmap.compress(CompressFormat.JPEG, 100, outStream);
  outStream.close();
} catch (Exception e) {
  e.printStackTrace();
}
  上边屏蔽部分打开后ok,屏蔽后有问题;屏蔽部分完成反转功能,证明图片本身有问题。

二、逐层排查

1.frameworks/av/services/camera/libcameraservice/CameraClient.cpp

void CameraClient::dataCallback(int32_t msgType,
        const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
  int takepicture_fd;
  takepicture_fd = open("/data/tankai.jpg",O_WRONLY|O_CREAT,00700);
  ALOGD("TK---------->>>>JPEG,dataPtr->size() is %d,takepicture_fd is %d\n",dataPtr->size(),takepicture_fd);
  ALOGD("TK---------->>>>JPEG,dataPtr->size() is 0x%x\n",dataPtr->pointer());
  int write_len = write(takepicture_fd, dataPtr->pointer(), dataPtr->size());
  close(takepicture_fd);
  ALOGD("TK--------_>>>>>>ok\n");
}
2.frameworks/av/camera/Camera.cpp
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata)
{
  int takepicture_fd;
  takepicture_fd = open("/data/tankai_CPP.jpg",O_WRONLY|O_CREAT,00700);
  ALOGD("TK---CPP------->>>>JPEG,dataPtr->size() is %d,takepicture_fd is %d\n",dataPtr->size(),takepicture_fd);
  ALOGD("TK->>>>CPP---->>>>>dataPtr->pointer() is 0x%x",dataPtr->pointer());
  int write_len = write(takepicture_fd, dataPtr->pointer(), dataPtr->size());
  close(takepicture_fd);
  ALOGD("TK---CPP-----_>>>>>>ok\n");
}
3.frameworks/base/core/jni/android_hardware_Camera.cpp
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
{
  jbyteArray obj = NULL;
  obj = env->NewByteArray(size);
  //add by tankai 
  int takepicture_fd;
  takepicture_fd = open("/data/tankai_jni2.jpg",O_WRONLY|O_CREAT,00700);
  ALOGD("TK-------JNI2--->>>>JPEG,size is %d,takepicture_fd is %d\n",size,takepicture_fd);
  ALOGD("TK----->>>>JNI2------>>>>>>data is 0x%x",data);
  int write_len = write(takepicture_fd, data, size);
  close(takepicture_fd);
  ALOGD("TK----JNI2----_>>>>>>ok\n");
  //end tankai
  env->SetByteArrayRegion(obj, 0, size, data);
  //add by tankai
  takepicture_fd = open("/data/tankai_jni3.jpg",O_WRONLY|O_CREAT,00700);
  ALOGD("TK-------JNI3--->>>>JPEG,size is %d,takepicture_fd is %d\n",size,takepicture_fd);
  jbyte * olddata = (jbyte*)env->GetByteArrayElements(obj, 0);
  char* bytearr = (char*)olddata;
  ALOGD("TK----->>>>JNI3------>>>>>>obj is 0x%x,bytearr is 0x%x",obj,bytearr);
  write_len = write(takepicture_fd, bytearr, size);
  close(takepicture_fd);
  ALOGD("TK----JNI3----_>>>>>>ok\n");
  //end tankai
  //调用mCameraJClass的fields.post_event方法,参数为后边的5个
  env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
            mCameraJObjectWeak, msgType, 0, 0, obj); 
}
4.frameworks/base/core/java/android/hardware/Camera.java
public void handleMessage(Message msg) {
  case CAMERA_MSG_COMPRESSED_IMAGE:
    //add by tankai
    Log.d(TAG,"TK-------->>>>>>java callback");
    Bitmap bitmap = BitmapFactory.decodeByteArray(((byte[])msg.obj), 0, ((byte[])msg.obj).length);
    Log.d(TAG,"TK----->>>>((byte[])msg.obj) is 0x%x" + ((byte[])msg.obj));
    String path = "/data/";
    String fileName = "tankai_java.jpg";
    File out = new File(path);
    if (!out.exists()) {
      out.mkdirs();
    }
    out = new File(path, fileName);
    try {
      FileOutputStream outStream = new FileOutputStream( out);
      bitmap.compress(CompressFormat.JPEG, 100,outStream);
      outStream.close();
      Log.d(TAG,"TK-------->>>>>>java callback>>>>ok");
    } catch (Exception e) {
      Log.d(TAG,"TK-------->>>>>>java callback>>>>fail");
      e.printStackTrace();
    }
    //end tankai
}
结果,当设置180度反转后,1-3中所存图片都为正;4为反。

三、原因

1.java部分使用bitmap存文件,没有直接保存;可能是这个原因。

2.修改应用程序中存JPEG文件的方式:

File ret = null;
BufferedOutputStream stream = null;
try {
  ret = new File("/data/1.jpg");
  FileOutputStream fstream = new FileOutputStream(ret);
  stream = new BufferedOutputStream(fstream);
  stream.write(data);
} catch (Exception e) {
  // log.error("helper:get file from byte process error!");  
  e.printStackTrace();
} finally {
  if (stream != null) {
    try {
      stream.close();
    } catch (IOException e) {
      // log.error("helper:get file from byte process error!");  
      e.printStackTrace();
    }
  }
}
结果,图片变正。

3.结论

问题出在bitmap转换上。

四、读取JPEG文件EXIF信息的ORIENTATION字段

1.源码

private int getBitmapDegree(String path) {
    int degree = 0;
    try {
        // 从指定路径下读取图片,并获取其EXIF信息
        ExifInterface exifInterface = new ExifInterface(path);
        // 获取图片的旋转信息
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);
        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            degree = 90;
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            degree = 180;
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            degree = 270;
            break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return degree;
}
result = getBitmapDegree("/data/tankai_CPP.jpg");
Log.d("TKTK","TK------>>>>>>>/data/tankai_CPP is " + result);


result = getBitmapDegree("/data/tankai_jni.jpg");
Log.d("TKTK","TK------>>>>>>>/data/tankai_jni1 is " + result);


result = getBitmapDegree("/data/tankai_jni2.jpg");
Log.d("TKTK","TK------>>>>>>>/data/tankai_jni2 is " + result);


result = getBitmapDegree("/data/tankai_jni3.jpg");
Log.d("TKTK","TK------>>>>>>>/data/tankai_jni3 is " + result);


result = getBitmapDegree("/data/tankai_java.jpg");
Log.d("TKTK","TK------>>>>>>>/data/tankai_java is " + result);


result = getBitmapDegree("/data/1.jpg");
Log.d("TKTK","TK------>>>>>>>/data/1.jpg is " + result);
2.结果

正常显示时该字段为180,没有变正的该字段为0。

五、结论

Android系统中Camera拍照时setRotation在各个平台功能不统一:有些平台会直接操作图片的存储地址,将数据反写;但绝大多数平台只是修改了JPEG编码时EXIF头部的ORIENTATION字段。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我可以给你一个Android使用Camera2Demo的示例代码:import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.ImageReader; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.util.Size; import android.util.SparseIntArray; import android.view.Surface; import android.view.TextureView; import android.widget.Toast;@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class Camera2Demo { private TextureView textureView; private Context context; private Size previewSize; private Handler backgroundHandler; private HandlerThread backgroundThread; private ImageReader imageReader; private CaptureRequest.Builder previewRequestBuilder; private CameraDevice cameraDevice; private CameraCaptureSession captureSession; private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } public Camera2Demo(TextureView textureView, Context context) { this.textureView = textureView; this.context = context; } public void openCamera() { CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); try { String cameraID = cameraManager.getCameraIdList()[0]; CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraID); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); previewSize = map.getOutputSizes(SurfaceTexture.class)[0]; // 权限检查 if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } cameraManager.openCamera(cameraID, stateCallBack, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private CameraDevice.StateCallback stateCallBack = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraDevice = camera; startPreview(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { camera.close(); cameraDevice = null; } @Override public void onError(@NonNull CameraDevice camera, int error) { Toast.makeText(context, "摄像头开启失败", Toast.LENGTH_SHORT).show(); } }; private void startPreview() { SurfaceTexture surfaceTexture = textureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); Surface previewSurface = new Surface(surfaceTexture); try { previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); previewRequestBuilder.addTarget(previewSurface); cameraDevice.createCaptureSession(Arrays.asList(previewSurface, imageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; try { captureSession.setRepeatingRequest(previewRequestBuilder.build(), null, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } }, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值