Bermain Dengan OpenCV di Android (Java) (bag. 2): Rekognisi Segi Empat (Rectangle)

Reading Time: 7 minutes

Selamat datang di UNY Developer Network. Kita akan kembali bermain dengan OpenCV di Android, dan kali ini melakukan rekognisi segi empat (rectangle). Kita akan melanjutkan project yang sudah kita mulai sebelumnya, sehingga untuk para pembaca yang melewatkan bagian pertama dari seri artikel ini dapat membacanya di sini: Bermain Dengan OpenCV di Android (Java) (bag. 1): Rekognisi Lingkaran.

Baik, apa saja yang harus kita lakukan untuk membuat rekognisi segi empat di Android? Apakah sulit? Mari kita simak artikel ini secara mendalam.

Sebelum kita melangkah lebih jauh, mari kita lihat terlebih dahulu bagaimana akhir dari project kita kali ini:

Ya, dapat Anda perhatikan pada video di atas, perangkat Android kita dapat mendeteksi bentuk segi empat secara langsung tanpa melalui proses di server. Hanya dengan mengandalkan library OpenCV dan Kamera, kita sudah mampu mendeteksi bentuk segi empat meskipun masih dalam tahap yang paling sederhana. Saya ingatkan sekali lagi, sebelum Anda membaca postingan ini, Anda sudah harus menyelesaikan terlebih dahulu tahapan-tahapan di postingan sebelumnya: Bermain Dengan OpenCV di Android (Java) (bag. 1): Rekognisi Lingkaran. Mengapa demikian? sebab di postingan ini, kita hanya akan melanjutkan dari project yang sudah dimulai sebelumnya, jadi tidak membuat project baru.

Ok, mari kita mulai. Kita mulai dengan mempersiapkan project yang sudah kita buat sebelumnya.

Setelah project terbuka, langsung saja buat Empty Activity baru dengan nama: RectangleDetection

Setelah membuat activity baru, project OpenCV kita akan tampak seperti tangkapan layar di bawah ini.

Selanjutnya, mari kita ikuti langkah-langkah di bawah ini.

1. Memodifikasi AndroidManifest

Langkah pertama, seperti biasa, adalah memodifikasi file manifest aplikasi kita. Tujuannya untuk menyeragamkan UX di aplikasi kita. Modifikasi blok activity .RectangleDetection sehingga seperti ini:

        <activity
            android:name=".RectangleDetection"
            android:exported="false"
            android:parentActivityName=".MainActivity"
            android:screenOrientation="landscape">
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>

Setelah selesai memodifikasi AndroidManifest, kita dapat menutup filenya.

2. Memodifikasi strings.xml

Langkah kedua adalah memodifikasi strings.xml. Kita perlu menambahkan satu buah string yang akan digunakan untuk Button navigasi ke Activity RectangleDetection. Modifikasi strings.xml sehingga menyerupai seperti di bawah ini.

<resources>
    <string name="app_name">OpenCV-Project</string>
    <string name="app_menu">Choose Mode:</string>
    <string name="btn_circle_camera">Circle Recognition</string>
    <string name="btn_rectangle_camera">Rectangle Recognition</string>
</resources>

Sama halnya dengan langkah pertama, setelah selesai mengedit strings.xml, dapat langsung ditutup.

3. Copy, Paste, dan Modifikasi Kode Layout Activity CircleDetection

Langkah berikutnya adalah membuat layout, karena layout yang akan kita buat sama persis dengan layout activity CircleDetection, maka kita dapat melakukan copy paste kode layout dari Activity CircleDetection ke Activity RectangleDetection. Namun, jangan lupa lakukan modifikasi pada baris kode:

tools:context=".CircleDetection"

Pada layout Activity RectangleDetection, ubah kode di atas menjadi:

tools:context=".RectangleDetection"

Sehingga secara keseluruhan, di akhir kode layout untuk Activity Rectangle Detection adalah seperti berikut ini.

<?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=".RectangleDetection">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:weightSum="10">

        <org.opencv.android.JavaCameraView
            android:id="@+id/opencv_camera"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <LinearLayout
            android:id="@+id/capture_button"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="9"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/teal_700"
                android:padding="20dp"
                android:src="@drawable/ic_action_capture"/>

        </LinearLayout>

    </LinearLayout>
</RelativeLayout>

4. Copy, Paste, dan Modifikasi Kode Activity CircleDetection

Langkah keempat hampir mirip dengan langkah sebelumnya, yakni mengcopy, mempaste, dan memodifikasi kode dari Activity CircleDetection ke Activity RectangleDetection. Oleh karena itu, lakukan proses copy paste terlebih dahulu dan jangan panik terlebih dahulu jika terdapat error. Karena kita memang harus melakukan modifikasi terhadap kode yang sudah dipaste tadi.

Modifikasi kode berikut

public class CircleDetection extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2

Menjadi

public class RectangleDetection extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2

Selanjutnya, ubah baris kode berikut ini

setContentView(R.layout.activity_circle_detection);

Menjadi

setContentView(R.layout.activity_rectangle_detection);

Baik, kita sudah memodifikasi pengaturan dasar untuk kode Activity RectangleDetection kita. Selanjutnya kita akan memodifikasi core dari method OpenCV yang kita gunakan di Activity ini sehingga dapat merekognisi segi empat.

Pertama, tambahkan kode berikut ini di bawah private LinearLayout captureButton;

private Mat mRGBa, mIntermediateMat, mGray, hierarchy;

Kedua, modifikasi kode pada blok method public void onCameraViewStarted(int width, int height) sehingga menjadi seperti di bawah ini:

    @Override
    public void onCameraViewStarted(int width, int height) {
        mRGBa = new Mat(height, width, CvType.CV_8UC4);
        mIntermediateMat = new Mat(height, width, CvType.CV_8UC4);
        mGray = new Mat(height, width, CvType.CV_8UC1);
        hierarchy = new Mat();
    }

Langkah yang sama juga dilakukan pada method public void onCameraViewStopped() sehingga:

    @Override
    public void onCameraViewStopped() {
        mRGBa.release();
        mIntermediateMat.release();
        mGray.release();
        hierarchy.release();
    }

Kemudian kita hapus total isi dari method public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) :

@Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        
    }

Kemudian kita ubah isinya dengan baris-baris kode berikut:

        mRGBa = inputFrame.rgba();
        List<MatOfPoint> contours = new ArrayList<>();
        hierarchy = new Mat();
        Imgproc.Canny(mRGBa, mIntermediateMat, 80, 100);
        Imgproc.findContours(mIntermediateMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0,0));
        hierarchy.release();

        for(int contourIdx = 0; contourIdx < contours.size(); contourIdx++){
            MatOfPoint2f approxCurve = new MatOfPoint2f();
            MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(contourIdx).toArray());
            double approxDistance = Imgproc.arcLength(contour2f, true) * 0.01;
            Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
            MatOfPoint points = new MatOfPoint(approxCurve.toArray());
            Rect rect = Imgproc.boundingRect(points);
            double height = rect.height;
            double width = rect.width;

            if(height > 300 && width > 300){
                Imgproc.rectangle(mRGBa, new Point(rect.x, rect.y), new Point(rect.x+ rect.width, rect.y+rect.height), new Scalar(0,255, 0, 0), 3);
            }
        }
        return mRGBa;

Sehingga di akhir, blok ini akan seperti ini:

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        mRGBa = inputFrame.rgba();
        List<MatOfPoint> contours = new ArrayList<>();
        hierarchy = new Mat();
        Imgproc.Canny(mRGBa, mIntermediateMat, 80, 100);
        Imgproc.findContours(mIntermediateMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0,0));
        hierarchy.release();

        for(int contourIdx = 0; contourIdx < contours.size(); contourIdx++){
            MatOfPoint2f approxCurve = new MatOfPoint2f();
            MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(contourIdx).toArray());
            double approxDistance = Imgproc.arcLength(contour2f, true) * 0.01;
            Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
            MatOfPoint points = new MatOfPoint(approxCurve.toArray());
            Rect rect = Imgproc.boundingRect(points);
            double height = rect.height;
            double width = rect.width;

            if(height > 300 && width > 300){
                Imgproc.rectangle(mRGBa, new Point(rect.x, rect.y), new Point(rect.x+ rect.width, rect.y+rect.height), new Scalar(0,255, 0, 0), 3);
            }
        }
        return mRGBa;
    }

5. Modifikasi Layout MainActivity

Langkah kelima yakni menghubungkan fitur deteksi segi empat ini ke MainActivity. Oleh karena itu, kita perlu memodifikasi terlebih dahulu layout MainActivity dengan menambahkan satu buah tombol. Tambahkan view berikut ini ke dalam layout MainActivity:

    <Button
        android:id="@+id/btn_rectangle_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="@string/btn_rectangle_camera"
        android:layout_below="@+id/btn_circle_camera"/>

Sehingga layout MainActivity akan menjadi:

<?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"
    android:layout_margin="20dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/app_menu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_menu"
        android:textStyle="bold"
        android:textSize="20sp"/>

    <Button
        android:id="@+id/btn_circle_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="@string/btn_circle_camera"
        android:layout_below="@+id/app_menu"/>

    <Button
        android:id="@+id/btn_rectangle_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="@string/btn_rectangle_camera"
        android:layout_below="@+id/btn_circle_camera"/>
</RelativeLayout>

6. Modifikasi Kode MainActivity

Langkah terakhir adalah memodifikasi MainActivity sehingga fitur deteksi segi empat dapat kita panggil dan kita gunakan.

Pertama, tambahkan kode berikut:

private Button rectangleDetection;

Sehingga akan ada dua object Button:

    private Button circleDetection;
    private Button rectangleDetection;

Namun, kita juga bisa menjadikannya satu sehingga:

private Button circleDetection, rectangleDetection;

Kedua, tambahkan kode berikut di method initLevel0():

rectangleDetection = findViewById(R.id.btn_rectangle_camera);

Sehingga di akhir, method initLevel0() menjadi:

    private void initLevel0(){
        circleDetection = findViewById(R.id.btn_circle_camera);
        rectangleDetection = findViewById(R.id.btn_rectangle_camera);
    }

Ketiga, tambahkan kode berikut ini di method initLevel1(){

        rectangleDetection.setOnTouchListener((view, motionEvent) -> {
            goToRectangleCamera();
            return false;
        });

Sehingga di akhir, method initLevel1() menjadI;

    @SuppressLint("ClickableViewAccessibility")
    private void initLevel1(){
        circleDetection.setOnTouchListener((view, motionEvent) -> {
            goToCircleCamera();
            return false;
        });

        rectangleDetection.setOnTouchListener((view, motionEvent) -> {
            goToRectangleCamera();
            return false;
        });
    }

Pada tahap ini, kita pasti akan melihat error pada baris kode goToRectangleCamera(). Yap, baris kode tersebut error karena kita belum memiliki method goToRectangleCamera(). Oleh karena itu, kita tambahkan method goToRectangleCamera():

    private void goToRectangleCamera(){
        Intent goRectangleCamera = new Intent(MainActivity.this, RectangleDetection.class);
        goRectangleCamera.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivity(goRectangleCamera);
    }

Di akhir, kode MainActivity adalah seperti berikut;

package com.unydevelopernetwork.opencv_project;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button circleDetection, rectangleDetection;

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

    private void initLevel0(){
        circleDetection = findViewById(R.id.btn_circle_camera);
        rectangleDetection = findViewById(R.id.btn_rectangle_camera);
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initLevel1(){
        circleDetection.setOnTouchListener((view, motionEvent) -> {
            goToCircleCamera();
            return false;
        });

        rectangleDetection.setOnTouchListener((view, motionEvent) -> {
            goToRectangleCamera();
            return false;
        });
    }

    private void goToCircleCamera(){
        Intent goCircleCamera = new Intent(MainActivity.this, CircleDetection.class);
        goCircleCamera.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivity(goCircleCamera);
    }

    private void goToRectangleCamera(){
        Intent goRectangleCamera = new Intent(MainActivity.this, RectangleDetection.class);
        goRectangleCamera.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivity(goRectangleCamera);
    }

}

Pembuatan fitur deteksi segi empat telah selesai, saatnya kita uji untuk melihat kinerja dari fungsi yang sudah kita tambahkan.

PENGUJIAN

Untuk pengujian, saya menggunakan device langsung (tidak menggunakan emulator) karena kita memerlukan fungsi kamera pada perangkat uji. Untuk perangkat uji, saya menggunakan Google Pixel 3A XL Android versi 12. Berikut hasil ujinya.

Dapat kita lihat, dari hasil uji, fitur deteksi segi empat yang kita tambahkan dapat bekerja baik. Bahkan juga dapat bekerja dengan baik untuk objek-objek nyata (dalam hal ini menggunakan gambar laptop). Object segi empat yang terdeteksi akan langsung diberikan border berwarna hijau.

Seperti halnya pada postingan sebelumnya, kita belum menambahkan fitur capture. Karena kita masih memiliki banyak bentuk geometri yang belum kita rekognisi. Fitur capture akan kita tambahkan sebagai fitur terakhir setelah kita berhasil merekognisi berbagai bentuk geometri dengan menggunakan openCV dan Android.

Baiklah, Demikianlah postingan saya tentang Bermain Dengan OpenCV di Android (Java) (bag. 2): Rekognisi Segi Empat (Rectangle). Anda dapat mengunduh project ini melalui tautan berikut ini: https://github.com/milstrike/OpenCV-Project.

Semoga postingan bermanfaat bagi Anda para pembaca. Apabila ada pertanyaan mengenai postingan ini, Anda dapat meninggalkannya di kolom komentar. Dan, Apabila Anda menemukan artikel ini berguna, Anda dapat membagikannya. Anda juga dapat mencuplik beberapa bagian dari artikel ini, namun jangan lupa untuk sertakan URL nya. Terima kasih.

^_^


ARTIKEL TERKAIT:

Advertisements

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *