Slide 1

Slide 1 text

Getting Started with CameraX @owahltinez @calren24

Slide 2

Slide 2 text

Camera Development can be difficult and frustrating

Slide 3

Slide 3 text

Camera Development can be difficult and frustrating ● Supporting different API versions

Slide 4

Slide 4 text

Camera Development can be difficult and frustrating ● Supporting different API versions ● Providing consistency across devices

Slide 5

Slide 5 text

Camera Development can be difficult and frustrating ● Supporting different API versions ● Providing consistency across devices ● Understanding the complex camera API

Slide 6

Slide 6 text

Camera Development can be difficult and frustrating ● Supporting different API versions ● Providing consistency across devices ● Understanding the complex camera API CameraX support library aims to help solve these problems

Slide 7

Slide 7 text

CameraX ● Backwards compatible to L (90% of devices)

Slide 8

Slide 8 text

CameraX ● Backwards compatible to L (90% of devices) ● Consistent behavior across devices

Slide 9

Slide 9 text

CameraX ● Backwards compatible to L (90% of devices) ● Consistent behavior across devices ● Easy to use APIs

Slide 10

Slide 10 text

Using CameraX results in 70% fewer lines of code vs camera2

Slide 11

Slide 11 text

Preview Image Analysis Capture

Slide 12

Slide 12 text

Preview Image Analysis Capture

Slide 13

Slide 13 text

// configure preview val previewConfig = PreviewConfig.Builder().build()t .setTargetRotation(Surface.ROTATION_180) his as LifecycleOwner, preview)

Slide 14

Slide 14 text

// create preview val previewConfig = PreviewConfig.Builder().build() .setTargetRotation(Surface.ROTATION_180) val preview = Preview(previewConfig)wner, preview)

Slide 15

Slide 15 text

// attach output to view val previewConfig = PreviewConfig.Builder().build() .setTargetRotation(Surface.ROTATION_180) val preview = Preview(previewConfig) preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> //your code here. e.g. use previewOutput?.surfaceTexture //and post to a GL renderer. } CameraX.bindToLifecycle(this as LifecycleOwner, preview)

Slide 16

Slide 16 text

// attach preview to lifecycle val previewConfig = PreviewConfig.Builder().build() .setTargetRotation(Surface.ROTATION_180) val preview = Preview(previewConfig) preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> //your code here. e.g. use previewOutput?.surfaceTexture //and post to a GL renderer. } CameraX.bindToLifecycle(this as LifecycleOwner, preview)

Slide 17

Slide 17 text

// display preview on screen val previewConfig = PreviewConfig.Builder().build() .setTargetRotation(Surface.ROTATION_180) val preview = Preview(previewConfig) preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> //your code here. e.g. use previewOutput?.surfaceTexture //and post to a GL renderer. } CameraX.bindToLifecycle(this as LifecycleOwner, preview)

Slide 18

Slide 18 text

Preview Image Analysis Capture

Slide 19

Slide 19 text

// configure image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder()

Slide 20

Slide 20 text

// set resolution val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build()

Slide 21

Slide 21 text

// create image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig)

Slide 22

Slide 22 text

// attach output val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig) imageAnalysis.setAnalyzer({ image: ImageProxy, rotationDegrees: Int -> val cropRect = image.cropRect // do some analysis })

Slide 23

Slide 23 text

// attach image analysis & preview to lifecycle val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig) imageAnalysis.setAnalyzer({ image: ImageProxy, rotationDegrees: Int -> val cropRect = image.cropRect // do some analysis }) CameraX.bindToLifecycle(this as LifecycleOwner, imageAnalysis, preview)

Slide 24

Slide 24 text

// full setup to process images val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig) imageAnalysis.setAnalyzer({ image: ImageProxy, rotationDegrees: Int -> val cropRect = image.cropRect // do some analysis }) CameraX.bindToLifecycle(this as LifecycleOwner, imageAnalysis, preview)

Slide 25

Slide 25 text

Preview Image Analysis Capture

Slide 26

Slide 26 text

// configure image capture val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetResolution(Size(1280, 720)) .build()

Slide 27

Slide 27 text

// create image capture val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageCapture = ImageCapture(imageCaptureConfig)

Slide 28

Slide 28 text

// bind all use cases val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageCapture = ImageCapture(imageCaptureConfig) CameraX.bindToLifecycle(this as LifecycleOwner, imageCapture, imageAnalysis, preview)

Slide 29

Slide 29 text

// full setup to capture images val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageCapture = ImageCapture(imageCaptureConfig) CameraX.bindToLifecycle(this as LifecycleOwner, imageCapture, imageAnalysis, preview)

Slide 30

Slide 30 text

// on user action

Slide 31

Slide 31 text

// on user action fun onClick() { val file = File(..) imageCaptureUseCase.takePicture(file, object : ImageCaptureUseCase.OnImageSavedListener { override fun onError(error: ImageCaptureUseCase.UseCaseError, message: String, exc: Throwable?) { //insert your code here } override fun onImageSaved(file: File) { //insert your code here } }) }

Slide 32

Slide 32 text

// on user action save a picture fun onClick() { val file = File(..) imageCapture.takePicture(file, object : ImageCaptureUseCase.OnImageSavedListener { override fun onError(error: ImageCaptureUseCase.UseCaseError, message: String, exc: Throwable?) { //insert your code here } override fun onImageSaved(file: File) { //insert your code here } }) }

Slide 33

Slide 33 text

// save a picture on user action fun onClick() { val file = File(..) imageCapture.takePicture(file, object : ImageCapture.OnImageSavedListener { override fun onError(error: ImageCapture.UseCaseError, message: String, exc: Throwable?) { //insert your code here } override fun onImageSaved(file: File) { //insert your code here } }) }

Slide 34

Slide 34 text

CameraX sample app g.co/camerax/sample CameraX dev mailing list g.co/camerax/developers CameraX StackOverflow tag stackoverflow.com/tags/android-camerax

Slide 35

Slide 35 text

CameraX sample app

Slide 36

Slide 36 text

CameraX sample app ● Under Android camera samples GitHub repo

Slide 37

Slide 37 text

CameraX sample app ● Under Android camera samples GitHub repo ● Monitoring all repo issues and pull requests

Slide 38

Slide 38 text

CameraX sample app ● Under Android camera samples GitHub repo ● Monitoring all repo issues and pull requests ● Sample issues != CameraX library issues

Slide 39

Slide 39 text

CameraX sample app (cont)

Slide 40

Slide 40 text

CameraX sample app (cont) ● Very basic indeed

Slide 41

Slide 41 text

CameraX sample app (cont) ● Very basic indeed ● Preview, image capture and image analysis

Slide 42

Slide 42 text

CameraX sample app (cont) ● Very basic indeed ● Preview, image capture and image analysis ● Future work: vendor extensions and video record

Slide 43

Slide 43 text

CameraX sample app ● Very basic indeed ● Preview, image capture and image analysis ● Future work: vendor extensions and video record

Slide 44

Slide 44 text

Handling state changes beyond Lifecycle

Slide 45

Slide 45 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals)

Slide 46

Slide 46 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle)

Slide 47

Slide 47 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached)

Slide 48

Slide 48 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 49

Slide 49 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 50

Slide 50 text

// Preview output update listener val preview: Preview = ... val viewFinder: TextureView = ... preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> // Remove it and re-add view (important!) val parent = viewFinder.parent as ViewGroup parent.removeView(viewFinder) parent.addView(viewFinder, 0) // Update internal Surface object viewFinder.surfaceTexture = previewOutput.surfaceTexture // Apply preview output transformations // TODO: Use PreviewOutput textureSize and rotationDegrees here }

Slide 51

Slide 51 text

// Preview output update listener val preview: Preview = ... val viewFinder: TextureView = ... preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> // Remove it and re-add view (important!) val parent = viewFinder.parent as ViewGroup parent.removeView(viewFinder) parent.addView(viewFinder, 0) // Update internal Surface object viewFinder.surfaceTexture = previewOutput.surfaceTexture // Apply preview output transformations // TODO: Use PreviewOutput textureSize and rotationDegrees here }

Slide 52

Slide 52 text

// Preview output update listener val preview: Preview = ... val viewFinder: TextureView = ... preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> // Remove it and re-add view (important!) val parent = viewFinder.parent as ViewGroup parent.removeView(viewFinder) parent.addView(viewFinder, 0) // Update internal Surface object viewFinder.surfaceTexture = previewOutput.surfaceTexture // Apply preview output transformations // TODO: Use PreviewOutput textureSize and rotationDegrees here }

Slide 53

Slide 53 text

// Preview output update listener val preview: Preview = ... val viewFinder: TextureView = ... preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> // Remove it and re-add view (important!) val parent = viewFinder.parent as ViewGroup parent.removeView(viewFinder) parent.addView(viewFinder, 0) // Update internal Surface object viewFinder.surfaceTexture = previewOutput.surfaceTexture // Apply preview output transformations // TODO: Use PreviewOutput textureSize and rotationDegrees here }

Slide 54

Slide 54 text

// Preview output update listener val preview: Preview = ... val viewFinder: TextureView = ... preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> // Remove it and re-add view (important!) val parent = viewFinder.parent as ViewGroup parent.removeView(viewFinder) parent.addView(viewFinder, 0) // Update internal Surface object viewFinder.surfaceTexture = previewOutput.surfaceTexture // Apply preview output transformations // TODO: Use PreviewOutput textureSize and rotationDegrees here }

Slide 55

Slide 55 text

// Preview output transformation View Finder

Slide 56

Slide 56 text

// Preview output transformation + View Finder Preview Output

Slide 57

Slide 57 text

// Preview output transformation = + View Finder Preview Output Result

Slide 58

Slide 58 text

// Preview output transformation = + View Finder Preview Output Fit

Slide 59

Slide 59 text

// Preview output transformation = + View Finder Preview Output Letterbox

Slide 60

Slide 60 text

// Preview output transformation = + View Finder Preview Output Center Crop

Slide 61

Slide 61 text

// Preview output transformation Center Crop Letterbox Fit

Slide 62

Slide 62 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 63

Slide 63 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 64

Slide 64 text

// Layout change listener val viewFinder: TextureView = ... viewFinder.addOnLayoutChangeListener { view, left, top, right, bottom, _, _, _, _ -> val newDimens = Size(right - left, bottom - top) // Apply preview output transformations // TODO: Use `newDimens` here }

Slide 65

Slide 65 text

// Layout change listener val viewFinder: TextureView = ... viewFinder.addOnLayoutChangeListener { view, left, top, right, bottom, _, _, _, _ -> val newDimens = Size(right - left, bottom - top) // Apply preview output transformations // TODO: Use `newDimens` here }

Slide 66

Slide 66 text

// Layout change listener val viewFinder: TextureView = ... viewFinder.addOnLayoutChangeListener { view, left, top, right, bottom, _, _, _, _ -> val newDimens = Size(right - left, bottom - top) // Apply preview output transformations // TODO: Use `newDimens` here }

Slide 67

Slide 67 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 68

Slide 68 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 69

Slide 69 text

// Display listener val viewFinder: TextureView = ... val viewFinderDisplayId: Int = viewFinder.display.displayId

Slide 70

Slide 70 text

// Display listener val viewFinder: TextureView = ... val viewFinderDisplayId: Int = viewFinder.display.displayId val displayListener = object : DisplayManager.DisplayListener { override fun onDisplayAdded(displayId: Int) = Unit override fun onDisplayRemoved(displayId: Int) = Unit override fun onDisplayChanged(displayId: Int) { if (displayId == viewFinderDisplayId) { val display = viewFinder.display // Apply preview output transformations // TODO: Use `display.rotation` here } } }

Slide 71

Slide 71 text

// Display listener val viewFinder: TextureView = ... val viewFinderDisplayId: Int = viewFinder.display.displayId val displayListener = object : DisplayManager.DisplayListener { override fun onDisplayAdded(displayId: Int) = Unit override fun onDisplayRemoved(displayId: Int) = Unit override fun onDisplayChanged(displayId: Int) { if (displayId == viewFinderDisplayId) { val display = viewFinder.display // Apply preview output transformations // TODO: Use `display.rotation` here } } }

Slide 72

Slide 72 text

// Display listener val viewFinder: TextureView = ... val viewFinderDisplayId: Int = viewFinder.display.displayId val displayListener = object : DisplayManager.DisplayListener { override fun onDisplayAdded(displayId: Int) = Unit override fun onDisplayRemoved(displayId: Int) = Unit override fun onDisplayChanged(displayId: Int) { if (displayId == viewFinderDisplayId) { val display = viewFinder.display // Apply preview output transformations // TODO: Use `display.rotation` here } } }

Slide 73

Slide 73 text

// Preview output transformation

Slide 74

Slide 74 text

// Preview output transformation // Option 1: apply rotation to view finder (on top of previous transformations) val matrix: Matrix = ... matrix.postRotate(-rotationDegrees, centerX, centerY)

Slide 75

Slide 75 text

// Preview output transformation // Option 1: apply rotation to view finder (on top of previous transformations) val matrix: Matrix = ... matrix.postRotate(-rotationDegrees, centerX, centerY) // Option 2: apply rotation to preview use case, which will trigger // Preview.OnPreviewOutputUpdateListener val preview: Preview = ... preview.setTargetRotation(display.rotation)

Slide 76

Slide 76 text

// Preview output transformation // Option 1: apply rotation to view finder (on top of previous transformations) val matrix: Matrix = ... matrix.postRotate(-rotationDegrees, centerX, centerY) // Option 2: apply rotation to preview use case, which will trigger // Preview.OnPreviewOutputUpdateListener val preview: Preview = ... preview.setTargetRotation(display.rotation) // Use PreviewOutput.rotationDegrees in Preview.OnPreviewOutputUpdateListener // Alternatively, get the view's display and always use display.rotation

Slide 77

Slide 77 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 78

Slide 78 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 79

Slide 79 text

Handling state changes beyond Lifecycle ● Preview output update (CameraX internals) ● Layout (TextureView lifecycle) ● Display (180 rotations, display removed / attached) ● Camera hardware (USB, retractable)

Slide 80

Slide 80 text

Handling state changes beyond Lifecycle

Slide 81

Slide 81 text

Handling state changes beyond Lifecycle ● Use AutoFitPreviewBuilder from the sample

Slide 82

Slide 82 text

@calren24 Start exploring CameraX today! @owahltinez