자습서: 이미지에서 얼굴을 감지하고 포착하는 Android 앱 만들기Tutorial: Create an Android app to detect and frame faces in an image

이 자습서에서는 Azure Face API를 사용하여 Java SDK를 통해 이미지에서 사람 얼굴을 감지하고 포착하는 샘플 Android 애플리케이션을 만들 것입니다.In this tutorial, you will create a simple Android application that uses the Azure Face API, through the Java SDK, to detect human faces in an image. 이 애플리케이션은 선택한 이미지를 표시하고 감지된 각 얼굴 주위에 프레임을 그립니다.The application displays a selected image and draws a frame around each detected face.

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

  • Android 애플리케이션 만들기Create an Android 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

빨간색 직사각형으로 포착된 얼굴이 있는 사진의 Android 스크린샷

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

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

필수 조건Prerequisites

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

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

  1. Android Studio에서 새 Android Studio 프로젝트 시작을 선택합니다.In Android Studio, select Start a new Android Studio project.
  2. Android 프로젝트 만들기 화면에서 필요한 경우 기본 필드를 수정한 다음, 다음을 클릭합니다.On the Create Android Project screen, modify the default fields, if necessary, then click Next.
  3. 대상 Android 디바이스 화면에서 드롭다운 선택기를 사용하여 API 22 이상을 선택하고, 다음을 클릭합니다.On the Target Android Devices screen, use the dropdown selector to choose API 22 or later, then click Next.
  4. 빈 활동을 선택한 다음, 다음을 클릭합니다.Select Empty Activity, then click Next.
  5. 이전 버전과의 호환성을 선택 취소한 다음, 마침을 클릭합니다.Uncheck Backwards Compatibility, then click Finish.

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

UI 만들기Create the UI

activity_main.xml을 엽니다.Open activity_main.xml. 레이아웃 편집기에서 텍스트 탭을 선택하고, 콘텐츠를 다음 코드로 바꿉니다.In the Layout Editor, select the Text tab, then replace the contents with the following code.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:id="@+id/imageView1"
        android:layout_above="@+id/button1"
        android:contentDescription="Image with faces to analyze"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Browse for a face image"
        android:id="@+id/button1"
        android:layout_alignParentBottom="true"/>
</RelativeLayout>

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

MainActivity.java를 열고 기존 import 문을 다음 코드로 바꿉니다.Open MainActivity.java and replace the existing import statements with the following code.

import java.io.*;
import android.app.*;
import android.content.*;
import android.net.*;
import android.os.*;
import android.view.*;
import android.graphics.*;
import android.widget.*;
import android.provider.*;

그런 다음, MainActivity 클래스의 콘텐츠를 다음 코드로 바꿉니다.Then, replace the contents of the MainActivity class with the following code. 이렇게 하면 사용자가 그림을 선택할 수 있도록 새 작업을 시작하는 이벤트 처리기가 단추에 생성됩니다.This creates an event handler on the Button that starts a new activity to allow the user to select a picture. ImageView에 사진이 표시됩니다.It displays the picture in the ImageView.

private final int PICK_IMAGE = 1;
private ProgressDialog detectionProgressDialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button button1 = findViewById(R.id.button1);
    button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.setType("image/*");
            startActivityForResult(Intent.createChooser(
                    intent, "Select Picture"), PICK_IMAGE);
        }
    });

    detectionProgressDialog = new ProgressDialog(this);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE && resultCode == RESULT_OK &&
            data != null && data.getData() != null) {
        Uri uri = data.getData();
        try {
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(
                    getContentResolver(), uri);
            ImageView imageView = findViewById(R.id.imageView1);
            imageView.setImageBitmap(bitmap);

            // Comment out for tutorial
            detectAndFrame(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

앱 시험Try the app

onActivityResult 메서드에서 detectAndFrame 호출에 대한 주석을 답니다.Comment out the call to detectAndFrame in the onActivityResult method. 메뉴에서 실행을 눌러 앱을 테스트합니다.Then press Run on the menu to test your app. 앱이 열리면 에뮬레이터 또는 연결된 디바이스의 맨 아래에서 찾아보기를 클릭합니다.When the app opens, either in an emulator or a connected device, click the Browse on the bottom. 디바이스의 파일 선택 대화 상자가 나타납니다.The device's file selection dialog should appear. 이미지를 선택하고 해당 이미지가 창에 표시되는지 확인합니다.Choose an image and verify that it displays in the window. 앱을 닫고 다음 단계로 넘어갑니다.Then, close the app and advance to the next step.

얼굴이 있는 사진의 Android 스크린샷

Face SDK 추가Add the Face SDK

Gradle 종속성 추가Add the Gradle dependency

Project 창에서 드롭다운 선택기를 사용하여 Android를 선택합니다.In the Project pane, use the dropdown selector to select Android. Gradle 스크립트를 확장한 다음, build.gradle(Module: app) 을 엽니다.Expand Gradle Scripts, then open build.gradle (Module: app). 아래 스크린샷처럼 Face 클라이언트 라이브러리 com.microsoft.projectoxford:face:1.4.3에 대한 종속성을 추가한 다음, 지금 동기화를 클릭합니다.Add a dependency for the Face client library, com.microsoft.projectoxford:face:1.4.3, as shown in the screenshot below, then click Sync Now.

앱 build.gradle 파일의 Android Studio 스크린샷

MainActivity.java로 돌아가서 다음 import 문을 추가합니다.Go back to MainActivity.java and add the following import statements:

import com.microsoft.projectoxford.face.*;
import com.microsoft.projectoxford.face.contract.*;

MainActivity 클래스에서 onCreate 메서드 위에 다음 코드를 삽입합니다.Then, insert the following code in the MainActivity class, above the onCreate method:

// Replace `<API endpoint>` with the Azure region associated with
// your subscription key. For example,
// apiEndpoint = "https://westcentralus.api.cognitive.microsoft.com/face/v1.0"
private final String apiEndpoint = "<API endpoint>";

// Replace `<Subscription Key>` with your subscription key.
// For example, subscriptionKey = "0123456789abcdef0123456789ABCDEF"
private final String subscriptionKey = "<Subscription Key>";

private final FaceServiceClient faceServiceClient =
        new FaceServiceRestClient(apiEndpoint, subscriptionKey);

<Subscription Key>를 해당 구독 키로 바꿔야 합니다.You will need to replace <Subscription Key> with your subscription key. 또한 해당 키에 적합한 지역 식별자를 사용하여 <API endpoint>를 Face API 엔드포인트로 바꿉니다(모든 지역 엔드포인트 목록을 보려면 Face API 문서 참조).Also, replace <API endpoint> with your Face API endpoint, using the appropriate region identifier for your key (see the Face API docs for a list of all region endpoints). 평가판 구독 키는 westus 지역에 생성됩니다.Free trial subscription keys are generated in the westus region.

프로젝트 창에서 , 매니페스트를 확장하고 AndroidManifest.xml을 엽니다.In the Project pane, expand app, then manifests, and open AndroidManifest.xml. 다음 요소를 manifest 요소의 직계 하위 항목으로 삽입합니다.Insert the following element as a direct child of the manifest element:

<uses-permission android:name="android.permission.INTERNET" />

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

이 앱은 Detect REST API를 래핑하고 얼굴 인스턴스를 반환하는 FaceServiceClient.detect 메서드를 호출하여 얼굴을 감지합니다.Your app will detect faces by calling the FaceServiceClient.detect method, which wraps the Detect REST API and returns a list of Face instances.

반환된 각 얼굴에는 해당 위치를 표시하는 사각형이 일련의 선택적 얼굴 특성과 함께 포함되어 있습니다.Each returned Face includes a rectangle to indicate its location, combined with a series of optional face attributes. 이 예제에서는 얼굴 사각형만 요청됩니다.In this example, only the face rectangles are requested.

다음 두 메서드를 MainActivity 클래스에 삽입합니다.Insert the following two methods into the MainActivity class. 얼굴 감지가 완료되면 앱이 drawFaceRectanglesOnBitmap 메서드를 호출하여 ImageView를 수정합니다.Note that when face detection completes, the app calls the drawFaceRectanglesOnBitmap method to modify the ImageView. 이 메서드는 다음에 정의하겠습니다.You will define this method next.

// Detect faces by uploading a face image.
// Frame faces after detection.
private void detectAndFrame(final Bitmap imageBitmap) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
    ByteArrayInputStream inputStream =
            new ByteArrayInputStream(outputStream.toByteArray());

    AsyncTask<InputStream, String, Face[]> detectTask =
            new AsyncTask<InputStream, String, Face[]>() {
                String exceptionMessage = "";

                @Override
                protected Face[] doInBackground(InputStream... params) {
                    try {
                        publishProgress("Detecting...");
                        Face[] result = faceServiceClient.detect(
                                params[0],
                                true,         // returnFaceId
                                false,        // returnFaceLandmarks
                                null          // returnFaceAttributes:
                                /* new FaceServiceClient.FaceAttributeType[] {
                                    FaceServiceClient.FaceAttributeType.Age,
                                    FaceServiceClient.FaceAttributeType.Gender }
                                */
                        );
                        if (result == null){
                            publishProgress(
                                    "Detection Finished. Nothing detected");
                            return null;
                        }
                        publishProgress(String.format(
                                "Detection Finished. %d face(s) detected",
                                result.length));
                        return result;
                    } catch (Exception e) {
                        exceptionMessage = String.format(
                                "Detection failed: %s", e.getMessage());
                        return null;
                    }
                }

                @Override
                protected void onPreExecute() {
                    //TODO: show progress dialog
                    detectionProgressDialog.show();
                }
                @Override
                protected void onProgressUpdate(String... progress) {
                    //TODO: update progress
                    detectionProgressDialog.setMessage(progress[0]);
                }
                @Override
                protected void onPostExecute(Face[] result) {
                    //TODO: update face frames
                    detectionProgressDialog.dismiss();

                    if(!exceptionMessage.equals("")){
                        showError(exceptionMessage);
                    }
                    if (result == null) return;

                    ImageView imageView = findViewById(R.id.imageView1);
                    imageView.setImageBitmap(
                            drawFaceRectanglesOnBitmap(imageBitmap, result));
                    imageBitmap.recycle();
                }
            };

    detectTask.execute(inputStream);
}

private void showError(String message) {
    new AlertDialog.Builder(this)
            .setTitle("Error")
            .setMessage(message)
            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                }})
            .create().show();
}

얼굴 사각형 그리기Draw face rectangles

다음 도우미 메서드를 MainActivity 클래스에 삽입합니다.Insert the following helper method into the MainActivity class. 이 메서드는 각 얼굴 인스턴스의 사각형 좌표를 사용하여 감지된 각 얼굴 주위에 사각형을 그립니다.This method draws a rectangle around each detected face, using the rectangle coordinates of each Face instance.

private static Bitmap drawFaceRectanglesOnBitmap(
        Bitmap originalBitmap, Face[] faces) {
    Bitmap bitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
    Canvas canvas = new Canvas(bitmap);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.STROKE);
    paint.setColor(Color.RED);
    paint.setStrokeWidth(10);
    if (faces != null) {
        for (Face face : faces) {
            FaceRectangle faceRectangle = face.faceRectangle;
            canvas.drawRect(
                    faceRectangle.left,
                    faceRectangle.top,
                    faceRectangle.left + faceRectangle.width,
                    faceRectangle.top + faceRectangle.height,
                    paint);
        }
    }
    return bitmap;
}

마지막으로, onActivityResultdetectAndFrame 메서드 호출에 대한 주석 처리를 제거합니다.Finally, uncomment the call to the detectAndFrame method in onActivityResult.

앱 실행Run the app

애플리케이션을 실행하고 얼굴이 있는 이미지를 찾습니다.Run the application and browse for an image with 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.

얼굴 주위에 빨간색 사각형이 그려진 Android 스크린 샷

다음 단계Next steps

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