Reputation: 297
I have a JavaCameraView subclass which allows the user to take and save a photo. The issue is that the preview frame I receive via onCameraFrame() does not share the same dimensions as the photo that is captured and saved, causing the saved image to appear cropped relative to the frame. The input frame is 1080 * 1440 (3:4) and the capture is 2988 * 5312 (9:16).
My code is as follows:
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);
}
In another class, I receive the picture and save it...
@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));
}
This class also implements CameraBridgeViewBase.CvCameraViewListener2...
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Log.d(TAG, "frame info " + frame.toString());
return frame;
}
Upvotes: 2
Views: 1013
Reputation: 57203
You are right. Using same aspect ratio for preview and capture is not recommended. So, in your subclass, you should override the initializeCamera()
method like this:
@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;
}
}
As you can see, I actually override the method AllocateCache()
, because OpenCV does not provide a better hook between opening the camera device and starting preview. To change camera parameters (even picture size) while preview is live may fail on some devices.
Note that the above code does not even try to set picture size that does not fit exactly the chosen preview. For 99.9% of the cases this is OK, but if a weird camera does not have matching picture size for the preview size that OpenCV chose for your layout, you have two options: either look for another preview size, or try some picture size that is "close enough".
Upvotes: 2
Reputation: 2357
You should use 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 in order to find Sizes which would suit your task most.
Upvotes: 0