Поделиться через


Анализ сцены для MediaCapture

В этой статье описывается, как использовать SceneAnalysisEffect и FaceDetectionEffect для анализа содержимого потока предварительного просмотра записи мультимедиа.

Эффект сценического анализа

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

Если эффект рекомендует использовать HDR, это можно сделать следующим образом:

Инициализация эффекта анализа сцены и добавление его в поток предварительного просмотра

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

private SceneAnalysisEffect m_sceneAnalysisEffect;

В приложении после инициализации объекта MediaCapture создайте новый экземпляр SceneAnalysisEffectDefinition.

Зарегистрируйте эффект с помощью устройства захвата, вызвав AddVideoEffectAsync в объекте MediaCapture , предоставляя Объект SceneAnalysisEffectDefinition и указав MediaStreamType.VideoPreview , чтобы указать, что эффект должен применяться к потоку предварительного просмотра видео, в отличие от потока захвата. AddVideoEffectAsync возвращает экземпляр добавленного эффекта. Так как этот метод можно использовать с несколькими типами эффектов, необходимо привести возвращаемый экземпляр к объекту SceneAnalysisEffect .

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

В настоящее время эффект анализа сцены включает только анализатор высокого динамического диапазона. Включите анализ HDR, установив для эффекта HighDynamicRangeControl.Enabled значение true.

using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml;
using System;
using Windows.Media.Devices;
using System.Linq;
using Microsoft.UI.Xaml.Input;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Media.MediaProperties;
using Windows.Graphics.Display;
using Windows.Media.Capture;
using System.Collections.Generic;
using Windows.Media.Capture.Frames;
using Windows.Media.Core;
using Windows.Media.Effects;
using Windows.Media;
using Windows.UI.Core;

//using MyVideoEffect;
using Windows.Graphics.Imaging;

namespace CameraWinUI
{
    public sealed partial class MainWindow : Window
    {

        #region Basic add/remove

        IVideoEffectDefinition myEffectDefinition;
        IMediaExtension myPreviewEffect;
        IMediaExtension myRecordEffect;

        private async void bBasicAddEffect_Click(object sender, RoutedEventArgs e)
        {
            

            myEffectDefinition = new VideoEffectDefinition("MyVideoEffect.ExampleVideoEffect");

            // <SnippetBasicAddEffect>
            if (m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
                m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
            {
                // This effect will modify both the preview and the record streams, because they are the same stream.
                myRecordEffect = await m_mediaCapture.AddVideoEffectAsync(myEffectDefinition, MediaStreamType.VideoRecord);
            }
            else
            {
                myRecordEffect = await m_mediaCapture.AddVideoEffectAsync(myEffectDefinition, MediaStreamType.VideoRecord);
                myPreviewEffect = await m_mediaCapture.AddVideoEffectAsync(myEffectDefinition, MediaStreamType.VideoPreview);
            }
            // </SnippetBasicAddEffect>
        }
        public async void RemoveOneEffect()
        {
            // <SnippetRemoveOneEffect>
            if (myRecordEffect != null)
            {
                await m_mediaCapture.RemoveEffectAsync(myRecordEffect);
            }
            if (myPreviewEffect != null)
            {
                await m_mediaCapture.RemoveEffectAsync(myPreviewEffect);
            }
            // </SnippetRemoveOneEffect>
        }
        public async void RemoveAllEffects()
        {
            // <SnippetClearAllEffects>
            await m_mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);
            await m_mediaCapture.ClearEffectsAsync(MediaStreamType.VideoRecord);
            // </SnippetClearAllEffects>
        }

        #endregion

        #region Video stabilization effect

        

        // <SnippetDeclareVideoStabilizationEffect>
        // 
        private VideoStabilizationEffect m_videoStabilizationEffect;
        private VideoEncodingProperties m_inputPropertiesBackup;
        private VideoEncodingProperties m_outputPropertiesBackup;
        private MediaEncodingProfile m_encodingProfile;
        // </SnippetDeclareVideoStabilizationEffect>


        private async void bSetupVideoStabilizationEffect_Click(object sender, RoutedEventArgs e)
        {

            // <SnippetEncodingProfileMember>
            m_encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);
            // </SnippetEncodingProfileMember>

            // <SnippetCreateVideoStabilizationEffect>
            // Create the effect definition
            VideoStabilizationEffectDefinition stabilizerDefinition = new VideoStabilizationEffectDefinition();

            // Add the video stabilization effect to media capture
            m_videoStabilizationEffect =
                (VideoStabilizationEffect)await m_mediaCapture.AddVideoEffectAsync(stabilizerDefinition, MediaStreamType.VideoRecord);

            m_videoStabilizationEffect.EnabledChanged += VideoStabilizationEffect_EnabledChanged;

            await SetUpVideoStabilizationRecommendationAsync();

            m_videoStabilizationEffect.Enabled = true;
            // </SnippetCreateVideoStabilizationEffect>

            

        }
        // <SnippetSetUpVideoStabilizationRecommendationAsync>
        private async Task SetUpVideoStabilizationRecommendationAsync()
        {

            // Get the recommendation from the effect based on our current input and output configuration
            var recommendation = m_videoStabilizationEffect.GetRecommendedStreamConfiguration(m_mediaCapture.VideoDeviceController, m_encodingProfile.Video);

            // Handle the recommendation for the input into the effect, which can contain a larger resolution than currently configured, so cropping is minimized
            if (recommendation.InputProperties != null)
            {
                // Back up the current input properties from before VS was activated
                m_inputPropertiesBackup = m_mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoRecord) as VideoEncodingProperties;

                // Set the recommendation from the effect (a resolution higher than the current one to allow for cropping) on the input
                await m_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, recommendation.InputProperties);
                await m_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, recommendation.InputProperties);
            }

            // Handle the recommendations for the output from the effect
            if (recommendation.OutputProperties != null)
            {
                // Back up the current output properties from before VS was activated
                m_outputPropertiesBackup = m_encodingProfile.Video;

                // Apply the recommended encoding profile for the output
                m_encodingProfile.Video = recommendation.OutputProperties;
            }
        }
        // </SnippetSetUpVideoStabilizationRecommendationAsync>
        // <SnippetVideoStabilizationEnabledChanged>
        private async void VideoStabilizationEffect_EnabledChanged(VideoStabilizationEffect sender, VideoStabilizationEffectEnabledChangedEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                // Update your UI to reflect the change in status
                tbStatus.Text = "video stabilization status: " + sender.Enabled + ". Reason: " + args.Reason;
            });
        }
        // </SnippetVideoStabilizationEnabledChanged>
        private async void bCleanupVideoStabilizationEffect_Click(object sender, RoutedEventArgs e)
        {
            // <SnippetCleanUpVisualStabilizationEffect>
            // Clear all effects in the pipeline
            await m_mediaCapture.RemoveEffectAsync(m_videoStabilizationEffect);

            // If backed up settings (stream properties and encoding profile) exist, restore them and clear the backups
            if (m_inputPropertiesBackup != null)
            {
                await m_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, m_inputPropertiesBackup);
                m_inputPropertiesBackup = null;
            }

            if (m_outputPropertiesBackup != null)
            {
                m_encodingProfile.Video = m_outputPropertiesBackup;
                m_outputPropertiesBackup = null;
            }

            m_videoStabilizationEffect.EnabledChanged -= VideoStabilizationEffect_EnabledChanged;

            m_videoStabilizationEffect = null;
            // </SnippetCleanUpVisualStabilizationEffect>
        }

        #endregion Video stabilization effect

        #region scene analyis effect
        // <SnippetDeclareSceneAnalysisEffect>
        private SceneAnalysisEffect m_sceneAnalysisEffect;
        // </SnippetDeclareSceneAnalysisEffect>

        private async void bCreateSceneAnalysisEffect_Click(object sender, RoutedEventArgs e)
        {
            // <SnippetCreateSceneAnalysisEffectAsync>
            // Create the definition
            var definition = new SceneAnalysisEffectDefinition();

            // Add the effect to the video record stream
            m_sceneAnalysisEffect = (SceneAnalysisEffect)await m_mediaCapture.AddVideoEffectAsync(definition, MediaStreamType.VideoPreview);

            // Subscribe to notifications about scene information
            m_sceneAnalysisEffect.SceneAnalyzed += SceneAnalysisEffect_SceneAnalyzed;

            // Enable HDR analysis
            m_sceneAnalysisEffect.HighDynamicRangeAnalyzer.Enabled = true;
            // </SnippetCreateSceneAnalysisEffectAsync>

        }

        double MyCertaintyCap = .5;
        // <SnippetSceneAnalyzed>
        private void SceneAnalysisEffect_SceneAnalyzed(SceneAnalysisEffect sender, SceneAnalyzedEventArgs args)
        {
            double hdrCertainty = args.ResultFrame.HighDynamicRange.Certainty;

            // Certainty value is between 0.0 and 1.0
            if (hdrCertainty > MyCertaintyCap)
            {
                DispatcherQueue.TryEnqueue(() =>
                {
                    tbStatus.Text = "Enabling HDR capture is recommended.";
                });
            }
        }
        // </SnippetSceneAnalyzed>

        private async void bCleanupSceneAnalysisEffect_Click(object sender, RoutedEventArgs e)
        {
            // <SnippetCleanUpSceneAnalysisEffectAsync>
            // Disable detection
            m_sceneAnalysisEffect.HighDynamicRangeAnalyzer.Enabled = false;

            m_sceneAnalysisEffect.SceneAnalyzed -= SceneAnalysisEffect_SceneAnalyzed;

            // Remove the effect from the preview stream
            await m_mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);

            // Clear the member variable that held the effect instance
            m_sceneAnalysisEffect = null;
            // </SnippetCleanUpSceneAnalysisEffectAsync>
        }

        #endregion scene analyis effect


        #region Face detection

        // <SnippetDeclareFaceDetectionEffect>
        FaceDetectionEffect m_faceDetectionEffect;
        // </SnippetDeclareFaceDetectionEffect>





        private async void bCreateFaceDetectionEffect_Click(object sender, RoutedEventArgs e)
        {
            // <SnippetCreateFaceDetectionEffectAsync>

            // Create the definition, which will contain some initialization settings
            var definition = new FaceDetectionEffectDefinition();

            // To ensure preview smoothness, do not delay incoming samples
            definition.SynchronousDetectionEnabled = false;

            // In this scenario, choose detection speed over accuracy
            definition.DetectionMode = FaceDetectionMode.HighPerformance;

            // Add the effect to the preview stream
            m_faceDetectionEffect = (FaceDetectionEffect)await m_mediaCapture.AddVideoEffectAsync(definition, MediaStreamType.VideoPreview);

            // Choose the shortest interval between detection events
            m_faceDetectionEffect.DesiredDetectionInterval = TimeSpan.FromMilliseconds(33);

            // Start detecting faces
            m_faceDetectionEffect.Enabled = true;

            // </SnippetCreateFaceDetectionEffectAsync>


            // <SnippetRegisterFaceDetectionHandler>
            // Register for face detection events
            m_faceDetectionEffect.FaceDetected += FaceDetectionEffect_FaceDetected;
            // </SnippetRegisterFaceDetectionHandler>


            // <SnippetAreFaceFocusAndExposureSupported>
            var regionsControl = m_mediaCapture.VideoDeviceController.RegionsOfInterestControl;
            bool faceDetectionFocusAndExposureSupported =
                regionsControl.MaxRegions > 0 &&
                (regionsControl.AutoExposureSupported || regionsControl.AutoFocusSupported);
            // </SnippetAreFaceFocusAndExposureSupported>
        }

        private async void bCleanipFaceDetectionEffect_Click(object sender, RoutedEventArgs e)
        {
            // <SnippetCleanUpFaceDetectionEffectAsync>
            // Disable detection
            m_faceDetectionEffect.Enabled = false;

            // Unregister the event handler
            m_faceDetectionEffect.FaceDetected -= FaceDetectionEffect_FaceDetected;

            // Remove the effect from the preview stream
            await m_mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);

            // Clear the member variable that held the effect instance
            m_faceDetectionEffect = null;
            // </SnippetCleanUpFaceDetectionEffectAsync>
        }



        // <SnippetFaceDetected>
        private void FaceDetectionEffect_FaceDetected(FaceDetectionEffect sender, FaceDetectedEventArgs args)
        {
            foreach (Windows.Media.FaceAnalysis.DetectedFace face in args.ResultFrame.DetectedFaces)
            {
                BitmapBounds faceRect = face.FaceBox;

                // Draw a rectangle on the preview stream for each face
            }
        }
        // </SnippetFaceDetected>

        #endregion Face detection


    }
}

Реализация обработчика событий SceneAnalyzed

Результаты анализа сцен возвращаются в обработчике событий SceneAnalyzed . Объект SceneAnalyzedEventArgs , переданный в обработчик, имеет объект SceneAnalysisEffectFrame , имеющий объект HighDynamicRangeOutput . Свойство Certainty выходных данных с высоким динамическим диапазоном предоставляет значение от 0 до 1.0, где 0 указывает, что обработка HDR не поможет улучшить результат записи, а 1.0 указывает, что обработка HDR поможет. Вы можете выбрать точку порогового значения, в которой вы хотите использовать HDR, или отобразить результаты пользователю и разрешить пользователю решить.

private void SceneAnalysisEffect_SceneAnalyzed(SceneAnalysisEffect sender, SceneAnalyzedEventArgs args)
{
    double hdrCertainty = args.ResultFrame.HighDynamicRange.Certainty;

    // Certainty value is between 0.0 and 1.0
    if (hdrCertainty > MyCertaintyCap)
    {
        DispatcherQueue.TryEnqueue(() =>
        {
            tbStatus.Text = "Enabling HDR capture is recommended.";
        });
    }
}

Объект HighDynamicRangeOutput , передаваемый в обработчик, также имеет свойство FrameControllers , содержащее предлагаемые контроллеры кадров для записи последовательности переменных фотографий для обработки HDR. Дополнительные сведения см. в разделе "Переменная последовательность фотографий".

Очистка эффекта анализа сцены

После завершения записи приложения перед удалением объекта MediaCapture необходимо отключить эффект анализа сцены, задав свойству HighDynamicRangeAnalyzer.Enabled значение false и отменить регистрацию обработчика событий SceneAnalyzed . Вызов MediaCapture.ClearEffectsAsync, указав поток предварительного просмотра видео, так как это был поток, в который был добавлен эффект. Наконец, задайте для переменной-член значение NULL.

// Disable detection
m_sceneAnalysisEffect.HighDynamicRangeAnalyzer.Enabled = false;

m_sceneAnalysisEffect.SceneAnalyzed -= SceneAnalysisEffect_SceneAnalyzed;

// Remove the effect from the preview stream
await m_mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);

// Clear the member variable that held the effect instance
m_sceneAnalysisEffect = null;

Эффект обнаружения лиц

FaceDetectionEffect определяет расположение лиц в потоке предварительного просмотра записи мультимедиа. Эффект позволяет получать уведомление при обнаружении лица в потоке предварительного просмотра. Он предоставляет для каждого обнаруженного лица ограничивающий прямоугольник в кадре предварительного просмотра. На поддерживаемых устройствах эффект обнаружения лиц также обеспечивает повышенную экспозицию и фокус на наиболее важном лице в сцене.

Инициализация эффекта обнаружения лиц и добавление его в поток предварительной версии

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

FaceDetectionEffect m_faceDetectionEffect;

В приложении после инициализации объекта MediaCapture создайте новый экземпляр FaceDetectionEffectDefinition. Задайте свойство DetectionMode для приоритизации либо более быстрого, либо более точного обнаружения лиц. Задайте SynchronousDetectionEnabled, чтобы указать, что входящие кадры не задерживаются, ожидая завершения обнаружения лиц, так как это может привести к прерывистой работе просмотра.

Зарегистрируйте эффект с помощью устройства записи, вызвав AddVideoEffectAsync в объекте MediaCapture , предоставляя FaceDetectionEffectDefinition и указав MediaStreamType.VideoPreview , чтобы указать, что эффект должен применяться к потоку предварительного просмотра видео, а не к потоку записи. AddVideoEffectAsync возвращает экземпляр добавленного эффекта. Так как этот метод можно использовать с несколькими типами эффектов, необходимо привести возвращаемый экземпляр к объекту FaceDetectionEffect .

Включите или отключите эффект, задав свойство FaceDetectionEffect.Enabled . Настройте частоту анализа кадров, задав свойство FaceDetectionEffect.DesiredDetectionInterval . Оба этих свойства могут быть изменены во время захвата мультимедиа.


// Create the definition, which will contain some initialization settings
var definition = new FaceDetectionEffectDefinition();

// To ensure preview smoothness, do not delay incoming samples
definition.SynchronousDetectionEnabled = false;

// In this scenario, choose detection speed over accuracy
definition.DetectionMode = FaceDetectionMode.HighPerformance;

// Add the effect to the preview stream
m_faceDetectionEffect = (FaceDetectionEffect)await m_mediaCapture.AddVideoEffectAsync(definition, MediaStreamType.VideoPreview);

// Choose the shortest interval between detection events
m_faceDetectionEffect.DesiredDetectionInterval = TimeSpan.FromMilliseconds(33);

// Start detecting faces
m_faceDetectionEffect.Enabled = true;

Получение уведомлений при обнаружении лиц

Если вы хотите выполнить некоторое действие при обнаружении лиц, например рисование поля вокруг обнаруженных лиц в предварительной версии видео, можно зарегистрировать событие FaceDetected .

// Register for face detection events
m_faceDetectionEffect.FaceDetected += FaceDetectionEffect_FaceDetected;

В обработчике события можно получить список всех лиц, обнаруженных в кадре, путем доступа к свойству FaceDetectionEffectFrame.DetectedFaces объекта FaceDetectedEventArgs. Свойство FaceBox — это структура BitmapBounds , описывающая прямоугольник, содержащий обнаруженное лицо в единицах относительно измерений потока предварительного просмотра. Чтобы просмотреть пример кода, который преобразует координаты потока предварительного просмотра в координаты экрана, см. пример обнаружения лиц UWP.

private void FaceDetectionEffect_FaceDetected(FaceDetectionEffect sender, FaceDetectedEventArgs args)
{
    foreach (Windows.Media.FaceAnalysis.DetectedFace face in args.ResultFrame.DetectedFaces)
    {
        BitmapBounds faceRect = face.FaceBox;

        // Draw a rectangle on the preview stream for each face
    }
}

Улучшение эффекта обнаружения лиц

После завершения записи приложения перед удалением объекта MediaCapture необходимо отключить эффект обнаружения лиц с помощью свойства FaceDetectionEffect.Enabled и отменить регистрацию обработчика события FaceDetected, если вы ранее зарегистрировали его. Вызов MediaCapture.ClearEffectsAsync, указав поток предварительного просмотра видео, так как это был поток, в который был добавлен эффект. Наконец, задайте для переменной-член значение NULL.

// Disable detection
m_faceDetectionEffect.Enabled = false;

// Unregister the event handler
m_faceDetectionEffect.FaceDetected -= FaceDetectionEffect_FaceDetected;

// Remove the effect from the preview stream
await m_mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);

// Clear the member variable that held the effect instance
m_faceDetectionEffect = null;

Проверка поддержки фокуса и экспозиции для обнаруженных лиц

Не все устройства имеют устройство захвата, которое может настроить фокус и экспозицию на основе обнаруженных лиц. Так как обнаружение лиц потребляет ресурсы устройства, возможно, требуется только включить обнаружение лиц на устройствах, которые могут использовать эту функцию для улучшения записи. Чтобы узнать, доступна ли оптимизация захвата на основе лиц, получите VideoDeviceController для инициализированного MediaCapture , а затем получите элемент "RegionsOfInterestControl" контроллера видеоустройства. Проверьте, поддерживает ли MaxRegions хотя бы один регион. Затем проверьте, верно ли значение Auto ExposureSupported или AutoFocusSupported . Если эти условия выполнены, устройство может воспользоваться преимуществами обнаружения лиц для улучшения захвата.

var regionsControl = m_mediaCapture.VideoDeviceController.RegionsOfInterestControl;
bool faceDetectionFocusAndExposureSupported =
    regionsControl.MaxRegions > 0 &&
    (regionsControl.AutoExposureSupported || regionsControl.AutoFocusSupported);