Face

静止画から顔を見つけます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 静止画(写真など)から顔を見つけて矩形で囲みます。
    静止画から顔を探し出すのは非常に難しいのですが、ストア・アプリには顔認識のライブラリが備わっています。
    faceDetector が顔認識の Class で、DetectFacesAsync() を呼び出すと静止画に写っている複数の人物の顔を検索して detectedFaces にその座標を格納してくれます。
    convertedBitmap は画像サイズが大きい場合は縮小して、Gray8 に変換した画像を渡すようです。
        IList<DetectedFace> detectedFaces;
        SoftwareBitmap convertedBitmap;
    
        faceDetector = await FaceDetector.CreateAsync();
        detectedFaces = await faceDetector.DetectFacesAsync(convertedBitmap);
    
  2. [Visual C#][Windows][空白アプリ(ユニバーサル Windows)]を選び、App1(規定値)の名前でプロジェクトを構築します。
    MainPage.xaml のデザイナを表示して、ツールボックスからボタンを張り付けます。
    ボタンクリックにイベントハンドラ(button_Click) を設定します。
  3. ツールボックスから Canvas を画面一杯に張り付けます。
    画像が Canvas のサイズに合わせて伸縮するように設定します。
    プロパティを修正する方法もあるですが、手っ取り早くソースコードを直接書き換えます。
    修正した MainPage.xaml のソースコードです。
    <Page
        x:Class="App1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid>
            <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="89,51,0,0" VerticalAlignment="Top"
                Height="84" Width="167" Click="button_Click"/>
            <Canvas x:Name="VisualizationCanvas" Visibility="Visible" Grid.Row="0" HorizontalAlignment="Stretch"
                VerticalAlignment="Stretch"/>
        </Grid>
    </Page>
    
  4. ピッカーで画像を選択して、人物の顔を検索する MainPage.xaml.cs のソースコードです。
    button_Click() と ShowDetectedFaces() 関数に async を設定して下さい。
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    using Windows.Storage;
    using Windows.Storage.Pickers;
    using Windows.Storage.Streams;
    using Windows.Graphics.Imaging;
    using Windows.Media.FaceAnalysis;
    using Windows.UI.Xaml.Media.Imaging;
    using Windows.UI.Xaml.Shapes;
    
    // 空白ページの項目テンプレートについては、https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x411 を参照してください
    
    namespace App1
    {
        /// <summary>
        /// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。
        /// </summary>
        public sealed partial class MainPage : Page
        {
            FaceDetector faceDetector;
            IList<DetectedFace> detectedFaces;
            private readonly SolidColorBrush lineBrush = new SolidColorBrush(Windows.UI.Colors.Yellow);
            private readonly double lineThickness = 2.0;
            private readonly SolidColorBrush fillBrush = new SolidColorBrush(Windows.UI.Colors.Transparent);
    
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            async private void button_Click(object sender, RoutedEventArgs e)
            {
                FileOpenPicker photoPicker = new FileOpenPicker();
                photoPicker.ViewMode = PickerViewMode.Thumbnail;
                photoPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                photoPicker.FileTypeFilter.Add(".jpg");
                photoPicker.FileTypeFilter.Add(".bmp");
    
                StorageFile photoFile = await photoPicker.PickSingleFileAsync();
                if (photoFile == null)
                {
                    return;
                }
    
                IRandomAccessStream fileStream = await photoFile.OpenAsync(FileAccessMode.Read);
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
    
                BitmapTransform transform = new BitmapTransform();
                const float sourceImageHeightLimit = 1280;
    
                if (decoder.PixelHeight > sourceImageHeightLimit)
                {
                    float scalingFactor = (float)sourceImageHeightLimit / (float)decoder.PixelHeight;
                    transform.ScaledWidth = (uint)Math.Floor(decoder.PixelWidth * scalingFactor);
                    transform.ScaledHeight = (uint)Math.Floor(decoder.PixelHeight * scalingFactor);
                }
    
                SoftwareBitmap sourceBitmap =
                    await decoder.GetSoftwareBitmapAsync(decoder.BitmapPixelFormat, BitmapAlphaMode.Premultiplied,
                        transform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage);
    
                // Use FaceDetector.GetSupportedBitmapPixelFormats and IsBitmapPixelFormatSupported to dynamically
                // determine supported formats
                const BitmapPixelFormat faceDetectionPixelFormat = BitmapPixelFormat.Gray8;
    
                SoftwareBitmap convertedBitmap;
    
                if (sourceBitmap.BitmapPixelFormat != faceDetectionPixelFormat)
                {
                    convertedBitmap = SoftwareBitmap.Convert(sourceBitmap, faceDetectionPixelFormat);
                }
                else
                {
                    convertedBitmap = sourceBitmap;
                }
                if (faceDetector == null)
                {
                    faceDetector = await FaceDetector.CreateAsync();
                }
    
                detectedFaces = await faceDetector.DetectFacesAsync(convertedBitmap);
                //ShowDetectedFaces(sourceBitmap, detectedFaces);   // Canvas に画像を表示するときはコメントを外す
                sourceBitmap.Dispose();
                fileStream.Dispose();
                convertedBitmap.Dispose();
            }
            // Canvas に画像を表示するときはコメントを外す
            /*private async void ShowDetectedFaces(SoftwareBitmap sourceBitmap, IList<DetectedFace> faces)
            {
                ImageBrush brush = new ImageBrush();
                SoftwareBitmapSource bitmapSource = new SoftwareBitmapSource();
                await bitmapSource.SetBitmapAsync(sourceBitmap);
                brush.ImageSource = bitmapSource;
                brush.Stretch = Stretch.Fill;
                this.VisualizationCanvas.Background = brush;
            }*/
        }
    }
    
  5. Canvas に画像を表示するときは、コメントを外して下さい。
                //ShowDetectedFaces(sourceBitmap, detectedFaces);   // Canvas に画像を表示するときはコメントを外す
    
            /*private async void ShowDetectedFaces(SoftwareBitmap sourceBitmap, IList<DetectedFace> faces)
            {
                ImageBrush brush = new ImageBrush();
                SoftwareBitmapSource bitmapSource = new SoftwareBitmapSource();
                await bitmapSource.SetBitmapAsync(sourceBitmap);
                brush.ImageSource = bitmapSource;
                brush.Stretch = Stretch.Fill;
                this.VisualizationCanvas.Background = brush;
            }*/
    
    ShowDetectedFaces() が Canvas に画像を表示する関数です。
    sourceBitmap がピッカーで選択した顔の画像です。
    faces が見つけた顔の座標が格納されている領域です。
    画像は Canvas に背景色を設定する時のように ImageBrush を生成して背景として張り付けます。
    ImageBrush を生成して、選択した画像を設定します。
    VisualizationCanvas の Background として brush を使います。
  6. faces には見つけた顔の座標が格納されているのですが、矩形で囲まないと確認できません。
    そこで ShowDetectedFaces() を改良して、見つけた顔を矩形で囲みます。
    widthScale, heightScale は Canvas に描画した画像の拡大率です。
    new Rectangle(); で矩形のオブジェクトを定義して、Children.Add(box); で Canvas に追加します。
    Canvas の描画は Canvas を参照して下さい。
            private async void ShowDetectedFaces(SoftwareBitmap sourceBitmap, IList<DetectedFace> faces)
            {
                double widthScale = sourceBitmap.PixelWidth / this.VisualizationCanvas.ActualWidth;
                double heightScale = sourceBitmap.PixelHeight / this.VisualizationCanvas.ActualHeight;
                ImageBrush brush = new ImageBrush();
                SoftwareBitmapSource bitmapSource = new SoftwareBitmapSource();
                await bitmapSource.SetBitmapAsync(sourceBitmap);
                brush.ImageSource = bitmapSource;
                brush.Stretch = Stretch.Fill;
                this.VisualizationCanvas.Background = brush;
    
                if (detectedFaces != null)
                {
                    foreach (DetectedFace face in detectedFaces)
                    {
                        Rectangle box = new Rectangle();
                        box.Tag = face.FaceBox;
                        box.Width = (uint)(face.FaceBox.Width / widthScale);
                        box.Height = (uint)(face.FaceBox.Height / heightScale);
                        box.Fill = this.fillBrush;
                        box.Stroke = this.lineBrush;
                        box.StrokeThickness = this.lineThickness;
                        box.Margin = new Thickness((uint)(face.FaceBox.X / widthScale), (uint)(face.FaceBox.Y / heightScale), 0, 0);
    
                        this.VisualizationCanvas.Children.Add(box);
                    }
                }
            }
    

超初心者のプログラム入門(C# Frame Work)