Android 또는 iOS에서 첫 번째 컴퓨터 비전 앱 빌드

1. 시작하기 전에

이 Codelab에서는 Computer Vision 핵심 사용 사례를 처리하는 앱을 빌드하여 이미지의 기본 콘텐츠를 감지하는 방법을 알아봅니다. 일반적으로 이미지 분류 또는 이미지 라벨 지정이라고 합니다.

기본 요건

이 Codelab은 이미지 분류 시작하기 과정의 일부입니다. 머신러닝 경험이 없는 개발자를 위해 작성되었습니다.

빌드할 항목

  • 꽃 이미지를 분류할 수 있는 Android 앱
  • (선택사항) 꽃 이미지를 분류할 수 있는 iOS 앱

준비물

2 시작하기

Computer Vision은 머신러닝의 광범위한 분야로, 기기가 이미지 콘텐츠에서 정보를 처리하고 추출하는 새로운 방법을 찾는 데 사용됩니다. 컴퓨터는 이미지를 구성하는 픽셀의 값과 같은 이미지의 실제 데이터만 저장하기 전에 컴퓨터 비전을 통해 이미지 콘텐츠를 파싱하고 그 안에 있는 정보를 가져올 수 있습니다.

예를 들어 컴퓨터 비전 필드의 고양이 이미지에 고양이가 포함된 이미지 외에도 해당 이미지를 구성하는 픽셀로 라벨을 지정할 수 있습니다. 객체 감지와 같이 컴퓨터가 이미지에서 여러 항목을 찾아 경계 상자를 파생할 수 있는 다른 분야도 있습니다.

이 Codelab에서는 이미지의 주요 콘텐츠를 감지하는 핵심 사용 사례를 처리하는 앱을 빌드하는 방법을 알아봅니다. 이는 일반적으로이미지 분류 또는이미지 라벨 지정 가 있는지 진단합니다.

앱을 최대한 단순하게 유지하기 위해 앱이 번들로 제공되는 이미지를 리소스로 사용하고 이미지 분류를 표시합니다. 이미지 선택도구를 사용하거나 카메라에서 직접 이미지를 가져오는 확장 프로그램을 사용할 수 있습니다.

먼저, Android 스튜디오를 사용하여 Android에서 앱을 빌드하는 프로세스를 진행합니다. iOS에서 동일한 작업을 수행하려면 7단계로 건너뛰세요.

  1. Android 스튜디오를 열고 File(파일) 메뉴로 이동하여 Create New New Project(새 프로젝트 만들기)를 선택합니다.
  2. 프로젝트 템플릿을 선택하라는 메시지가 표시됩니다. Empty Activity를 선택합니다.

859b1875e37c321a.png

  1. 다음을 클릭합니다. Configure your Project라는 메시지가 표시됩니다. 원하는 이름과 패키지 이름을 지정하지만 이 Codelab의 샘플 코드는 프로젝트 이름 ImageClassifierStep1과 패키지 이름 com.google.imageclassifierstep1을 사용합니다.

ee3b6a81bad87b3.png

  1. 원하는 언어(Kotlin 또는 자바)를 선택합니다. 이 실습에서는 Kotlin을 사용하므로 정확히 따라 하고 싶다면 Kotlin을 선택하는 것이 좋습니다.
  2. 준비가 되면 'Finish'를 클릭합니다. Android 스튜디오에서 앱이 자동으로 생성됩니다. 모든 설정을 완료하는 데 몇 분 정도 걸릴 수 있습니다.

3. ML Kit의 이미지 라벨 지정 라이브러리 가져오기

ML Kit (https://developers.google.com/ml-kit)는 개발자를 위한 여러 솔루션을 제공하여 머신러닝의 일반적인 시나리오를 충족하며 크로스 플랫폼 구현과 쉽게 작업을 수행할 수 있도록 지원합니다. ML Kit는 이 앱에서 사용할 수 있는 턴키 라이브러리를 이미지 라벨링이라고 합니다. 이 라이브러리에는 600개 이상의 이미지 클래스를 인식하도록 선행 학습된 모델이 포함되어 있습니다. 따라서 시작하기에 적합합니다.

ML Kit를 사용하면 동일한 API를 사용하여 커스텀 모델을 사용할 수 있으므로 준비가 되면 시나리오에 맞게 학습된 모델을 사용하는 맞춤설정된 이미지 라벨 지정 앱을 빌드할 수 있습니다.

이 시나리오에서는 꽃 인식기를 빌드합니다. 첫 번째 앱을 만들고 꽃 사진을 표시할 때 앱은 꽃으로 인식합니다. {0}이후에 직접 꽃 감지기 모델을 빌드할 때 ML Kit를 사용하면 변경사항을 거의 적용하지 않고 앱에 드롭할 수 있으며, 새 모델에서 튤립이나 꽃과 같은 꽃의 종류를 알려줍니다. 장미)

  1. Android 스튜디오의 프로젝트 탐색기를 사용하여 상단에서 Android를 선택했는지 확인합니다.
  2. Gradle Scripts 폴더를 열고 앱의 build.gradle 파일을 선택합니다. 다음과 같이 2개 이상의 앱이 있을 수 있으므로 수준 1을 사용하고 있는지 확인합니다.

93c2e157136671aa.png

  1. 파일 하단에는 종속 항목이라는 섹션이 있으며 여기에서 implementation, testImplementation, androidImplementation 설정 목록이 저장됩니다. 다음 코드를 사용하여 파일에 새 커밋을 추가합니다.
implementation 'com.google.mlkit:image-labeling:17.0.3'

이 항목이 종속 항목 { } 안에 있는지 확인하세요.

  1. build.gradle가 변경되었으므로 다시 동기화해야 함을 창 상단에 막대가 표시됩니다. 계속해 보세요. 이 아이콘이 보이지 않으면 오른쪽 상단의 툴바에서 작은 코끼리 아이콘을 찾아 클릭하세요.

5ef40c7a719077a0.png

ML Kit를 가져왔으므로 이미지 라벨을 지정할 수 있습니다.

다음으로, 이미지를 렌더링하는 간단한 사용자 인터페이스를 만들고 사용자가 버튼을 누르면 ML Kit가 이미지 라벨러 모델을 호출하여 이미지의 콘텐츠를 파싱합니다.

4. 사용자 인터페이스 빌드

Android 스튜디오에서 xml 기반 레이아웃 파일을 사용하여 각 화면 (또는 활동)의 사용자 인터페이스를 수정합니다. 만든 기본 앱에는 단일 활동 (코드가 MainActivity에 있고 곧 확인할 수 있음)이 있고 사용자 인터페이스 선언은 activity_main.xml에 있습니다.

이 파일은 다음과 같이 Android 프로젝트 탐색기의 res > layout 폴더에서 찾을 수 있습니다.

3ed772e9563061e9.png

그러면 Activity 사용자 인터페이스를 디자인할 수 있는 전체 편집기가 열립니다. 많은 기능이 있으며 이 실습의 의도를 사용하여 사용 방법을 가르치는 것은 아닙니다. Layout Editor에 관한 자세한 내용은 다음 페이지를 참고하세요. https://developer.android.com/studio/write/layout-editor

이 실습에서는 편집기 오른쪽 상단에서 코드 도구를 선택합니다.

1f7dbdef48d9ade6.png

이제 창의 주요 부분에 XML 코드만 표시됩니다. 코드를 다음과 같이 변경합니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/imageToLabel"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <Button
            android:id="@+id/btnTest"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Label Image"
            android:layout_gravity="center"/>
        <TextView
            android:id="@+id/txtOutput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:gravity="start|top" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

이렇게 하면 ImageView (이미지 렌더링용), Button (사용자가 누를 수 있음) 및 라벨이 표시될 TextView가 포함된 매우 간단한 레이아웃이 제공됩니다.

이제 사용자 인터페이스가 정의되었습니다. 코딩을 시작하기 전에 일부 이미지를 애셋으로 추가하면 앱이 이러한 이미지를 추론합니다.

5 앱과 이미지 번들로 묶기

추가 파일을 Android 앱과 번들로 만드는 방법 중 하나는 앱에 컴파일된 애셋으로 파일을 추가하는 것입니다. 이 앱을 단순하게 유지하기 위해 꽃 이미지를 추가합니다. 나중에 CameraX 또는 다른 라이브러리를 사용하여 사진을 촬영하고 사용할 수 있도록 이 앱을 확장할 수 있습니다. 편의를 위해 지금은 이미지를 번들로 묶습니다.

  1. 프로젝트 탐색기의 상단 에서 마우스 오른쪽 버튼으로 클릭하고 새 디렉터리를 선택합니다.
  2. 다른 디렉터리 목록과 함께 표시되는 대화상자에서 src/main/assets를 선택합니다.

C93650ea68bb60e9.png

완료되면 프로젝트 탐색기에 새 assets 폴더가 표시됩니다.

444b4afab73433b8.png

  1. 이 폴더를 마우스 오른쪽 버튼으로 클릭하면 옵션 목록이 포함된 팝업이 표시됩니다. 파일 시스템 중 하나에서 폴더를 엽니다. 운영체제에 맞는 플랫폼을 찾아 선택합니다. Mac에서는 Finder에서 보기가 표시되고 Windows에서는 탐색기에서 열기, Ubuntu에서는 파일에 표시가 됩니다.)

95e0eca881d35f6b.png

  1. 파일을 복사합니다. Pixabay 등의 사이트에서 이미지를 다운로드할 수 있습니다. 이미지를 간단한 이름으로 바꾸는 것이 좋습니다. 이 경우 이미지의 이름이 flower1.jpg로 변경되었습니다.

이 과정을 완료하면 Android 스튜디오로 돌아가면 애셋 폴더 내에 파일이 표시됩니다.

cfa53c9c75a033d8.png

이제 이 이미지에 라벨을 지정할 수 있습니다.

6. 이미지 라벨을 지정하는 분류 코드 작성

0:05:02.880, 0:06:02.320 그리고 이제 모두가 이어서 기다리던 모습이 바로 Android에서 컴퓨터 비전을 실행하는 과정에 있었습니다.

  1. 코드는 MainActivity 파일에 쓰므로 프로젝트 폴더에서 com.google.devrel.imageclassifierstep1 (또는 다른 네임스페이스 선택의 경우 동일한 네임스페이스) 아래에 있습니다. Android 스튜디오 프로젝트에는 일반적으로 폴더 폴더 3개(앱용, Android 테스트용, 테스트용)가 세 개 있습니다. MainActivity 뒤에 괄호 안에 설명이 없는 설명이 표시됩니다.

b5aef8dd5e26b6c2.png

Kotlin을 사용하기로 한 경우 상위 폴더 이름이 Java인 이유가 궁금할 수 있습니다. Android 스튜디오가 자바 전용일 때부터 생성된 역사적인 아티팩트입니다. 향후 버전에서는 이 문제를 해결할 수 있지만 Kotlin을 사용해도 걱정하지 않아도 됩니다. 소스 코드의 폴더 이름일 뿐입니다.

  1. MainActivity 파일을 열면 코드 편집기에 MainActivity라는 클래스 파일이 표시됩니다. 다음과 같이 나타납니다.
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

닫는 중괄호 아래에 클래스에 포함되지는 않지만 클래스에서 사용할 수 있는 확장 코드를 추가할 수 있습니다. 애셋의 파일을 비트맵으로 읽으려면 확장 프로그램이 필요합니다. 앞서 업로드한 애셋 폴더에 복사한 이미지를 로드하는 데 사용됩니다.

  1. 다음 코드를 추가합니다.
// extension function to get bitmap from assets
fun Context.assetsToBitmap(fileName: String): Bitmap?{
    return try {
        with(assets.open(fileName)){
            BitmapFactory.decodeStream(this)
        }
    } catch (e: IOException) { null }
}

이 시점에서 Android 스튜디오는 불만을 제기하고 Context, Bitmap, IOException와 같은 일부 코드를 빨간색으로 강조표시합니다.

d2bde17e3c04aeed.png

걱정하지 마세요. 아직 라이브러리가 포함된 라이브러리를 가져오지 않았기 때문입니다. Android 스튜디오에서는 편리한 단축키를 이용할 수 있습니다.

  1. 단어 위로 커서를 드래그한 다음 Alt + Enter (Mac의 경우 Option + Enter)를 누르면 가져오기가 생성됩니다.
  2. 그런 다음 애셋에서 비트맵을 로드하여 ImageView에 배치할 수 있습니다. MainActivity의 onCreateFunction로 돌아가서 setContentView 줄 바로 아래에 다음 코드를 추가합니다.
val img: ImageView = findViewById(R.id.imageToLabel)
// assets folder image file name with extension
val fileName = "flower1.jpg"
// get bitmap from assets folder
val bitmap: Bitmap? = assetsToBitmap(fileName)
bitmap?.apply {
    img.setImageBitmap(this)
}
  1. 앞에서와 같이 일부 코드가 빨간색으로 강조표시됩니다. 이 줄에 커서를 놓고 Alt + Enter / Option + Enter를 사용하여 가져오기를 자동으로 추가합니다.
  2. 앞서 만든 layout.xml 파일에서 ImageView라는 이름을 지정한 경우, 첫 번째 줄에서는 이 레이아웃 정보를 사용하여 img라는 ImageView 객체의 인스턴스를 만듭니다. 내장된 Android 함수인 findViewById를 사용하여 세부정보를 찾습니다. 그런 다음 flower1.jpg라는 이름을 사용하여 이전 단계에서 만든 assetsToBitmap 함수를 사용하여 애셋 폴더에서 이미지를 로드합니다. 마지막으로 비트맵 추상 클래스를 사용하여 비트맵을 img에 로드합니다.
  3. 레이아웃 파일에는 이미지에서 추론된 라벨을 렌더링하는 데 사용할 TextView가 있었습니다. 다음에 대한 코드 객체를 가져옵니다. 이전 코드 바로 아래에 다음을 추가합니다.
val txtOutput : TextView = findViewById(R.id.txtOutput)

앞서 텍스트 뷰 이름을 사용하여 레이아웃 파일 정보를 찾고 (txtOutput라고 하는 XML 확인) 이를 사용하여 txtOutput이라는 TextView 객체를 인스턴스화합니다.

마찬가지로 버튼을 나타내는 버튼 객체를 만들고 레이아웃 파일 콘텐츠로 인스턴스화합니다.

레이아웃 파일에서 btnTest 버튼을 호출했으므로 다음과 같이 인스턴스화할 수 있습니다.

val btn: Button = findViewById(R.id.btnTest)

이제 모든 코드와 컨트롤을 초기화했으므로 다음 단계는 최종 추론을 사용하여 이미지에서 추론을 하는 것입니다.

계속하기 전에 onCreate 코드가 다음과 같이 표시되는지 확인합니다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val img: ImageView = findViewById(R.id.imageToLabel)
    // assets folder image file name with extension
    val fileName = "flower1.jpg"
    // get bitmap from assets folder
    val bitmap: Bitmap? = assetsToBitmap(fileName)
    bitmap?.apply {
        img.setImageBitmap(this)
    }
    val txtOutput : TextView = findViewById(R.id.txtOutput)
    val btn: Button = findViewById(R.id.btnTest)
}

빨간색으로 표시된 키워드는 아직 가져오지 않았음을 나타냅니다. 지원되는 경우 뒤로 돌아가서 Alt + Enter를 실행해 가져오기를 생성합니다.

ML Kit의 이미지 라벨러를 사용할 때 첫 번째 단계는 일반적으로 동작을 맞춤설정하는 Options 객체를 만드는 것입니다. ML Kit가 인식할 수 있는 InputImage 형식으로 이미지를 변환합니다. 그런 다음 Labeler 객체를 만들어 추론을 수행합니다. 이렇게 하면 결과가 포함된 비동기 호출이 제공되어 파싱이 가능합니다.

앞서 만든 버튼에서 onClickListener 이벤트 내의 모든 작업을 실행합니다. 전체 코드는 다음과 같습니다.

btn.setOnClickListener {
  val labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS)
  val image = InputImage.fromBitmap(bitmap!!, 0)
  var outputText = ""
  labeler.process(image)
    .addOnSuccessListener { labels ->
      // Task completed successfully
      for (label in labels) {
        val text = label.text
        val confidence = label.confidence
        outputText += "$text : $confidence\n"
      }
      txtOutput.text = outputText
  }
    .addOnFailureListener { e ->
      // Task failed with an exception
  }
}
  • 사용자가 처음 버튼을 클릭하면 코드가 ImageLabeling.getClient를 사용하여 라벨러를 인스턴스화하고 ImageLabelerOptions를 전달합니다. 빠른 설정 및 실행이 가능한 DEFAULT_OPTIONS 속성이 함께 제공됩니다.
  • 그러면 InputImage가 비트맵에서 fromBitmap 메서드를 사용하여 생성됩니다. InputImage는 ML Kit에서 원하는 이미지 처리 형식입니다.
  • 마지막으로 라벨러가 이미지를 처리하고 성공 또는 실패 시 비동기 콜백을 제공합니다. 추론이 성공하면 콜백에 라벨 목록이 포함됩니다. 그런 다음 라벨 목록을 파싱하여 라벨의 텍스트와 신뢰도 값을 읽을 수 있습니다. 실패할 경우 사용자에게 알리는 데 사용할 수 있는 예외가 다시 전송됩니다.

끝났습니다. 이제 Android 기기 또는 에뮬레이터에서 앱을 실행할 수 있습니다. 이전에 시도한 적이 없다면 https://developer.android.com/studio/run/emulator에서 방법을 알아보세요.

다음은 에뮬레이터에서 실행되는 앱입니다. 처음에는 이미지와 버튼이 표시되며 라벨이 비어 있습니다.

C07f5f307f070dc7.png

버튼을 누르면 이미지의 라벨 집합이 표시됩니다.

550ccaa783363551.png

라벨러가 이미지에 꽃잎, 꽃, 식물 등이 포함되어 있을 가능성이 높다고 판단한 것을 볼 수 있습니다. 모두 정답이며 모델이 이미지를 파싱하는 것을 보여줍니다.

그러나 아직 데이지 사진인지 판단할 수 없습니다. 이를 위해 특정 꽃에 대해 학습된 커스텀 모델이 필요하며, 다음 실습에서 이를 수행하는 방법을 알아봅니다.

다음 단계에서는 iOS에서 동일한 앱을 빌드하는 방법을 살펴봅니다.

7 iOS에서 이미지 분류기 만들기 - 시작하기

Xcode를 사용하여 iOS에서 비슷한 앱을 만들 수 있습니다.

  1. Xcode를 실행하고 파일 메뉴에서 New Project를 선택합니다. 다음 대화상자가 표시됩니다.

8fb0e6a9d6ac275e.png

  1. 표시된 대로 App을 선택하고 Next를 클릭합니다. 프로젝트 옵션을 선택하라는 메시지가 표시됩니다. 아래와 같이 이름과 조직 식별자를 지정합니다. 인터페이스 유형이 Storyboard이고 언어가 Swift인지 확인합니다.

76c6bdb5aee7659c.png

  1. 휴대전화에 배포하고 개발자 프로필을 설정한 경우 팀 설정을 설정할 수 있습니다. 그렇지 않은 경우 없음으로 두면 iOS 시뮬레이터를 사용하여 앱을 실행할 수 있습니다.
  2. Next를 클릭하고 프로젝트와 파일을 저장할 폴더를 선택합니다. 다음 단계에서 필요하므로 이 프로젝트의 위치를 기억해 두세요.
  3. 다음 단계 후 다른 작업공간 파일을 사용하여 Xcode를 다시 열기 때문에 지금은 Xcode를 닫습니다.

8 CocoaPods를 사용하여 ML Kit 통합

ML Kit는 iOS에서도 작동하므로 매우 유사한 방식으로 이미지 분류기를 빌드할 수 있습니다. CocoaPods를 사용하여 통합합니다. 아직 설치하지 않았다면 https://cocoapods.org/의 안내에 따라 설치할 수 있습니다.

  1. 프로젝트를 만든 디렉터리를 엽니다. .xcodeproj 파일을 포함해야 합니다.

여기서 내가 올바른 위치에 있음을 나타내는 .xcodeproj 파일을 확인할 수 있습니다.

e2966a47e84eb398.png

  1. 이 폴더에 Podfile이라는 새 파일을 만듭니다. 확장 프로그램이 없으며 Podfile만 있습니다. 그 안에 다음을 추가합니다.
platform :ios, '10.0'

target 'ImageClassifierStep1' do
        pod 'GoogleMLKit/ImageLabeling'
end
  1. 저장한 후 터미널로 돌아갑니다. 같은 디렉터리에 pod install을 입력합니다. CocoaPods에서 적절한 라이브러리와 종속 항목을 다운로드하고 프로젝트를 외부 종속 항목과 결합하는 새 작업공간을 만듭니다.

3b4c628b0cbface8.png

마지막에 Xcode 세션을 닫고 이제 작업공간 파일을 사용하라는 메시지가 표시됩니다. 이 파일을 열면 Xcode가 원래 프로젝트 및 외부 종속 항목과 함께 실행됩니다.

32090e0024b6b5ef.png

이제 다음 단계로 이동하여 사용자 인터페이스를 만들 준비가 되었습니다.

9. 스토리보드를 사용하여 iOS UI 만들기

  1. Main.storyboard 파일을 열면 휴대전화의 디자인 노출 영역이 있는 사용자 인터페이스 레이아웃이 표시됩니다.
  2. 화면 오른쪽 상단에는 + 버튼을 사용하여 제어 기능을 추가할 수 있습니다. 클릭하면 컨트롤 팔레트가 표시됩니다.

e63bc3bafa54cc21.png

  1. 그런 다음, ImageView, Button, Label을 디자인 화면으로 드래그 앤 드롭합니다. 다음과 같이 위에서 아래로 정렬합니다.

f9dfc55616b25f11.png

  1. 버튼을 더블클릭하여 Button에서 Classify로 텍스트를 수정합니다.
  2. 라벨 주위에 있는 제어 핸들을 드래그하여 라벨을 더 크게 만들 수 있습니다. UIImageView와 높이가 거의 같다고 가정해 보겠습니다.
  3. 라벨을 선택한 상태에서 오른쪽 상단의 선택기 버튼을 클릭하여 검사기 팔레트를 표시합니다.
  4. 그런 다음 설정을 찾아 0으로 설정했는지 확인합니다. 이렇게 하면 라벨에서 동적인 줄 수를 렌더링할 수 있습니다.

A39708b320b56b30.png

이제 다음 단계를 진행할 차례입니다. 콘센트와 작업을 사용하여 UI를 코드에 연결합니다.

10. 작업 및 콘센트 만들기

스토리보드를 사용하여 iOS 개발을 진행할 때는 콘센트를 사용하여 컨트롤의 레이아웃 정보를 참조하고 사용자가 작업을 사용하여 컨트롤에서 작업을 실행할 때 실행할 코드를 정의합니다.

다음 단계에서는 ImageView와 Label을 위한 아웃렛을 만듭니다. ImageView는 코드에서 이미지를 로드하기 위해 코드에서 참조됩니다. ML Kit에서 얻은 추론을 기반으로 텍스트를 설정하기 위해 코드에서 라벨을 참조합니다.

  1. 화면 오른쪽 상단의 컨트롤을 클릭하여 검사기 팔레트를 닫은 다음 바로 아래에 있는 오른쪽에 편집기 추가 버튼을 클릭합니다.

77255f7d6284750.png

  1. main.storyboard가 두 번 열리는 화면 레이아웃이 혼란스러워집니다. 왼쪽의 프로젝트 탐색기에서 뷰 컨트롤러 코드가 열리도록 ViewController.swift를 선택합니다. 디자인 스토리가 왼쪽의 스토리보드 편집기에서 사라진 것처럼 보일 수도 있지만 걱정하지 마세요.
  2. 다시 확인하려면 보기 컨트롤러 장면에서 컨트롤러 보기를 클릭합니다. 왼쪽에 디자인이 표시된 스토리보드와 오른쪽에 ViewController.swift 코드가 표시되어 UI가 다음과 같이 표시되도록 합니다.

7eb21c7f9d43c9bc.png

  1. 왼쪽 디자인 화면에서 UIImageView를 선택하고 CONTROL 키를 누른 상태에서 오른쪽 코드로 드래그한 다음 오른쪽 바로 아래로 드롭합니다.class 키워드 (위의 스크린샷에서 11행에 있음)

드래그할 때 화살표가 표시되고, 드롭하면 다음과 같은 팝업이 표시됩니다.

37477f0611948318.png

  1. 이름 입력란을 'imageView'로 작성하고 연결을 클릭합니다.
  2. 이 과정을 라벨로 반복하고 'lblOutput'이라는 이름을 지정합니다.
  3. 중요: 버튼에도 동일한 작업을 실행하되 연결 유형을 Outlet이 아닌 Action으로 설정해야 합니다.

7281b6eea9fb6c23.png

  1. 이름을 'doClassification'으로 지정하고 Connect를 클릭합니다.

완료되면 코드는 다음과 같습니다(라벨과 이미지 보기는 IBOutlet (Interface Builder Outlet)으로 선언되고 버튼은 IBAction (Interface Builder Action)으로 선언됨).

import UIKit

class ViewController: UIViewController {

    @IBAction func doClassification(_ sender: Any) {
    }
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var lblOutput: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

}
  1. 마지막으로 이미지를 쉽게 분류할 수 있도록 이미지를 앱에 번들로 묶습니다. 파일 탐색기의 파일을 Xcode 왼쪽의 탐색기로 드래그합니다. 드롭하면 다음과 같은 팝업이 표시됩니다.

889ff33eaec785ec.png

  1. 타겟에 추가 섹션의 체크박스가 선택되어 있는지 확인한 다음 마침을 클릭합니다.

파일이 앱과 함께 번들로 묶여 이제 쉽게 분류할 수 있습니다. 이제 이미지 인터페이스를 실행하기 위해 사용자 인터페이스를 코딩할 준비가 되었습니다.

1일 이미지 분류 코드 작성

이제 모든 사항이 설정되었으므로 이미지 분류를 실행하기 위한 코드를 작성하는 것이 매우 간단합니다.

  1. 먼저 디자인 화면의 왼쪽 상단에서 X를 클릭하여 스토리보드 디자이너를 닫습니다. 이렇게 하면 코드에만 집중할 수 있습니다. 이 실습의 나머지 부분에서는 ViewController.swift를 수정합니다.
  2. 상단 UIKit 가져오기 바로 아래에 이 코드를 추가하여 MLKitVision 및 MLKit ImageLabeling 라이브러리를 가져옵니다.
import MLKitVision
import MLKitImageLabeling
  1. 그런 다음 viewDidLoad 함수에서 앱에 번들로 포함된 파일을 사용하여 ImageView를 초기화합니다.
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    imageView.image = UIImage(named:"flower1.jpg")
}
  1. 이미지 라벨을 viewDidLoad() 바로 아래 가져오는 도우미 함수를 만듭니다.
func getLabels(with image: UIImage){
  1. 이미지에서 VisionImage를 만듭니다. ML Kit는 이미지 분류를 실행할 때 이 유형을 사용합니다. 따라서 getLabels 함수에서 다음 코드를 추가합니다.
let visionImage = VisionImage(image: image)
visionImage.orientation = image.imageOrientation
  1. 다음으로, 이미지 라벨러를 위한 옵션을 생성합니다. 이 옵션을 사용하여 초기화됩니다. 여기서는 confidenceThreshold의 기본 옵션만 설정합니다. 즉, 라벨러에게 신뢰도 0.4 이상인 라벨만 반환하도록 요청하게 됩니다. 예를 들어 꽃의 경우 '식물' 또는 '페탈'과 같은 클래스에는 신뢰도가 높지만 '농구'나 '자동차'와 같은 클래스는 신뢰도가 낮습니다.
let options = ImageLabelerOptions()
options.confidenceThreshold = 0.4
  1. 이제 다음 옵션을 사용하여 라벨러를 만듭니다.
let labeler = ImageLabeler.imageLabeler(options: options)
  1. 라벨러가 있으면 처리할 수 있습니다. 이렇게 하면 라벨 (성공한 경우) 및 오류 (실패한 경우)가 포함된 비동기 콜백을 제공합니다. 그런 다음 잠시 후에 만들 다른 함수에서 처리할 수 있습니다.
labeler.process(visionImage) { labels, error in
    self.processResult(from: labels, error: error)
  }

Xcode에 processResult 회원이 없다고 불만을 제기하더라도 걱정하지 마세요. 아직 구현하지 않았으니 다음 단계에서 구현해 보겠습니다.

편의를 위해 전체 getLabel 기능은 다음과 같습니다.

// This is called when the user presses the button
func getLabels(with image: UIImage){
    // Get the image from the UI Image element and set its orientation
    let visionImage = VisionImage(image: image)
    visionImage.orientation = image.imageOrientation

    // Create Image Labeler options, and set the threshold to 0.4
    // so we will ignore all classes with a probability of 0.4 or less
    let options = ImageLabelerOptions()
    options.confidenceThreshold = 0.4

    // Initialize the labeler with these options
    let labeler = ImageLabeler.imageLabeler(options: options)

    // And then process the image, with the callback going to self.processresult
    labeler.process(visionImage) { labels, error in
        self.processResult(from: labels, error: error)
 }
}

이제 processResult 함수를 구현해야 합니다. 현재 반환되는 라벨과 오류 객체가 있으므로 이는 매우 간단합니다. 라벨은 ML Kit에서 ImageLabel 유형으로 변환해야 합니다.

그런 다음에는 일련의 라벨을 반복하면서 설명과 신뢰도 값을 가져온 다음 labeltexts라는 var에 추가하기만 하면 됩니다. 모든 과정을 반복한 후 lblOutput.text를 이 값으로 설정하기만 하면 됩니다.

전체 함수는 다음과 같습니다.

// This gets called by the labeler's callback
func processResult(from labels: [ImageLabel]?, error: Error?){
    // String to hold the labels
    var labeltexts = ""
    // Check that we have valid labels first
    guard let labels = labels else{
        return
    }
  // ...and if we do we can iterate through the set to get the description and confidence
    for label in labels{
        let labelText = label.text + " : " + label.confidence.description + "\n"
        labeltexts += labelText
    }
    // And when we're done we can update the UI with the list of labels
    lblOutput.text = labeltexts
}

사용자가 버튼을 누르면 getLabels를 호출하기만 하면 됩니다.

작업을 만들 때는 모든 것이 연결되었으므로 이전에 만든 doClassificaiton이라는 IBAction을 업데이트하여 getLabels를 호출하면 됩니다.

다음은 imageView의 내용만 호출하는 코드입니다.

@IBAction func doClassification(_ sender: Any) {
    getLabels(with: imageView.image!)
}

이제 앱을 실행하고 사용해 봅니다. 실제 작동 방식은 아래에서 확인할 수 있습니다.

eb8e6c1b2e2c65e0.png

레이아웃은 기기에 따라 다르게 표시될 수 있습니다.

Codelab에서는 기기마다 다른 레이아웃 유형을 탐색하지 않으며, 이는 그 자체로 매우 복잡한 개념입니다. UI가 제대로 표시되지 않으면 스토리보드 편집기로 돌아가면 하단에 View as: 섹션이 표시됩니다. 여기서 특정 기기를 선택할 수 있습니다. 테스트 대상인 이미지 또는 기기와 일치하는 하나를 선택하고 이에 맞게 UI를 수정합니다.

iOS 개발에 더 많이 참여하면서 제약 조건을 사용하여 휴대전화 간에 UI의 일관성을 유지하는 방법을 알아보겠습니다. 하지만 이 실습에서는 다루지 않습니다.

12. 축하합니다.

지금까지 Android와 iOS 모두에 일반 모델로 기본 컴퓨터 비전을 제공하는 앱을 구현했습니다. 힘든 작업을 대부분 완료했습니다.

다음 Codelab에서는 다양한 유형의 꽃을 인식하는 커스텀 모델을 몇 줄만 빌드하여 이 앱에서 커스텀 모델을 구현하여 더 유용하게 만들 수 있습니다.