CameraX और Jetpack Compose की मदद से स्पॉटलाइट इफ़ेक्ट बनाना
पढ़ने में 8 मिनट लगेंगे
नमस्ते! CameraX और Jetpack Compose के बारे में बताने वाली हमारी सीरीज़ में आपका फिर से स्वागत है. पिछली पोस्ट में, हमने कैमरा प्रीव्यू सेट अप करने की बुनियादी बातें बताई थीं. साथ ही, टैप-टू-फ़ोकस की सुविधा जोड़ी थी.
🧱 पहला हिस्सा: नए camera-compose आर्टफ़ैक्ट का इस्तेमाल करके, बुनियादी कैमरा प्रीव्यू बनाना. हमने अनुमति मैनेज करने और बुनियादी इंटिग्रेशन के बारे में बताया था.
👆 दूसरा हिस्सा: Compose के जेस्चर सिस्टम, ग्राफ़िक्स, और कोरोटीन का इस्तेमाल करके, विज़ुअल टैप-टू-फ़ोकस लागू करना.
🔦 तीसरा हिस्सा (यह पोस्ट): Compose के यूज़र इंटरफ़ेस (यूआई) एलिमेंट को अपने कैमरा प्रीव्यू पर ओवरले करने का तरीका जानना, ताकि उपयोगकर्ता को बेहतर अनुभव मिल सके.
📂 चौथा हिस्सा: फ़ोल्ड किए जा सकने वाले फ़ोन पर, टेबलटॉप मोड में आसानी से ऐनिमेट करने के लिए, अडैप्टिव एपीआई और Compose के ऐनिमेशन फ़्रेमवर्क का इस्तेमाल करना.
इस पोस्ट में, हम कुछ ज़्यादा विज़ुअली आकर्षक चीज़ के बारे में जानेंगे. जैसे, अपने कैमरा प्रीव्यू पर स्पॉटलाइट इफ़ेक्ट लागू करना. इसके लिए, हम चेहरे की पहचान को इफ़ेक्ट के आधार के तौर पर इस्तेमाल करेंगे. आप पूछ सकते हैं कि ऐसा क्यों? मुझे पक्के तौर पर नहीं पता. हालांकि, यह देखने में काफ़ी अच्छा लगता है 🙂. इससे भी अहम बात यह है कि इससे यह पता चलता है कि हम सेंसर के कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में आसानी से कैसे बदल सकते हैं. इससे हमें Compose में उनका इस्तेमाल करने में मदद मिलती है!
चेहरे की पहचान की सुविधा चालू करना
सबसे पहले, CameraPreviewViewModel में बदलाव करके, चेहरे की पहचान की सुविधा चालू करते हैं. हम Camera2Interop एपीआई का इस्तेमाल करेंगे. इससे हमें CameraX से, Camera2 API के साथ इंटरैक्ट करने की अनुमति मिलती है. इससे हमें कैमरे की उन सुविधाओं का इस्तेमाल करने का मौका मिलता है जो CameraX से सीधे तौर पर उपलब्ध नहीं हैं. हमें ये बदलाव करने होंगे:
- एक StateFlow बनाएं, जिसमें चेहरे की सीमाओं को
Rectकी सूची के तौर पर शामिल किया गया हो. STATISTICS_FACE_DETECT_MODEकैप्चर अनुरोध के विकल्प को FULL पर सेट करें. इससे चेहरे की पहचान की सुविधा चालू हो जाती है.- कैप्चर के नतीजे से चेहरे की जानकारी पाने के लिए,
CaptureCallbackसेट करें.
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 { ... }
इन बदलावों के बाद, हमारा व्यू मॉडल अब Rect ऑब्जेक्ट की सूची दिखाता है. यह सूची, सेंसर के कोऑर्डिनेट में पहचाने गए चेहरों के बाउंडिंग बॉक्स को दिखाती है.
सेंसर के कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में बदलना
पिछले सेक्शन में, पहचाने गए चेहरों के बाउंडिंग बॉक्स में सेंसर कोऑर्डिनेट सिस्टम के कोऑर्डिनेट का इस्तेमाल किया गया है. अपने यूज़र इंटरफ़ेस (यूआई) में बाउंडिंग बॉक्स बनाने के लिए, हमें इन कोऑर्डिनेट को बदलना होगा, ताकि वे Compose कोऑर्डिनेट सिस्टम में सही हों. हमें यह करना होगा:
- सेंसर के कोऑर्डिनेट को प्रीव्यू बफ़र के कोऑर्डिनेट में बदलना
- प्रीव्यू बफ़र के कोऑर्डिनेट को Compose के यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में बदलना
इन बदलावों को ट्रांसफ़ॉर्मेशन मैट्रिक्स का इस्तेमाल करके किया जाता है. हर बदलाव के लिए, अलग मैट्रिक्स होता है:
- हमारे
SurfaceRequestमेंTransformationInfoइंस्टेंस होता है. इसमेंsensorToBufferTranformमैट्रिक्स होता है. - हमारे
CameraXViewfinderसे,CoordinateTransformerजुड़ा होता है. आपको याद होगा कि हमने पिछली ब्लॉग पोस्ट में, टैप-टू-फ़ोकस के कोऑर्डिनेट को बदलने के लिए, इस ट्रांसफ़ॉर्मर का इस्तेमाल किया था.
हम एक हेल्पर तरीका बना सकते हैं, जो हमारे लिए बदलाव कर सकता है:
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 }
- हम पहचाने गए चेहरों की सूची पर इटरेट करते हैं. साथ ही, हर चेहरे के लिए बदलाव करते हैं.
CoordinateTransformer.transformMatrix, जो हमें अपनेCameraXViewfinderसे मिलता है, डिफ़ॉल्ट रूप से यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट को बफ़र के कोऑर्डिनेट में बदलता है. हमारे मामले में, हम चाहते हैं कि मैट्रिक्स दूसरी तरह से काम करे. यानी, बफ़र के कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में बदले. इसलिए, हम मैट्रिक्स को उलटने के लिए,invert()तरीके का इस्तेमाल करते हैं.- हम सबसे पहले,
sensorToBufferTransformMatrixका इस्तेमाल करके, चेहरे को सेंसर के कोऑर्डिनेट से बफ़र के कोऑर्डिनेट में बदलते हैं. इसके बाद,bufferToUiTransformMatrixका इस्तेमाल करके, उन बफ़र के कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में बदलते हैं.
स्पॉटलाइट इफ़ेक्ट लागू करना
अब, स्पॉटलाइट इफ़ेक्ट बनाने के लिए, CameraPreviewContent कंपोज़ेबल को अपडेट करते हैं. हम प्रीव्यू पर ग्रेडिएंट मास्क बनाने के लिए, Canvas कंपोज़ेबल का इस्तेमाल करेंगे. इससे पहचाने गए चेहरे दिखेंगे:
@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 ) } } } } }
यह सुविधा इस तरह से काम करती है:
- हम व्यू मॉडल से चेहरों की सूची इकट्ठा करते हैं.
- यह पक्का करने के लिए कि पहचाने गए चेहरों की सूची में बदलाव होने पर, हम पूरी स्क्रीन को फिर से कंपोज़ न करें, हम
derivedStateOfका इस्तेमाल करके यह ट्रैक करते हैं कि कोई चेहरा पहचाना गया है या नहीं. इसके बाद, इसका इस्तेमालAnimatedVisibilityके साथ किया जा सकता है, ताकि रंगीन ओवरले को ऐनिमेट किया जा सके. surfaceRequestमें वह जानकारी होती है जिसकी हमेंSurfaceRequest.TransformationInfoमें, सेंसर के कोऑर्डिनेट को बफ़र के कोऑर्डिनेट में बदलने के लिए ज़रूरत होती है. हम सर्फ़ेस अनुरोध में लिसनर सेट अप करने के लिए,produceStateफ़ंक्शन का इस्तेमाल करते हैं. साथ ही, कंपोज़ेबल के कंपोज़िशन ट्री से बाहर निकलने पर, इस लिसनर को साफ़ कर देते हैं.- हम पूरी स्क्रीन को कवर करने के लिए, पारदर्शी गुलाबी रंग का आयत बनाने के लिए,
Canvasका इस्तेमाल करते हैं. - हम
sensorFaceRectsवैरिएबल को तब तक नहीं पढ़ते, जब तक हमCanvasड्रॉ ब्लॉक के अंदर न हों. इसके बाद, हम कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में बदलते हैं. - हम पहचाने गए चेहरों पर इटरेट करते हैं. साथ ही, हर चेहरे के लिए, रेडियल ग्रेडिएंट बनाते हैं. इससे चेहरे के आयत का अंदरूनी हिस्सा पारदर्शी हो जाएगा.
- हम
BlendMode.DstOutका इस्तेमाल करके यह पक्का करते हैं कि हम गुलाबी आयत से ग्रेडिएंट को काट रहे हैं. इससे स्पॉटलाइट इफ़ेक्ट बनता है.
ध्यान दें: जब आप कैमरे को DEFAULT_FRONT_CAMERA पर बदलते हैं, तो आपको दिखेगा कि स्पॉटलाइट मिरर की तरह दिख रहा है! यह एक जानी-मानी समस्या है, जिसे Google Issue Tracker. में ट्रैक किया जाता है.
नतीजा
इस कोड की मदद से, हमारे पास पूरी तरह से काम करने वाला स्पॉटलाइट इफ़ेक्ट है. इससे पहचाने गए चेहरों को हाइलाइट किया जाता है. कोड का पूरा स्निपेट यहां देखा जा सकता है.
यह इफ़ेक्ट सिर्फ़ शुरुआत है. Compose की मदद से, कैमरे के कई शानदार अनुभव बनाए जा सकते हैं. सेंसर और बफ़र के कोऑर्डिनेट को Compose के यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में और वापस बदलने की सुविधा का मतलब है कि हम Compose के यूज़र इंटरफ़ेस (यूआई) की सभी सुविधाओं का इस्तेमाल कर सकते हैं. साथ ही, उन्हें कैमरे के बुनियादी सिस्टम के साथ आसानी से इंटिग्रेट कर सकते हैं. ऐनिमेशन, यूज़र इंटरफ़ेस (यूआई) के बेहतर ग्राफ़िक्स, यूज़र इंटरफ़ेस (यूआई) के आसान स्टेट मैनेजमेंट, और जेस्चर पर पूरा कंट्रोल होने की वजह से, आपकी कल्पना की कोई सीमा नहीं है!
सीरीज़ की आखिरी पोस्ट में, हम फ़ोल्ड किए जा सकने वाले डिवाइसों पर, कैमरे के अलग-अलग यूज़र इंटरफ़ेस (यूआई) के बीच आसानी से ट्रांज़िशन करने के लिए, अडैप्टिव एपीआई और Compose के ऐनिमेशन फ़्रेमवर्क का इस्तेमाल करने का तरीका जानेंगे. हमारे साथ बने रहें!
इस ब्लॉग में दिए गए कोड स्निपेट के लिए, यह लाइसेंस लागू होता है:
// Copyright 2024 Google LLC. SPDX-License-Identifier: Apache-2.0
समीक्षा करने और सुझाव देने के लिए, Nick Butcher, Alex Vanyo, Trevor McGuire, Don Turner, और Lauren Ward का बहुत-बहुत धन्यवाद. यह Yasith Vidanaarachch की कड़ी मेहनत की वजह से मुमकिन हो पाया है.
-
तरीका बताने वाले लेखइस लेख में, Compose में waitUntil टेस्ट एपीआई का इस्तेमाल करने का तरीका बताया गया है. इससे कुछ शर्तों के पूरा होने तक इंतज़ार किया जा सकता है.
Jose Alcérreca • पढ़ने में 3 मिनट लगेंगे -
तरीका बताने वाले लेखऐप्लिकेशन की परफ़ॉर्मेंस को अक्सर, बेहतर यूज़र इंटरफ़ेस (यूआई) और तेज़ी से शुरू होने के समय के तौर पर देखा जाता है. हालांकि, मेमोरी वह आधार है जिस पर ये दिखने वाली मेट्रिक बनती हैं. यह कोई रहस्य नहीं है कि हम एक ऐसे बदलाव को देख रहे हैं जहां डिवाइस की मेमोरी पहले से कहीं ज़्यादा ज़रूरी है.
Alice Yuan, Ajesh Pai, Fung Lam • पढ़ने में 10 मिनट लगेंगे -
तरीका बताने वाले लेखआज, हमें Google की ओर से जारी किए गए, पुष्टि किए गए नए ईमेल क्रेडेंशियल के बारे में बताते हुए खुशी हो रही है. डेवलपर अब इसे सीधे Android के Credential Manager Digital Credential API से पा सकते हैं.
Niharika Arora, Jean-Pierre Pralle • पढ़ने में 3 मिनट लगेंगे
Android डेवलपमेंट से जुड़ी नई जानकारी, हर हफ़्ते अपने इनबॉक्स में पाएं.