Android
Camera2
影像處理理踩坑經驗
Takuma@Android Study Group
1
Takuma Lee@Android Study Group
vmgsahm1@gmail.com
Individual App Developer@⾃自宅警備員
App Developer@秘密
Today’s source code
http://bit.ly/android-camera2-sample
Google Repo:
https://github.com/googlesamples/android-Camera2Basic
Licensed under the Charlie Tsai
2
Android Developer 開發讀書會
• 瘋狂熱愛(?) Android 的⼀一群⼈人聚集的社群
• 社群中常發⽣生寫⼀一寫 Android 就跳去寫 iOS 最後變成 iOS ⼯工程師的事件
• 設立:2013 / 09
• 社群⼈人數:7455(DevFest 時 6745)
• Google Community Group
• 每週三舉辦線下聚會
• 每⽉月舉辦⽉月會(主題不限)
3
如何找到讀書會
• FB 社群:http://bit.ly/TADSG_FB
• Github:https://github.com/TADSG
• Meetup:https://www.meetup.com/Taiwan-
Android-Developer-Study-Group/
• Telegram:http://bit.ly/TADSG_Telegram
Agenda
• Guide
• Setting
• Preview Callback
• Face Detection
• 坑
6
Guide
Setting
/**

* ID of the current {@link CameraDevice}.

*/

private String mCameraId;



/**

* An {@link AutoFitTextureView} for camera preview.

*/

private AutoFitTextureView mTextureView;



/**

* A {@link CameraCaptureSession } for camera preview.

*/

private CameraCaptureSession mCaptureSession;



/**

* A reference to the opened {@link CameraDevice}.

*/

private CameraDevice mCameraDevice;



/**

* The {@link android.util.Size} of camera preview.

*/

private Size mPreviewSize;
Setting
/**

* {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.

*/

private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {



@Override

public void onOpened(@NonNull CameraDevice cameraDevice) {

// This method is called when the camera is opened. We start camera preview here.

mCameraOpenCloseLock.release();

mCameraDevice = cameraDevice;

createCameraPreviewSession();

}



@Override

public void onDisconnected(@NonNull CameraDevice cameraDevice) {

mCameraOpenCloseLock.release();

cameraDevice.close();

mCameraDevice = null;

}



@Override

public void onError(@NonNull CameraDevice cameraDevice, int error) {

mCameraOpenCloseLock.release();

cameraDevice.close();

mCameraDevice = null;

Activity activity = getActivity();

if (null != activity) {

activity.finish();

}

}



};
Setting
/**

* An additional thread for running tasks that shouldn't block the UI.

*/

private HandlerThread mBackgroundThread;



/**

* A {@link Handler} for running tasks in the background.

*/

private Handler mBackgroundHandler;



/**

* An {@link ImageReader} that handles still image capture.

*/

private ImageReader mImageReader;
Setting
@Override

public void onResume() {

super.onResume();

startBackgroundThread();



// When the screen is turned off and turned back on, the SurfaceTexture is already

// available, and "onSurfaceTextureAvailable" will not be called. In that case, we
can open

// a camera and start preview from here (otherwise, we wait until the surface is
ready in

// the SurfaceTextureListener).

if (mTextureView.isAvailable()) {

openCamera(mTextureView.getWidth(), mTextureView.getHeight());

} else {

mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);

}

}



@Override

public void onPause() {

closeCamera();

stopBackgroundThread();

super.onPause();

}
Setting
/**

* A {@link CameraCaptureSession.CaptureCallback} that handles events related to
JPEG capture.

*/

private CameraCaptureSession.CaptureCallback mCaptureCallback

= new CameraCaptureSession.CaptureCallback() {







@Override

public void onCaptureProgressed(@NonNull CameraCaptureSession session,

@NonNull CaptureRequest request,

@NonNull CaptureResult partialResult) {

process(partialResult);

}



@Override

public void onCaptureCompleted(@NonNull CameraCaptureSession session,

@NonNull CaptureRequest request,

@NonNull TotalCaptureResult result) {

process(result);

}
};
Setting
private void openCamera(int width, int height) {

if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)

!= PackageManager.PERMISSION_GRANTED) {

requestCameraPermission();

return;

}

setUpCameraOutputs(width, height);

configureTransform(width, height);

Activity activity = getActivity();

CameraManager manager = (CameraManager)
activity.getSystemService(Context.CAMERA_SERVICE);

try {

if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {

throw new RuntimeException("Time out waiting to lock camera opening.");

}

manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);

} catch (CameraAccessException e) {

e.printStackTrace();

} catch (InterruptedException e) {

throw new RuntimeException("Interrupted while trying to lock camera
opening.", e);

}

}
Preview Callback
/**

* Creates a new {@link CameraCaptureSession} for camera preview.

*/

private void createCameraPreviewSession() {

try {

SurfaceTexture texture = mTextureView.getSurfaceTexture();

assert texture != null;



// We configure the size of default buffer to be the size of camera preview we want.

texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());



// This is the output Surface we need to start preview.

Surface surface = new Surface(texture);



// We set up a CaptureRequest.Builder with the output Surface.

mPreviewRequestBuilder

= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

mPreviewRequestBuilder.addTarget(surface);
createCameraSession();





} catch (CameraAccessException e) {

e.printStackTrace();

}

}
Preview Callback
// Here, we create a CameraCaptureSession for camera preview.

mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),

new CameraCaptureSession.StateCallback() {



@Override

public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

// The camera is already closed

if (null == mCameraDevice) {

return;

}



// When the session is ready, we start displaying the preview.

mCaptureSession = cameraCaptureSession;

try {

// Auto focus should be continuous for camera preview.

mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,

CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

// Flash is automatically enabled when necessary.

setAutoFlash(mPreviewRequestBuilder);



// Finally, we start displaying the camera preview.

mPreviewRequest = mPreviewRequestBuilder.build();

mCaptureSession.setRepeatingRequest(mPreviewRequest,

mCaptureCallback, mBackgroundHandler);

} catch (CameraAccessException e) {

e.printStackTrace();

}

}



@Override

public void onConfigureFailed(

@NonNull CameraCaptureSession cameraCaptureSession) {

showToast("Failed");

}

}, null

);
Face Detection
// Here, we create a CameraCaptureSession for camera preview.

mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),

new CameraCaptureSession.StateCallback() {



@Override

public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

// The camera is already closed

if (null == mCameraDevice) {

return;

}



// When the session is ready, we start displaying the preview.

mCaptureSession = cameraCaptureSession;

try {

// Auto focus should be continuous for camera preview.

mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,

CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

// Face detection mode setting

mPreviewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, faceDetectMode);

// Flash is automatically enabled when necessary.

setAutoFlash(mPreviewRequestBuilder);



// Finally, we start displaying the camera preview.

mPreviewRequest = mPreviewRequestBuilder.build();

mCaptureSession.setRepeatingRequest(mPreviewRequest,

mCaptureCallback, mBackgroundHandler);

} catch (CameraAccessException e) {

e.printStackTrace();

}

}



@Override

public void onConfigureFailed(

@NonNull CameraCaptureSession cameraCaptureSession) {

showToast("Failed");

}

}, null

);
CameraDevice
public static final int TEMPLATE_PREVIEW = 1;
public static final int TEMPLATE_STILL_CAPTURE = 2;
public static final int TEMPLATE_RECORD = 3;
public static final int TEMPLATE_VIDEO_SNAPSHOT = 4;
public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5;
public static final int TEMPLATE_MANUAL = 6;
Preview Callback
// For still image captures, we use the largest available
size.

Size largest = Collections.max(

Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),

new CompareSizesByArea());

mImageReader = ImageReader.newInstance(largest.getWidth(),
largest.getHeight(),

ImageFormat.JPEG, /*maxImages*/2);

mImageReader.setOnImageAvailableListener(

mOnImageAvailableListener, mBackgroundHandler);
Preview Callback
/**

* Creates a new {@link CameraCaptureSession} for camera preview.

*/

private void createCameraPreviewSession() {

try {

SurfaceTexture texture = mTextureView.getSurfaceTexture();

assert texture != null;



// We configure the size of default buffer to be the size of camera preview we want.

texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());



// This is the output Surface we need to start preview.

Surface surface = new Surface(texture);



// We set up a CaptureRequest.Builder with the output Surface.

mPreviewRequestBuilder

= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

mPreviewRequestBuilder.addTarget(surface);
createCameraSession();





} catch (CameraAccessException e) {

e.printStackTrace();

}

}
Preview Callback
// Use Preview Callback

mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
Preview Callback
Demo
Face Detection
Demo
坑
坑 - 畫臉部偵測
26
Q&A

Android camera2

  • 1.
  • 2.
    Takuma Lee@Android StudyGroup vmgsahm1@gmail.com Individual App Developer@⾃自宅警備員 App Developer@秘密 Today’s source code http://bit.ly/android-camera2-sample Google Repo: https://github.com/googlesamples/android-Camera2Basic Licensed under the Charlie Tsai 2
  • 3.
    Android Developer 開發讀書會 •瘋狂熱愛(?) Android 的⼀一群⼈人聚集的社群 • 社群中常發⽣生寫⼀一寫 Android 就跳去寫 iOS 最後變成 iOS ⼯工程師的事件 • 設立:2013 / 09 • 社群⼈人數:7455(DevFest 時 6745) • Google Community Group • 每週三舉辦線下聚會 • 每⽉月舉辦⽉月會(主題不限) 3
  • 5.
    如何找到讀書會 • FB 社群:http://bit.ly/TADSG_FB •Github:https://github.com/TADSG • Meetup:https://www.meetup.com/Taiwan- Android-Developer-Study-Group/ • Telegram:http://bit.ly/TADSG_Telegram
  • 6.
    Agenda • Guide • Setting •Preview Callback • Face Detection • 坑 6
  • 7.
  • 8.
    Setting /**
 * ID ofthe current {@link CameraDevice}.
 */
 private String mCameraId;
 
 /**
 * An {@link AutoFitTextureView} for camera preview.
 */
 private AutoFitTextureView mTextureView;
 
 /**
 * A {@link CameraCaptureSession } for camera preview.
 */
 private CameraCaptureSession mCaptureSession;
 
 /**
 * A reference to the opened {@link CameraDevice}.
 */
 private CameraDevice mCameraDevice;
 
 /**
 * The {@link android.util.Size} of camera preview.
 */
 private Size mPreviewSize;
  • 9.
    Setting /**
 * {@link CameraDevice.StateCallback}is called when {@link CameraDevice} changes its state.
 */
 private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
 
 @Override
 public void onOpened(@NonNull CameraDevice cameraDevice) {
 // This method is called when the camera is opened. We start camera preview here.
 mCameraOpenCloseLock.release();
 mCameraDevice = cameraDevice;
 createCameraPreviewSession();
 }
 
 @Override
 public void onDisconnected(@NonNull CameraDevice cameraDevice) {
 mCameraOpenCloseLock.release();
 cameraDevice.close();
 mCameraDevice = null;
 }
 
 @Override
 public void onError(@NonNull CameraDevice cameraDevice, int error) {
 mCameraOpenCloseLock.release();
 cameraDevice.close();
 mCameraDevice = null;
 Activity activity = getActivity();
 if (null != activity) {
 activity.finish();
 }
 }
 
 };
  • 10.
    Setting /**
 * An additionalthread for running tasks that shouldn't block the UI.
 */
 private HandlerThread mBackgroundThread;
 
 /**
 * A {@link Handler} for running tasks in the background.
 */
 private Handler mBackgroundHandler;
 
 /**
 * An {@link ImageReader} that handles still image capture.
 */
 private ImageReader mImageReader;
  • 11.
    Setting @Override
 public void onResume(){
 super.onResume();
 startBackgroundThread();
 
 // When the screen is turned off and turned back on, the SurfaceTexture is already
 // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
 // a camera and start preview from here (otherwise, we wait until the surface is ready in
 // the SurfaceTextureListener).
 if (mTextureView.isAvailable()) {
 openCamera(mTextureView.getWidth(), mTextureView.getHeight());
 } else {
 mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
 }
 }
 
 @Override
 public void onPause() {
 closeCamera();
 stopBackgroundThread();
 super.onPause();
 }
  • 12.
    Setting /**
 * A {@linkCameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
 */
 private CameraCaptureSession.CaptureCallback mCaptureCallback
 = new CameraCaptureSession.CaptureCallback() {
 
 
 
 @Override
 public void onCaptureProgressed(@NonNull CameraCaptureSession session,
 @NonNull CaptureRequest request,
 @NonNull CaptureResult partialResult) {
 process(partialResult);
 }
 
 @Override
 public void onCaptureCompleted(@NonNull CameraCaptureSession session,
 @NonNull CaptureRequest request,
 @NonNull TotalCaptureResult result) {
 process(result);
 } };
  • 13.
    Setting private void openCamera(intwidth, int height) {
 if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
 != PackageManager.PERMISSION_GRANTED) {
 requestCameraPermission();
 return;
 }
 setUpCameraOutputs(width, height);
 configureTransform(width, height);
 Activity activity = getActivity();
 CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
 try {
 if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
 throw new RuntimeException("Time out waiting to lock camera opening.");
 }
 manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
 } catch (CameraAccessException e) {
 e.printStackTrace();
 } catch (InterruptedException e) {
 throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
 }
 }
  • 14.
    Preview Callback /**
 * Createsa new {@link CameraCaptureSession} for camera preview.
 */
 private void createCameraPreviewSession() {
 try {
 SurfaceTexture texture = mTextureView.getSurfaceTexture();
 assert texture != null;
 
 // We configure the size of default buffer to be the size of camera preview we want.
 texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
 
 // This is the output Surface we need to start preview.
 Surface surface = new Surface(texture);
 
 // We set up a CaptureRequest.Builder with the output Surface.
 mPreviewRequestBuilder
 = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
 mPreviewRequestBuilder.addTarget(surface); createCameraSession();
 
 
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 }
  • 15.
    Preview Callback // Here,we create a CameraCaptureSession for camera preview.
 mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
 new CameraCaptureSession.StateCallback() {
 
 @Override
 public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
 // The camera is already closed
 if (null == mCameraDevice) {
 return;
 }
 
 // When the session is ready, we start displaying the preview.
 mCaptureSession = cameraCaptureSession;
 try {
 // Auto focus should be continuous for camera preview.
 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
 // Flash is automatically enabled when necessary.
 setAutoFlash(mPreviewRequestBuilder);
 
 // Finally, we start displaying the camera preview.
 mPreviewRequest = mPreviewRequestBuilder.build();
 mCaptureSession.setRepeatingRequest(mPreviewRequest,
 mCaptureCallback, mBackgroundHandler);
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 }
 
 @Override
 public void onConfigureFailed(
 @NonNull CameraCaptureSession cameraCaptureSession) {
 showToast("Failed");
 }
 }, null
 );
  • 16.
    Face Detection // Here,we create a CameraCaptureSession for camera preview.
 mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
 new CameraCaptureSession.StateCallback() {
 
 @Override
 public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
 // The camera is already closed
 if (null == mCameraDevice) {
 return;
 }
 
 // When the session is ready, we start displaying the preview.
 mCaptureSession = cameraCaptureSession;
 try {
 // Auto focus should be continuous for camera preview.
 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
 // Face detection mode setting
 mPreviewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, faceDetectMode);
 // Flash is automatically enabled when necessary.
 setAutoFlash(mPreviewRequestBuilder);
 
 // Finally, we start displaying the camera preview.
 mPreviewRequest = mPreviewRequestBuilder.build();
 mCaptureSession.setRepeatingRequest(mPreviewRequest,
 mCaptureCallback, mBackgroundHandler);
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 }
 
 @Override
 public void onConfigureFailed(
 @NonNull CameraCaptureSession cameraCaptureSession) {
 showToast("Failed");
 }
 }, null
 );
  • 17.
    CameraDevice public static finalint TEMPLATE_PREVIEW = 1; public static final int TEMPLATE_STILL_CAPTURE = 2; public static final int TEMPLATE_RECORD = 3; public static final int TEMPLATE_VIDEO_SNAPSHOT = 4; public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5; public static final int TEMPLATE_MANUAL = 6;
  • 18.
    Preview Callback // Forstill image captures, we use the largest available size.
 Size largest = Collections.max(
 Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
 new CompareSizesByArea());
 mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
 ImageFormat.JPEG, /*maxImages*/2);
 mImageReader.setOnImageAvailableListener(
 mOnImageAvailableListener, mBackgroundHandler);
  • 19.
    Preview Callback /**
 * Createsa new {@link CameraCaptureSession} for camera preview.
 */
 private void createCameraPreviewSession() {
 try {
 SurfaceTexture texture = mTextureView.getSurfaceTexture();
 assert texture != null;
 
 // We configure the size of default buffer to be the size of camera preview we want.
 texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
 
 // This is the output Surface we need to start preview.
 Surface surface = new Surface(texture);
 
 // We set up a CaptureRequest.Builder with the output Surface.
 mPreviewRequestBuilder
 = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
 mPreviewRequestBuilder.addTarget(surface); createCameraSession();
 
 
 } catch (CameraAccessException e) {
 e.printStackTrace();
 }
 }
  • 20.
    Preview Callback // UsePreview Callback
 mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.