Android、OpenCv、捕获的照片尺寸与预览不匹配
Android, OpenCv, captured photo dimensions do not match preview
我有一个 JavaCameraView subclass,它允许用户拍摄和保存照片。问题是我通过 onCameraFrame() 收到的预览帧与拍摄和保存的照片不共享相同的尺寸,导致保存的图像相对于帧出现裁剪。输入帧为 1080 * 1440(3:4),捕获为 2988 * 5312(9:16)。
我的代码如下:
public void takePicture() {
Log.i(CaptureActivity.TAG, "MyCameraView, takePicture()");
// Postview and jpeg are sent in the same buffers if the queue is not empty when performing a capture.
// Clear up buffers to avoid mCamera.takePicture to be stuck because of a memory issue
mCamera.setPreviewCallback(null);
// PictureTakenListener is implemented by the current class
mCamera.takePicture(null, null, this);
}
@Override
public void onPictureTaken(byte[] bytes, Camera camera) {
// The camera preview was automatically stopped. Start it again.
mCamera.startPreview();
mCamera.setPreviewCallback(this);
Mat mat = Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
Log.d(CaptureActivity.TAG, mat.toString());
//an interface method to be implemented by CaptureActivity
if (mPictureTakenListener != null)
mPictureTakenListener.receivePicture(mat);
}
在另一个class中,我收到图片并保存...
@Override
public void receivePicture(Mat mat) {
File path = new File(Environment.getExternalStorageDirectory() + "/myCamera/");
path.mkdirs();
File file = new File(path, System.currentTimeMillis() + "recieved" + ".png");
String filename = file.toString();
Log.d(CaptureActivity.TAG, "was imwrite a success? "
+ Imgcodecs.imwrite(filename, mat));
}
这个class也实现了CameraBridgeViewBase.CvCameraViewListener2...
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Log.d(TAG, "frame info " + frame.toString());
return frame;
}
您应该使用 setPreviewSize and setPictureSize in order to configure what you get from preview and takePicture. Note that it you won't necessarily find identical sizes for both of them (usually takePicture available sizes can be higher than preview ones), so take a look into getSupportedPreviewSizes and getSupportedPictureSizes 来找到最适合您任务的尺寸。
你是对的。不建议使用相同的宽高比进行预览和捕获。因此,在您的子类中,您应该像这样重写 initializeCamera()
方法:
@Override
protected void AllocateCache() {
super.AllocateCache();
setPictureSize();
}
protected boolean setPictureSize() {
try {
Camera.Parameters params = mCamera.getParameters();
Log.d(TAG, "getSupportedPictureSizes()");
List<Camera.Size> sizes = params.getSupportedPictureSizes();
if (sizes == null) {
Log.w(TAG, "getSupportedPictureSizes() = null, cannot set a custom size");
return false;
}
int maxSize = 0;
// choose the largest size that matches the preview AR
for (android.hardware.Camera.Size size : sizes) {
if (size.height * mFrameWidth != size.width * mFrameHeight) {
continue; // the picture size doesn't match
}
if (maxSize > size.width * size.height) {
continue; // don't need this size
}
params.setPictureSize(size.width, size.height);
maxSize = size.width * size.height;
}
if (maxSize == 0) {
Log.w(TAG, "getSupportedPictureSizes() has no matches for " + mFrameWidth + 'x' + mFrameHeight);
return false;
}
Log.d(TAG, "try Picture size " + params.getPictureSize().width + 'x' + params.getPictureSize().height);
mCamera.setParameters(params);
} catch (Exception e) {
Log.e(TAG, "setPictureSize for " + mFrameWidth + 'x' + mFrameHeight, e);
return false;
}
return true;
}
}
如您所见,我实际上重写了方法 AllocateCache()
,因为 OpenCV 在打开相机设备和开始预览之间没有提供更好的挂钩。在实时预览时更改相机参数(甚至图片大小)在某些设备上可能会失败。
请注意,上面的代码甚至没有尝试 来设置不完全适合所选预览的图片大小。对于 99.9% 的情况,这是可以的,但是如果一个奇怪的相机没有与 OpenCV 为您的布局选择的预览尺寸匹配的图片尺寸,您有两个选择:寻找另一个预览尺寸,或者尝试一些图片尺寸是 "close enough".
我有一个 JavaCameraView subclass,它允许用户拍摄和保存照片。问题是我通过 onCameraFrame() 收到的预览帧与拍摄和保存的照片不共享相同的尺寸,导致保存的图像相对于帧出现裁剪。输入帧为 1080 * 1440(3:4),捕获为 2988 * 5312(9:16)。
我的代码如下:
public void takePicture() {
Log.i(CaptureActivity.TAG, "MyCameraView, takePicture()");
// Postview and jpeg are sent in the same buffers if the queue is not empty when performing a capture.
// Clear up buffers to avoid mCamera.takePicture to be stuck because of a memory issue
mCamera.setPreviewCallback(null);
// PictureTakenListener is implemented by the current class
mCamera.takePicture(null, null, this);
}
@Override
public void onPictureTaken(byte[] bytes, Camera camera) {
// The camera preview was automatically stopped. Start it again.
mCamera.startPreview();
mCamera.setPreviewCallback(this);
Mat mat = Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
Log.d(CaptureActivity.TAG, mat.toString());
//an interface method to be implemented by CaptureActivity
if (mPictureTakenListener != null)
mPictureTakenListener.receivePicture(mat);
}
在另一个class中,我收到图片并保存...
@Override
public void receivePicture(Mat mat) {
File path = new File(Environment.getExternalStorageDirectory() + "/myCamera/");
path.mkdirs();
File file = new File(path, System.currentTimeMillis() + "recieved" + ".png");
String filename = file.toString();
Log.d(CaptureActivity.TAG, "was imwrite a success? "
+ Imgcodecs.imwrite(filename, mat));
}
这个class也实现了CameraBridgeViewBase.CvCameraViewListener2...
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Log.d(TAG, "frame info " + frame.toString());
return frame;
}
您应该使用 setPreviewSize and setPictureSize in order to configure what you get from preview and takePicture. Note that it you won't necessarily find identical sizes for both of them (usually takePicture available sizes can be higher than preview ones), so take a look into getSupportedPreviewSizes and getSupportedPictureSizes 来找到最适合您任务的尺寸。
你是对的。不建议使用相同的宽高比进行预览和捕获。因此,在您的子类中,您应该像这样重写 initializeCamera()
方法:
@Override
protected void AllocateCache() {
super.AllocateCache();
setPictureSize();
}
protected boolean setPictureSize() {
try {
Camera.Parameters params = mCamera.getParameters();
Log.d(TAG, "getSupportedPictureSizes()");
List<Camera.Size> sizes = params.getSupportedPictureSizes();
if (sizes == null) {
Log.w(TAG, "getSupportedPictureSizes() = null, cannot set a custom size");
return false;
}
int maxSize = 0;
// choose the largest size that matches the preview AR
for (android.hardware.Camera.Size size : sizes) {
if (size.height * mFrameWidth != size.width * mFrameHeight) {
continue; // the picture size doesn't match
}
if (maxSize > size.width * size.height) {
continue; // don't need this size
}
params.setPictureSize(size.width, size.height);
maxSize = size.width * size.height;
}
if (maxSize == 0) {
Log.w(TAG, "getSupportedPictureSizes() has no matches for " + mFrameWidth + 'x' + mFrameHeight);
return false;
}
Log.d(TAG, "try Picture size " + params.getPictureSize().width + 'x' + params.getPictureSize().height);
mCamera.setParameters(params);
} catch (Exception e) {
Log.e(TAG, "setPictureSize for " + mFrameWidth + 'x' + mFrameHeight, e);
return false;
}
return true;
}
}
如您所见,我实际上重写了方法 AllocateCache()
,因为 OpenCV 在打开相机设备和开始预览之间没有提供更好的挂钩。在实时预览时更改相机参数(甚至图片大小)在某些设备上可能会失败。
请注意,上面的代码甚至没有尝试 来设置不完全适合所选预览的图片大小。对于 99.9% 的情况,这是可以的,但是如果一个奇怪的相机没有与 OpenCV 为您的布局选择的预览尺寸匹配的图片尺寸,您有两个选择:寻找另一个预览尺寸,或者尝试一些图片尺寸是 "close enough".