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

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

问题描述:

我想使用Camera2 API从摄像机获取单帧并使用ImageView显示它。 我发现了一些问题,近如使用camera2 API获取单个图像并使用ImageView显示

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

同时,我已经看了Camera2Basic的例子,但它太复杂,不正是我需要的。

我编写了代码,这是基于我在网上看到的一些例子,应该这样做,但它不工作,我不明白为什么。

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

此外,该应用程序是logcat说,“该应用程序可能在背景上做了太多的工作..”我不明白怎么可能,因为我做了一个captureRequest而不是repeatingCaptureRequest

下面的代码和logcat的: 代码:

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(); 
} 

而且(相关)的logcat:

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: [email protected] 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; 
    } 
+0

谢谢你的回答,但你有没有测试过你的代码?它包含一些错误,我也不需要将帧保存在手机存储器中,我只想在imageView .. – Noam

+0

上显示它的简单想法,您可以从byte []创建位图而不保存它。 –

是RGB_565列为该相机设备支持的格式,从CameraManager.getCameraCharacteristics(ID)获得(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为止是个好主意;这仍然会将焦点留在潜在的糟糕状态,但是您是否想要处理焦点取决于用例和输出分辨率。