본문 바로가기
과학/딥러닝

Teachable machine으로 이미지 인식 어플만들기

by 루민즈 2023. 11. 2.

안녕하세요 

이미지를 인식하기 위해서는 관련 알고리즘으로 학습을 한뒤 tflite 파일로 변환시킨후 안드로이드 코드에 적용시켜야 됩니다. 하지만 구글에서는 간단한 이미지 인식 앱을 만들려고 저런 번거로운 작업을 노코드로 해주는 사이트가 있습니다. 

 

바로 Teachable machine 이라는 사이트 입니다. 

https://teachablemachine.withgoogle.com/ (Teachable machine 사이트) 

 

Teachable machine

해당 사이트를 접속하면 시작하기 버튼이 있는데 이를 눌러줍시다. 

 

그다음 이미지 프로젝트가 있는데 이를 눌러줍시다. 휴대폰 안드로이드 어플에 적용 시킬것이므로 표준 이미지 모델을 눌러주세요 그럼 다음과 같은 화면이 나옵니다.

 

Teachable machine 사이트

이미지 샘플을 추가할수있고 2개의 클래스를 적용시키면 모델을 학습할수 있습니다. 

먼저 이미지를 다운받아 봅시다. 이미지를 다운받기위해 kaggle이라는 사이트에 접속해 봅시다. 

 

https://www.kaggle.com/datasets/gpiosenka/coffee-bean-dataset-resized-224-x-224 (kaggle coffee bean 이미지)

Teachable machine은 표준 이미지 규격은 224 x 224px 입니다. 따라서 kaggle 사이트에 224 x 224 px로 이루어진 이미지를 다운받아 봅시다. 해당 링크를 들어가 보면 

 

 

이러한 사이트가 나오는데 여기서 Download를 클릭해 줍시다. 그러면 로그인 창으로 나오는데 여기서 로그인을 해줍니다. 그리고 다시 Download를 클릭해주면 다운로드가 진행됩니다. 그런다음 압축을 풀어주세요 

 

 

 

test,train ,Coffee Bean 파일이 있는데 여기서 train으로 들어가 줍니다. 

그럼 Dark,Green, Light ,Medium 폴더가 보입니다. 여기서 Dark,Green,Light 폴더에 있는 파일만 쓸겁니다. 

다시 Teachable machine 사이트로 들어가줍니다. 

 

Class1부분을 클릭하여 Dark로 바꾸어줍니다. 

그다음 Class2 부분을 클릭하여 Green으로 변경해주고 클래스 추가를 누른뒤 똑같이 Light로 변경해줍니다. 

 

Teachable machine

 

그다음 각각의 이름에 맞게 파일을 업로드 해줍니다. 먼저 업로드를 클릭해준다음 

해당 파일들을 전부다 선택하고 업로드 해줍니다. 참고로 전부 선택 단축키는 ctrl + A 입니다. Dark Green Light 전부다 업로드 합니다. 

 

 

전부다 올리셨다면 모델 학습시키기를 눌러줍니다. 설정을 그대로 하고 학습하기를 누릅니다. 

다하셨다면 모델 내보내기를 누르고 Tensorflow Lite를 누릅니다. 그리고 모델 변환 유형은 부동소수점으로 하시고 모델 다운로드를 누릅니다. 몇분 정도 걸리니 기다려주세요 

 

 

모델 변환이 완료되면 converted_tflite 파일이 나올겁니다. 압축을 풀어주세요 

여기서 model_unquant.tflite파일을 model.tflite파일로 이름을 바꿔주세요 

 

 

안드로이드 어플 코드

준비는 다됬습니다. 이제 안드로이드 스튜디오로 넘어가주세요 

안드로이드 프로젝트 파일은 [Java] 로 만들어 주세요 그다음 방금 막 만든 tflite을 넣어 줍니다. 

app -> src -> main 우클릭 New -> Other -> Tensorflow Lite Model을 눌러 방금 만든 tflite의 경로를 입력해줍니다. 

 

 

 

그다음 안드로이드 코드를 작성해봅시다. 

 

MainActivity.java 

 

package com.grandra.sample_deeplearning;
/*
 * Created by ishaanjav
 * github.com/ishaanjav
 */

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Locale;
import java.util.Objects;

import com.grandra.sample_deeplearning.ml.Model;

public class MainActivity extends AppCompatActivity {

    TextView result, confidence;
    ImageView imageView;
    Button picture;
    int imageSize = 224;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        result = findViewById(R.id.result);
        confidence = findViewById(R.id.confidence);
        imageView = findViewById(R.id.imageView);
        picture = findViewById(R.id.button);

        picture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Launch camera if we have permission
                if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    startActivityForResult(cameraIntent, 1);
                } else {
                    //Request camera permission if we don't have it.
                    requestPermissions(new String[]{Manifest.permission.CAMERA}, 100);
                }
            }
        });
    }

    public void classifyImage(Bitmap image){
        try {
            Model model = Model.newInstance(getApplicationContext());

            // Creates inputs for reference.
            TensorBuffer inputFeature0 = TensorBuffer.createFixedSize(new int[]{1, 224, 224, 3}, DataType.FLOAT32);
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4 * imageSize * imageSize * 3);
            byteBuffer.order(ByteOrder.nativeOrder());

            // get 1D array of 224 * 224 pixels in image
            int [] intValues = new int[imageSize * imageSize];
            image.getPixels(intValues, 0, image.getWidth(), 0, 0, image.getWidth(), image.getHeight());

            // iterate over pixels and extract R, G, and B values. Add to bytebuffer.
            int pixel = 0;
            for(int i = 0; i < imageSize; i++){
                for(int j = 0; j < imageSize; j++){
                    int val = intValues[pixel++]; // RGB
                    byteBuffer.putFloat(((val >> 16) & 0xFF) * (1.f / 255.f));
                    byteBuffer.putFloat(((val >> 8) & 0xFF) * (1.f / 255.f));
                    byteBuffer.putFloat((val & 0xFF) * (1.f / 255.f));
                }
            }

            inputFeature0.loadBuffer(byteBuffer);

            // Runs model inference and gets result.
            Model.Outputs outputs = model.process(inputFeature0);
            TensorBuffer outputFeature0 = outputs.getOutputFeature0AsTensorBuffer();

            float[] confidences = outputFeature0.getFloatArray();
            // find the index of the class with the biggest confidence.
            int maxPos = 0;
            float maxConfidence = 0;
            for(int i = 0; i < confidences.length; i++){
                if(confidences[i] > maxConfidence){
                    maxConfidence = confidences[i];
                    maxPos = i;
                }
            }
            String[] classes = {
                    "Dark",
                    "Green",
                    "Light"
            };
            result.setText(classes[maxPos]);

            StringBuilder s = new StringBuilder();
            for(int i = 0; i < classes.length; i++){
                s.append(String.format(Locale.KOREA, "%s: %.1f%%\n", classes[i], confidences[i] * 100));
            }
            confidence.setText(s.toString());


            // Releases model resources if no longer used.
            model.close();
        } catch (IOException e) {
            // TODO Handle the exception
        }
    }


    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == 1 && resultCode == RESULT_OK) {
            assert data != null;
            Bitmap image = (Bitmap) Objects.requireNonNull(data.getExtras()).get("data");
            assert image != null;
            int dimension = Math.min(image.getWidth(), image.getHeight());
            image = ThumbnailUtils.extractThumbnail(image, dimension, dimension);
            imageView.setImageBitmap(image);

            image = Bitmap.createScaledBitmap(image, imageSize, imageSize, false);
            classifyImage(image);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

 

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:id="@+id/button"
        android:text="Take Picture"
        android:textAllCaps="false"
        android:layout_alignParentBottom="true"
        android:textSize="21sp"
        android:textStyle="bold"
        />
    <ImageView
        android:layout_width="370sp"
        android:layout_height="370sp"
        android:layout_centerHorizontal="true"
        android:id="@+id/imageView"
        android:layout_marginTop="10sp"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Classified as:"
        android:textStyle="bold"
        android:textSize="20sp"
        android:id="@+id/classified"
        android:layout_below="@+id/imageView"
        android:layout_marginTop="10sp"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text=""
        android:textColor="#C30000"
        android:textStyle="bold"
        android:textSize="27sp"
        android:id="@+id/result"
        android:layout_below="@+id/classified"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/confidencesText"
        android:text="Confidences:"
        android:textStyle="bold"
        android:textSize="20sp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/result"
        android:layout_marginTop="30sp"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text=""
        android:textColor="#000"
        android:textSize="22sp"
        android:id="@+id/confidence"
        android:layout_below="@+id/confidencesText"
        />

</RelativeLayout>

 

이제 앱을 실행시켜 줍니다. 

 

 

 

실행 결과

 

실행 결과입니다.~

728x90
반응형