Google Mobile Vision CameraSource调用takePicture返回低分辨率图的问题及修复
Alright, let's break down why you're seeing low-res images from CameraSource.takePicture() on some devices, and how to fix it.
The core issue comes down to how Google's Mobile Vision CameraSource is designed, plus device-specific camera HAL (Hardware Abstraction Layer) behavior:
- Default priority is preview, not photography: Mobile Vision is built for real-time detection tasks (like face or barcode scanning), so
CameraSourceprioritizes low-latency, small-resolution preview streams by default. When you calltakePicture(), it reuses the same resolution parameters as the active preview—hence the 320x240 output. - Device compatibility inconsistencies: Different manufacturers implement camera HALs differently. Some devices (like your Samsung tablet) might automatically switch to a higher resolution for still captures, while others (like the Lenovo Tab 3) stick to the preview resolution because of how their HAL handles
CameraSourcerequests.
Here are actionable solutions to get higher-res photos with CameraSource:
1. Customize CameraSource to Force Photo Resolution
The stock CameraSource doesn't expose an API to set photo resolution, so you'll need to extend or modify it to override camera parameters before taking a picture.
Step-by-Step Implementation:
First, create a custom subclass of CameraSource that lets you set a target photo size:
public class CustomCameraSource extends CameraSource { private Size mTargetPictureSize; public CustomCameraSource(Context context, Detector<?> detector) { super(context, detector); } public void setTargetPictureSize(Size pictureSize) { mTargetPictureSize = pictureSize; } @Override public void takePicture(Surface shutter, PictureCallback callback) throws IOException { synchronized (getCameraLock()) { if (getCamera() == null) return; Camera.Parameters params = getCamera().getParameters(); // Save original preview size to restore later Size originalPreviewSize = new Size(params.getPreviewSize().width, params.getPreviewSize().height); // Set the desired photo resolution if specified if (mTargetPictureSize != null) { params.setPictureSize(mTargetPictureSize.getWidth(), mTargetPictureSize.getHeight()); getCamera().setParameters(params); } // Trigger the photo capture super.takePicture(shutter, callback); // Restore preview resolution to avoid breaking real-time detection params.setPreviewSize(originalPreviewSize.getWidth(), originalPreviewSize.getHeight()); getCamera().setParameters(params); } } // Helper to access private fields via reflection private Object getCameraLock() { try { Field lockField = CameraSource.class.getDeclaredField("mCameraLock"); lockField.setAccessible(true); return lockField.get(this); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); return new Object(); } } private Camera getCamera() { try { Field cameraField = CameraSource.class.getDeclaredField("mCamera"); cameraField.setAccessible(true); return (Camera) cameraField.get(this); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); return null; } } }
Next, find a supported high-res photo size for your device and apply it:
// Get supported photo sizes from the camera Camera tempCamera = Camera.open(); Camera.Parameters tempParams = tempCamera.getParameters(); List<Camera.Size> supportedPictureSizes = tempParams.getSupportedPictureSizes(); tempCamera.release(); // Pick a target size (e.g., 1280x720 or the largest available) Camera.Size targetSize = null; for (Camera.Size size : supportedPictureSizes) { // Adjust this to match your desired resolution if (size.width == 1280 && size.height == 720) { targetSize = size; break; } } // Fallback to largest size if target isn't found if (targetSize == null) { targetSize = supportedPictureSizes.get(0); } // Initialize your custom CameraSource CustomCameraSource cameraSource = new CustomCameraSource(this, yourDetector); cameraSource.setTargetPictureSize(new Size(targetSize.width, targetSize.height)); // Optional: Set a matching preview size to avoid resolution mismatches cameraSource.setRequestedPreviewSize(1280, 720);
2. Switch to Camera2 API (For Android 5.0+)
If your app targets Android 5.0 (API 21) or higher, consider using the Camera2 API instead of the old Camera API that CameraSource relies on. Camera2 gives you full control over capture resolutions, so you can explicitly request high-res stills without workarounds.
You'll need to integrate Mobile Vision's Detector with Camera2's capture pipeline—this requires more code, but it's a more robust long-term solution for modern devices.
3. Preview Frame Workaround (Quick Fix)
If you don't want to modify CameraSource, you can try increasing the preview resolution and capturing frames from the preview stream instead of using takePicture():
// Set a higher preview resolution first cameraSource.setRequestedPreviewSize(1280, 720); // Capture frames from the preview stream cameraSource.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { // Convert preview frame to Bitmap (handle rotation/format as needed) Camera.Parameters params = camera.getParameters(); Camera.Size previewSize = params.getPreviewSize(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inMutable = true; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options); // Rotate bitmap to match device orientation bitmap = rotateBitmap(bitmap, params.getRotation()); // Use or save the bitmap here Glide.with(getApplicationContext()).load(bitmap).into(capturedImg); } });
Note: This can impact real-time detection performance since higher-res previews use more CPU/GPU resources.
- Always verify that the resolution you're requesting is supported by the camera (use
getSupportedPictureSizes()orgetSupportedPreviewSizes()). - Image rotation varies by device—make sure to adjust the bitmap orientation based on the camera's rotation parameters.
内容的提问来源于stack exchange,提问作者V.Bhat

