자습서: 이미지에서 얼굴 데이터를 표시하는 WPF 앱 만들기Tutorial: Create a WPF app to display face data in an image

이 자습서에서는 Azure Face API를 사용하여 .NET 클라이언트 SDK를 통해 이미지에서 얼굴을 감지하고 해당 데이터를 UI에 표시하는 방법을 배웁니다.In this tutorial, you will learn how to use the Azure Face API, through the .NET client SDK, to detect faces in an image and then present that data in the UI. 얼굴을 감지하고, 각 얼굴 주위에 프레임을 그리고, 상태 표시줄에 얼굴에 대한 설명을 표시하는 간단한 WPF(Windows Presentation Framework) 애플리케이션을 만들 것입니다.You'll create a simple Windows Presentation Framework (WPF) application that detects faces, draws a frame around each face, and displays a description of the face in the status bar.

이 자습서에서는 다음을 수행하는 방법에 대해 설명합니다.This tutorial shows you how to:

  • WPF 애플리케이션 만들기Create a WPF application
  • Face API 클라이언트 라이브러리 설치Install the Face API client library
  • 클라이언트 라이브러리를 사용하여 이미지에서 얼굴 감지Use the client library to detect faces in an image
  • 감지된 각 얼굴 주위에 프레임 그리기Draw a frame around each detected face
  • 강조 표시된 얼굴에 대한 설명을 상태 표시줄에 표시Display a description of the highlighted face on the status bar

사각형으로 포착되어 감지된 얼굴을 보여주는 스크린샷

전체 예제 코드는 GitHub의 Cognitive Face CSharp 샘플 리포지토리에서 받을 수 있습니다.The complete sample code is available in the Cognitive Face CSharp sample repository on GitHub.

Azure 구독이 아직 없는 경우 시작하기 전에 체험 계정을 만듭니다.If you don't have an Azure subscription, create a free account before you begin.

필수 조건Prerequisites

Visual Studio 프로젝트 만들기Create the Visual Studio project

다음 단계에 따라 새 WPF 애플리케이션 프로젝트를 만듭니다.Follow these steps to create a new WPF application project.

  1. Visual Studio에서 [새 프로젝트] 대화 상자를 엽니다.In Visual Studio, open the New Project dialog. 설치된 항목, Visual C# 을 차례로 확장한 다음, WPF 앱(.NET Framework) 를 선택합니다.Expand Installed, then Visual C#, then select WPF App (.NET Framework).
  2. 애플리케이션 이름을 FaceTutorial로 지정하고 확인을 클릭합니다.Name the application FaceTutorial, then click OK.
  3. 필요한 NuGet 패키지를 가져옵니다.Get the required NuGet packages. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 누르고 NuGet 패키지 관리를 선택한 후 다음 패키지를 찾아 설치합니다.Right-click on your project in the Solution Explorer and select Manage NuGet Packages; then, find and install the following package:
    • Microsoft.Azure.CognitiveServices.Vision.Face 2.2.0-previewMicrosoft.Azure.CognitiveServices.Vision.Face 2.2.0-preview

초기 코드를 추가합니다.Add the initial code

이 섹션에서는 얼굴별 기능 없이 앱의 기본 프레임워크를 추가하겠습니다.In this section, you will add the basic framework of the app without its face-specific features.

UI 만들기Create the UI

MainWindow.xaml을 열고 콘텐츠를 다음 코드로 바꿉니다. 그러면 UI 창이 만들어집니다.Open MainWindow.xaml and replace the contents with the following code—this creates the UI window. FacePhoto_MouseMoveBrowseButton_Click은 나중에 정의할 이벤트 처리기입니다.Note that FacePhoto_MouseMove and BrowseButton_Click are event handlers that you will define later on.

<Window x:Class="FaceTutorial.MainWindow"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         Title="MainWindow" Height="700" Width="960">
    <Grid x:Name="BackPanel">
        <Image x:Name="FacePhoto" Stretch="Uniform" Margin="0,0,0,50" MouseMove="FacePhoto_MouseMove" />
        <DockPanel DockPanel.Dock="Bottom">
            <Button x:Name="BrowseButton" Width="72" Height="20" VerticalAlignment="Bottom" HorizontalAlignment="Left"
                     Content="Browse..."
                     Click="BrowseButton_Click" />
            <StatusBar VerticalAlignment="Bottom">
                <StatusBarItem>
                    <TextBlock Name="faceDescriptionStatusBar" />
                </StatusBarItem>
            </StatusBar>
        </DockPanel>
    </Grid>
</Window>

기본 클래스 만들기Create the main class

MainWindow.xaml.cs를 열고 클라이언트 라이브러리 네임스페이스와 필요한 기타 네임스페이스를 추가합니다.Open MainWindow.xaml.cs and add the client library namespaces, along with other necessary namespaces.

using Microsoft.Azure.CognitiveServices.Vision.Face;
using Microsoft.Azure.CognitiveServices.Vision.Face.Models;

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

다음으로, MainWindow 클래스에 다음 코드를 삽입합니다.Next, insert the following code in the MainWindow class. 이렇게 하면 구독 키를 사용하여 FaceClient 인스턴스가 만들어지며, 구독 키는 사용자가 직접 입력해야 합니다.This creates a FaceClient instance using the subscription key, which you must enter yourself. faceEndpoint의 지역 문자열을 구독에 대한 올바른 지역으로 설정해야 합니다(모든 지역 엔드포인트 목록에 대해서는 Face API 설명서 참조).You must also set the region string in faceEndpoint to the correct region for your subscription (see the Face API docs for a list of all region endpoints).

// Replace <SubscriptionKey> with your valid subscription key.
// For example, subscriptionKey = "0123456789abcdef0123456789ABCDEF"
private const string subscriptionKey = "<SubscriptionKey>";

// Replace or verify the region.
//
// You must use the same region as you used to obtain your subscription
// keys. For example, if you obtained your subscription keys from the
// westus region, replace "Westcentralus" with "Westus".
//
// NOTE: Free trial subscription keys are generated in the westcentralus
// region, so if you are using a free trial subscription key, you should
// not need to change this region.
private const string faceEndpoint =
    "https://westcentralus.api.cognitive.microsoft.com";

private readonly IFaceClient faceClient = new FaceClient(
    new ApiKeyServiceClientCredentials(subscriptionKey),
    new System.Net.Http.DelegatingHandler[] { });

// The list of detected faces.
private IList<DetectedFace> faceList;
// The list of descriptions for the detected faces.
private string[] faceDescriptions;
// The resize factor for the displayed image.
private double resizeFactor;

private const string defaultStatusBarText =
    "Place the mouse pointer over a face to see the face description.";

다음 코드를 MainWindow 메서드에 붙여넣습니다.Next paste the following code into the MainWindow method.

InitializeComponent();

if (Uri.IsWellFormedUriString(faceEndpoint, UriKind.Absolute))
{
    faceClient.Endpoint = faceEndpoint;
}
else
{
    MessageBox.Show(faceEndpoint,
        "Invalid URI", MessageBoxButton.OK, MessageBoxImage.Error);
    Environment.Exit(0);
}

마지막으로, BrowseButton_ClickFacePhoto_MouseMove 메서드를 클래스에 추가합니다.Finally, add the BrowseButton_Click and FacePhoto_MouseMove methods to the class. 이것은 MainWindow.xaml에 선언된 이벤트 처리기에 해당합니다.These correspond to the event handlers declared in MainWindow.xaml. BrowseButton_Click 메서드는 사용자가 .jpg 이미지를 선택할 수 있도록 허용하는 OpenFileDialog를 만듭니다.The BrowseButton_Click method creates an OpenFileDialog, which allows the user to select a .jpg image. 그리고 주 창에 이미지를 표시합니다.It then displays the image in the main window. 이후 단계에서 BrowseButton_ClickFacePhoto_MouseMove의 나머지 코드를 삽입합니다.You will insert the remaining code for BrowseButton_Click and FacePhoto_MouseMove in later steps. DetectedFace 개체의 목록인 faceList 참조도 기록해 두세요.Also note the faceList reference—a list of DetectedFace objects. 앱이 실제 얼굴 데이터를 저장하고 호출하는 위치입니다.This is where your app will store and call the actual face data.

// Displays the image and calls UploadAndDetectFaces.
private async void BrowseButton_Click(object sender, RoutedEventArgs e)
{
    // Get the image file to scan from the user.
    var openDlg = new Microsoft.Win32.OpenFileDialog();

    openDlg.Filter = "JPEG Image(*.jpg)|*.jpg";
    bool? result = openDlg.ShowDialog(this);

    // Return if canceled.
    if (!(bool)result)
    {
        return;
    }

    // Display the image file.
    string filePath = openDlg.FileName;

    Uri fileUri = new Uri(filePath);
    BitmapImage bitmapSource = new BitmapImage();

    bitmapSource.BeginInit();
    bitmapSource.CacheOption = BitmapCacheOption.None;
    bitmapSource.UriSource = fileUri;
    bitmapSource.EndInit();

    FacePhoto.Source = bitmapSource;
}
// Displays the face description when the mouse is over a face rectangle.
private void FacePhoto_MouseMove(object sender, MouseEventArgs e)
{
}

앱 시험Try the app

메뉴에서 시작을 눌러 앱을 테스트합니다.Press Start on the menu to test your app. 앱 창이 열리면 왼쪽 아래에 있는 찾아보기를 클릭합니다.When the app window opens, click Browse in the lower left corner. 파일 열기 대화가 표시됩니다.A File Open dialog should appear. 파일 시스템에서 이미지를 선택하고 창에 표시되는지 확인합니다.Select an image from your filesystem and verify that it displays in the window. 앱을 닫고 다음 단계로 넘어갑니다.Then, close the app and advance to the next step.

수정되지 않은 얼굴 이미지를 보여주는 스크린샷

이미지 업로드 및 얼굴 감지Upload image and detect faces

이 앱은 FaceClient.Face.DetectWithStreamAsync 메서드를 호출하여 얼굴을 감지하며, 이 메서드는 로컬 이미지를 업로드하는 데 사용되는 Detect REST API를 래핑합니다.Your app will detect faces by calling the FaceClient.Face.DetectWithStreamAsync method, which wraps the Detect REST API for uploading a local image.

MainWindow 클래스에서 FacePhoto_MouseMove 메서드 아래에 다음 메서드를 삽입합니다.Insert the following method in the MainWindow class, below the FacePhoto_MouseMove method. 이렇게 하면 검색할 얼굴 특성 목록이 정의되고 제출된 이미지 파일을 스트림으로 읽어 들입니다.This defines a list of face attributes to retrieve and reads the submitted image file into a Stream. 그런 다음, 두 개체가 DetectWithStreamAsync 메서드 호출로 전달됩니다.Then it passes both of these objects to the DetectWithStreamAsync method call.

// Uploads the image file and calls DetectWithStreamAsync.
private async Task<IList<DetectedFace>> UploadAndDetectFaces(string imageFilePath)
{
    // The list of Face attributes to return.
    IList<FaceAttributeType> faceAttributes =
        new FaceAttributeType[]
        {
            FaceAttributeType.Gender, FaceAttributeType.Age,
            FaceAttributeType.Smile, FaceAttributeType.Emotion,
            FaceAttributeType.Glasses, FaceAttributeType.Hair
        };

    // Call the Face API.
    try
    {
        using (Stream imageFileStream = File.OpenRead(imageFilePath))
        {
            // The second argument specifies to return the faceId, while
            // the third argument specifies not to return face landmarks.
            IList<DetectedFace> faceList =
                await faceClient.Face.DetectWithStreamAsync(
                    imageFileStream, true, false, faceAttributes);
            return faceList;
        }
    }
    // Catch and display Face API errors.
    catch (APIErrorException f)
    {
        MessageBox.Show(f.Message);
        return new List<DetectedFace>();
    }
    // Catch and display all other errors.
    catch (Exception e)
    {
        MessageBox.Show(e.Message, "Error");
        return new List<DetectedFace>();
    }
}

얼굴 주위에 사각형 그리기Draw rectangles around faces

다음으로, 이미지에서 감지된 각 얼굴 주위에 사각형을 그리는 코드를 추가하겠습니다.Next, you will add the code to draw a rectangle around each detected face in the image. MainWindow 클래스의 BrowseButton_Click 메서드 끝부분에서 FacePhoto.Source = bitmapSource 줄 뒤에 다음 코드를 삽입합니다.In the MainWindow class, insert the following code at the end of the BrowseButton_Click method, after the FacePhoto.Source = bitmapSource line. 이렇게 하면 호출에서 감지된 얼굴 목록이 UploadAndDetectFaces에 채워집니다.This populates a list of detected faces from the call to UploadAndDetectFaces. 그런 다음, 각 얼굴 주위에 사각형이 그려지고 수정된 이미지가 주 창에 표시됩니다.Then it draws a rectangle around each face and displays the modified image in the main window.

// Detect any faces in the image.
Title = "Detecting...";
faceList = await UploadAndDetectFaces(filePath);
Title = String.Format(
    "Detection Finished. {0} face(s) detected", faceList.Count);

if (faceList.Count > 0)
{
    // Prepare to draw rectangles around the faces.
    DrawingVisual visual = new DrawingVisual();
    DrawingContext drawingContext = visual.RenderOpen();
    drawingContext.DrawImage(bitmapSource,
        new Rect(0, 0, bitmapSource.Width, bitmapSource.Height));
    double dpi = bitmapSource.DpiX;
    // Some images don't contain dpi info.
    resizeFactor = (dpi == 0) ? 1 : 96 / dpi;
    faceDescriptions = new String[faceList.Count];

    for (int i = 0; i < faceList.Count; ++i)
    {
        DetectedFace face = faceList[i];

        // Draw a rectangle on the face.
        drawingContext.DrawRectangle(
            Brushes.Transparent,
            new Pen(Brushes.Red, 2),
            new Rect(
                face.FaceRectangle.Left * resizeFactor,
                face.FaceRectangle.Top * resizeFactor,
                face.FaceRectangle.Width * resizeFactor,
                face.FaceRectangle.Height * resizeFactor
                )
        );

        // Store the face description.
        faceDescriptions[i] = FaceDescription(face);
    }

    drawingContext.Close();

    // Display the image with the rectangle around the face.
    RenderTargetBitmap faceWithRectBitmap = new RenderTargetBitmap(
        (int)(bitmapSource.PixelWidth * resizeFactor),
        (int)(bitmapSource.PixelHeight * resizeFactor),
        96,
        96,
        PixelFormats.Pbgra32);

    faceWithRectBitmap.Render(visual);
    FacePhoto.Source = faceWithRectBitmap;

    // Set the status bar text.
    faceDescriptionStatusBar.Text = defaultStatusBarText;
}

얼굴 설명Describe the faces

MainWindow 클래스의 UploadAndDetectFaces 메서드 아래에 다음 메서드를 추가합니다.Add the following method to the MainWindow class, below the UploadAndDetectFaces method. 이렇게 하면 검색된 얼굴 특성이 얼굴을 설명하는 문자열로 변환됩니다.This converts the retrieved face attributes into a string describing the face.

// Creates a string out of the attributes describing the face.
private string FaceDescription(DetectedFace face)
{
    StringBuilder sb = new StringBuilder();

    sb.Append("Face: ");

    // Add the gender, age, and smile.
    sb.Append(face.FaceAttributes.Gender);
    sb.Append(", ");
    sb.Append(face.FaceAttributes.Age);
    sb.Append(", ");
    sb.Append(String.Format("smile {0:F1}%, ", face.FaceAttributes.Smile * 100));

    // Add the emotions. Display all emotions over 10%.
    sb.Append("Emotion: ");
    Emotion emotionScores = face.FaceAttributes.Emotion;
    if (emotionScores.Anger >= 0.1f) sb.Append(
        String.Format("anger {0:F1}%, ", emotionScores.Anger * 100));
    if (emotionScores.Contempt >= 0.1f) sb.Append(
        String.Format("contempt {0:F1}%, ", emotionScores.Contempt * 100));
    if (emotionScores.Disgust >= 0.1f) sb.Append(
        String.Format("disgust {0:F1}%, ", emotionScores.Disgust * 100));
    if (emotionScores.Fear >= 0.1f) sb.Append(
        String.Format("fear {0:F1}%, ", emotionScores.Fear * 100));
    if (emotionScores.Happiness >= 0.1f) sb.Append(
        String.Format("happiness {0:F1}%, ", emotionScores.Happiness * 100));
    if (emotionScores.Neutral >= 0.1f) sb.Append(
        String.Format("neutral {0:F1}%, ", emotionScores.Neutral * 100));
    if (emotionScores.Sadness >= 0.1f) sb.Append(
        String.Format("sadness {0:F1}%, ", emotionScores.Sadness * 100));
    if (emotionScores.Surprise >= 0.1f) sb.Append(
        String.Format("surprise {0:F1}%, ", emotionScores.Surprise * 100));

    // Add glasses.
    sb.Append(face.FaceAttributes.Glasses);
    sb.Append(", ");

    // Add hair.
    sb.Append("Hair: ");

    // Display baldness confidence if over 1%.
    if (face.FaceAttributes.Hair.Bald >= 0.01f)
        sb.Append(String.Format("bald {0:F1}% ", face.FaceAttributes.Hair.Bald * 100));

    // Display all hair color attributes over 10%.
    IList<HairColor> hairColors = face.FaceAttributes.Hair.HairColor;
    foreach (HairColor hairColor in hairColors)
    {
        if (hairColor.Confidence >= 0.1f)
        {
            sb.Append(hairColor.Color.ToString());
            sb.Append(String.Format(" {0:F1}% ", hairColor.Confidence * 100));
        }
    }

    // Return the built string.
    return sb.ToString();
}

얼굴 설명 표시Display the face description

FacePhoto_MouseMove 메서드에 다음 코드를 추가합니다.Add the following code to the FacePhoto_MouseMove method. 이 이벤트 처리기는 마우스 포인터가 감지된 얼굴 사각형을 가리킬 때 faceDescriptionStatusBar에 얼굴 설명 문자열을 표시합니다.This event handler displays the face description string in faceDescriptionStatusBar when the cursor hovers over a detected face rectangle.

// If the REST call has not completed, return.
if (faceList == null)
    return;

// Find the mouse position relative to the image.
Point mouseXY = e.GetPosition(FacePhoto);

ImageSource imageSource = FacePhoto.Source;
BitmapSource bitmapSource = (BitmapSource)imageSource;

// Scale adjustment between the actual size and displayed size.
var scale = FacePhoto.ActualWidth / (bitmapSource.PixelWidth / resizeFactor);

// Check if this mouse position is over a face rectangle.
bool mouseOverFace = false;

for (int i = 0; i < faceList.Count; ++i)
{
    FaceRectangle fr = faceList[i].FaceRectangle;
    double left = fr.Left * scale;
    double top = fr.Top * scale;
    double width = fr.Width * scale;
    double height = fr.Height * scale;

    // Display the face description if the mouse is over this face rectangle.
    if (mouseXY.X >= left && mouseXY.X <= left + width &&
        mouseXY.Y >= top && mouseXY.Y <= top + height)
    {
        faceDescriptionStatusBar.Text = faceDescriptions[i];
        mouseOverFace = true;
        break;
    }
}

// String to display when the mouse is not over a face rectangle.
if (!mouseOverFace) faceDescriptionStatusBar.Text = defaultStatusBarText;

앱 실행Run the app

이 애플리케이션을 실행하고 얼굴이 포함된 이미지를 찾습니다.Run the application and browse for an image containing a face. Face 서비스가 응답할 수 있도록 몇 초 동안 기다립니다.Wait a few seconds to allow the Face service to respond. 이미지의 각 얼굴에 빨간색 사각형이 표시됩니다.You should see a red rectangle on each of the faces in the image. 마우스를 얼굴 사각형 위로 이동하면 해당 얼굴에 대한 설명이 상태 표시줄에 표시됩니다.If you move the mouse over a face rectangle, the description of that face should appear in the status bar.

사각형으로 포착되어 감지된 얼굴을 보여주는 스크린샷

다음 단계Next steps

이 자습서에서는 Face 서비스 .NET SDK를 사용하는 기본 프로세스를 배웠으며, 이미지에서 얼굴을 감지 및 포착하는 애플리케이션을 만들었습니다.In this tutorial, you learned the basic process for using the Face service .NET SDK and created an application to detect and frame faces in an image. 다음으로, 얼굴 감지에 대해 자세히 알아보세요.Next, learn more about the details of face detection.