कैसे करें

CameraX और Jetpack Compose की मदद से स्पॉटलाइट इफ़ेक्ट बनाना

आठ मिनट में पढ़ें
Jolanda Verhoef
डेवलपर रिलेशंस इंजीनियर

नमस्ते! CameraX और Jetpack Compose के बारे में बताने वाली हमारी सीरीज़ में आपका फिर से स्वागत है. पिछली पोस्ट में, हमने कैमरा प्रीव्यू सेट अप करने के बुनियादी सिद्धांतों के बारे में बताया था. साथ ही, टैप-टू-फ़ोकस की सुविधा जोड़ी थी.

🧱 पहला हिस्सा: नए camera-compose आर्टफ़ैक्ट का इस्तेमाल करके, कैमरा प्रीव्यू की बुनियादी सुविधा बनाना. हमने अनुमति मैनेज करने और बुनियादी इंटिग्रेशन के बारे में बताया है.

👆 दूसरा हिस्सा: Compose के जेस्चर सिस्टम, ग्राफ़िक्स, और कोराउटीन का इस्तेमाल करके, टैप-टू-फ़ोकस की सुविधा को विज़ुअल तरीके से लागू करना.

🔦 तीसरा हिस्सा (यह पोस्ट): इसमें बताया गया है कि बेहतर उपयोगकर्ता अनुभव के लिए, Compose यूज़र इंटरफ़ेस (यूआई) एलिमेंट को कैमरा प्रीव्यू के ऊपर कैसे ओवरले करें.

📂 चौथा हिस्सा: फ़ोल्ड किए जा सकने वाले फ़ोन पर टेबलटॉप मोड को आसानी से चालू और बंद करने के लिए, अडैप्टिव एपीआई और Compose ऐनिमेशन फ़्रेमवर्क का इस्तेमाल करना.

इस पोस्ट में, हम कुछ और दिलचस्प चीज़ों के बारे में जानेंगे. जैसे, कैमरे की झलक के ऊपर स्पॉटलाइट इफ़ेक्ट लागू करना. इसके लिए, चेहरे का पता लगाने की सुविधा का इस्तेमाल करना. ऐसा क्यों, आप पूछेंगे? मुझे पक्के तौर पर नहीं पता. हालांकि, यह देखने में काफ़ी अच्छा लगता है 🙂. साथ ही, इससे यह भी पता चलता है कि हम सेंसर के कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में आसानी से कैसे बदल सकते हैं. इससे हमें Compose में इनका इस्तेमाल करने की सुविधा मिलती है!

face-detection.gif

चेहरे की पहचान करने की सुविधा चालू करें

सबसे पहले, चेहरे की पहचान करने की सुविधा चालू करने के लिए, 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 UI के कोऑर्डिनेट में बदलना

इन ट्रांसफ़ॉर्मेशन को ट्रांसफ़ॉर्मेशन मैट्रिक्स का इस्तेमाल करके किया जाता है. हर ट्रांसफ़ॉर्मेशन की अपनी मैट्रिक्स होती है:

  • हमारा 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
}
  • हम पहचाने गए चेहरों की सूची को दोहराते हैं और हर चेहरे के लिए, बदलाव लागू करते हैं.
  • हमें CameraXViewfinder से मिलने वाला CoordinateTransformer.transformMatrix, डिफ़ॉल्ट रूप से यूज़र इंटरफ़ेस (यूआई) से बफ़र कोऑर्डिनेट में बदलता है. इस मामले में, हमें मैट्रिक्स को दूसरी तरह से काम करने की ज़रूरत है. यानी, बफ़र के कोऑर्डिनेट को यूज़र इंटरफ़ेस (यूआई) के कोऑर्डिनेट में बदलना है. इसलिए, हम मैट्रिक्स को उलटने के लिए 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 UI के कोऑर्डिनेट में बदलने और वापस लाने की सुविधा का मतलब है कि हम Compose UI की सभी सुविधाओं का इस्तेमाल कर सकते हैं. साथ ही, उन्हें कैमरे के सिस्टम के साथ आसानी से इंटिग्रेट कर सकते हैं. इसमें एनिमेशन, बेहतर यूज़र इंटरफ़ेस ग्राफ़िक्स, यूज़र इंटरफ़ेस की स्थिति को आसानी से मैनेज करने की सुविधा, और जेस्चर कंट्रोल की सुविधा मिलती है. इसलिए, अपनी कल्पना को उड़ान दें!

सीरीज़ की इस आखिरी पोस्ट में, हम फ़ोल्ड किए जा सकने वाले डिवाइसों पर अलग-अलग कैमरा यूज़र इंटरफ़ेस (यूआई) के बीच आसानी से ट्रांज़िशन करने के लिए, अडैप्टिव एपीआई और Compose ऐनिमेशन फ़्रेमवर्क का इस्तेमाल करने के तरीके के बारे में जानेंगे. हमारे साथ बने रहें!


इस ब्लॉग में दिए गए कोड स्निपेट के लिए यह लाइसेंस लागू होता है:

// Copyright 2024 Google LLC. SPDX-License-Identifier: Apache-2.0

समीक्षा करने और सुझाव देने के लिए, निक बुचर, ऐलेक्स वैन्यो, ट्रेवर मैकगायर, डॉन टर्नर, और लॉरेन वार्ड का बहुत-बहुत धन्यवाद. यसिथ विदानाअरच्च की मेहनत की वजह से यह मुमकिन हो पाया है.

 

इसे लिखा है:

पढ़ना जारी रखें