我正在使用相机的Surface View来显示相机并拍照 我需要相机预览4:3,Instagram是一个正方形,我的是一个矩形。
如果您查看Instagram应用程序,相机预览不会拉伸或压缩,但在我的压缩中。
这是我的相机预览类:
class CustomCam extends SurfaceView implements SurfaceHolder.Callback {
private final String TAG = "PIC-FRAME";
private static final double ASPECT_RATIO = 4.0 / 3.0;
private static final int PICTURE_SIZE_MAX_WIDTH = 1280;
private static final int PREVIEW_SIZE_MAX_WIDTH = 640;
private SurfaceHolder mHolder;
private Camera mCamera;
private Display display;
public List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public CustomCam(Activity context, Camera camera) {
super(context);
mCamera = camera;
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
for (Camera.Size str : mSupportedPreviewSizes)
Log.e(TAG, str.width + "/" + str.height);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
setKeepScreenOn(true);
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
this.getHolder().removeCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
private Camera.Size getBestPreviewSize(int width, int height) {
Camera.Size result = null;
Camera.Parameters p = mCamera.getParameters();
for (Camera.Size size : p.getSupportedPreviewSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
return result;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//This line helped me set the preview Display Orientation to Portrait
//Only works API Level 8 and higher unfortunately.
try {
Camera.Parameters parameters = mCamera.getParameters();
// Camera.Size size = getBestPreviewSize(width, height);
// Camera.Size size = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
// parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
// initialCameraPictureSize(parameters);
// parameters.setPreviewSize(size.width, size.height);
Camera.Size bestPreviewSize = determineBestPreviewSize(parameters);
Camera.Size bestPictureSize = determineBestPictureSize(parameters);
parameters.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);
parameters.setPictureSize(bestPictureSize.width, bestPictureSize.height);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
mCamera.setDisplayOrientation(90);
mCamera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setPreviewDisplay(mHolder);
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void initialCameraPictureSize(Camera.Parameters parameters) {
List list = parameters.getSupportedPictureSizes();
if (list != null) {
Camera.Size size = null;
Iterator iterator = list.iterator();
do {
if (!iterator.hasNext())
break;
Camera.Size size1 = (Camera.Size) iterator.next();
if (Math.abs(3F * ((float) size1.width / 4F) - (float) size1.height) < 0.1F * (float) size1.width && (size == null || size1.height > size.height && size1.width < 3000))
size = size1;
} while (true);
if (size != null)
parameters.setPictureSize(size.width, size.height);
else
Log.e("CameraSettings", "No supported picture size found");
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
/**
* Measure the view and its content to determine the measured width and the
* measured height.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if (width > height * ASPECT_RATIO) {
width = (int) (height * ASPECT_RATIO + 0.5);
} else {
height = (int) (width / ASPECT_RATIO + 0.5);
}
setMeasuredDimension(width, height);
}
protected Camera.Size determineBestSize(List<Camera.Size> sizes, int widthThreshold) {
Camera.Size bestSize = null;
for (Camera.Size currentSize : sizes) {
boolean isDesiredRatio = (currentSize.width / 4) == (currentSize.height / 3);
boolean isBetterSize = (bestSize == null || currentSize.width > bestSize.width);
boolean isInBounds = currentSize.width <= PICTURE_SIZE_MAX_WIDTH;
if (isDesiredRatio && isInBounds && isBetterSize) {
bestSize = currentSize;
}
}
if (bestSize == null) {
return sizes.get(0);
}
return bestSize;
}
private Camera.Size determineBestPreviewSize(Camera.Parameters parameters) {
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
return determineBestSize(sizes, PREVIEW_SIZE_MAX_WIDTH);
}
private Camera.Size determineBestPictureSize(Camera.Parameters parameters) {
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
return determineBestSize(sizes, PICTURE_SIZE_MAX_WIDTH);
}
}
我的自定义框架布局:
CustomFrameLayout extends FrameLayout {
private static final float RATIO = 4f / 3f;
public CustomFrameLayout(Context context) {
super(context);
}
public CustomFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();
int maxWidth = (int) (heigthWithoutPadding * RATIO);
int maxHeight = (int) (widthWithoutPadding / RATIO);
if (widthWithoutPadding > maxWidth) {
width = maxWidth + getPaddingLeft() + getPaddingRight();
} else {
height = maxHeight + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
但是在框架布局内压缩凸轮预览我该如何解决?问题?
更新
好了,经过一些研究后才知道它是因为onMeasure ASPECT_RATIO = 4:3
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if (width > height * ASPECT_RATIO) {
width = (int) (height * ASPECT_RATIO + 0.5);
} else {
height = (int) (width / ASPECT_RATIO + 0.5);
}
setMeasuredDimension(width, height);
}
解决方案
所以我想到一个解决方案,可能(就像Instagram那样)让你的相机全尺寸然后隐藏布局的某些区域只是为了使它看起来像4:3的比例。然后通过使用一些裁剪机制必须剪切图像以使图像看起来像4:3。
说我总是以4:3的比例从顶部显示预览,下面部分的其余部分被隐藏,所以现在我拍摄照片后我想要将图像从顶部裁剪为4:3并保存。< / p>
我如何实现这一目标,这是一个可行的解决方案吗?
答案 0 :(得分:2)
据我所知,您当前的问题是如何裁剪您收到的图像并进行显示。这是一个小例子:
@OnClick(R.id.btn_record_start)
public void takePhoto() {
if (null != actions) {
EasyCamera.PictureCallback callback = new EasyCamera.PictureCallback() {
public void onPictureTaken(byte[] data, EasyCamera.CameraActions actions) {
// store picture
Bitmap bitmap = ImageUtils.getExifOrientedBitmap(data);
if ((portrait && bitmap.getHeight() < bitmap.getWidth()) ||
(!portrait && bitmap.getHeight() > bitmap.getWidth())) {
Matrix matrix = new Matrix();
matrix.postRotate(90);
bitmap =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
matrix, true);
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
if (Camera.CameraInfo.CAMERA_FACING_FRONT == info.facing) {
Matrix matrix = new Matrix();
matrix.postRotate(180);
bitmap =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
matrix, true);
}
showPhoto(bitmap);
}
};
actions.takePicture(EasyCamera.Callbacks.create()
.withJpegCallback(callback));
}
}
这是我用来处理照片后处理图像方向的方法。
它可以很容易地修改,以处理裁剪。要实现这一点,您必须指定图像的目标宽度和高度(目前我正在发送整个位图的大小)。一种可能的解决方案是获取图像的高度并删除过大的宽度 - 因此您发送到createBitmap方法的参数将是bitmap.getHeight() * 4.0 / 3.0
和bitmap.getHeight()
。以下是修改后的示例:
@OnClick(R.id.btn_record_start)
public void takePhoto() {
if (null != actions) {
EasyCamera.PictureCallback callback = new EasyCamera.PictureCallback() {
public void onPictureTaken(byte[] data, EasyCamera.CameraActions actions) {
// store picture
Bitmap bitmap = ImageUtils.getExifOrientedBitmap(data);
if ((portrait && bitmap.getHeight() < bitmap.getWidth()) ||
(!portrait && bitmap.getHeight() > bitmap.getWidth())) {
Matrix matrix = new Matrix();
matrix.postRotate(90);
bitmap =
Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
matrix, true);
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
if (Camera.CameraInfo.CAMERA_FACING_FRONT == info.facing) {
Matrix matrix = new Matrix();
matrix.postRotate(180);
bitmap =
Bitmap.createBitmap(bitmap, 0, 0, (int) (bitmap.getHeight() * 4.0 / 3.0), bitmap.getHeight(),
matrix, true);
}
showPhoto(bitmap);
}
};
actions.takePicture(EasyCamera.Callbacks.create()
.withJpegCallback(callback));
}
}
有几点需要注意:
4.0 / 3.0
变量ASPECT_RATIO
部分
以下是我正在使用的其他ImageUtils方法:
<强> getExifOrientedBitmap 强>
public static Bitmap getExifOrientedBitmap(byte[] data) {
File newPhotoFile = writeToFile(data);
if (newPhotoFile == null) {
return null;
}
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
bitmap = fixOrientationIfNeeded(newPhotoFile, bitmap);
newPhotoFile.delete();
return bitmap;
}
<强>将writeToFile 强>
@Nullable
public static File writeToFile(byte[] data) {
File dir = PhotoMessageComposer.getPhotoDir();
if (!dir.exists()) {
dir.mkdir();
}
File newPhotoFile = new File(dir, ImageUtils.getRandomFilename());
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(newPhotoFile);
fos.write(data);
fos.close();
} catch (Exception error) {
return null;
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return newPhotoFile;
}
<强> getPhotoDir 强>
@NonNull
public static File getPhotoDir() {
return new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) +
PICTURES_DIR);
}
<强> getRandomFileName 强>
public static String getRandomFilename() {
return UUID.randomUUID().toString() + IMAGE_EXTENSION;
}
<强> fixOrientationIfNeeded 强>
public static Bitmap fixOrientationIfNeeded(File sourceFile, Bitmap source) {
ExifInterface exif;
try {
exif = new ExifInterface(sourceFile.getAbsolutePath());
int exifOrientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
if (exifOrientation != ExifInterface.ORIENTATION_NORMAL) {
Matrix matrix = new Matrix();
int angle = findRotationAngle(exifOrientation);
matrix.postRotate(angle);
source = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, true);
return source;
}
} catch (IOException e) {
e.printStackTrace();
}
return source;
}
<强> findRotationAngle 强>
protected static int findRotationAngle(int exifOrientation) {
switch (exifOrientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
default:
return 0;
}
}
P.S。自ImageUtils
类实现以来已经有几年了,所以可能有更好的方法来处理这些操作。尽管如此,它们应该足够好了。