使用 camera2 API 获取单个图像并使用 ImageView 显示它



我想使用 Camera2 API 从相机获取单个帧并使用 ImageView 显示它。 我发现了一些紧密的问题,例如

https://stackoverflow.com/questions/25462277/camera-preview-image-data-processing-with-android-l-and-camera2-api

我也看过Camera2Basic的例子,但它太复杂了,不完全是我所需要的。

我编写了代码,这是基于我在网上看到的一些示例,应该这样做,但它不起作用,我不知道为什么。

该应用程序不会崩溃,但只是不会在 ImageView 上显示任何内容。 我在任何函数调用中使用了日志消息,以尝试保持logcat清晰。

此外,该应用程序是logcat说"应用程序可能在后台做了太多工作。我不明白这怎么可能,因为我做了一个captureRequest而不是一个repeatingCaptureRequest.

这是代码和日志: 法典:

public class CameraImageReaderActivity extends AppCompatActivity {
private final static String TAG = "CAMERA_IMAGE_READY: ";
private ImageReader imageReader;
private String cameraId;
private CameraDevice camera;
private HandlerThread handlerThread;
private Handler handler;
private Surface imageReaderSurface;
private ImageView imageView;
private CameraDevice.StateCallback cameraStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
Log.d(TAG, "onOpend: CAMERA OPENED");
camera = cameraDevice;
getFrames();
}
@Override
public void onDisconnected(CameraDevice cameraDevice) {
Log.d(TAG, "onDisconnected: CAMERA DISCONNECTED");
cameraDevice.close();
camera = null;
}
@Override
public void onError(CameraDevice cameraDevice, int i) {
Log.d(TAG, "onError: CAMERA ERROR");
cameraDevice.close();
camera = null;
}
};
private CameraCaptureSession.StateCallback captureSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
Log.d(TAG, "onConfigured: build request and capture");
try {
CaptureRequest.Builder requestBuilder = cameraCaptureSession.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
requestBuilder.addTarget(imageReaderSurface);
cameraCaptureSession.capture(requestBuilder.build(), null, handler);
} catch (CameraAccessException e) {
Log.d(TAG, "onConfigured: CANT CREATE CAPTURE REQUEST");
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Log.d(TAG, "onConfiguredFailed: CANT CONFIGURE CAMERA");
}
};
private ImageReader.OnImageAvailableListener imageReaderListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader imageReader) {
Log.d(TAG, "onImageAvailable: IMAGE AVAILABLE");
Image image = imageReader.acquireLatestImage();
int imgFormat = image.getFormat();
ByteBuffer pixelArray1 = image.getPlanes()[0].getBuffer();
int pixelStride = image.getPlanes()[0].getPixelStride();
int rowStride = image.getPlanes()[0].getRowStride();
int rowPadding = rowStride - pixelStride * 640;
Bitmap bitmap = Bitmap.createBitmap(640 + rowPadding/pixelStride, 480, Bitmap.Config.RGB_565);
bitmap.copyPixelsFromBuffer(pixelArray1);
imageView.setImageBitmap(bitmap);
image.close();
}
};
/**
* Sets the cameraId with the front camera id and sets imageReader properties.
*/
public void setupCamera(int width, int height) {
imageReader = ImageReader.newInstance(width, height, ImageFormat.RGB_565, 30);
CameraManager cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
try {
for (String allCamerasId : cameraManager.getCameraIdList()) {
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(allCamerasId);
if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
cameraId = allCamerasId;
Log.d(TAG, "setupCamera: CameraId is: " + cameraId);
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Connects to the front facing camera.
* After the connection to the camera, the onOpened callback method will be invoked.
*/
public void connectCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "CANT OPEN CAMERA");
// TODO: Consider calling
//    ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
//   public void onRequestPermissionsResult(int requestCode, String[] permissions,
//                                          int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
cameraManager.openCamera(cameraId, cameraStateCallback, handler);
Log.d(TAG, "connectCamera: CAMERA OPENED!");
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Build the captureSessionRequest and start in repeat.
*/
public void getFrames() {
Log.d(TAG, "getFrames: CREATE CAPTURE SESSION");
imageReaderSurface = imageReader.getSurface();
List<Surface> surfaceList = new ArrayList<>();
surfaceList.add(imageReaderSurface);
try {
camera.createCaptureSession(surfaceList, captureSessionStateCallback, handler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
public void startBackgroundThread() {
handlerThread = new HandlerThread("CameraImageReaderActivity");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
public void stopBackgroundThread() {
handlerThread.quitSafely();
try {
handlerThread.join();
handlerThread = null;
handler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void closeCamera() {
if (camera != null) {
camera.close();
camera = null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_image_reader);
imageView = (ImageView) findViewById(R.id.imageView);
setupCamera(640, 480);
connectCamera();
}
@Override
protected void onPause() {
closeCamera();
startBackgroundThread();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
startBackgroundThread();
//connectCamera();
}

和(相关的)日志猫:

03-22 14:27:32.900 18806-18806/com.example.noamm_000.talkwithcompviawifi D/CAMERA_IMAGE_READY:: setupCamera: CameraId is: 0
03-22 14:27:32.904 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value mw_continuous-picture
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value emboss
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value sketch
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value neon
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value asd
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value backlight
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value flowers
03-22 14:27:32.905 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value AR
03-22 14:27:32.912 18806-18806/com.example.noamm_000.talkwithcompviawifi I/CameraManager: Using legacy camera HAL.
03-22 14:27:33.685 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value mw_continuous-picture
03-22 14:27:33.685 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value emboss
03-22 14:27:33.685 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value sketch
03-22 14:27:33.685 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value neon
03-22 14:27:33.686 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value asd
03-22 14:27:33.686 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value backlight
03-22 14:27:33.686 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value flowers
03-22 14:27:33.686 18806-18806/com.example.noamm_000.talkwithcompviawifi W/ArrayUtils: Ignoring invalid value AR
03-22 14:27:33.702 18806-18806/com.example.noamm_000.talkwithcompviawifi D/CAMERA_IMAGE_READY:: connectCamera: CAMERA OPENED!
03-22 14:27:33.719 18806-18806/com.example.noamm_000.talkwithcompviawifi I/Choreographer: Skipped 56 frames!  The application may be doing too much work on its main thread.
03-22 14:27:33.787 18806-18806/com.example.noamm_000.talkwithcompviawifi D/CAMERA_IMAGE_READY:: onOpend: CAMERA OPENED
03-22 14:27:33.787 18806-18806/com.example.noamm_000.talkwithcompviawifi D/CAMERA_IMAGE_READY:: getFrames: CREATE CAPTURE SESSION
03-22 14:27:33.789 18806-18806/com.example.noamm_000.talkwithcompviawifi I/CameraDeviceState: Legacy camera service transitioning to state CONFIGURING
03-22 14:27:33.789 18806-19149/com.example.noamm_000.talkwithcompviawifi I/RequestThread-0: Configure outputs: 1 surfaces configured.
03-22 14:27:33.790 18806-19149/com.example.noamm_000.talkwithcompviawifi D/Camera: app passed NULL surface
03-22 14:27:33.838 18806-18806/com.example.noamm_000.talkwithcompviawifi I/CameraDeviceState: Legacy camera service transitioning to state IDLE
03-22 14:27:33.843 18806-19150/com.example.noamm_000.talkwithcompviawifi D/CAMERA_IMAGE_READY:: onConfigured: build request and capture
03-22 14:27:33.874 18806-19149/com.example.noamm_000.talkwithcompviawifi W/LegacyRequestMapper: convertRequestMetadata - control.awbRegions setting is not supported, ignoring value
03-22 14:27:33.875 18806-19149/com.example.noamm_000.talkwithcompviawifi W/LegacyRequestMapper: Only received metering rectangles with weight 0.
03-22 14:27:33.875 18806-19149/com.example.noamm_000.talkwithcompviawifi W/LegacyRequestMapper: Only received metering rectangles with weight 0.
03-22 14:27:34.070 18806-18806/com.example.noamm_000.talkwithcompviawifi I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@13c07070 time:331143683
03-22 14:27:34.317 18806-19155/com.example.noamm_000.talkwithcompviawifi I/CameraDeviceState: Legacy camera service transitioning to state CAPTURING
03-22 14:27:34.353 18806-19149/com.example.noamm_000.talkwithcompviawifi I/CameraDeviceState: Legacy camera service transitioning to state IDLE
03-22 14:27:34.403 18806-18806/com.example.noamm_000.talkwithcompviawifi D/BubblePopupHelper: isShowingBubblePopup : false
03-22 14:27:34.403 18806-18806/com.example.noamm_000.talkwithcompviawifi D/BubblePopupHelper: isShowingBubblePopup : false
03-22 14:27:34.404 18806-18806/com.example.noamm_000.talkwithcompviawifi D/BubblePopupHelper: isShowingBubblePopup : false
03-22 14:27:34.404 18806-18806/com.example.noamm_000.talkwithcompviawifi D/BubblePopupHelper: isShowingBubblePopup : false
03-22 14:28:07.684 18806-18823/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.684 18806-18822/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.684 18806-19184/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.685 18806-18823/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.685 18806-18822/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.685 18806-19184/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.686 18806-18823/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned
03-22 14:28:07.686 18806-18822/com.example.noamm_000.talkwithcompviawifi E/BufferQueueProducer: [unnamed-18806-1] cancelBuffer: BufferQueue has been abandoned

谢谢 诺姆

尝试以这种方式更好地在后台线程中处理它。

public void onImageAvailable(ImageReader reader) {
new ImageSaver(reader.acquireLatestImage());
} 
private class ImageSaver implements Runnable {
private final Image mImage;
public ImageSaver(Image image) {
mImage = image;
}
@Override
public void run() {
File mImageFileName = null;
if (mImage != null) {
ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
FileOutputStream fileOutputStream = null;
try {
mImageFileName = createImageFileName();
fileOutputStream = new FileOutputStream(mImageFileName);
fileOutputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
mImage.close();
if (mImageFileName != null) {
Intent mediaStoreUpdateIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaStoreUpdateIntent.setData(Uri.fromFile(mImageFileName));
sendBroadcast(mediaStoreUpdateIntent);
loadImageFromStorage(mImageFileName);
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
private void loadImageFromStorage(File mImageFileName) {
imageView.setImageBitmap(BitmapFactory.decodeFile(mImageFileName.getAbsolutePath()));
}
}
private File createImageFileName() throws IOException {
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String prepend = "IMAGE_" + timestamp + "_";
File imageFile = File.createTempFile(prepend, ".jpg", createImageFolder());
return imageFile;
}
private File createImageFolder() {
File imageFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File mImageFolder = new File(imageFile, "myFolder");
if (!mImageFolder.exists()) {
mImageFolder.mkdirs();
}
return mImageFolder;
}

由于这个问题有点旧,不确定它是否仍然是热门话题,但我会尝试回答。

您正在创建ImageRader的实例,一个ImageReader.OnImageAvailableListener,但未将侦听器分配给您拥有的 imageReader 实例。

setupCamera方法中,实例化新的 ImageReader 后,将侦听器设置为该侦听器:

/**
* Sets the cameraId with the front camera id and sets imageReader properties.
*/
public void setupCamera(int width, int height) {
imageReader = ImageReader.newInstance(width, height, ImageFormat.RGB_565, 30);
// this line is missing
imageReader.setOnImageAvailableListener(imageReaderListener, handler);
...

希望这对您或其他人尝试您的代码作为示例有所帮助。

RGB_565是否列为此相机设备支持的格式,来自 CameraManager.getCameraFeatures(id).get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputFormats()?

否则,这通常不起作用,尽管它应该会导致会话创建失败。 保证支持的唯一格式是 ImageFormat.JPEG 和 ImageFormat.YUV_420_888。

前者可能更容易处理(尽管效率较低) - 将 JPEG 平面 [0] ByteBuffer 复制到 byte[] 中,并使用 BitmapFactory.decodeByteArray() 从中创建位图进行显示。

但是,对于您的特定情况,您似乎从未调用过imageReader.setOnImageAvailableListener(imageReaderListener),因此您永远不会收到有关捕获缓冲区的通知。

编辑:此外,您从相机收到的前几张图像可能曝光不佳(如果您处于光线不足的位置,则可能全黑),并且白平衡,对焦等较差。 您需要让相机运行几帧,以便在捕获最终图像之前,它可以将自动曝光/对焦/等调整为正确的值。

通常,使用带有TEMPLATE_PREVIEW的重复请求并等待直到在捕获结果中看到至少CONTROL_AE_STATE_CONVERGED是一个好主意;这仍然会使焦点处于潜在的不良状态,但是否要处理焦点取决于用例和输出分辨率。

最新更新