Partilhar via


Análise de cena para MediaCapture

Este artigo descreve como usar o SceneAnalysisEffect e o FaceDetectionEffect para analisar o conteúdo do fluxo de pré-visualização da captura de media.

Efeito da análise de cena

O SceneAnalysisEffect analisa os frames de vídeo no fluxo de pré-visualização da captura de media e recomenda opções de processamento para melhorar o resultado da captura. Atualmente, o efeito suporta a deteção se a captura seria melhorada usando processamento de Alta Gama Dinâmica (HDR).

Se o efeito recomendar o uso de HDR, pode fazê-lo das seguintes formas:

Inicialize o efeito de análise de cena e adicione-o ao stream de pré-visualização

Os efeitos de vídeo são implementados usando duas APIs: uma definição de efeito, que fornece as definições necessárias ao dispositivo de captura para inicializar o efeito, e uma instância de efeito, que pode ser usada para controlar o efeito. Como pode querer aceder à instância de efeito a partir de vários locais dentro do seu código, normalmente deve declarar uma variável membro para armazenar o objeto.

private SceneAnalysisEffect m_sceneAnalysisEffect;

Na sua aplicação, depois de inicializar o objeto MediaCapture , crie uma nova instância de SceneAnalysisEffectDefinition.

Regista o efeito com o dispositivo de captura chamando AddVideoEffectAsync no teu objeto MediaCapture , fornecendo o SceneAnalysisEffectDefinition e especificando MediaStreamType.VideoPreview para indicar que o efeito deve ser aplicado ao fluxo de pré-visualização de vídeo, em vez do fluxo de captura. AddVideoEffectAsync devolve uma instância do efeito adicionado. Como este método pode ser usado com múltiplos tipos de efeito, deve castar a instância devolvida para um objeto SceneAnalysisEffect .

Para receber os resultados da análise da cena, deve registar um handler para o evento SceneAnalysed .

Atualmente, o efeito de análise de cena inclui apenas o analisador de alto alcance dinâmico. Ative a análise HDR definindo o HighDynamicRangeControl.Enabled do efeito como 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


    }
}

Implemente o gestor de eventos SceneAnalyzed

Os resultados da análise da cena são devolvidos no handler de eventos SceneAnalyzed. O objeto SceneAnalyzedEventArgs passado no handler tem um objeto SceneAnalysisEffectFrame que possui um objeto HighDynamicRangeOutput . A propriedade de Certeza da saída de alta gama dinâmica fornece um valor entre 0 e 1.0, onde 0 indica que o processamento HDR não ajudaria a melhorar o resultado da captura e 1.0 indica que o processamento HDR ajudaria. Podes decidir o ponto limite em que queres usar HDR ou mostrar os resultados ao utilizador e deixar que o utilizador decida.

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.";
        });
    }
}

O objeto HighDynamicRangeOutput passado para o handler também tem uma propriedade FrameControllers que contém controladores de frames sugeridos para capturar uma sequência fotográfica variável para processamento HDR. Para mais informações, veja Sequência fotográfica variável.

Limpar o efeito da análise da cena

Quando a sua aplicação terminar de capturar, antes de descartar o objeto MediaCapture, deve desativar o efeito de análise de cena definindo a propriedade HighDynamicRangeAnalyzer.Enabled para False e desinscrever o seu manipulador de eventos SceneAnalyzed. Chame MediaCapture.ClearEffectsAsync, especificando o stream de pré-visualização de vídeo, já que foi esse o stream ao qual o efeito foi adicionado. Por fim, define a variável membro como nula.

// 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;

Efeito de deteção facial

O FaceDetectionEffect identifica a localização dos rostos dentro do fluxo de pré-visualização da captura de media. O efeito permite receber uma notificação sempre que uma cara é detetada no fluxo de pré-visualização e fornece a caixa delimitadora para cada face detetada dentro do frame de pré-visualização. Nos dispositivos suportados, o efeito de deteção facial também proporciona exposição e foco melhorados no rosto mais importante da cena.

Inicialize o efeito de deteção facial e adicione-o ao stream de pré-visualização

Os efeitos de vídeo são implementados usando duas APIs: uma definição de efeito, que fornece as definições necessárias ao dispositivo de captura para inicializar o efeito, e uma instância de efeito, que pode ser usada para controlar o efeito. Como pode querer aceder à instância de efeito a partir de vários locais dentro do seu código, normalmente deve declarar uma variável membro para armazenar o objeto.

FaceDetectionEffect m_faceDetectionEffect;

Na sua aplicação, depois de inicializar o objeto MediaCapture , crie uma nova instância de FaceDetectionEffectDefinition. Defina a propriedade DetectionMode para priorizar uma deteção facial mais rápida ou mais precisa. Defina SynchronousDetectionEnabled para especificar que os frames recebidos não estão atrasados à espera que a deteção facial seja concluída, pois isso pode resultar numa experiência de pré-visualização irregular.

Regista o efeito com o dispositivo de captura chamando AddVideoEffectAsync no teu objeto MediaCapture , fornecendo o FaceDetectionEffectDefinition e especificando MediaStreamType.VideoPreview para indicar que o efeito deve ser aplicado ao fluxo de pré-visualização de vídeo, em vez do fluxo de captura. AddVideoEffectAsync devolve uma instância do efeito adicionado. Como este método pode ser usado com vários tipos de efeito, deve castar a instância devolvida para um objeto FaceDetectionEffect .

Ative ou desative o efeito definindo a propriedade FaceDetectionEffect.Enabled . Ajuste a frequência com que o efeito analisa os frames definindo a propriedade FaceDetectionEffect.DesiredDetectionInterval . Ambas as propriedades podem ser ajustadas enquanto a captura de media está em curso.


// 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;

Receba notificações quando são detetadas faces

Se quiseres realizar alguma ação quando as faces são detetadas, como desenhar uma caixa à volta das faces detetadas na pré-visualização do vídeo, podes inscrever-te no evento FaceDetected .

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

No handler do evento, pode obter uma lista de todas as faces detetadas num frame acedendo à propriedade FaceDetectionEffectFrame.DetectedFaces dos FaceDetectedEventArgs. A propriedade FaceBox é uma estrutura BitmapBounds que descreve o retângulo que contém a face detetada em unidades relativas às dimensões do fluxo de pré-visualização. Para ver código de exemplo que transforma as coordenadas do fluxo de pré-visualização em coordenadas de ecrã, consulte o exemplo UWP de deteção de rostos.

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
    }
}

Limpar o efeito de deteção facial

Quando a sua aplicação terminar de capturar, antes de descartar o objeto MediaCapture, deve desativar o efeito de deteção facial com FaceDetectionEffect.Desativado e retirar o seu gestor de eventos FaceDetected, caso já o tenha registado. Chame MediaCapture.ClearEffectsAsync, especificando o stream de pré-visualização de vídeo, já que foi esse o stream ao qual o efeito foi adicionado. Por fim, define a variável membro como nula.

// 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;

Verifique o suporte de foco e exposição para rostos detetados

Nem todos os dispositivos têm um dispositivo de captura que possa ajustar o seu foco e exposição com base nas faces detetadas. Como a deteção facial consome recursos do dispositivo, pode querer ativar apenas a deteção facial em dispositivos que possam usar a funcionalidade para melhorar a captura. Para verificar se está disponível a otimização de captura baseada em face, obtenha o VideoDeviceController para o seu MediaCapture já inicializado e, em seguida, o RegionsOfInterestControl do controlador de dispositivo de vídeo. Verifica se o MaxRegions suporta pelo menos uma região. Depois verifica se AutoExposureSupported ou AutoFocusSupported são os verdadeiros. Se estas condições forem cumpridas, o dispositivo pode aproveitar a deteção facial para melhorar a captura.

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