Nasıl yapılır? rehberleri
CameraX ve Jetpack Compose ile spot ışığı 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şfedin.
📂 4. Bölüm: Katlanabilir telefonlarda masaüstü moduna 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 özellik kesinlikle çok havalı görünüyor 🙂. 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'ten temel Camera2 API ile etkileşim kurmamıza olanak tanıyan Camera2Interop API'yi kullanacağız. Bu sayede, doğrudan CameraX tarafından 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 seçenek, 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şiklikler uygulandıktan sonra 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ı çizmek 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,sensorToBufferTranformmatrisini 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 }
- Tespit edilen yüzlerin listesini tekrarlayarak her yüz için dönüşümü gerçekleştiririz.
CameraXViewfinderadlı uygulamamızdan aldığımızCoordinateTransformer.transformMatrix, varsayılan olarak kullanıcı arayüzündeki koordinatları arabellek koordinatlarına dönüştürür. Bizim durumumuzda, arabelleğe alma koordinatlarını kullanıcı arayüzü koordinatlarına dönüştürerek matrisin ters yönde çalışmasını istiyoruz. Bu nedenle, matrisi tersine çevirmek içininvert()yöntemini kullanırız.- Önce
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. Algılanan yüzleri görünür hale getirmek için önizlemenin üzerine gradyan maskesi çizmek amacıyla bir Canvas composable'ı kullanacağız:
@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ş şekli:
- 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, renkli yer paylaşımını animasyonla içeri ve dışarı taşımak içinAnimatedVisibilityile birlikte kullanılabilir. surfaceRequest, sensör koordinatlarınıSurfaceRequest.TransformationInfoiçinde 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 yineleriz ve 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 ışığı efekti elde ederiz. 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ı sayısız 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ü özelliklerini kullanabileceğ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 hayal gücünüzü sınırlamayın.
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.
Okumaya devam edin
-
"Nasıl yapılır?" rehberleri
Bu 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 dk.
-
"Nasıl yapılır?" rehberleri
Aşırı pilin hızlı tükenmesinin Android kullanıcıları için akla ilk gelen sorunlardan biri olduğunun farkında olan Google, geliştiricilerin daha az güç tüketen uygulamalar geliştirmesine yardımcı olmak için önemli adımlar atmaktadır.
Alice Yuan • Okuma süresi: 8 dk.
-
"Nasıl yapılır?" rehberleri
Hem cihaz üzerinde hem de bulut modellerini kullanan yapay zeka destekli özelliklerin örneklerini sunarak kullanıcılarınız için keyifli deneyimler oluşturmanıza ilham vermek istedik.
Thomas Ezan, Ivy Knight • Okuma süresi: 2 dakika
Gelişmelerden haberdar olun
Android geliştirmeyle ilgili en son analizleri her hafta gelen kutunuza alın.