Nutzer erwarten, dass Drittanbieter-Apps Fotos und Videos mit derselben hohen Qualität aufnehmen wie die native Kamera ihres Geräts, unabhängig von den Lichtverhältnissen oder der Hardwarestufe. Diese Konsistenz im wachsenden Android-Ökosystem zu gewährleisten, erfordert wichtige architektonische Überlegungen. In dieser Session wird gezeigt, wie ihr robuste, professionelle Erfassungspipelines erstellt, die die Gerätefunktionen optimal nutzen und gleichzeitig auf allen Formfaktoren zuverlässig funktionieren.
Wir stellen die neuesten Fortschritte bei CameraX vor und zeigen, wie Sie innovative Funktionen wie Fotos mit Bewegtbild und KI-Optimierungen auf dem Gerät über die Google Play-Dienste sowie räumliche Erlebnisse mit dem Jetpack XR SDK einbinden können. Außerdem sehen wir uns unseren CameraX-Skill an, mit dem der Coding-Agent Ihrer Wahl die Konzepte aus diesem Vortrag und mehr automatisch implementieren kann.
CameraXViewfinder passt die Kameravorschau an verschiedene Formfaktoren wie Foldables an. In CameraX 1.7 werden Gestensteuerung und Unterstützung für das Aufleuchten bei Benachrichtigungen hinzugefügt.ProjectedDeviceContext übergibst, kann CameraX Bilder direkt von der Datenbrille aufnehmen.Transkript
Hallo zusammen.
Willkommen zu unserer Session.
Ich bin Aman und bei mir ist Scott.
Heute möchten wir darüber sprechen, wie ihr mit CameraX und On-Device-Intelligence die Kamerafunktionen auf verschiedenen Geräten verbessern könnt.
Legen wir gleich los.
Denken Sie darüber nach, wie Sie Ihre Smartphones verwenden.
Wir verwenden die Smartphone-Kamera oft, um Momente festzuhalten und mit unseren Liebsten zu teilen.
Die Kamera ist zu einem zentralen Bestandteil der Smartphone-Nutzung geworden.
Tatsächlich ist eine gute Kamera oft ein entscheidender Faktor beim Kauf eines Smartphones.
Heute behandeln wir zwei Themenbereiche für die Entwicklung von Premium-Kamerafunktionen.
Zuerst sehen wir uns einige CameraX-Funktionen an und dann einige Techniken für maschinelles Lernen auf dem Gerät.
Beginnen wir mit CameraX.
Jetpack CameraX ist die Grundlage für die Kamerafunktionen unter Android.
Neben CameraX gibt es auch Camera1 und Camera2, die Teil des Android-Plattform-Frameworks sind.
Welche Bibliothek sollten Sie also verwenden?
Camera1 war die ursprüngliche Kamera-API unter Android.
Sie wird nicht mehr unterstützt. Neue Apps sollten daher entweder Camera2 oder CameraX verwenden.
Kamera2 hat Kamera1 ersetzt.
Wenn Sie also für komplexe Anwendungsfälle eine grundlegende Kamerasteuerung benötigen, ist Camera2 eine gute Option.
Allerdings müssen Sie gerätespezifische Konfigurationen selbst verwalten.
Und schließlich baut Jetpack CameraX auf Camera2 auf.
Sie bietet eine einheitliche API und verarbeitet gerätespezifische Konfigurationen automatisch.
Diese Option wird für die meisten Entwickler empfohlen.
Wir haben einen sehr wichtigen Hinweis für Sie.
Wir planen, Camera1 zu deaktivieren. Dies beginnt mit der Durchsetzung der Google Play-Richtlinien im nächsten Jahr in Android 18.
Wenn Sie noch die alte Camera1 API verwenden, müssen Sie zu CameraX oder Camera2 migrieren.
Warum sollten Sie also CameraX verwenden?
Erstens wird Android 7 und höher unterstützt, sodass er mit fast 99 % der aktiven Android-Geräte kompatibel ist.
Zweitens ist sie einfach zu verwenden und bietet einfache APIs für gängige Anwendungsfälle wie Vorschau, Aufnahme und Aufzeichnung mit weniger Code.
Drittens bietet sie eine einheitliche Darstellung auf allen Geräten, da gerätespezifische Probleme wie das Strecken der Vorschau, das bei faltbaren Geräten und Tablets recht häufig vorkommt, reduziert werden.
Und schließlich vereinfacht es die Implementierung erweiterter OEM-Funktionen wie Nachtmodus und Porträt.
Jetzt möchte ich Scott bitten, über einige der CameraX-Funktionen zu sprechen.
Du bist dran, Scott.
Vielen Dank, Aman.
Hallo zusammen.
Ich heiße Scott.
Heute werden wir uns mehrere CameraX-Funktionen ansehen, mit denen ihr eine professionelle Kamera-App entwickeln könnt.
Als Erstes wäre da der adaptive Sucher.
Sie wurde entwickelt, um die Vorschau auf verschiedenen Formfaktoren und Bildschirmgrößen zu vereinfachen.
Nutzer haben jetzt eine Vielzahl von Geräten, darunter Foldables, Smartphones, Laptops und XR-Geräte.
Daher ist es wichtiger denn je, eine Kamera zu entwickeln, die an die neuen Formfaktoren angepasst ist.
Und die Kameravorschau ist bekanntermaßen schwierig, besonders auf dem faltbaren Breitbild-Display.
Bei diesen Geräten ist die innere Kamera im aufgeklappten Zustand horizontal und die Rückkamera vertikal ausgerichtet.
Wenn Sie also davon ausgehen, dass alle Kamerasensoren vertikal montiert sind, führt dies zu einer räumlichen Diskrepanz und zu Verzerrungen oder seitlichen Verschiebungen.
Und CameraXViewfinder ist so konzipiert, dass die Komplexität vollständig für Sie übernommen wird, damit die Kameravorschau auf allen Formfaktoren nahtlos funktioniert.
Sehen wir uns an, wie die CameraXViewfinder-Komposition verwendet wird.
var surfaceRequest by remember {
mutableStateOf<SurfaceRequest?>(null)
}
LaunchedEffect(...) {
preview.setSurfaceProvider { surfaceRequest = it }
cameraProvider.bindToLifecycle(..., preview)
}
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
contentScale = ContentScale.Fit
)
}Zuerst deklarieren Sie die Inhaltsvariable surfaceRequest, um die Surface-Anfrage zu speichern.
Tut mir leid.
Zweitens legen Sie in LaunchedEffect den Surface-Anbieter so fest, dass er ein Surface-Anfrageobjekt abfängt.
Anschließend binden Sie die Kamera an den Lebenszyklus und starten sie.
Und schließlich, wenn die Statusaktualisierung mit einer gültigen Oberflächenanfrage erfolgt und eine Neuzusammensetzung auslöst, legen Sie die Anfrage direkt auf CameraXViewfinder fest.
Hier können Sie die contentScale je nach Bedarf auf „Anpassen“ oder „Zuschneiden“ festlegen.
Das war's auch schon.
In CameraX 1.7 haben wir der CameraXViewfinder mehrere neue Funktionen hinzugefügt.
Dazu gehören neue Gesten wie „Zum Fokussieren tippen“ oder „Zum Zoomen auseinander- oder zusammenziehen“.
Außerdem wird die Funktion „Aufleuchten bei Benachrichtigungen“ unterstützt, mit der du das Display verwenden kannst, um dein Selfie aufzuhellen.
Die Implementierung dieser Funktion ist einfach.
CameraXViewfinder(
surfaceRequest = viewModel.surfaceRequest,
contentScale = ContentScale.Fit,
isTapToFocusEnabled = true,
isPinchToZoomEnabled = true,
onScreenFlashReady =
{
imageCapture.screenFlash = it
imageCapture.flashMode = FLASH_MODE_SCREEN
}
)Sie müssen nur „Zum Fokussieren tippen“ und „Zum Zoomen auseinander- und zusammenziehen“ auf „true“ setzen.
Für das Aufleuchten bei Benachrichtigungen rufen Sie die ScreenFlash-Instanz über den Callback onScreenFlashReady ab, übergeben sie an ImageCapture und legen den Blitzmodus auf FLASH_MODE_SCREEN fest.
Das wars!
Wir haben gesehen, wie CameraX eine Kameravorschau ermöglicht, die auf allen Formfaktoren nahtlos funktioniert.
Gehen wir nun einen Schritt weiter und sehen uns die Kameraaufnahme unter Android XR an.
Es gibt drei Kategorien von XR-Geräten: Erstens, XR-Headsets.
Diese Headsets sind vollständig geschlossen und immersiv. Sie verwenden Kameras, um die physische Welt zu erfassen und im Headset anzuzeigen.
Und zweitens: kabelgebundene XR-Datenbrillen.
Wir haben heute in der Keynote die XREAL Aura in dieser Kategorie angekündigt.
Wie Sie in den Keynotes sehen können, handelt es sich um eine transparente Brille, die mit dem Compute Puck verbunden ist und es Ihnen ermöglicht, die virtuellen Inhalte über die reale Welt zu legen.
Und drittens: Audio- und Display-Brillen.
Das ist die leichte Brille.
Sie können Audio, Kamera und Display kombinieren, um die Bedienung per Sprachbefehl und die Anzeige von Informationen auf einen Blick zu ermöglichen.
Die ersten beiden Kategorien, XR-Headsets und kabelgebundene XR-Datenbrillen, unterstützen auch Camera2 und CameraX, genau wie ein normales Smartphone.
Heute konzentrieren wir uns auf Audio- und Display-Brillen.
Bei dieser Datenbrille werden Apps auf dem Android-Smartphone ausgeführt, nicht auf der Datenbrille. Es wird jedoch auf die Kamera des Smartphones und die Kamera der Datenbrille zugegriffen.
Die entscheidende Frage ist: Woher weißt du, mit welcher Kamera du sprichst?
Es ist also ganz einfach.
Mit dem Standardkontext greift CameraX auf die Smartphone-Kamera zu.
Und mit dem ProjectedDeviceContext greift CameraX auf die Kamera der Datenbrille zu.
Unterschiedliche Kontexte führen also zu unterschiedlichen Zielkameras.
Denken Sie daran, dass die Berechtigungen für die Kamera und die Datenbrille separat sind.
Die Berechtigungen für das Smartphone und die Brille sind voneinander getrennt. Du musst die Berechtigung für die Brille also separat anfordern.
Die andere Option ist die Verwendung der projizierten Aktivität, die ebenfalls auf den Android-Hostgeräten ausgeführt wird, wobei die Benutzeroberfläche auf die Brille projiziert wird.
Hier kehrt sich die Regel um.
Wenn Sie den Standardkontext verwenden, greifen Sie auf die Kamera der Datenbrille zu.
Wenn Sie auf die Smartphone-Kamera zugreifen möchten, müssen Sie den HostContext verwenden.
Sehen wir uns den Code an, mit dem wir die Kamera der Datenbrille verwenden können.
// In a coroutine
val imageCapture = ImageCapture.Builder().build()
ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
.collectLatest { isConnected ->
if (isConnected) {
val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setRepeatingStreamForced(false)
.build()
val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
}
}
// Shutter button clicked
imageCapture.takePicture(...)Zuerst rufen Sie isProjectedDeviceConnected auf, um zu prüfen, ob die Datenbrille verbunden ist.
Anschließend rufen Sie createProjectedDeviceContext auf, um ein projectedContext zu erhalten.
Zweitens verwenden Sie hier den LifecycleCameraProvider, um CameraX zu initialisieren, anstatt ProcessCameraProvider.
ProcessCameraProvider ist also ein Singleton, dessen Kontext nach der Initialisierung nicht mehr geändert werden kann.
Mit LifecycleCameraProvider können Sie den Kontext also jederzeit ändern.
Als Nächstes müssen Sie den zusätzlichen wiederholten Stream in CameraX deaktivieren.
Das ist erforderlich, da die heutigen Brillen nur einen einzigen Stream unterstützen.
Die API ist derzeit intern, wird aber bald veröffentlicht.
Das wars!
Ab hier ist es Standard-CameraX. Sie binden einfach „ImageCapture“ ein und nehmen jetzt über die Brille auf.
Okay.
Als Nächstes sehen wir uns an, wie CameraX Ihnen hilft, die richtige Kombination von Funktionen zu finden, die zusammen funktionieren und das Beste aus dem Gerät herausholen.
Moderne Kamera-Apps zeichnen sich durch leistungsstarke Funktionen aus. Nutzer erwarten, dass die Kamera Funktionen wie HDR, 60 fps und Vorschaustabilisierung gleichzeitig unterstützt.
Als Entwickler wissen wir jedoch, dass es nicht immer einfach ist, diese Funktionen zu kombinieren.
Wenn Sie die Unterstützung einzelner Funktionen prüfen, ist das keine Garantie dafür, dass sie zusammen funktionieren.
Diese Unsicherheit hat Entwickler dazu gezwungen, konservativ zu sein und die Möglichkeiten der Nutzer auf leistungsfähigen Geräten einzuschränken.
Um dieses Problem zu beheben, haben wir die Feature Group API eingeführt, die auf dem Abfrage-Framework für die Kombination von Camera2-Funktionen basiert.
Es gibt zwei Möglichkeiten, sie zu verwenden: Erstens können Sie mit requiredFeatureGroup prüfen, ob die Funktionskombination unterstützt wird, und sie aktivieren.
Zweitens können Sie Ihre Funktionspriorität nach preferredFeatureGroup angeben. CameraX wählt dann die am besten unterstützte Kombination für Sie aus.
Hier ist das Beispiel.
Rechts, links, sorry, mit „Nichts aktivieren“, nur ein normales Smartphone, normale Aufnahme.
Links aktivieren wir HDR, 60 fps und die Stabilisierung der Vorschau.
Die Qualität ist deutlich besser, das Video ist heller, flüssiger und ohne Verwacklungen.
Sehen wir uns den Code an.
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
SessionConfig(
useCases = listOf(preview, videoCapture),
// HDR_HLG10 > FPS_60 > Preview Stabilization.
preferredFeatureGroup =
listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
)
)Hier verwenden wir also preferredFeatureGroup, um unsere Liste der Funktionsprioritäten anzugeben: HDR, dann 60 fps und dann die Stabilisierung der Vorschau.
CameraX prüft die Kombination und wählt die besten vom Gerät unterstützten aus.
Mit CameraX 1.6 und höher fügen wir die 4K-Aufnahme und die Videostabilisierung direkt in die Feature Group API ein.
// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
useCases = listOf(preview, videoCapture),
requiredFeatureGroup =
setOf(UHD_RECORDING, VIDEO_STABILIZATION),
effects = listOf(overlayEffect)
)Die Kombination aus 4K und Videostabilisierung wird von vielen Premium-Geräten unterstützt.
Mit der Feature Group API können Sie sie jetzt ganz einfach zusammen aktivieren.
Darüber hinaus können Sie den Echtzeiteffekt jetzt mit der Feature Group API anwenden, was zuvor nicht möglich war.
Sehen wir uns nun an, wie Sie mit CameraX Bilderfassung in hoher Qualität erhalten können.
CameraX unterstützt jetzt die Aufnahme von DNG-RAW-Bildern.
Du erhältst Zugriff auf das unbearbeitete, unkomprimierte RAW-Bild direkt vom Kamerasensor, um es professionell zu bearbeiten.
Außerdem haben wir die Erweiterung für den Nachtmodus verbessert.
Mit dieser Erweiterung können Sie bei wenig Licht helle und klare Fotos ohne Blitz aufnehmen.
Außerdem haben wir eine neue API für die Nachtmodus-Anzeige eingeführt.
Sie überwacht, ob die aktuelle Umgebung dunkel ist und sich für den Nachtmodus eignet. Sie können sie verwenden, um die Nachtmodusanzeige einzublenden.
Und Ultra HDR wird jetzt in CameraX-Erweiterungen unterstützt.
Dadurch werden im Nachtmodus ein größerer Dynamikumfang und lebendigere Farben ermöglicht. Der Nachtmodus mit Ultra HDR wird derzeit auf vielen aktuellen Premium-Geräten unterstützt.
Hier siehst du den Unterschied zwischen dem normalen Modus, dem Nachtmodus und dem Nachtmodus mit Ultra-HDR.
Sehen wir uns an, wie das funktioniert.
val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))
val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)
val imageCapture = ImageCapture.Builder().apply {
if (imageCapabilities.supportedOutputFormats
.contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
}
}.build()
val extensionSessionConfig = ExtensionSessionConfig(
ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
)
cameraProvider.bindToLifecycle(..., extensionSessionConfig)Zuerst erstellen Sie eine ExtensionSessionConfig für den Nachtmodus und rufen damit die CameraInfo-Instanz ab.
Als Nächstes rufen Sie mit „CameraInfo“ die Bilderfassungsfunktion ab und fragen dann ab, ob Ultra HDR unterstützt wird.
Anschließend legen Sie das Ausgabeformat auf Ultra HDR fest.
Schließlich erstellen Sie die ExtensionSessionConfig noch einmal mit den Anwendungsfällen und binden sie an den Lebenszyklus.
Jetzt kannst du mit dem Nachtmodus und Ultra HDR Fotos aufnehmen.
Außerdem unterstützen wir jetzt Fotos mit Bewegtbild in CameraX.
Bei Fotos mit Bewegtbild werden auch die Geräusche und Bewegungen rund um das Foto aufgenommen. So wird der Moment festgehalten, nicht nur ein Standbild.
Wir standardisieren dies für verschiedene Smartphones und Android-Geräte und vereinfachen die Unterstützung von Fotos mit Bewegtbild.
Fotos mit Bewegtbild werden im ersten Quartal 2027 in CameraX eingeführt.
Sobald die Funktion verfügbar ist, kannst du den Modus „Fotos mit Bewegtbild“ in ImageCapture.Builder aktivieren und Fotos mit Bewegtbild aufnehmen.
val imageCapture = ImageCapture.Builder()
.setMotionPhotoMode(
ImageCapture.MOTION_PHOTO_MODE_ON,
customConfig
)
.build()
cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)Als Nächstes sehen wir uns an, wie CameraX Highspeed-Videoaufnahmen ermöglicht.
Die Möglichkeit, schnelle Bewegungen klar aufzunehmen, ist eine wichtige Funktion moderner Kamera-Apps.
Das ist besonders bei schnellen Bewegungen wie beim Sport nützlich.
Dazu werden die Aufnahmen mit einer hohen Geschwindigkeit von 120 oder 240 fps gemacht.
Es gibt zwei Möglichkeiten, sie zu verwenden.
Zuerst wird das Video mit derselben Framerate codiert, dann wird ein Video mit einer echten hohen Framerate erstellt.
Die andere Möglichkeit besteht darin, das Video mit 30 fps zu codieren, um ein abspielbereites Zeitlupenvideo zu erstellen.
Beispiel: Eine Sekunde, die mit 120 fps aufgenommen wurde, dauert bei der Wiedergabe mit 30 fps vier Sekunden.
Diese Zeitspanne erzeugt also einen Zeitlupeneffekt.
Und um die Hochgeschwindigkeitsaufzeichnung zu implementieren, erstellen Sie mit Ihrem videoCapture ein HighSpeedVideoSessionConfig.
val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.withOutput(recorder)
val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
HighSpeedVideoSessionConfig(videoCapture, preview)).first()
val sessionConfig = HighSpeedVideoSessionConfig(
videoCapture,
preview,
frameRateRange = frameRateRange,
isSlowMotionEnabled = true
)
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, sessionConfig)
// Start recording slow motion videos.
val recording = recorder.prepareRecording(context, outputOption)
.start(executor, {})Anschließend legen Sie isSlowMotionEnabled auf „true“ fest, um das Video mit 30 fps zu codieren und ein Zeitlupenvideo zu erstellen, oder auf „false“, um ein Video mit hoher Framerate zu erstellen.
Wenn die Zeitlupe aktiviert ist, wird sie von Google Fotos automatisch erkannt und der Miniaturansicht wird ein Zeitlupensymbol hinzugefügt.
Sehen wir uns an, wie das in Google Fotos funktioniert.
Hier verleiht die Zeitlupe der Katze einen dramatischen Effekt.
Meine Katze möchte nie, dass ich sie berühre.
Tut mir leid.
Sehen wir uns nun einige Verbesserungen an der Concurrent Camera API an.
Mit der Concurrent API kann deine App gleichzeitig von der Front- und Rückkamera streamen.
Dieses Jahr haben wir einige Verbesserungen an der Concurrent Camera API vorgenommen.
Sie können die Kamerakomposition dynamisch aktualisieren, nachdem Sie sie an den Lebenszyklus gebunden und eine Kamera geöffnet haben.
Dazu gehören das Ändern der Position, der Größe und der Rendering-Reihenfolge.
Sie können auch Stile wie abgerundete Ecken, Rahmenbreite und Rahmenfarbe hinzufügen.
Und so gehts: Mit CompositionSettings können Sie Stile wie abgerundete Ecken, Rahmenbreite und Farbe konfigurieren und mit setZOrder die Renderingreihenfolge anpassen.
val concurrentCamera =
cameraProvider.bindToLifecycle(list(config1, config2))
// CameraX 1.7 features: rounded corner, border width & color
val pipSettings = CompositionSettings.Builder()
.setOffset(0.2f, 0.2f)
.setScale(0.35f, 0.35f)
.setRoundedCornerRatio(0.15f)
.setBorderWidthRatio(0.02f)
.setBorderColor(Color.WHITE)
.setZOrder(1)
.build()
// CameraX 1.7 features: dynamical composition update
concurrentCamera.setCompositionSettings(
listOf(mainSettings, pipSettings))Als Nächstes rufen Sie einfach setCompositionSettings für die aktive gleichzeitige Kamera auf, um die Komposition dynamisch zu aktualisieren.
Das wars!
Sie können die Komposition jetzt dynamisch aktualisieren.
Okay.
Wir haben uns jetzt alle Funktionen angesehen.
Wir haben hier einen wichtigen Hinweis und empfehlen dringend, ein Upgrade auf CameraX 1.6 oder höher durchzuführen.
So können Abstürze auf zukünftigen Geräten, die das neue Format Eclipsa Video unterstützen, vermieden werden. Außerdem wird so sichergestellt, dass deine CameraX-Erweiterung auch nach dem zukünftigen Pixel-Update funktioniert.
Damit sind wir am Ende unserer ersten Session angekommen.
Wir haben die CameraX-Funktionen behandelt, mit denen ihr eine professionelle Kamera-App entwickeln könnt. Jetzt möchte ich Aman einladen, die nächste Session zum Thema On-Device-KI zu leiten.
Vielen Dank, Scott, dass du uns die CameraX-Funktionen vorgestellt hast.
Sehen wir uns nun an, wie Sie On-Device Machine Learning nutzen können, um intelligente Kamerafunktionen zu entwickeln.
Das On-Device Machine Learning ermöglicht eine Vielzahl von Kamera-Anwendungsfällen in zwei Hauptbereichen.
Zuerst die Voraufnahme.
Hier können Sie die Kameravorschau in Echtzeit analysieren, um beispielsweise Objekte zu erkennen, oder die Vorschau selbst verbessern.
Der zweite Schritt ist die Nachbearbeitung.
Nachdem Sie das Bild aufgenommen haben, können Sie die Qualität optimieren oder eine Beschreibung für das Bild generieren lassen.
Sehen wir uns nun einige der On-Device-Lösungen für maschinelles Lernen an, die wir für Ihre Kamera-Apps anbieten.
Die erste On-Device-ML-Bibliothek, über die wir sprechen, ist ML Kit.
ML Kit wurde hauptsächlich entwickelt, um gängige Probleme in den Bereichen Vision und natürliche Sprache zu lösen, z. B. Texterkennung, Gesichtserkennung und Sprachübersetzung.
Letztes Jahr haben wir das ML Kit eingeführt.
Hier sind einige Beispiele für ML Kit Vision APIs, z. B. Gesichtsgittererkennung, Motivsegmentierung und Objekterkennung in Echtzeit.
Ja.
Letztes Jahr haben wir die GenAI APIs eingeführt.
Diese APIs nutzen die Leistungsfähigkeit von Gemini Nano, um Funktionen wie Textzusammenfassung, Bildbeschreibung und vieles mehr zu ermöglichen.
Hier ist ein Beispiel für eine Bildbeschreibung mit der Prompt API.
Wir übergeben die Bild-Bitmap mit dem Text-Prompt an das Modell. Die Ergebnisbeschreibung lautet: „Ein kleiner grüner Android-Roboter mit einem kaktusähnlichen Design sitzt auf einer schwarzen Oberfläche.“
Und all das passiert auf dem Gerät.
Nicht schlecht, oder?
Adobe Photoshop verwendet die Prompt API, um die Nutzerfreundlichkeit seiner App zu verbessern.
Beim Bearbeiten von Assets müssen Nutzer in der Photoshop App oft mehrere Ebenen verwalten.
Mithilfe der Prompt API kann die App die unorganisierten Ebenen jetzt automatisch umbenennen.
Die App verwendet auch die Prompt API, um Bildunterschriften und Hashtags für Bilder zu generieren, was für das Teilen in sozialen Medien sehr nützlich ist.
Sehen wir uns den Code für die Bildbeschreibung mit der Prompt API an.
// 1. Initialize the on-device GenerativeModel client
val generativeModel = Generation.getClient()
// 2. Check whether Gemini Nano is available on the device
val status = generativeModel.checkStatus()
when (status) {
FeatureStatus.UNAVAILABLE -> {}
FeatureStatus.DOWNLOADABLE -> {}
FeatureStatus.DOWNLOADING -> {}
FeatureStatus.AVAILABLE -> {}
}Zuerst benötigen Sie ein generativeModel-Clientobjekt. Prüfen Sie dann, ob Gemini Nano auf Ihrem Gerät verfügbar oder herunterladbar ist.
Als Nächstes wird eine Inhaltsanfrage generiert, indem das Bild und der Text-Prompt kombiniert werden. Schließlich wird diese Anfrage an den generateContent-Aufruf übergeben, um die Inferenz auf Gemini Nano auszuführen und die Bildbeschreibung zu erhalten.
// 3. Build a multimodal request with the image and text prompt
val request = generateContentRequest(
ImagePart(bitmap),
TextPart("Describe this image in detail.")
) {}
// 4. Run inference on-device and get the description
val response = generativeModel.generateContent(request)
val description = response.candidates.first().textJa, die nächste On-Device-ML-Bibliothek ist MediaPipe.
MediaPipe bietet eine Vielzahl von Lösungen für maschinelles Lernen für Ihre Anwendungen.
Auf der MediaPipe-Website können Sie verschiedene Beispiele wie Gesichtserkennung, Objekterkennung, Segmentierung und vieles mehr ausprobieren.
Alle diese Anwendungsfälle klingen sehr ähnlich wie ML Kit. Welche Bibliothek sollten Sie also verwenden?
MediaPipe und ML Kit lösen zwar ähnliche Probleme, es gibt jedoch einige wichtige Unterschiede.
Erstens: Plattformunterstützung.
MediaPipe funktioniert unter Android, iOS, im Web und mit Python, während ML Kit nur auf Android und iOS ausgerichtet ist.
Außerdem bietet MediaPipe die Möglichkeit, Modelle anzupassen, während ML Kit einsatzbereite APIs bereitstellt.
Und schließlich ist MediaPipe Open Source, während ML Kit ein Google SDK ist.
Hier ist ein Beispiel für die Gestenerkennung mit den MediaPipe Vision APIs.
Diese API erkennt Handgesten in Echtzeit anhand der Kameraeingabe.
So könnte man beispielsweise die Kameraaufnahme mit einfachen Handgesten starten oder stoppen, während man sich in einiger Entfernung zum Smartphone befindet.
Sehen wir uns den Code an.
Zuerst müssen Sie das GestureRecognizerOptions-Objekt erstellen und den Modellpfad, den Livestreaming-Modus und einen Ergebnis-Listener angeben.
// 1. Build GestureRecognizerOptions
val options = GestureRecognizerOptions.builder().setBaseOptions(
BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
.setRunningMode(RunningMode.LIVE_STREAM)
.setResultListener { result, image -> /* Handle detected gesture */ }
.build()
// 2. Create the GestureRecognizer
val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)
// 3. Convert CameraX ImageProxy to MediaPipe MPImage
val bitmap = rotateBitmap(imageProxy.toBitmap(),
imageProxy.imageInfo.rotationDegrees)
val mpImage = BitmapImageBuilder(bitmap).build()
// 4. Run gesture recognition on the image
gestureRecognizer.recognizeAsync(mpImage, frameTime)Erstellen Sie dann ein Gestenerkennungsobjekt aus den Optionen.
Rufen Sie als Nächstes das CameraX-ImageProxy ab, konvertieren Sie es in eine Bitmap und generieren Sie mit der Bitmap ein MediaPipe-mpImage-Objekt.
Schließlich müssen Sie das MediaPipe-mpImage-Objekt an den recognizeAsync-Aufruf übergeben, um Gesten in Echtzeit zu erkennen.
Als Nächstes haben wir die On-Device-Bild- und Videooptimierung, die über die Google Play-Dienste API verfügbar ist.
Dieses Modell bietet Funktionen wie Tonemapping, Unschärfe entfernen und verbesserte Auflösung auf den meisten Premium-Geräten.
Hier ist ein Beispiel für ein Bild, das mit diesem Modell tonemapped und hochskaliert wurde.
Auf dem optimierten Bild sind die Farben des Himmels und des Meeres deutlich satter und die Schärfe ist verbessert.
Sehen wir uns den Code für die Bildoptimierung an.
// Step 1: Verify device support & install module
val enhancementClient = Enhancement.getClient(context)
suspend fun prepare(): Boolean {
if (!enhancementClient.isDeviceSupported().await()) return false
if (!enhancementClient.isModuleInstalled().await()) {
check(enhancementClient.installModule().await()) {
"Enhancement module install failed"
}
}
return true
}Zuerst müssen Sie prüfen, ob das zugrunde liegende Gerät die Funktion unterstützt. Falls nicht, müssen Sie das Erweiterungsmodul auf Ihrem Gerät installieren.
Erstellen Sie als Nächstes das EnhancementOptions-Objekt, indem Sie die Bitmap und die gewünschten Optimierungen angeben.
// Step 2: Select Enhancement options & create session
val options = EnhancementOptions(
width = bitmap.width,
height = bitmap.height,
enhancementMode = EnhancementMode.BITMAP,
isTonemappingEnabled = true,
isDeblurAndDenoisePhotoEnabled = true,
isDeblurAndDenoiseVideoEnabled = false,
isUpscalePhotoEnabled = false,
isUpscaleVideoEnabled = false
)
val enhancementSession = enhancementClient
.createSessionAsync(options, enhancementExecutor)Erstellen Sie als Nächstes eine Clientsitzung mit der Google Play-Dienste API, definieren Sie einen Callback, um die resultierende Bitmap zu verarbeiten, und rufen Sie schließlich die Verarbeitungsfunktion mit der Bitmap und den Optionen zur Bildoptimierung auf.
// Step 3: Process Enhancement
val enhancedBitmap = suspendCancellableCoroutine { continuation ->
val enhancementCallback = object : EnhancementCallback {
override fun onBitmapProcessed(result: Bitmap) {
if (continuation.isActive) continuation.resume(result)
}
override fun onError(statusCode: Int) {
if (continuation.isActive) {
continuation.resumeWithException(
Exception("Bitmap processing failed: $statusCode")
)
}
}
override fun onSurfaceProcessed(timestamp: Long) {}
}
enhancementSession.process(bitmap, options, enhancementCallback)
}Wir haben vor Kurzem die Video-Optimierung APIs in Jetpack Media3 integriert.
In unserem Medienvortrag erfahren Sie mehr über die Image and Video Enhancement APIs.
Die letzte On-Device-ML-Funktion, die wir uns ansehen, ist der Low Light-Modus.
Aufnahmen bei schlechten Lichtverhältnissen sind für Smartphone-Nutzer oft ein Problem.
Stell dir vor, Nutzer nehmen Bilder auf oder führen Videoanrufe in einer schwach beleuchteten Umgebung.
Der Low Light-Modus löst dieses Problem, indem er die Kameravorschau in Echtzeit aufhellt.
Es gibt zwei Varianten des Nachtsicht-Boosts: Hardware und Software.
Die Hardwareversion verwendet die OEM-Hardware, um die schwach beleuchteten Frames ohne maschinelles Lernen zu verbessern.
Bei der Softwareversion wird dagegen ein On-Device-ML-Modell verwendet, um die Frames zu verbessern.
Sehen wir uns kurz an, wie wir CameraX mit dem Software-Low-Light-Modus (auch als Google-Low-Light-Modus bezeichnet) integrieren können.
Google Low Light Boost erfordert zwei wichtige Oberflächen: Erstens benötigen Sie eine Eingabeoberfläche für die Rohbilder der Kamera und eine Ausgabeoberfläche für die verbesserten Ausgabebilder.
Wenn du die Funktion in CameraX einbinden möchtest, musst du die Google Low Light Boost-Pipeline nur in einen CameraEffect einbinden.
CameraX übernimmt automatisch den Lebenszyklus der Oberfläche und das Routing für Sie.
Im 4. Quartal 2026 führen wir außerdem den Low Light Boost-Effekt in CameraX ein.
// Integrates SW Low Light Boost
val lowLightBoostEffect =
LowLightBoostEffect(
CameraEffect.PREVIEW or
CameraEffect.VIDEO_CAPTURE or
CameraEffect.IMAGE_CAPTURE
)
val sessionConfig = SessionConfig(
useCases = listOf(preview, imageCapture),
effects = lowLightBoostEffect
)
cameraProvider.bindToLifecycle(.. sessionConfig)Damit könnt ihr ein Low Light Boost-Objekt erstellen und es eurer Sitzungskonfiguration hinzufügen. Anschließend bindet ihr die Sitzungskonfiguration an den Lebenszyklus der Kamera und es funktioniert auf unterstützten Geräten.
Damit sind wir am Ende beider Abschnitte angelangt.
Fassen wir kurz zusammen, was wir heute besprochen haben.
Heute haben wir darüber gesprochen, wie Sie die Kamerafunktionen auf verschiedenen Geräten mit CameraX und On-Device Machine Learning verbessern können.
Mit CameraXViewfinder können Sie Ihre Kameravorschau an verschiedene Formfaktoren anpassen.
Als Nächstes haben wir gesehen, dass CameraX auch Aufnahmen auf XR-Geräten ermöglicht.
Die Feature Group API hilft Ihnen dann, die optimale Kombination von Hardwarefunktionen zu finden.
Außerdem haben wir uns die Verbesserungen bei der Bilderfassung und die Funktion „Highspeed-Videoaufnahme“ für Zeitlupenvideos angesehen.
Schließlich haben wir gelernt, wie wir die Vorschau der gleichzeitigen Kamera dynamisch verbessern können.
Als Nächstes haben wir uns einige der On-Device-Lösungen für maschinelles Lernen wie ML Kit und MediaPipe angesehen, mit denen sich intelligente visuelle Funktionen erstellen lassen.
Und schließlich haben wir die Bild- und Video-Optimierung auf dem Gerät sowie die Funktion „Aufhellung bei wenig Licht“ gesehen, mit der die Kameravorschau in Echtzeit aufgehellt wird.
Wie bereits erwähnt, planen wir, Camera1 zu deaktivieren. Wir empfehlen Ihnen daher dringend, zu CameraX zu migrieren.
Um diese Migration zu unterstützen, haben wir vor Kurzem den CameraX-Skill eingeführt.
Außerdem gibt es eine Migrationsanleitung und ein Repository mit Kamera-Beispielen, die dir helfen können.
Über diesen Link können Sie auf weitere Ressourcen zu den heute behandelten Funktionen zugreifen.
Sie enthält Links zu Dokumentation, Blog und Migrationsressourcen – alles an einem Ort.
Ich nehme mir einen Moment Zeit und halte inne.
Okay.
Vielen Dank an alle.
Damit sind wir am Ende unseres Vortrags angelangt.
var surfaceRequest by remember {
mutableStateOf<SurfaceRequest?>(null)
}
LaunchedEffect(...) {
preview.setSurfaceProvider { surfaceRequest = it }
cameraProvider.bindToLifecycle(..., preview)
}
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
contentScale = ContentScale.Fit
)
}CameraXViewfinder(
surfaceRequest = viewModel.surfaceRequest,
contentScale = ContentScale.Fit,
isTapToFocusEnabled = true,
isPinchToZoomEnabled = true,
onScreenFlashReady =
{
imageCapture.screenFlash = it
imageCapture.flashMode = FLASH_MODE_SCREEN
}
)// In a coroutine
val imageCapture = ImageCapture.Builder().build()
ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
.collectLatest { isConnected ->
if (isConnected) {
val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setRepeatingStreamForced(false)
.build()
val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
}
}
// Shutter button clicked
imageCapture.takePicture(...)cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
SessionConfig(
useCases = listOf(preview, videoCapture),
// HDR_HLG10 > FPS_60 > Preview Stabilization.
preferredFeatureGroup =
listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
)
)// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
useCases = listOf(preview, videoCapture),
requiredFeatureGroup =
setOf(UHD_RECORDING, VIDEO_STABILIZATION),
effects = listOf(overlayEffect)
)val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))
val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)
val imageCapture = ImageCapture.Builder().apply {
if (imageCapabilities.supportedOutputFormats
.contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
}
}.build()
val extensionSessionConfig = ExtensionSessionConfig(
ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
)
cameraProvider.bindToLifecycle(..., extensionSessionConfig)val imageCapture = ImageCapture.Builder()
.setMotionPhotoMode(
ImageCapture.MOTION_PHOTO_MODE_ON,
customConfig
)
.build()
cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.withOutput(recorder)
val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
HighSpeedVideoSessionConfig(videoCapture, preview)).first()
val sessionConfig = HighSpeedVideoSessionConfig(
videoCapture,
preview,
frameRateRange = frameRateRange,
isSlowMotionEnabled = true
)
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, sessionConfig)
// Start recording slow motion videos.
val recording = recorder.prepareRecording(context, outputOption)
.start(executor, {})val concurrentCamera =
cameraProvider.bindToLifecycle(list(config1, config2))
// CameraX 1.7 features: rounded corner, border width & color
val pipSettings = CompositionSettings.Builder()
.setOffset(0.2f, 0.2f)
.setScale(0.35f, 0.35f)
.setRoundedCornerRatio(0.15f)
.setBorderWidthRatio(0.02f)
.setBorderColor(Color.WHITE)
.setZOrder(1)
.build()
// CameraX 1.7 features: dynamical composition update
concurrentCamera.setCompositionSettings(
listOf(mainSettings, pipSettings))// 1. Initialize the on-device GenerativeModel client
val generativeModel = Generation.getClient()
// 2. Check whether Gemini Nano is available on the device
val status = generativeModel.checkStatus()
when (status) {
FeatureStatus.UNAVAILABLE -> {}
FeatureStatus.DOWNLOADABLE -> {}
FeatureStatus.DOWNLOADING -> {}
FeatureStatus.AVAILABLE -> {}
}// 3. Build a multimodal request with the image and text prompt
val request = generateContentRequest(
ImagePart(bitmap),
TextPart("Describe this image in detail.")
) {}
// 4. Run inference on-device and get the description
val response = generativeModel.generateContent(request)
val description = response.candidates.first().text// 1. Build GestureRecognizerOptions
val options = GestureRecognizerOptions.builder().setBaseOptions(
BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
.setRunningMode(RunningMode.LIVE_STREAM)
.setResultListener { result, image -> /* Handle detected gesture */ }
.build()
// 2. Create the GestureRecognizer
val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)
// 3. Convert CameraX ImageProxy to MediaPipe MPImage
val bitmap = rotateBitmap(imageProxy.toBitmap(),
imageProxy.imageInfo.rotationDegrees)
val mpImage = BitmapImageBuilder(bitmap).build()
// 4. Run gesture recognition on the image
gestureRecognizer.recognizeAsync(mpImage, frameTime)// Step 1: Verify device support & install module
val enhancementClient = Enhancement.getClient(context)
suspend fun prepare(): Boolean {
if (!enhancementClient.isDeviceSupported().await()) return false
if (!enhancementClient.isModuleInstalled().await()) {
check(enhancementClient.installModule().await()) {
"Enhancement module install failed"
}
}
return true
}// Step 2: Select Enhancement options & create session
val options = EnhancementOptions(
width = bitmap.width,
height = bitmap.height,
enhancementMode = EnhancementMode.BITMAP,
isTonemappingEnabled = true,
isDeblurAndDenoisePhotoEnabled = true,
isDeblurAndDenoiseVideoEnabled = false,
isUpscalePhotoEnabled = false,
isUpscaleVideoEnabled = false
)
val enhancementSession = enhancementClient
.createSessionAsync(options, enhancementExecutor)// Step 3: Process Enhancement
val enhancedBitmap = suspendCancellableCoroutine { continuation ->
val enhancementCallback = object : EnhancementCallback {
override fun onBitmapProcessed(result: Bitmap) {
if (continuation.isActive) continuation.resume(result)
}
override fun onError(statusCode: Int) {
if (continuation.isActive) {
continuation.resumeWithException(
Exception("Bitmap processing failed: $statusCode")
)
}
}
override fun onSurfaceProcessed(timestamp: Long) {}
}
enhancementSession.process(bitmap, options, enhancementCallback)
}// Integrates SW Low Light Boost
val lowLightBoostEffect =
LowLightBoostEffect(
CameraEffect.PREVIEW or
CameraEffect.VIDEO_CAPTURE or
CameraEffect.IMAGE_CAPTURE
)
val sessionConfig = SessionConfig(
useCases = listOf(preview, imageCapture),
effects = lowLightBoostEffect
)
cameraProvider.bindToLifecycle(.. sessionConfig)