Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camera2 API support #921

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e07a940
Mirror the commit https://github.com/googlesamples/mlkit/commit/8b31d…
miworking May 2, 2023
d2cdbcc
Camera to Camera2 API upgrade related changes done.
Jul 19, 2023
4458127
Camera to Camera2 API upgrade related changes done.
Jul 20, 2023
c7508f9
Gradle library updated to version 7.2
eeshanjamal Jul 30, 2023
e249855
MLImage class utilized for processing image frames.
eeshanjamal Jul 30, 2023
31c9857
Fixed the frame buffer leaking issue and some code cleanup.
eeshanjamal Jul 30, 2023
c573810
Updated the preferences to use Camera 2 API for resolving resolutions.
eeshanjamal Aug 1, 2023
3499113
Logging to debug the aspect ratio issue.
eeshanjamal Aug 1, 2023
7d9d984
Revert "Logging to debug the aspect ratio issue."
eeshanjamal Aug 10, 2023
4499902
Fixed the camera preview appearance size by applying center crop scal…
eeshanjamal Aug 10, 2023
8bdcb05
Fixed the preview issue (happening when opening & closing camera prev…
eeshanjamal Aug 16, 2023
7c0bd52
Annotate the start or stop camera preview functions to be executed in…
eeshanjamal Aug 16, 2023
a9f81f3
Code cleanup and class variables & functions documentation.
eeshanjamal Aug 23, 2023
cca6e77
Merge remote-tracking branch 'origin/camera-upgrade' into camera2-api…
eeshanjamal Feb 4, 2025
313681c
- Introduced CameraSource factory pattern to support both Camera & Ca…
eeshanjamal Feb 14, 2025
3a99933
Removed the dependency of retrieving preview sizes inside Settings fr…
eeshanjamal Feb 14, 2025
4aadf3c
Started using Camera2InputInfo class for Camera2 frame processing whi…
eeshanjamal Feb 25, 2025
0ba6451
- Removed the pictureSize property from CameraSource and also its usa…
eeshanjamal Feb 25, 2025
459adcf
Revert the changes done for enforced testing of CameraAPISource on ne…
eeshanjamal Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/material-showcase/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ android {
compileSdkVersion 31
defaultConfig {
applicationId "com.google.mlkit.md"
minSdkVersion 19
minSdkVersion 21
targetSdkVersion 31
versionCode 1
versionName "1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.google.mlkit.md.camera.GraphicOverlay
import com.google.mlkit.md.camera.WorkflowModel
import com.google.mlkit.md.camera.WorkflowModel.WorkflowState
import com.google.mlkit.md.camera.CameraSource
import com.google.mlkit.md.camera.CameraSourceFactory
import com.google.mlkit.md.camera.CameraSourcePreview
import com.google.mlkit.md.objectdetection.MultiObjectProcessor
import com.google.mlkit.md.objectdetection.ProminentObjectProcessor
Expand Down Expand Up @@ -81,7 +82,7 @@ class CustomModelObjectDetectionActivity : AppCompatActivity(), OnClickListener
preview = findViewById(R.id.camera_preview)
graphicOverlay = findViewById<GraphicOverlay>(R.id.camera_preview_graphic_overlay).apply {
setOnClickListener(this@CustomModelObjectDetectionActivity)
cameraSource = CameraSource(this)
cameraSource = CameraSourceFactory.createCameraSource(this)
}
promptChip = findViewById(R.id.bottom_prompt_chip)
promptChipAnimator =
Expand Down Expand Up @@ -160,15 +161,15 @@ class CustomModelObjectDetectionActivity : AppCompatActivity(), OnClickListener
R.id.flash_button -> {
if (flashButton?.isSelected == true) {
flashButton?.isSelected = false
cameraSource?.updateFlashMode(Camera.Parameters.FLASH_MODE_OFF)
cameraSource?.setFlashStatus(false)
} else {
flashButton?.isSelected = true
cameraSource?.updateFlashMode(Camera.Parameters.FLASH_MODE_TORCH)
cameraSource?.setFlashStatus(true)
}
}
R.id.settings_button -> {
settingsButton?.isEnabled = false
startActivity(Intent(this, SettingsActivity::class.java))
startActivity(SettingsActivity.newIntent(this, cameraSource))
}
}
}
Expand Down Expand Up @@ -275,11 +276,11 @@ class CustomModelObjectDetectionActivity : AppCompatActivity(), OnClickListener

// Observes changes on the object to search, if happens, show detected object labels as
// product search results.
objectToSearch.observe(this@CustomModelObjectDetectionActivity, Observer { detectObject ->
val productList: List<Product> = detectObject.labels.map { label ->
objectToSearch.observe(this@CustomModelObjectDetectionActivity, Observer { confirmedObject ->
val productList: List<Product> = confirmedObject.labels.map { label ->
Product("" /* imageUrl */, label.text, "" /* subtitle */)
}
workflowModel?.onSearchCompleted(detectObject, productList)
workflowModel?.onSearchCompleted(confirmedObject, productList)
})

// Observes changes on the object that has search completed, if happens, show the bottom sheet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package com.google.mlkit.md

import android.graphics.Bitmap
import android.media.Image
import com.google.mlkit.md.camera.FrameMetadata
import java.nio.ByteBuffer

interface InputInfo {
//TODO: Make it optional
fun getBitmap(): Bitmap
}

Expand All @@ -42,6 +44,22 @@ class CameraInputInfo(
}
}

class Camera2InputInfo(
private val frameImage: Image,
private val frameRotation: Int
) : InputInfo {

private var bitmap: Bitmap? = null

@Synchronized
override fun getBitmap(): Bitmap {
return bitmap ?: let {
bitmap = Utils.convertToBitmap(frameImage, frameRotation)
bitmap!!
}
}
}

class BitmapInputInfo(private val bitmap: Bitmap) : InputInfo {
override fun getBitmap(): Bitmap {
return bitmap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package com.google.mlkit.md
import android.animation.AnimatorInflater
import android.animation.AnimatorSet
import android.content.Intent
import android.hardware.Camera
import android.os.Bundle
import android.util.Log
import android.view.View
Expand All @@ -29,17 +28,17 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.google.android.material.chip.Chip
import com.google.common.base.Objects
import com.google.mlkit.md.camera.GraphicOverlay
import com.google.mlkit.md.camera.WorkflowModel
import com.google.mlkit.md.camera.WorkflowModel.WorkflowState
import com.google.mlkit.md.barcodedetection.BarcodeField
import com.google.mlkit.md.barcodedetection.BarcodeProcessor
import com.google.mlkit.md.barcodedetection.BarcodeField
import com.google.mlkit.md.barcodedetection.BarcodeResultFragment
import com.google.mlkit.md.camera.CameraSource
import com.google.mlkit.md.camera.CameraSourcePreview
import com.google.mlkit.md.camera.CameraSourceFactory
import com.google.mlkit.md.camera.GraphicOverlay
import com.google.mlkit.md.camera.WorkflowModel
import com.google.mlkit.md.camera.WorkflowModel.WorkflowState
import com.google.mlkit.md.settings.SettingsActivity
import java.io.IOException
import java.util.ArrayList

/** Demonstrates the barcode scanning workflow using camera preview. */
class LiveBarcodeScanningActivity : AppCompatActivity(), OnClickListener {
Expand All @@ -61,7 +60,7 @@ class LiveBarcodeScanningActivity : AppCompatActivity(), OnClickListener {
preview = findViewById(R.id.camera_preview)
graphicOverlay = findViewById<GraphicOverlay>(R.id.camera_preview_graphic_overlay).apply {
setOnClickListener(this@LiveBarcodeScanningActivity)
cameraSource = CameraSource(this)
cameraSource = CameraSourceFactory.createCameraSource(this)
}

promptChip = findViewById(R.id.bottom_prompt_chip)
Expand Down Expand Up @@ -115,16 +114,16 @@ class LiveBarcodeScanningActivity : AppCompatActivity(), OnClickListener {
flashButton?.let {
if (it.isSelected) {
it.isSelected = false
cameraSource?.updateFlashMode(Camera.Parameters.FLASH_MODE_OFF)
cameraSource?.setFlashStatus(false)
} else {
it.isSelected = true
cameraSource!!.updateFlashMode(Camera.Parameters.FLASH_MODE_TORCH)
cameraSource!!.setFlashStatus(true)
}
}
}
R.id.settings_button -> {
settingsButton?.isEnabled = false
startActivity(Intent(this, SettingsActivity::class.java))
startActivity(SettingsActivity.newIntent(this, cameraSource))
}
}
}
Expand All @@ -149,12 +148,18 @@ class LiveBarcodeScanningActivity : AppCompatActivity(), OnClickListener {
if (workflowModel.isCameraLive) {
workflowModel.markCameraFrozen()
flashButton?.isSelected = false
preview?.stop()
try {
preview?.stop()
}
catch (e: Throwable){
Log.e(TAG, "Failed to stop camera preview: ${e.message}")
}

}
}

private fun setUpWorkflowModel() {
workflowModel = ViewModelProviders.of(this).get(WorkflowModel::class.java)
workflowModel = ViewModelProviders.of(this)[WorkflowModel::class.java]

// Observes the workflow state changes, if happens, update the overlay view indicators and
// camera preview state.
Expand Down Expand Up @@ -197,13 +202,13 @@ class LiveBarcodeScanningActivity : AppCompatActivity(), OnClickListener {
}
})

workflowModel?.detectedBarcode?.observe(this, Observer { barcode ->
workflowModel?.detectedBarcode?.observe(this) { barcode ->
if (barcode != null) {
val barcodeFieldList = ArrayList<BarcodeField>()
barcodeFieldList.add(BarcodeField("Raw Value", barcode.rawValue ?: ""))
BarcodeResultFragment.show(supportFragmentManager, barcodeFieldList)
}
})
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import android.view.View
import android.view.View.OnClickListener
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
Expand All @@ -42,6 +43,7 @@ import com.google.mlkit.md.camera.GraphicOverlay
import com.google.mlkit.md.camera.WorkflowModel
import com.google.mlkit.md.camera.WorkflowModel.WorkflowState
import com.google.mlkit.md.camera.CameraSource
import com.google.mlkit.md.camera.CameraSourceFactory
import com.google.mlkit.md.camera.CameraSourcePreview
import com.google.mlkit.md.objectdetection.MultiObjectProcessor
import com.google.mlkit.md.objectdetection.ProminentObjectProcessor
Expand Down Expand Up @@ -85,7 +87,7 @@ class LiveObjectDetectionActivity : AppCompatActivity(), OnClickListener {
preview = findViewById(R.id.camera_preview)
graphicOverlay = findViewById<GraphicOverlay>(R.id.camera_preview_graphic_overlay).apply {
setOnClickListener(this@LiveObjectDetectionActivity)
cameraSource = CameraSource(this)
cameraSource = CameraSourceFactory.createCameraSource(this)
}
promptChip = findViewById(R.id.bottom_prompt_chip)
promptChipAnimator =
Expand Down Expand Up @@ -160,15 +162,15 @@ class LiveObjectDetectionActivity : AppCompatActivity(), OnClickListener {
R.id.flash_button -> {
if (flashButton?.isSelected == true) {
flashButton?.isSelected = false
cameraSource?.updateFlashMode(Camera.Parameters.FLASH_MODE_OFF)
cameraSource?.setFlashStatus(false)
} else {
flashButton?.isSelected = true
cameraSource?.updateFlashMode(Camera.Parameters.FLASH_MODE_TORCH)
cameraSource?.setFlashStatus(true)
}
}
R.id.settings_button -> {
settingsButton?.isEnabled = false
startActivity(Intent(this, SettingsActivity::class.java))
startActivity(SettingsActivity.newIntent(this, cameraSource))
}
}
}
Expand Down Expand Up @@ -274,9 +276,9 @@ class LiveObjectDetectionActivity : AppCompatActivity(), OnClickListener {
})

// Observes changes on the object to search, if happens, fire product search request.
objectToSearch.observe(this@LiveObjectDetectionActivity, Observer { detectObject ->
searchEngine!!.search(detectObject) { detectedObject, products ->
workflowModel?.onSearchCompleted(detectedObject, products)
objectToSearch.observe(this@LiveObjectDetectionActivity, Observer { confirmObject ->
searchEngine!!.search(confirmObject) { confirmedObject, products ->
workflowModel?.onSearchCompleted(confirmedObject, products)
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.chip.Chip
import com.google.common.collect.ImmutableList
import com.google.mlkit.md.objectdetection.ConfirmedObjectInfo
import com.google.mlkit.md.productsearch.BottomSheetScrimView
import com.google.mlkit.md.objectdetection.DetectedObjectInfo
import com.google.mlkit.md.objectdetection.StaticObjectDotView
Expand Down Expand Up @@ -249,16 +250,16 @@ class StaticObjectDetectionActivity : AppCompatActivity(), View.OnClickListener
} else {
searchedObjectMap.clear()
for (i in objects.indices) {
searchEngine?.search(DetectedObjectInfo(objects[i], i, image)) { detectedObject, products ->
onSearchCompleted(detectedObject, products)
searchEngine?.search(ConfirmedObjectInfo.from(DetectedObjectInfo(objects[i], i, image))) { confirmedObject, products ->
onSearchCompleted(confirmedObject, products)
}
}
}
}

private fun onSearchCompleted(detectedObject: DetectedObjectInfo, productList: List<Product>) {
Log.d(TAG, "Search completed for object index: ${detectedObject.objectIndex}")
searchedObjectMap[detectedObject.objectIndex] = SearchedObject(resources, detectedObject, productList)
private fun onSearchCompleted(confirmedObject: ConfirmedObjectInfo, productList: List<Product>) {
Log.d(TAG, "Search completed for object index: ${confirmedObject.objectIndex}")
searchedObjectMap[confirmedObject.objectIndex] = SearchedObject(resources, confirmedObject, productList)
if (searchedObjectMap.size < detectedObjectNum) {
// Hold off showing the result until the search of all detected objects completes.
return
Expand Down
Loading