Основы работы с приложениями

Как улучшить работу камеры на разных устройствах с помощью CameraX и ИИ на устройстве

Колонки

О приложении

О выступлении

Пользователи ожидают, что сторонние приложения будут снимать фото и видео с тем же высоким качеством, что и встроенная камера устройства, независимо от условий освещения или уровня аппаратного обеспечения. Чтобы обеспечить такое единообразие в расширяющейся экосистеме Android, необходимо учитывать ключевые архитектурные особенности. На этом занятии мы покажем, как создавать надежные профессиональные конвейеры захвата, которые максимально используют возможности устройств и при этом стабильно работают на всех форм-факторах.

Мы рассмотрим последние достижения в CameraX и покажем, как интегрировать передовые возможности, такие как фото с движением и улучшения от ИИ на устройстве, через сервисы Google Play, а также пространственные возможности с помощью Jetpack XR SDK. Мы также рассмотрим навык CameraX, который позволяет выбранному вами агенту кодирования автоматически реализовывать концепции, описанные в этом докладе, и многое другое.

Сводка

  • Jetpack CameraX – это рекомендуемая библиотека камеры для приложений Android, поскольку Camera1 устарела. В следующем году, когда выйдет Android 18, мы начнем отключать Camera1 в соответствии с правилами Google Play.
  • CameraXViewfinder адаптирует предварительный просмотр камеры для разных форм-факторов, например складных устройств. В CameraX 1.7 добавлено управление жестами и поддержка подсветки экрана.
  • Аудио- и видеоочки запускают приложения на основном телефоне, при этом камеры работают на обоих устройствах. Передача ProjectedDeviceContext позволяет CameraX делать снимки напрямую с очков.
  • Feature Group API помогает приложениям выбирать поддерживаемые комбинации возможностей камеры. Начиная с версии CameraX 1.6, поддерживается сочетание записи в формате 4K со стабилизацией видео.
  • Машинное обучение на устройстве анализирует предпросмотр камеры и записанные медиафайлы с помощью ML Kit и MediaPipe. Разработчики могут выполнять самые разные задачи – от распознавания жестов в реальном времени до генерации описаний изображений.

Ресурсы

Gemini Notebook

Темы

  • camerax
  • android-camera
  • on-device-ml
  • gemini-nano
  • mediapipe
  • ml-kit
  • android-xr

Расшифровка

Введение и обзор CameraX

  • Здравствуйте!

  • Добро пожаловать на наше занятие.

  • Меня зовут Аман, а это Скотт.

  • Сегодня мы рады рассказать о том, как вы можете улучшить работу камеры на разных устройствах с помощью CameraX и искусственного интеллекта на устройстве.

  • Давайте начнем.

  • Подумайте о том, как вы используете свои телефоны.

  • Мы часто используем камеру телефона, чтобы запечатлеть моменты и поделиться ими с близкими.

  • Камера стала центральным элементом смартфона.

  • На самом деле, хорошая камера часто является ключевым фактором при покупке смартфонов.

  • Сегодня мы рассмотрим две основные темы, связанные с созданием премиум-функций камеры.

  • Сначала мы рассмотрим некоторые функции CameraX, а затем узнаем о некоторых методах машинного обучения на устройстве.

  • Начнем с CameraX.

  • Jetpack CameraX обеспечивает работу камеры на Android.

  • Помимо CameraX, есть ещё Camera1 и Camera2, которые входят в состав фреймворка платформы Android.

  • Какую библиотеку выбрать?

  • Camera1 – это исходный API камеры на Android.

  • Сейчас он устарел, поэтому в новых приложениях следует использовать Camera2 или CameraX.

  • Camera2 – это преемник Camera1.

  • Поэтому, если вам требуется низкоуровневое управление камерой для сложных сценариев использования, Camera2 – хороший вариант.

  • Однако в этом случае вам придется самостоятельно настраивать конфигурации для разных устройств.

  • И наконец, Jetpack CameraX основана на Camera2.

  • Он предоставляет согласованный API и автоматически обрабатывает конфигурации для конкретных устройств.

  • Рекомендуется для большинства разработчиков.

  • Мы хотим сделать очень важное заявление.

  • Мы планируем отключить Camera1. Это начнется с принудительного применения правил Google Play в следующем году в Android 18.

  • Если вы все ещё используете устаревший API Camera1, перейдите на CameraX или Camera2.

  • Почему стоит использовать CameraX?

  • Во-первых, он поддерживает Android 7 и более поздние версии, что делает его совместимым почти с 99 % активных устройств Android.

  • Во-вторых, он прост в использовании и предоставляет простые API для распространенных сценариев использования, таких как предварительный просмотр, захват и запись с меньшим количеством кода.

  • В-третьих, он обеспечивает единообразие на разных устройствах, уменьшая количество проблем, связанных с конкретными устройствами, таких как растяжение предварительного просмотра, что довольно часто встречается на складных устройствах и планшетах.

  • И наконец, он упрощает реализацию расширенных возможностей OEM, таких как ночной и портретный режимы.

  • А теперь я передаю слово Скотту, который расскажет о некоторых функциях CameraX.

  • Передаю слово Скотту.

  • Спасибо, Аман.

  • Здравствуйте!

  • Меня зовут Скотт.

  • Сегодня мы расскажем о нескольких функциях CameraX, которые помогут вам создать приложение для камеры профессионального уровня.

Функции адаптивного видоискателя

  • Первая функция – адаптивный видоискатель.

  • Он позволяет легко просматривать, как будет выглядеть интерфейс на устройствах разных типов и с разными размерами экрана.

  • Сейчас у пользователей есть множество устройств, включая складные телефоны, ноутбуки и XR-устройства.

  • Поэтому сейчас как никогда важно адаптировать камеру к новым форм-факторам.

  • А предпросмотр камеры – это вообще отдельная история, особенно на складных устройствах с широким экраном.

  • На таких устройствах в разложенном виде внутренняя камера расположена горизонтально, а задняя – вертикально.

  • Если предположить, что все датчики камеры установлены вертикально, то это приведет к пространственному несоответствию и вызовет искажение или боковое смещение.

  • CameraXViewfinder, он разработан, чтобы полностью справиться со сложностью за вас, обеспечивая бесперебойную работу предварительного просмотра камеры во всех форм-факторах.

  • Давайте посмотрим, как использовать composable-функцию CameraXViewfinder.

    var surfaceRequest by remember {
        mutableStateOf<SurfaceRequest?>(null)
    }
    
    LaunchedEffect(...) {
        preview.setSurfaceProvider { surfaceRequest = it }
        cameraProvider.bindToLifecycle(..., preview)
    }
    
    surfaceRequest?.let { request ->
        CameraXViewfinder(
            surfaceRequest = request,
            contentScale = ContentScale.Fit
        )
    }
  • Сначала вы объявляете переменную контента surfaceRequest, которая будет содержать запрос к Surface.

  • Извините.

  • Во-вторых, в LaunchedEffect вы настраиваете поставщика Surface, чтобы он получал объект запроса Surface.

  • Затем вы привязываетесь к жизненному циклу и запускаете камеру.

  • И наконец, когда состояние обновляется с помощью действительного запроса поверхности и запускает рекомпозицию, вы устанавливаете запрос непосредственно в CameraXViewfinder.

  • Здесь вы можете выбрать, как будет показываться contentScale: вписываться в экран или обрезаться.

  • Вот и все.

  • В CameraX 1.7 мы добавили несколько новых функций в CameraXViewfinder.

  • в том числе новые жесты, например нажатие для фокусировки или сведение пальцев для масштабирования.

  • Также поддерживается вспышка экрана, которая позволяет использовать экран для осветления селфи.

  • Внедрить эту функцию очень просто.

    CameraXViewfinder(
        surfaceRequest = viewModel.surfaceRequest,
        contentScale = ContentScale.Fit,
        isTapToFocusEnabled = true,
        isPinchToZoomEnabled = true,
        onScreenFlashReady =
            {
               imageCapture.screenFlash = it
               imageCapture.flashMode = FLASH_MODE_SCREEN
            }
    )
  • Вам нужно лишь включить фокусировку по нажатию и масштабирование сведением пальцев.

  • Для подсветки экрана вы получаете экземпляр Screen Flash из обратного вызова onScreenFlashReady, а затем передаете его в ImageCapture и устанавливаете режим вспышки FLASH_MODE_SCREEN.

  • нужно будет

Как снимать на камеру в Android XR

  • Мы увидели, как CameraX позволяет просматривать изображения с камеры, которые без проблем работают на всех форм-факторах.

  • Теперь давайте сделаем ещё один шаг и попробуем сделать снимок на камеру в Android XR.

  • Существует три категории XR-устройств. Первая – XR-шлемы.

  • Это полностью закрытые гарнитуры с эффектом погружения, которые используют камеру для съемки физического мира и отображения его внутри гарнитуры.

  • Второй – проводные XR-очки.

  • Сегодня мы анонсировали XREAL Aura в этой категории.

  • Как вы видите на презентации, это прозрачные очки, подключенные к вычислительному блоку, которые позволяют видеть виртуальный контент, наложенный на реальный мир.

  • И третий – аудио- и видеоочки.

  • Это легкие очки.

  • Они могут сочетать аудио, камеру и дисплей для голосового управления и удобного использования.

  • Первые две категории, XR-шлемы и проводные XR-очки, также поддерживают Camera2 и CameraX, как и обычные телефоны.

  • Поэтому сегодня мы сосредоточимся только на аудио и видеоочках.

  • На этих очках приложения запускаются на телефоне Android, а не на очках, но здесь есть доступ как к камере телефона, так и к камере очков.

  • Главный вопрос заключается в том, как узнать, к какой камере вы обращаетесь?

  • Все просто.

  • При стандартном контексте CameraX получает доступ к камере телефона.

  • А с помощью ProjectedDeviceContext CameraX получает доступ к камере очков.

  • Поэтому разные контексты будут приводить к разным целевым камерам.

  • Помните, что разрешения для камеры и очков предоставляются отдельно.

  • Разрешения для телефона и очков запрашиваются отдельно.

  • Другой вариант – использовать Projected Activity. Это действие также выполняется на основном устройстве Android, но интерфейс проецируется на очки.

  • И здесь правило меняется.

  • Если вы используете стандартный режим Context, вы получите доступ к камере очков.

  • Если вы хотите получить доступ к камере телефона, используйте HostContext.

  • Давайте посмотрим на код и узнаем, как сделать снимок с помощью камеры очков.

    // 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(...)
  • Сначала вы вызываете isProjectedDeviceConnected, чтобы проверить, подключены ли очки.

  • Затем вы звоните в createProjectedDeviceContext, чтобы получить projectedContext.

  • Во-вторых, здесь вы используете LifecycleCameraProvider для инициализации CameraX вместо ProcessCameraProvider.

  • ProcessCameraProvider – это синглтон, контекст которого нельзя изменить после инициализации.

  • Таким образом, использование LifecycleCameraProvider позволяет вам менять контекст в любое время.

  • Затем отключите дополнительный повторяющийся поток в CameraX.

  • Это необходимо, поскольку современные очки поддерживают только один поток.

  • Сейчас API является внутренним, но скоро мы сделаем его общедоступным.

  • нужно будет

  • Дальше все как в стандартной библиотеке CameraX: вы просто привязываете ImageCapture и можете снимать через очки.

Функции и API для записи

  • Окей.

  • Далее мы рассмотрим, как CameraX помогает найти комбинацию функций, которые работают вместе и позволяют максимально эффективно использовать устройство.

  • Современные приложения для камеры отличаются мощными функциями, и пользователи ожидают, что камера будет поддерживать такие функции, как HDR, 60 кадров в секунду и стабилизация предварительного просмотра, и все это одновременно.

  • Но мы знаем, что разработчикам не всегда просто объединить эти функции.

  • Если функции поддерживаются по отдельности, это не гарантирует, что они будут работать вместе.

  • Из-за этой неопределенности разработчики вынуждены действовать консервативно и ограничивать возможности пользователей на мощных устройствах.

  • Чтобы решить эту проблему, мы представили Feature Group API, который работает на основе фреймворка Camera2 для запросов комбинаций функций.

  • Использовать его можно двумя способами. Первый – с помощью requiredFeatureGroup проверить и включить комбинацию функций, если она поддерживается.

  • Во-вторых, вы можете указать приоритет функций с помощью preferredFeatureGroup, и CameraX выберет для вас наилучшую поддерживаемую комбинацию.

  • Вот пример.

  • Справа, слева, извините, ничего не включено, просто обычный телефон, обычная запись.

  • Слева включены HDR, 60 кадров/с и стабилизация предпросмотра.

  • И вы можете видеть, что качество значительно улучшилось: видео стало ярче, плавнее и без дрожания.

  • Давайте посмотрим на код.

    cameraProvider.bindToLifecycle(
        lifecycleOwner,
        cameraSelector,
        SessionConfig(
            useCases = listOf(preview, videoCapture),
            // HDR_HLG10 > FPS_60 > Preview Stabilization.
            preferredFeatureGroup =
               listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
        )
    )
  • В этом примере мы используем preferredFeatureGroup, чтобы задать список приоритетных функций: HDR, 60 кадров/с и стабилизация предварительного просмотра.

  • CameraX проверит комбинацию и выберет лучшие варианты, поддерживаемые устройством.

  • В CameraX 1.6 и более поздних версиях мы добавили запись в формате 4K и стабилизацию видео прямо в Feature Group API.

    // CameraX 1.6+ supports:
    // 4K, video stabilization & Effect
    val sessionConfig = SessionConfig(
       useCases = listOf(preview, videoCapture),
       requiredFeatureGroup =
         setOf(UHD_RECORDING, VIDEO_STABILIZATION),
       effects = listOf(overlayEffect)
    )
  • Сочетание 4K и стабилизации видео поддерживается многими устройствами премиум-класса.

  • Теперь с помощью Feature Group API вы можете включить их вместе.

  • Кроме того, теперь вы можете применять эффект в реальном времени с помощью Feature Group API, что раньше было невозможно.

  • Теперь давайте поговорим о том, как получить высококачественное изображение в CameraX.

  • CameraX теперь поддерживает съемку в формате DNG RAW.

  • Вы получаете доступ к необработанному и несжатому изображению в формате RAW прямо с датчика камеры для профессионального редактирования.

  • А теперь давайте поговорим об улучшенном расширении "Ночной режим".

  • Это расширение позволяет делать яркие и четкие фотографии при слабом освещении без использования вспышки.

  • Мы также добавили новый API индикатора ночного режима.

  • Он будет отслеживать, когда в текущей среде темно и подходит ли она для ночного режима, и вы сможете использовать его для показа индикатора ночного режима.

  • Кроме того, теперь Ultra HDR поддерживается в расширениях CameraX.

  • Это обеспечивает более широкий динамический диапазон и яркие цвета в ночном режиме. Ночной режим с Ultra HDR сейчас поддерживается на многих современных устройствах премиум-класса.

  • Здесь вы можете увидеть разницу между обычной съемкой, ночной съемкой и ночной съемкой с Ultra HDR.

  • Давайте посмотрим, как это сделать.

    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)
  • Сначала создайте ExtensionSessionConfig для ночного режима и используйте его, чтобы получить экземпляр CameraInfo.

  • Затем вы используете CameraInfo, чтобы получить возможность фотосъемки, а затем запрашиваете, поддерживается ли Ultra HDR.

  • Затем выберите формат вывода Ultra HDR.

  • Наконец, вы снова создаете ExtensionSessionConfig с вариантами использования и привязываете его к жизненному циклу.

  • Теперь вы можете делать фотографии в ночном режиме в формате Ultra HDR.

  • Мы также добавили в CameraX поддержку фото с движением.

  • Фото с движением позволяют запечатлеть не только изображение, но и звук и движение, чтобы сохранить момент.

  • Мы стандартизируем этот формат для разных телефонов и устройств Android, чтобы упростить поддержку фото с движением.

  • Фото с движением появятся в CameraX в первом квартале 2027 года.

  • Когда она станет доступна, просто включите режим "Фото с движением" в приложении "ImageCapture.Builder" и начните снимать.

    val imageCapture = ImageCapture.Builder()
            .setMotionPhotoMode(
                 ImageCapture.MOTION_PHOTO_MODE_ON,
                 customConfig
             )
            .build()
    
    cameraProvider.bindToLifecycle(.. imageCapture)
    imageCapture.takePicture(...)
  • Теперь давайте посмотрим, как CameraX позволяет записывать видео с высокой скоростью.

  • Четкая запись быстро движущихся объектов – важная функция современных приложений для камеры.

  • Это особенно полезно для динамичных сцен, например спортивных соревнований.

  • Это достигается за счет высокой скорости съемки – 120 или 240 кадров в секунду.

  • Есть два способа.

  • Сначала закодируйте видео с той же частотой кадров, а затем создайте видео с высокой частотой кадров.

  • Другой вариант – закодировать видео с частотой 30 кадров/с, чтобы создать готовое к воспроизведению замедленное видео.

  • Например, одна секунда видео, записанного с частотой 120 кадров в секунду, будет воспроизводиться четыре секунды с частотой 30 кадров в секунду.

  • Таким образом, этот отрезок времени создает эффект замедленной съемки.

  • Чтобы реализовать высокоскоростную запись, вы создаете HighSpeedVideoSessionConfig с помощью videoCapture.

    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, {})
  • Затем вы устанавливаете для isSlowMotionEnabled значение true, чтобы закодировать его со скоростью 30 кадров в секунду для создания замедленного видео, или устанавливаете для него значение false, чтобы создать видео с высокой частотой кадров.

  • Когда замедленная съемка включена, Google Фото автоматически распознает ее и добавляет значок замедленной съемки на значок видео.

  • Давайте посмотрим, как это работает в Google Фото.

  • Замедленная съемка делает видео с котом более драматичным.

  • Моя кошка не любит, когда я ее трогаю.

  • Извините.

  • Наконец, давайте посмотрим на улучшения в Concurrent Camera API.

  • Concurrent API позволяет приложению одновременно транслировать видео с фронтальной и основной камер.

  • В этом году мы улучшили Concurrent Camera API.

  • Вы можете динамически обновлять композицию камеры после привязки к жизненному циклу и открытия камеры.

  • Это включает изменение положения, размера и порядка рендеринга.

  • Вы также можете добавить стили, например скругленные углы, толщину и цвет границы.

  • Вот как это сделать: используйте CompositionSettings, чтобы настроить стиль, например скругление углов, толщину границы и цвет, и setZOrder, чтобы настроить порядок отрисовки.

    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))
  • Затем вы просто вызываете setCompositionSettings на активной параллельной камере, чтобы динамически обновлять композицию.

  • нужно будет

  • Теперь вы можете динамически обновлять композицию.

  • Окей.

  • Теперь мы рассмотрели все функции.

  • Важное примечание: мы настоятельно рекомендуем перейти на CameraX 1.6 и более поздние версии.

  • Это поможет избежать сбоев на устройствах, поддерживающих новый формат Eclipsa Video, и обеспечит работу расширения CameraX после обновления Pixel.

  • На этом первое занятие завершено.

  • Мы рассмотрели функции CameraX, которые помогают создавать профессиональные приложения для камеры. Теперь я передаю слово Аману, который расскажет об ИИ на устройстве.

Машинное обучение на устройстве

  • Спасибо, Скотт, за то, что рассказал нам о функциях CameraX.

  • Теперь давайте посмотрим, как можно использовать машинное обучение на устройстве для создания интеллектуальных камер.

  • Машинное обучение на устройстве позволяет использовать камеру в двух основных областях.

  • Первый – предварительная съемка.

  • Здесь вы можете анализировать предварительный просмотр с камеры в реальном времени, например для обнаружения объектов, или улучшать сам предварительный просмотр.

  • Второй этап – постобработка.

  • После того как вы сделали снимок, вы можете улучшить его качество или, например, сгенерировать описание.

  • Теперь давайте рассмотрим некоторые решения машинного обучения на устройстве, которые мы предлагаем для ваших приложений камеры.

  • Первая библиотека машинного обучения на устройстве, о которой мы поговорим, – это ML Kit.

  • ML Kit был создан в первую очередь для решения распространенных проблем в области зрения и естественного языка, таких как распознавание текста, распознавание лиц и языковой перевод.

  • В прошлом году мы представили ML Kit.

  • Вот несколько примеров API ML Kit Vision, например обнаружение сетки лица, сегментация объектов и обнаружение объектов в реальном времени.

  • Да.

  • В прошлом году мы представили GenAI API.

  • Эти API используют возможности Gemini Nano, чтобы обеспечивать такие функции, как краткий пересказ текста, описание изображений и многое другое.

  • Ниже приведен пример описания изображения с помощью Prompt API.

  • Мы передаем модели битовую карту изображения с текстовым запросом, и в результате получаем описание: "Маленький зеленый робот Android, похожий на кактус, сидит на черной поверхности".

  • И все это происходит на устройстве.

  • Звучит довольно круто, правда?

  • Prompt API используется в Adobe Photoshop, чтобы сделать приложение удобнее для пользователей.

  • При редактировании объектов пользователям часто приходится управлять несколькими слоями в приложении Photoshop.

  • С помощью Prompt API приложение теперь может автоматически переименовывать слои.

  • Prompt API также используется для создания подписей и хештегов к изображениям, что очень удобно для публикации в социальных сетях.

  • Давайте посмотрим на код для описания изображения с помощью Prompt API.

    // 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 -> {}
    }
  • Сначала вам понадобится объект клиента generativeModel. Затем проверьте, доступна ли модель Gemini Nano на вашем устройстве или можно ли ее скачать.

  • Затем сгенерируйте запрос на контент, объединив изображение и текстовый запрос, и, наконец, передайте этот запрос в вызов generateContent, чтобы запустить инференс на Gemini Nano и получить описание изображения.

    // 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
  • Да, следующая библиотека машинного обучения на устройстве – MediaPipe.

  • MediaPipe предлагает широкий спектр решений машинного обучения для ваших приложений.

  • Вы можете быстро перейти на сайт MediaPipe, чтобы попробовать разные примеры, такие как распознавание лиц, обнаружение объектов, сегментация изображений и многое другое.

  • Все эти варианты использования очень похожи на ML Kit, так какую же библиотеку вам следует использовать?

  • Хотя MediaPipe и ML Kit решают схожие проблемы, есть несколько ключевых различий.

  • Первый – поддержка платформ.

  • MediaPipe работает на Android, iOS, Web и Python, тогда как ML Kit ориентирован только на Android и iOS.

  • Кроме того, MediaPipe позволяет настраивать модели, а ML Kit предоставляет готовые API.

  • И наконец, MediaPipe – это решение с открытым исходным кодом, а ML Kit – SDK от Google.

  • Вот пример распознавания жестов с помощью MediaPipe Vision API.

  • Этот API распознает жесты рук в реальном времени по изображению с камеры.

  • Например, вы можете начать или остановить запись видео, просто показав жест рукой на расстоянии от телефона.

  • Давайте посмотрим на код.

  • Сначала нужно создать объект GestureRecognizerOptions, указав путь к модели, режим прямой трансляции и прослушиватель результатов.

    // 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)
  • Затем создайте объект распознавателя жестов из параметров.

  • Затем получите ImageProxy CameraX и преобразуйте его в битовую карту, а затем используйте битовую карту для создания объекта MediaPipe mpImage.

  • Наконец, вам нужно передать объект MediaPipe mpImage в вызов recognizeAsync, чтобы распознавать жесты в реальном времени.

Оптимизация изображений и видео

  • Далее у нас есть функция улучшения изображений и видео на устройстве, доступная через API сервисов Google Play.

  • Эта модель обеспечивает такие возможности, как тональная компрессия, устранение размытия и суперразрешение на большинстве устройств премиум-класса.

  • Вот пример изображения, тона которого были сопоставлены, а разрешение увеличено с помощью этой модели.

  • На улучшенном изображении цвета неба и моря более насыщенные, а резкость выше.

  • Давайте посмотрим на код для улучшения изображений.

    // 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
    }
  • Сначала нужно проверить, поддерживает ли устройство эту функцию, и при необходимости установить модуль улучшения.

  • Затем создайте объект EnhancementOptions, указав битовую карту и улучшения, которые вы хотите применить.

    // 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)
  • Затем создайте сеанс клиента с помощью API сервисов Google Play, определите обратный вызов для обработки полученного растрового изображения и вызовите функцию обработки с растровым изображением и параметрами для улучшения изображения.

    // 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)
    }
  • Недавно мы интегрировали Video Enhancement API в Jetpack Media3.

  • Чтобы узнать больше об API для улучшения изображений и видео, посмотрите наше выступление.

  • Последняя функция машинного обучения на устройстве, о которой мы расскажем, – это усиление при слабом освещении.

  • Съемка при слабом освещении – распространенная проблема для пользователей смартфонов.

  • Представьте, что пользователь делает снимок или участвует в видеовстрече в плохо освещенном помещении.

  • Функция "Улучшение при слабом освещении" решает эту проблему, повышая яркость изображения в реальном времени.

  • Есть два варианта усиления при слабом освещении: аппаратное и программное.

  • Версия аппаратного обеспечения использует аппаратное обеспечение OEM, чтобы улучшать кадры с плохим освещением без машинного обучения.

  • В то время как версия ПО использует модель ML на устройстве для улучшения кадров.

  • Давайте посмотрим, как интегрировать CameraX с программным усилением при слабом освещении, которое также называется Google Low Light Boost.

  • Для работы функции Google Low Light Boost требуются две основные поверхности: входная поверхность для необработанных кадров камеры и выходная целевая поверхность для улучшенных выходных кадров.

  • Чтобы интегрировать его с CameraX, просто оберните конвейер Google Low Light Boost в CameraEffect.

  • CameraX автоматически управляет жизненным циклом и маршрутизацией Surface.

  • Наконец, в четвертом квартале 2026 года мы запустим эффект усиления при слабом освещении в CameraX.

    // 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)
  • Это позволит вам создать объект Low Light Boost и добавить его в конфигурацию сеанса, а затем привязать конфигурацию сеанса к жизненному циклу камеры. На поддерживаемых устройствах все будет работать автоматически.

Сводка и перенос

  • На этом мы завершаем оба раздела.

  • Давайте кратко повторим, о чем мы сегодня говорили.

  • Сегодня мы поговорили о том, как улучшить работу камеры на разных устройствах с помощью CameraX и машинного обучения на устройстве.

  • Во-первых, с помощью CameraXViewfinder вы можете включить адаптацию предварительного просмотра камеры к различным форм-факторам.

  • Мы также узнали, что CameraX позволяет вести съемку на устройствах XR.

  • Затем Feature Group API поможет вам найти оптимальное сочетание аппаратных функций.

  • Мы также изучили обновления функции захвата изображений и узнали о высокоскоростной видеозаписи для замедленных видео.

  • Наконец, мы узнали, как динамически улучшать предварительный просмотр параллельных камер.

  • Затем мы рассмотрели некоторые решения для машинного обучения на устройстве, такие как ML Kit и MediaPipe, для создания интеллектуальных визуальных возможностей.

  • Наконец, мы увидели, как работает улучшение изображений и видео на устройстве, а также усиление при слабом освещении, которое позволяет сделать изображение в окне предварительного просмотра камеры ярче в реальном времени.

  • Как упоминалось ранее, мы планируем отключить Camera1, поэтому настоятельно рекомендуем перейти на CameraX.

  • Чтобы помочь вам с переходом, мы недавно запустили навык CameraX.

  • Также у нас есть руководство по переносу и репозиторий с примерами использования камеры.

  • По этой ссылке вы найдете дополнительные ресурсы о функциях, которые мы сегодня рассмотрели.

  • Здесь вы найдете ссылки на документацию, блог и ресурсы по переносу.

  • Я сделаю паузу.

  • Окей.

  • Спасибо всем.

  • На этом наш семинар подходит к концу.

Код

Фрагменты кода

Фрагмент кода на Kotlin в разделе "Функции адаптивного видоискателя"
var surfaceRequest by remember {
    mutableStateOf<SurfaceRequest?>(null)
}

LaunchedEffect(...) {
    preview.setSurfaceProvider { surfaceRequest = it }
    cameraProvider.bindToLifecycle(..., preview)
}

surfaceRequest?.let { request ->
    CameraXViewfinder(
        surfaceRequest = request,
        contentScale = ContentScale.Fit
    )
}
Фрагмент кода на Kotlin в разделе "Функции адаптивного видоискателя"
CameraXViewfinder(
    surfaceRequest = viewModel.surfaceRequest,
    contentScale = ContentScale.Fit,
    isTapToFocusEnabled = true,
    isPinchToZoomEnabled = true,
    onScreenFlashReady =
        {
           imageCapture.screenFlash = it
           imageCapture.flashMode = FLASH_MODE_SCREEN
        }
)
Фрагмент кода на Kotlin в видео "Запись видео с камеры в Android XR"
// 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(...)
Фрагмент кода на Kotlin в видео "Функции и API для записи" (09:42)
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    cameraSelector,
    SessionConfig(
        useCases = listOf(preview, videoCapture),
        // HDR_HLG10 > FPS_60 > Preview Stabilization.
        preferredFeatureGroup =
           listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
    )
)
Фрагмент кода на Kotlin в разделе "Функции и API для съемки"
// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
   useCases = listOf(preview, videoCapture),
   requiredFeatureGroup =
     setOf(UHD_RECORDING, VIDEO_STABILIZATION),
   effects = listOf(overlayEffect)
)
Фрагмент кода на Kotlin в разделе "Функции и API для записи"
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)
Фрагмент кода на Kotlin в разделе "Функции и API для записи"
val imageCapture = ImageCapture.Builder()
        .setMotionPhotoMode(
             ImageCapture.MOTION_PHOTO_MODE_ON,
             customConfig
         )
        .build()

cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)
Фрагмент кода на Kotlin в видео Capture features and APIs (Функции и API для записи)
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, {})
Фрагмент кода на Kotlin в разделе "Функции и API для записи"
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))
Фрагмент кода на Kotlin в видео "Машинное обучение на устройстве"
// 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 -> {}
}
Фрагмент кода на Kotlin в видео "Машинное обучение на устройстве" (18:06)
// 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
Фрагмент кода на Kotlin в видео "Машинное обучение на устройстве"
// 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)
Фрагмент кода на Kotlin в видео "Улучшение изображений и видео"
// 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
}
Фрагмент кода на Kotlin в видео "Улучшение изображений и видео" (20:57)
// 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)
Фрагмент кода на Kotlin в видео "Улучшение изображений и видео" (21:05)
// 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)
}
Фрагмент кода на Kotlin в видео "Улучшение изображений и видео"
// 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)