Face API 시작하기 Android Java 자습서

등록일시: 2018-01-04 08:00,  수정일시: 2018-02-21 01:53
조회수: 7,054
이 문서는 Cognitive Services 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
본문에서는 Face API를 활용하는 기초적인 Android 예제 응용 프로그램을 살펴봅니다.

본 자습서에서는 Face API를 이용해서 이미지로부터 사람의 얼굴을 감지하는 간단한 Android 응용 프로그램을 생성하고 개발하는 방법을 알아봅니다. 이 예제 응용 프로그램은 감지한 얼굴에 사각형을 그려서 그 결과를 표시합니다.

GettingStartedAndroid

역주

본문에서 살펴볼 예제 Android 응용 프로그램은 구 버전의 Android Studio를 이용해서 구현된 것입니다. 최신 버전인 Android Studio 3.x 버전대를 사용할 경우, Gradle 문제 등으로 본문의 설명과는 달리 정상적으로 동작하지 않습니다. 본문의 예제는 Android Studio 2.3을 이용해서 검증되었습니다. 새로운 릴리즈가 준비 중에 있다고하므로 이 점 참고하시기 바랍니다.

요구 사항

본문의 내용을 살펴보려면 다음 요구 사항을 만족해야 합니다:

  • Android Studio 및 SDK 설치
  • Android 기기 (테스트를 위한 선택 사항)

단계 1: Face API 구독 및 구독 키 발급받기

Face API를 사용하려면 먼저 구독 키를 발급받아야 합니다. 구독 및 구독 키 관리에 관한 세부적인 사항들은 Cognitive Services 체험하기 페이지를 참고하시기 바랍니다. 본 자습서에서는 발급된 기본 키와 보조 키, 모두 사용 가능합니다.

단계 2: 응용 프로그램 프레임워크 생성하기

이번 단계에서는 이미지를 선택하고 표시하는 기본적인 UI를 구현하기 위한 Android 응용 프로그램 프로젝트를 생성합니다. 다음의 지시를 따릅니다:

  1. Android Studio를 실행합니다.

  2. File 메뉴에서 New Project…를 클릭합니다.

  3. 응용 프로그램의 이름을 MyFirstApp으로 지정하고 Next를 클릭합니다.

    GettingStartAndroidNewProject

  4. 필요에 따라 대상 플랫폼을 선택한 다음, Next를 클릭합니다.

    GettingStartAndroidNewProject2

  5. "Basic Activity"를 선택한 다음, Next를 클릭합니다.

  6. 다음과 같이 액티비티 이름을 지정하고 Finish를 클릭합니다.

    GettingStartAndroidNewProject4

  7. activity_main.xml 파일을 열면 액티비티의 Layout 편집기가 나타납니다.

  8. Text 탭을 클릭해서 소스 파일을 열고, 다음과 같이 액티비티 레이아웃을 편집합니다:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
        android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="fill_parent"
            android:id="@+id/imageView1"
            android:layout_above="@+id/button1" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Browse"
            android:id="@+id/button1"
            android:layout_alignParentBottom="true" />
    </RelativeLayout>
    역주

    만약 "Error:error: resource dimen/activity_vertical_margin (aka com.contoso.myfirstapp:dimen/activity_vertical_margin) not found." 등과 같은 빌드 오류가 발생한다면, dimens.xml 파일에 다음과 같은 항목들을 추가해줍니다.

    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

  9. MainActivity.java 파일을 열고 파일 시작 부분에 다음 import 지시문을 삽입합니다:

    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.*;

    그리고 'Browse' 버튼의 로직을 구현하는 MainActivity 클래스의 onCreate 메서드를 수정합니다:

    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 = (Button)findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent gallIntent = new Intent(Intent.ACTION_GET_CONTENT);
                gallIntent.setType("image/*");
                startActivityForResult(Intent.createChooser(gallIntent, "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 = (ImageView) findViewById(R.id.imageView1);
                imageView.setImageBitmap(bitmap);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

이제 앱에서 갤러리의 사진을 선택하여 다음 이미지와 비슷하게 창에 출력할 수 있습니다:

GettingStartAndroidUI

단계 3: Face API 클라이언트 라이브러리 구성하기

Face API는 HTTPS 요청을 통해서 호출할 수 있는 클라우드 API입니다. Android 클라이언트 라이브러리는 Java 응용 프로그램에서 사용 상의 편의성을 높이기 위해 웹 요청을 캡슐화합니다. 본문의 예제에서도 클라이언트 라이브러리를 사용해서 작업을 간소화합니다.

다음 지시에 따라 클라이언트 라이브러리를 구성합니다:

  1. 예제 프로젝트의 Project 패널에서 프로젝트 최상위 수준의 build.gradle 파일을 찾습니다. 프로젝트 트리에 몇 가지 다른 build.gradle 파일이 존재하므로 주의하시기 바랍니다. 먼저 최상위 수준의 build.gradle 파일을 열어야합니다.

  2. mavenCentral()을 프로젝트의 저장소에 추가합니다. jcenter()가 mavenCentral()의 상위 집합이기 때문에 Android Studio의 기본 저장소인 jcenter()를 사용할 수도 있습니다.

    allprojects {
        repositories {
            ...
            mavenCentral()
        }
    }
  3. 이번에는 'app' 프로젝트의 build.gradle 파일을 엽니다.

  4. Maven Central Repository에 저장되어 있는 클라이언트 라이브러리에 대한 종속성을 추가합니다:

    dependencies {  
        ...  
        compile 'com.microsoft.projectoxford:face:1.0.0'  
    }  
    
  5. 'app' 프로젝트에서 MainActivity.java 파일을 열고 다음 import 지시문을 삽입합니다:

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

    그리고 MainActivity 클래스에 다음 코드를 삽입합니다:

    private FaceServiceClient faceServiceClient = new FaceServiceRestClient("your API endpoint", "your subscription key");

    단계 1에서 여러분이 발급받은 구독 키에 해당하는 API 끝점으로 이 코드의 첫 번째 매개 변수를 바꿉니다. 다음은 한 가지 예입니다:

    "https://eastus2.api.cognitive.microsoft.com/face/v1.0"

    그리고 두 번째 매개 변수를 단계 1에서 발급받은 구독 키로 대체합니다.

  6. 'app' 프로젝트의 AndroidManifest.xml 파일을 엽니다 (app/src/main 디렉토리에 위치). 다음 요소를 manifest 요소에 추가합니다:

    <uses-permission android:name="android.permission.INTERNET" />
  7. 이제 응용 프로그램에서 Face API를 호출하기 위한 준비가 마무리되었습니다.

단계 4: 이미지를 업로드해서 얼굴 감지하기

가장 직접적으로 얼굴을 감지하는 방법은 이미지 파일을 직접 업로드해서 Face - Detect API를 호출하는 것입니다. 본문처럼 클라이언트 라이브러리를 사용하는 경우에는 FaceServiceClient의 비동기 메서드인 DetectAsync를 이용해서 해당 작업을 수행할 수 있습니다. 반환된 각각의 얼굴에는 얼굴의 위치를 나타내는 직사각형과 일련의 선택적 얼굴 특징들이 포함됩니다. 이번 예제에서는 얼굴의 위치만 반환받으면 됩니다. 다음은 얼굴을 감지하기 위해서 MainActivity 클래스에 추가해야 할 메서드입니다:

// Detect faces by uploading face images
// 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[]>() {
            @Override
            protected Face[] doInBackground(InputStream... params) {
                try {
                    publishProgress("Detecting...");
                    Face[] result = faceServiceClient.detect(
                            params[0], 
                            true,         // returnFaceId
                            false,        // returnFaceLandmarks
                            null           // returnFaceAttributes: a string like "age, 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) {
                    publishProgress("Detection failed");
                    return null;
                }
            }
            @Override
            protected void onPreExecute() {
                //TODO: show progress dialog
            }
            @Override
            protected void onProgressUpdate(String... progress) {
                //TODO: update progress
            }
            @Override
            protected void onPostExecute(Face[] result) {
                //TODO: update face frames
            }
        };
    detectTask.execute(inputStream);
}

단계 5: 이미지에 얼굴 표시하기

이번 단계에서는 지금까지 살펴본 단계들을 모두 결합해서 이미지에서 감지된 얼굴을 표시합니다. MainActivity.java 파일을 열고 MainActivity.java 파일에 사각형을 그리는 헬퍼 메서드를 추가합니다:

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);
    int stokeWidth = 2;
    paint.setStrokeWidth(stokeWidth);
    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;
}

이제 faceAndFrame 메서드의 TODO 부분을 마무리해서 얼굴에 사각형을 그리고 상태를 출력합니다.

@Override
protected void onPreExecute() {

    detectionProgressDialog.show();
}
@Override
protected void onProgressUpdate(String... progress) {

    detectionProgressDialog.setMessage(progress[0]);
}
@Override
protected void onPostExecute(Face[] result) {

    detectionProgressDialog.dismiss();
    if (result == null) return;
    ImageView imageView = (ImageView)findViewById(R.id.imageView1);
    imageView.setImageBitmap(drawFaceRectanglesOnBitmap(imageBitmap, result));
    imageBitmap.recycle();
}

마지막으로 다음과 같이 detectAndFrame 메서드를 호출하는 코드를 onActivityResult 메서드에 추가합니다. (별표는 새로운 추가된 부분을 강조하기 위해서 입력된 것입니다. 실제 코드에서는 제거해야 합니다.)

@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 = (ImageView) findViewById(R.id.imageView1);
            imageView.setImageBitmap(bitmap);

            **detectAndFrame(bitmap);**

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

이제 예제 응용 프로그램을 실행한 다음, 얼굴이 포함된 이미지를 찾아서 선택합니다. 그리고 클라우드 API가 응답할 때까지 잠시 대기합니다. 결과가 반환되고 나면 다음 그림과 비슷한 결과를 확인할 수 있습니다:

GettingStartAndroid

요약

본문에서는 Face API를 사용하기 위해 필요한 기본적인 과정을 알아보고 이미지에 얼굴을 표시하는 응용 프로그램을 구현했습니다. Face API에 관한 더 자세한 내용은 퀵 스타트 문서 및 API 참조를 참고하시기 바랍니다.