CameraX ve Jetpack Compose ile öne çıkarma efekti oluşturma
Okuma süresi 8 dakika
Merhaba! CameraX ve Jetpack Compose ile ilgili serimize tekrar hoş geldiniz. Önceki gönderilerde kamera önizlemesi ayarlamanın temellerini ele almış ve dokunarak odaklanma işlevini eklemiştik.
🧱 1. Bölüm: Yeni camera-compose yapısını kullanarak temel bir kamera önizlemesi oluşturma. İzin işleme ve temel entegrasyon konularını ele aldık.
👆 2. bölüm: Görsel bir dokunarak odaklanma özelliği uygulamak için Compose hareket sistemini, grafikleri ve eşzamanlı rutinleri kullanma.
🔦 3. Bölüm (bu gönderi): Daha zengin bir kullanıcı deneyimi için Compose kullanıcı arayüzü öğelerini kamera önizlemenizin üzerine yerleştirme yöntemlerini keşfediyoruz.
📂 4. Bölüm: Katlanabilir telefonlarda masaüstü moduna ve masaüstü modundan sorunsuz bir şekilde geçiş yapmak için uyarlanabilir API'leri ve Compose animasyon çerçevesini kullanma.
Bu yayında, biraz daha görsel olarak ilgi çekici bir konuya, yani efektin temeli olarak yüz algılamayı kullanarak kamera önizlememizin üzerine bir spot ışığı efekti uygulama konusuna değineceğiz. Neden mi? Emin değilim. Ancak bu görselin çok havalı göründüğü kesin 🙂. Daha da önemlisi, sensör koordinatlarını kullanıcı arayüzü koordinatlarına nasıl kolayca çevirebileceğimizi gösteriyor. Böylece bu koordinatları Compose'da kullanabiliyoruz.
Yüz algılamayı etkinleştirme
İlk olarak, yüz algılamayı etkinleştirmek için CameraPreviewViewModel'ı değiştirelim. CameraX'teki temel Camera2 API ile etkileşim kurmamıza olanak tanıyan Camera2Interop API'yi kullanacağız. Bu sayede, CameraX tarafından doğrudan sunulmayan kamera özelliklerini kullanabiliriz. Aşağıdaki değişiklikleri yapmamız gerekiyor:
- Yüz sınırlarını
Rectlistesi olarak içeren bir StateFlow oluşturun. STATISTICS_FACE_DETECT_MODEyakalama isteği seçeneğini FULL olarak ayarlayın. Bu ayar, yüz algılamayı etkinleştirir.- Yakalama sonucundaki yüz bilgilerini almak için
CaptureCallbackayarlayın.
class CameraPreviewViewModel : ViewModel() { ... private val _sensorFaceRects = MutableStateFlow(listOf<Rect>()) val sensorFaceRects: StateFlow<List<Rect>> = _sensorFaceRects.asStateFlow() private val cameraPreviewUseCase = Preview.Builder() .apply { Camera2Interop.Extender(this) .setCaptureRequestOption( CaptureRequest.STATISTICS_FACE_DETECT_MODE, CaptureRequest.STATISTICS_FACE_DETECT_MODE_FULL ) .setSessionCaptureCallback(object : CameraCaptureSession.CaptureCallback() { override fun onCaptureCompleted( session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult ) { super.onCaptureCompleted(session, request, result) result.get(CaptureResult.STATISTICS_FACES) ?.map { face -> face.bounds.toComposeRect() } ?.toList() ?.let { faces -> _sensorFaceRects.update { faces } } } }) } .build().apply { ... }
Bu değişikliklerin uygulanmasıyla birlikte görünüm modelimiz artık sensör koordinatlarında algılanan yüzlerin sınırlayıcı kutularını temsil eden bir Rect nesne listesi yayınlıyor.
Sensör koordinatlarını kullanıcı arayüzü koordinatlarına çevirme
Son bölümde depoladığımız algılanan yüzlerin sınırlayıcı kutuları, sensör koordinat sistemindeki koordinatları kullanır. Kullanıcı arayüzümüzde sınırlayıcı kutuları çizebilmek için bu koordinatları Compose koordinat sisteminde doğru olacak şekilde dönüştürmemiz gerekir. Yapmamız gerekenler:
- Sensör koordinatlarını önizleme arabelleği koordinatlarına dönüştürme
- Önizleme arabelleği koordinatlarını Compose kullanıcı arayüzü koordinatlarına dönüştürme
Bu dönüşümler, dönüşüm matrisleri kullanılarak yapılır. Her dönüşümün kendi matrisi vardır:
SurfaceRequest,sensorToBufferTranformmatrisi içeren birTransformationInfoörneğini tutar.CameraXViewfinderürünümüzle ilişkili birCoordinateTransformervar. Bu dönüştürücüyü, önceki blog yayınında dokunarak odaklanma koordinatlarını dönüştürmek için kullandığımızı hatırlayabilirsiniz.
Dönüşümü bizim için yapabilecek bir yardımcı yöntem oluşturabiliriz:
private fun List<Rect>.transformToUiCoords( transformationInfo: SurfaceRequest.TransformationInfo?, uiToBufferCoordinateTransformer: MutableCoordinateTransformer ): List<Rect> = this.map { sensorRect -> val bufferToUiTransformMatrix = Matrix().apply { setFrom(uiToBufferCoordinateTransformer.transformMatrix) invert() } val sensorToBufferTransformMatrix = Matrix().apply { transformationInfo?.let { setFrom(it.sensorToBufferTransform) } } val bufferRect = sensorToBufferTransformMatrix.map(sensorRect) val uiRect = bufferToUiTransformMatrix.map(bufferRect) uiRect }
- Algılanan yüzlerin listesini yineleriz ve her yüz için dönüşümü gerçekleştiririz.
CameraXViewfinderöğemizden aldığımızCoordinateTransformer.transformMatrix, varsayılan olarak koordinatları kullanıcı arayüzünden arabellek koordinatlarına dönüştürür. Bizim durumumuzda matrisin ters yönde çalışmasını, yani arabellek koordinatlarını kullanıcı arayüzü koordinatlarına dönüştürmesini istiyoruz. Bu nedenle, matrisi tersine çevirmek içininvert()yöntemini kullanırız.- Öncelikle
sensorToBufferTransformMatrixkullanarak yüzü sensör koordinatlarından arabellek koordinatlarına, ardındanbufferToUiTransformMatrixkullanarak arabellek koordinatlarını kullanıcı arayüzü koordinatlarına dönüştürürüz.
Ön plana alma efektini uygulama
Şimdi, CameraPreviewContent composable'ı güncelleyerek spot ışığı efektini çizelim. Önizlemenin üzerine gradyan maskesi çizmek için Canvas composable'ını kullanırız. Böylece algılanan yüzler görünür hale gelir:
@Composable fun CameraPreviewContent( viewModel: CameraPreviewViewModel, modifier: Modifier = Modifier, lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current ) { val surfaceRequest by viewModel.surfaceRequest.collectAsStateWithLifecycle() val sensorFaceRects by viewModel.sensorFaceRects.collectAsStateWithLifecycle() val transformationInfo by produceState<SurfaceRequest.TransformationInfo?>(null, surfaceRequest) { try { surfaceRequest?.setTransformationInfoListener(Runnable::run) { transformationInfo -> value = transformationInfo } awaitCancellation() } finally { surfaceRequest?.clearTransformationInfoListener() } } val shouldSpotlightFaces by remember { derivedStateOf { sensorFaceRects.isNotEmpty() && transformationInfo != null} } val spotlightColor = Color(0xDDE60991) .. surfaceRequest?.let { request -> val coordinateTransformer = remember { MutableCoordinateTransformer() } CameraXViewfinder( surfaceRequest = request, coordinateTransformer = coordinateTransformer, modifier = .. ) AnimatedVisibility(shouldSpotlightFaces, enter = fadeIn(), exit = fadeOut()) { Canvas(Modifier.fillMaxSize()) { val uiFaceRects = sensorFaceRects.transformToUiCoords( transformationInfo = transformationInfo, uiToBufferCoordinateTransformer = coordinateTransformer ) // Fill the whole space with the color drawRect(spotlightColor) // Then extract each face and make it transparent uiFaceRects.forEach { faceRect -> drawRect( Brush.radialGradient( 0.4f to Color.Black, 1f to Color.Transparent, center = faceRect.center, radius = faceRect.minDimension * 2f, ), blendMode = BlendMode.DstOut ) } } } } }
İşleyişi şöyledir:
- Görünüm modelinden yüzlerin listesini toplarız.
- Algılanan yüzlerin listesi her değiştiğinde ekranın tamamını yeniden oluşturmamak için
derivedStateOfkullanarak yüz algılanıp algılanmadığını takip ederiz. Bu değer daha sonraAnimatedVisibilityile birlikte kullanılarak renkli yer paylaşımının animasyonla gösterilip gizlenmesi sağlanabilir. surfaceRequest, sensör koordinatlarınıSurfaceRequest.TransformationInfoiçindeki arabellek koordinatlarına dönüştürmek için ihtiyacımız olan bilgileri içerir. Yüzey isteğinde bir dinleyici ayarlamak ve composable, kompozisyon ağacından ayrıldığında bu dinleyiciyi temizlemek içinproduceStateişlevini kullanırız.- Ekranın tamamını kaplayan yarı saydam pembe bir dikdörtgen çizmek için
Canvaskullanırız. sensorFaceRectsdeğişkeninin okunmasını,Canvasçizim bloğunun içine girene kadar erteleriz. Ardından, koordinatları kullanıcı arayüzü koordinatlarına dönüştürürüz.- Algılanan yüzleri tekrarlayarak her yüz için, yüz dikdörtgeninin içini şeffaf hale getirecek dairesel renk geçişi çizeriz.
BlendMode.DstOutaracını kullanarak pembe dikdörtgenin gradyanını kesip çıkararak spot ışığı efekti oluştururuz.
Not: Kamerayı DEFAULT_FRONT_CAMERA olarak değiştirdiğinizde spot ışığının yansıtıldığını fark edeceksiniz. Bu bilinen bir sorundur ve Google Sorun İzleyici'de takip edilmektedir.
Sonuç
Bu kodla, algılanan yüzleri vurgulayan tam işlevsel bir spot ışığı efektine sahip oluyoruz. Kod snippet'inin tamamını burada bulabilirsiniz.
Bu efekt yalnızca başlangıç. Compose'un gücünü kullanarak görsel açıdan çarpıcı birçok kamera deneyimi oluşturabilirsiniz. Sensör ve arabellek koordinatlarını Compose kullanıcı arayüzü koordinatlarına ve tekrar geri dönüştürebilmek, tüm Compose kullanıcı arayüzü özelliklerinden yararlanabileceğimiz ve bunları temel kamera sistemiyle sorunsuz bir şekilde entegre edebileceğimiz anlamına gelir. Animasyonlar, gelişmiş kullanıcı arayüzü grafikleri, basit kullanıcı arayüzü durumu yönetimi ve tam hareket kontrolü ile tek sınır hayal gücünüzdür.
Serinin son yayınında, katlanabilir cihazlarda farklı kamera kullanıcı arayüzleri arasında sorunsuz geçiş yapmak için uyarlanabilir API'leri ve Compose animasyon çerçevesini nasıl kullanacağımızı ayrıntılı olarak ele alacağız. Bizi izlemeye devam edin.
Bu blogdaki kod snippet'leri aşağıdaki lisansa sahiptir:
// Copyright 2024 Google LLC. SPDX-License-Identifier: Apache-2.0
İnceleme ve geri bildirimleri için Nick Butcher, Alex Vanyo, Trevor McGuire, Don Turner ve Lauren Ward'a teşekkür ederiz. Yasith Vidanaarachch'ın sıkı çalışması sayesinde mümkün olmuştur.
-
"Nasıl yapılır?" rehberleriBu makalede, belirli koşulların karşılanmasını beklemek için Compose'da waitUntil test API'sini nasıl kullanacağınız açıklanmaktadır.
Jose Alcérreca • Okuma süresi 3 dakika -
"Nasıl yapılır?" rehberleriUygulama performansı genellikle sorunsuz bir kullanıcı arayüzü ve hızlı başlangıç süreleriyle eşdeğer tutulsa da bellek, bu görünür metriklerin üzerine inşa edildiği sessiz temeldir. Cihaz belleğinin her zamankinden daha önemli hale geldiği bir değişim sürecinde olduğumuz bir sır değil.
Alice Yuan, Ajesh Pai, Fung Lam • Okuma süresi 10 dakika -
"Nasıl yapılır?" rehberleriBugün, Google tarafından verilen yeni bir doğrulanmış e-posta kimlik bilgisini duyurmaktan heyecan duyuyoruz. Geliştiriciler artık bu kimlik bilgisini doğrudan Android'in Kimlik Bilgisi Yöneticisi Dijital Kimlik Bilgisi API'sinden alabilir.
Niharika Arora, Jean-Pierre Pralle • Okuma süresi 3 dakika
Android geliştirmeyle ilgili en son analizleri her hafta gelen kutunuza alın.