ใน ep นี้ เราจะสอน ขั้นตอนการสร้างแอพพลิเคชั่น AI ที่มีความสามารถ Image Classification แบบ Multi-class Classification จำแนกรูปภาพ ด้วย TensorFlow Lite โดยใช้โมเดล MobileNet ซึ่งถูก Pre-trained, Quantization และ Convert ไว้เรียบร้อยแล้ว มาประกอบเป็น App สำหรับรันบนมือถือ Android ด้วยภาษา Kotlin

สอนเขียน App AI มือถือ Image Classification บน Android

เราสามารถพัฒนา App บนมือถือ Android ให้มีความสามารถด้าน AI / Machine Learning มีฟังก์ชันจำแนกรูปภาพ Image Classification เช่น รูปรีโมท ที่ถ่ายจากกล้องมือถือ

App ได้ predict ว่าเป็น remote control 34.51% Out of the box โดยที่เราไม่ได้ทำ Transfer Learning ใด ๆ เลย ดังตัวอย่างด้านล่าง

AI App Image Classification Android with TensorFlow Lite Predict Remote Control from Front Camera Real-Time
AI App Image Classification Android with TensorFlow Lite Predict Remote Control from Front Camera Real-Time

ในเคสนี้ เราจะใช้รูปภาพกล้องมือถือ ทำให้โค้ดซับซ้อนยิ่งขึ้น เนื่องจากต้องจัดการเรื่องต่าง ๆ เพิ่มเติม เช่น จัดการกล้อง, นำภาพจากกล้องมาแสดงบน App, แปลงข้อมูลภาพจากกล้อง ในรูปแบบ YUV เป็น RGB, จัดการเรื่องสิทธิ์ Permission ในการเข้าถึงกล้อง, สิทธิ์เข้าถึง Storage, จัดการการหมุนหน้าจอ, etc.

TensorFlow Lite on Android Code Example

นอกจาก build.gradle และ AndroidManifest.xml โค้ดตัวอย่างจะประกอบด้วย 3 ไฟล์ คือ Classifier.kt เป็น Class จัดการโมเดล, ImageUtils.java จัดการกล้องและรูปภาพ และ Camera2BasicFragment.kt เป็น UI

build.gradle

ใน Build Script เราจะกำหนดค่าสำหรับ dependencies ไปยัง org.tensorflow:tensorflow-lite:0.0.0-nightly และ กำหนด aaptOptions ให้ไม่ต้องบีบอัดไฟล์ FlatBuffer นามสกุล tflite, lite ซึ่งเก็บโมเดล TensorFlow Lite

android {
    ...
    aaptOptions {
        noCompress "tflite"
        noCompress "lite"
    }
}
dependencies {
    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
}

AndroidManifest.xml

ใน Manifest จะขอสิทธิ์การเข้าถึงกล้อง และ Storage

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

Classifier.kt

คล้ายกับใน ep ที่แล้ว tflite ep.5 แต่จะเปลี่ยนมาใช้โมเดลที่ Pre-trained แล้ว และทำ Quantization มาเรียบร้อยแทนแล้ว

    private val mModelPath = "mobilenet_v1_1.0_224_quant.tflite"
    private val mLabelPath = "labels_mobilenet_quant_v1_224.txt"

ImageUtils.java

ประกาศฟังก์ชันแปลง YUV420 จากกล้อง ไปเป็น ARGB8888

    public static void convertYUV420ToARGB8888(
            byte[] yData,
            byte[] uData,
            byte[] vData,
            int width,
            int height,
            int yRowStride,
            int uvRowStride,
            int uvPixelStride,
            int[] out) {
        int yp = 0;
        for (int j = 0; j < height; j++) {
            int pY = yRowStride * j;
            int pUV = uvRowStride * (j >> 1);

            for (int i = 0; i < width; i++) {
                int uv_offset = pUV + (i >> 1) * uvPixelStride;

                out[yp++] = YUV2RGB(0xff & yData[pY + i], 0xff & uData[uv_offset], 0xff & vData[uv_offset]);
            }
        }
    }

เข้าสูตรแปลงทีละ Pixel

    private static int YUV2RGB(int y, int u, int v) {
        // Adjust and check YUV values
        y = (y - 16) < 0 ? 0 : (y - 16);
        u -= 128;
        v -= 128;

        // This is the floating point equivalent. We do the conversion in integer
        // because some Android devices do not have floating point in hardware.
        // nR = (int)(1.164 * nY + 2.018 * nU);
        // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
        // nB = (int)(1.164 * nY + 1.596 * nV);
        int y1192 = 1192 * y;
        int r = (y1192 + 1634 * v);
        int g = (y1192 - 833 * v - 400 * u);
        int b = (y1192 + 2066 * u);

        // Clipping RGB values to be inside boundaries [ 0 , kMaxChannelValue ]
        r = r > kMaxChannelValue ? kMaxChannelValue : (r < 0 ? 0 : r);
        g = g > kMaxChannelValue ? kMaxChannelValue : (g < 0 ? 0 : g);
        b = b > kMaxChannelValue ? kMaxChannelValue : (b < 0 ? 0 : b);

        return 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
    }

Camera2BasicFragment.kt

Girls taking selfie at Paris road, University of Rajshahi, Bangladesh. Credit https://commons.wikimedia.org/wiki/File:Selfie_Shot.jpg
Girls taking selfie at Paris road, University of Rajshahi, Bangladesh. Credit https://commons.wikimedia.org/wiki/File:Selfie_Shot.jpg

Bind Event กล้องเข้ากับฟังก์ชัน ให้เรียกฟังก์ชันทุกครั้งที่มีการเปลี่ยนแปลง มีภาพใหม่

    private val onImageAvailableListener = ImageReader.OnImageAvailableListener {
        ....
    }

ดึงข้อมูลภาพ จากกล้อง ในรูปแบบ YUV

        val image: Image = it?.acquireLatestImage()!!        
        val planes = image!!.getPlanes()

        fillBytes(planes, yuvBytes)
        yRowStride = planes[0].getRowStride()
        val uvRowStride = planes[1].getRowStride()
        val uvPixelStride = planes[1].getPixelStride()

ส่งไปให้ ImageUtils แปลงเป็น RGB พร้อมแสดงบนหน้าจอ App

        imageConverter = Runnable {
            ImageUtils.convertYUV420ToARGB8888(
                    yuvBytes[0],
                    yuvBytes[1],
                    yuvBytes[2],
                    previewSize.width,
                    previewSize.height,
                    yRowStride,
                    uvRowStride,
                    uvPixelStride,
                    rgbBytes
            )
        }

        rgbFrameBitmap?.setPixels(
                getRgbBytes(), 0, previewSize.width, 0, 0, previewSize.width,
                previewSize.height
        )

นำข้อมูลรูปที่แปลงเป็น RGB เรียบร้อยแล้ว ส่งไปให้โมเดล predict แล้วนำผลลัพธ์แสดงบนหน้าจอ

        var results = rgbFrameBitmap?.let { it1 -> classifier.recognizeImage(it1) };
        activity?.runOnUiThread({
            itemNameTextView.setText(results?.get(0)?.title)
            confidenceTextView.setText(String.format("%.2f",(results?.get(0)?.confidence?.times(100)))+"%");
        })

เรามาเริ่มกันเลยดีกว่า

Classifier.kt

ImageUtils.java

Camera2BasicFragment.kt

Build

Compile และ Build Project

AI App Image Classification Android with TensorFlow Lite Predict Remote Control from Front Camera Real-Time Build Completed Successfully
AI App Image Classification Android with TensorFlow Lite Predict Remote Control from Front Camera Real-Time Build Completed Successfully

Run on Android Emulator

Deploy บน Android Emulator

AI App Image Classification Android with TensorFlow Lite Predict Remote Control from Front Camera Real-Time
AI App Image Classification Android with TensorFlow Lite Predict Remote Control from Front Camera Real-Time

Credit

แชร์ให้เพื่อน:

Surapong Kanoktipsatharporn on Linkedin
Surapong Kanoktipsatharporn
CTO at Bua Labs
The ultimate test of your knowledge is your capacity to convey it to another.

Published by Surapong Kanoktipsatharporn

The ultimate test of your knowledge is your capacity to convey it to another.