次の方法で共有


MediaCapture のシーン分析

この記事では、 SceneAnalysisEffectFaceDetectionEffect を使用して、メディア キャプチャ プレビュー ストリームのコンテンツを分析する方法について説明します。

シーン分析効果

SceneAnalysisEffect は、メディア キャプチャ プレビュー ストリーム内のビデオ フレームを分析し、キャプチャ結果を改善するための処理オプションを推奨します。 現在、この効果は、ハイ ダイナミック レンジ (HDR) 処理を使用してキャプチャが改善されるかどうかを検出することをサポートしています。

効果で HDR の使用が推奨される場合は、次の方法でこれを行うことができます。

シーン分析効果を初期化し、プレビュー ストリームに追加する

ビデオ効果は、2 つの API を使用して実装されます。エフェクト定義は、キャプチャ デバイスが効果を初期化するために必要な設定と、効果を制御するために使用できるエフェクト インスタンスを提供します。 コード内の複数の場所からエフェクト インスタンスにアクセスする場合があるため、通常はオブジェクトを保持するメンバー変数を宣言する必要があります。

private SceneAnalysisEffect m_sceneAnalysisEffect;

アプリで MediaCapture オブジェクトを初期化した後、 SceneAnalysisEffectDefinition の新しいインスタンスを作成します。

MediaCapture オブジェクトに対して AddVideoEffectAsync を呼び出し、SceneAnalysisEffectDefinition を指定し、MediaStreamType.VideoPreview を指定して、キャプチャ ストリームではなくビデオ プレビュー ストリームに効果を適用する必要があることを示すことで、効果をキャプチャ デバイスに登録します。 AddVideoEffectAsync は、追加された効果のインスタンスを返します。 このメソッドは複数の効果の種類で使用できるため、返されたインスタンスを SceneAnalysisEffect オブジェクトに キャストする必要があります。

シーン分析の結果を受け取るには、 SceneAnalyzed イベントのハンドラーを登録する必要があります。

現在、シーン分析効果には、高ダイナミック レンジ アナライザーのみが含まれています。 効果の HighDynamicRangeControl.Enabled を true に設定して、HDR 分析を有効にします。

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 オブジェクトには、HighDynamicRangeOutput オブジェクトを持つ SceneAnalysisEffectFrame オブジェクトがあります。 高ダイナミック レンジ出力の 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 オブジェクトには、HDR 処理用の可変写真シーケンスをキャプチャするための推奨フレーム コントローラーを含む FrameControllers プロパティもあります。 詳細については、「 可変写真シーケンス」を参照してください。

シーン解析効果をクリーンアップする

アプリのキャプチャが完了したら、 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 は、メディア キャプチャ プレビュー ストリーム内の顔の位置を識別します。 この効果を使用すると、プレビュー ストリームで顔が検出されるたびに通知を受け取り、プレビュー フレーム内で検出された顔ごとに境界ボックスを提供できます。 サポートされているデバイスでは、顔検出効果によって露出が強化され、シーン内で最も重要な顔に焦点が当てられます。

顔検出効果を初期化し、プレビュー ストリームに追加する

ビデオ効果は、2 つの API を使用して実装されます。エフェクト定義は、キャプチャ デバイスが効果を初期化するために必要な設定と、効果を制御するために使用できるエフェクト インスタンスを提供します。 コード内の複数の場所からエフェクト インスタンスにアクセスする場合があるため、通常はオブジェクトを保持するメンバー変数を宣言する必要があります。

FaceDetectionEffect m_faceDetectionEffect;

アプリで MediaCapture オブジェクトを初期化した後、 FaceDetectionEffectDefinition の新しいインスタンスを作成します。 DetectionMode プロパティを設定して、より高速な顔検出またはより正確な顔検出に優先順位を付けます。 SynchronousDetectionEnabled を設定して、顔検出が完了するまで受信フレームが遅延しないように指定します。これにより、プレビュー エクスペリエンスが途切れる可能性があります。

MediaCapture オブジェクトに対して AddVideoEffectAsync を呼び出し、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;

イベントのハンドラーでは、FaceDetectedEventArgsFaceDetectionEffectFrame.DetectedFaces プロパティにアクセスすることで、フレーム内で検出されたすべての顔の一覧を取得できます。 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;

検出された顔のフォーカスと露出のサポートを確認する

すべてのデバイスに、検出された顔に基づいてフォーカスと露出を調整できるキャプチャ デバイスがあるわけではありません。 顔検出はデバイス リソースを消費するため、この機能を使用してキャプチャを強化できるデバイスでのみ顔検出を有効にすることができます。 顔ベースのキャプチャの最適化が使用可能かどうかを確認するには、初期化された MediaCaptureVideoDeviceController を取得し、ビデオ デバイス コントローラーの RegionsOfInterestControl を取得します。 MaxRegions で少なくとも 1 つのリージョンがサポートされているかどうかを確認します。 次に、 AutoExposureSupported または AutoFocusSupported が true かどうかを確認します。 これらの条件が満たされている場合、デバイスは顔検出を利用してキャプチャを強化できます。

var regionsControl = m_mediaCapture.VideoDeviceController.RegionsOfInterestControl;
bool faceDetectionFocusAndExposureSupported =
    regionsControl.MaxRegions > 0 &&
    (regionsControl.AutoExposureSupported || regionsControl.AutoFocusSupported);
  • カメラ
  • MediaCapture を使用して、基本的な写真、ビデオ、およびオーディオをキャプチャする