使用Android相机拍摄的照片的奇怪尺寸
问题描述:
我正在开发一个Android应用程序,使用Android相机API打开预览并从中拍摄照片。该应用程序只能在肖像模式下工作,并且必须可以同时使用设备的前置摄像头和后置摄像头(如果设备具有两个摄像头)。使用Android相机拍摄的照片的奇怪尺寸
我已经在我的应用程序中打开预览,我已经正确设置了显示方向(使用方法旋转90度的方向:mCamera.setDisplayOrientation(90)),以便可以在纵向模式下查看应用程序预览,并且我添加了按钮,可以在前后摄像头之间切换。所有这些东西在应用程序中正常工作。
问题是当我拍照时:为了以正确的方式(以肖像)旋转拍摄的照片,我将设备的方向设为对角,并将图片旋转为所需的方向。但是,当照片保存到图库中时,照片尺寸很奇怪:如果使用前置相机拍摄,则照片尺寸为全屏,而不是使用后置照相机拍摄。我的目标是始终全屏照相。
这两个屏幕截图显示的问题:
---后相机的照片:
---前置摄像头PHOTO:
这里,有我的CameraPreview代码:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null){
return;
}
try {
mCamera.stopPreview();
} catch (Exception e){
}
try {
// Start preview in portrait mode
mCamera.setDisplayOrientation(90);
// Set the list of supported preview size in the related variable
if(mCamera != null){
if(mCamera.getParameters().getSupportedPreviewSizes() != null){
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
}
}
// Get the parameters of camera
Camera.Parameters parameters = mCamera.getParameters();
// Set output format to NV21 (which is guranteed to be supported on all devices)
parameters.setPreviewFormat(ImageFormat.NV21);
// Set the correct preview size (after applying the getOptimalPreviewSize)
if(mPreviewSize != null) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
Log.d(TAG,"Preview size is ("+mPreviewSize.width+";"+mPreviewSize.height+")");
// initializing bitmap and pixels
bitmap = Bitmap.createBitmap(mPreviewSize.width, mPreviewSize.height, Bitmap.Config.ARGB_8888);
pixels = new int[mPreviewSize.width * mPreviewSize.height];
}
// Correct the size - orientation of picture taken
if(isTablet(getContext()) == Boolean.FALSE){
onOrientationChanged(getScreenRotationOnPhone(),parameters);
}else{
onOrientationChanged(getScreenRotationOnTablet(),parameters);
}
mCamera.setPreviewDisplay(mHolder);
// Set to turn the Flash ON
// parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
// Call the setPreviewCallback and onPreviewFrame to get the incoming frame
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame (byte[] data, Camera camera){
Log.i(TAG, "Ma entro nella onPreviewFrame?");
Camera.Parameters parameters = mCamera.getParameters();
int format = parameters.getPreviewFormat();
Log.i(TAG, "Il formato del frame e': " + format);
//YUV formats require more conversion
if (format == ImageFormat.NV21 || format == ImageFormat.YUY2 || format == ImageFormat.NV16) {
int w = parameters.getPreviewSize().width;
int h = parameters.getPreviewSize().height;
}
}
});
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
private int getScreenRotationOnPhone() {
final Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if(display.getRotation() == Surface.ROTATION_0){
System.out.println("SCREEN_ORIENTATION_PORTRAIT");
return Surface.ROTATION_0;
}else if(display.getRotation() == Surface.ROTATION_90){
System.out.println("SCREEN_ORIENTATION_LANDSCAPE");
return Surface.ROTATION_90;
}else if(display.getRotation() == Surface.ROTATION_180){
System.out.println("SCREEN_ORIENTATION_REVERSE_PORTRAIT");
return Surface.ROTATION_180;
}else if(display.getRotation() == Surface.ROTATION_270){
System.out.println("SCREEN_ORIENTATION_REVERSE_LANDSCAPE");
return Surface.ROTATION_270;
}else{
System.out.println("SCREEN_ORIENTATION_NOT_ADMISSIBLE");
return -1;
}
}
private int getScreenRotationOnTablet() {
final Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if(display.getRotation() == Surface.ROTATION_0){
System.out.println("SCREEN_ORIENTATION_LANDSCAPE");
return Surface.ROTATION_0;
}else if(display.getRotation() == Surface.ROTATION_90){
System.out.println("SCREEN_ORIENTATION_REVERSE_PORTRAIT");
return Surface.ROTATION_90;
}else if(display.getRotation() == Surface.ROTATION_180){
System.out.println("SCREEN_ORIENTATION_REVERSE_LANDSCAPE");
return Surface.ROTATION_180;
}else if(display.getRotation() == Surface.ROTATION_270){
System.out.println("SCREEN_ORIENTATION_PORTRAIT");
return Surface.ROTATION_270;
}else{
System.out.println("SCREEN_ORIENTATION_NOT_ADMISSIBLE");
return -1;
}
}
public boolean isTablet(Context context) {
boolean xlarge = ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 4);
boolean large = ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE);
return (xlarge || large);
}
public void onOrientationChanged(int orientation, Camera.Parameters mParameters) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(CameraActivity.getOpenedCamera(), info);
Log.i(TAG, "onOrientationChanged -> Camera opened actually is: "+CameraActivity.getOpenedCamera());
orientation = (orientation + 45)/90 * 90;
int rotation = 0;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + orientation) % 360;
}
Log.i(TAG, "onOrientationChanged -> Orientation of pictures setted to: "+rotation);
mParameters.setRotation(rotation);
}
在这里,有CameraActivity的onPictureTaken代码,切换摄像头的方法:使用工具的这种方法
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// TODO Auto-generated method stub
File pictureFile = Utility.getOutputMediaFile();
if (pictureFile == null){
Toast.makeText(this, "Couldn't create file", Toast.LENGTH_SHORT).show();
Log.d(TAG,"Couldn't create file");
return;//?
}else{
try{
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.flush();
fos.close();
}
catch (FileNotFoundException e){
Toast.makeText(this, "File not found exception", Toast.LENGTH_SHORT).show();
Log.d(TAG,"File not found: "+e.getMessage());
}
catch (IOException e){
Toast.makeText(this, "IO Exception", Toast.LENGTH_SHORT).show();
Log.d(TAG, "Error accessing file: "+e.getMessage());
}
//Per farle comparire subito nella cartella le foto:
this.mPictureFile = pictureFile;
MediaScannerConnection.scanFile(getApplicationContext(),
new String[]{this.mPictureFile.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i(TAG, "ExternalStorage Scanned " + path + ":");
Log.i(TAG, "ExternalStorage -> uri=" + uri);
}
});
camera.startPreview();
imageSaved.sendEmptyMessage(0);
}
}
public void switchCam(){
if (hasFrontCam == Boolean.TRUE && hasBackCam == Boolean.TRUE) {
// The phone has front camera and back camera
if(openedCam==Camera.CameraInfo.CAMERA_FACING_BACK){
//Chiudi la preview
if (mCamera!=null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//Apri la nuova camera
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
init();
openedCam=Camera.CameraInfo.CAMERA_FACING_FRONT;
}else{
//openedCam==Camera.CameraInfo.CAMERA_FACING_FRONT
//Chiudi la preview
if (mCamera!=null){
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//Apri la nuova camera
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
//riprendi la preview
init();
openedCam=Camera.CameraInfo.CAMERA_FACING_BACK;
}
}
}
:
/** Create a File for saving an image */
public static File getOutputMediaFile(){
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
if (!mediaStorageDir.exists()){
if (!mediaStorageDir.mkdirs()){
Log.d(TAG,"Failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
return mediaFile;
}else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.d(TAG,"External storage not writable but only readable");
return null;
}else{
Log.d(TAG,"External storage not writable");
return null;
}
}
有人可以帮助我解决这个问题?
答
我已经解决了这个问题。
我在支持的背面和前置摄像头的图片尺寸列表中找到了解决方案。
就我而言,我使用的是三星S4的开发,并支持图片尺寸列表中,为后相机,是:
0) (4128*3096) aspect_ratio=1.333 ---> Choosen automatically
1) (4128*2322) aspect_ratio=1.777
2) (3264*2448) aspect_ratio=1.333
3) (3264*1836) aspect_ratio=1.777
4) (2048*1536) aspect_ratio=1.333
5) (2048*1152) aspect_ratio=1.777
6) (1280*720) aspect_ratio=1.777
7) (640*480) aspect_ratio=1.777
对于前置摄像头:
0) (1920*1080) aspect_ratio=1.777 ---> Choosen automatically
1) (1440*1080) aspect_ratio=1.333
2) (1280*720) aspect_ratio=1.777
3) (960*720) aspect_ratio=1.333
4) (720*480) aspect_ratio=1.5
5) (640*480) aspect_ratio=1.333
6) (320*240) aspect_ratio=1.333
假设尺寸是(宽度;高度),宽高比计算如下:宽度/高度。
Android自动选择前后相机的最佳图片尺寸;它不检查前后相机选择的尺寸是否具有相同的宽高比。但纵横比必须相同,才能在相同尺寸的图库照片中显示。
我注意到图片翻转了。是对的吗? – hasan83 2014-12-03 10:04:29
在前后凸轮之间切换的代码在哪里。 – hasan83 2014-12-03 10:08:49
是的,图片被翻转以纠正其保存到Gallery中的方向。改变的作品,实际上照片的方向是正确的。我还添加了切换摄像头的方法。 – user140888 2014-12-03 10:24:48