Assalamu'alaikum Wr. Wb. pada kesempatan ini saya ingin melanjutkan tutorial sebelumnya yaitu Deep Learning dengan Convolutional Neural Network (CNN) pada Google Colaboratory. Pada tutorial ini akan menerapkan model hasil pelatihan CNN dari postingan sebelumnya pada perangkat Android. Agar CNN tersebut dapat digunakan pada perangkat Android maka diperlukan model hasil pelatihannya dalam bentuk TensorFlow Lite. TensorFlow Lite berfungsi untuk menjalankan model hasil pelatihan CNN pada perangkat dengan kebutuhan sumber daya yang rendah dan membutuhkan kecepatan proses yang tinggi. File Model TensorFlow Lite (.tflite) tersebut nantinya akan diimpor ke dalam folder project pada Android Studio.
Sebelum menuju langkah pembuatannya, pastikan Anda telah memenuhi persyaratan sebagai berikut :
- Paham dalam menggunakan perangkat lunak Android Studio IDE.
- Mengerti bahasa pemrograman Java/Kotlin.
- Mengerti bahasa pemrograman Xml.
- Sudah melatih dan menyimpan model hasil pelatihan arsitektur CNN pada Google Colab.
- Sudah memasang aplikasi Android Studio IDE.
- Pastikan terkoneksi dengan Wi-Fi atau menggunakan paket internet unlimited, karena Android Studio akan men-download resources yang dibutuhkan dengan ukuran yang cukup besar selama pembuatan aplikasi Android.
Jika sudah terpenuhi semuanya maka langsung saja ke tahap pembuatannya.
- Buka software Android Studio yang sudah di-install sebelumnya.
- Membuat project baru dengan cara klik Menu File → New → New Project....
- Pada jendela "Create New Project" pilih "Empty Activity" dan klik "Next".
- Kemudian atur seperti pada gambar, untuk "Name" dan "Package Name" isi sesuai keinginan dan "Save location" sesuaikan dengan keinginan Anda.
- Tunggu proses sinkronisasi dan download resources sampai selesai.
- Jika sudah selesai prosesnya maka tampilannya akan seperti berikut.
- Selanjutnya buka "AndroidManifest.xml" dan tambahkan beberapa perizinan dan fitur yang nantinya akan digunakan seperti pada gambar.
- <dist:module dist:instant="true"/>
- <uses-permission android:name="android.permission.CAMERA" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
- <uses-feature android:name="android.hardware.camera" />
- <uses-feature android:name="android.hardware.camera.autofocus" />
- <!--<uses-feature android:name="android.hardware.camera.flash" />-->
- <uses-permission android:name="android.permission.FLASHLIGHT"/>
- Jika terdapat source code yang error (berwarna merah) pada text editor maka cukup menekan tombol Alt+Enter pada keyboard untuk menambahkan attribute yang dibutuhkan.
- Setelah itu source code tersebut sudah tidak error lagi.
- Kemudian terdapat source code yang diberi highlight berwarna kuning yang menandakan adanya peringatan dalam source code tersebut. Peringatan tersebut terjadi karena program yang dibuat tidak dapat diindeks oleh pencarian Google.
- Untuk menghilangkan peringatannya maka perlu ditambah atribut seperti ini.
- Maka peringatan tersebut sudah tidak muncul kembali.
- Sesudah itu kita akan membuat sebuah Class Kotlin baru seperti berikut.
- Kemudian isi nama class-nya "Bantuan" dan ganti jenis file menjadi "Class".
- Selanjutnya klik "OK" pada jendela "New Kotlin File/Class".
- Lengkapi source code berikut pada Class Kotlin "Bantuan.kt". Class ini bertujuan untuk membuat aktivitas baru dengan title bar "Halaman Bantuan". Class ini nantinya digunakan untuk memberi petunjuk cara kerja dan cara menggunakan apikasi ini.
- class Bantuan : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_bantuan)
- if (supportActionBar != null) {
- (supportActionBar as ActionBar).title = "Halaman Bantuan"
- }
- }
- }
- Selanjutnya tambahkan class "Bantuan.kt" ke dalam "AndroidManifest.xml" dengan cara seperti pada gambar di bawah ini.
- Setelah ditambahkan ke manifest, maka dapat dicek pada "AndroidManifest.xml" apakah aktivitas class "Bantuan" sudah ditambahkan secara otomatis.
- Selanjutnya kembali lagi ke aktivitas "Bantuan.kt". Terdapat referensi layout yang tidak ditemukan sehingga menjadi error (source code berwarna merah). Hal itu terjadi karena kita belum membuat tampilan layout dari class Bantuan. Layout tersebut dibuat dengan nama "activity_bantuan.xml". Untuk cara membuatnya seperti berikut.
- Setelah terbuka jendela "New Resource File" lakukan seperti berikut.
- Kemudian akan dibuat secara otomatis layout-nya pada direktori app → res → layout → activity_bantuan.xml di dalam project Android tersebut.
- Untuk mengedit antarmuka "activity_bantuan.xml" nanti akan dilakukan di langkah terakhir dikarenakan nilai string yang dimasukkan cukup banyak.
- Setelah itu kita membuat file class baru dengan nama "Deskripsi" serta tampilan layout pada aplikasi Android-nya dengan nama "activity_deskripsi". class Deskripsi ini berfungsi untuk menampilkan informasi varietas beras. Pembuatan antarmuka layout aktivitas tersebut juga sama akan di buat di bagian akhir.
- class Deskripsi : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_deskripsi)
- if (supportActionBar != null) {
- (supportActionBar as ActionBar).title = "Deskripsi Varietas Beras"
- }
- }
- }
- Sehingga program pada text editor-nya menjadi seperti ini.
- Setelah itu buat class "Klasifikasi.kt" dan lengkapi source code-nya seperti ini.
- <div style="-goog-ms-box-shadow: 0 0 10px #000000; -moz-box-shadow: 0 0 10px #000000; -webkit-box-shadow: 0 0 10px #000000; border: 6px double #3399FF; color: #b4040; height: 200px; overflow: auto; padding: 0px; width: 450px;">
- <ol>
- <li style="text-align: justify;"><span style="color: orange; font-family: calibri;">class Deskripsi : AppCompatActivity() {</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";">
- </span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> override fun onCreate(savedInstanceState: Bundle?) {</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> super.onCreate(savedInstanceState)</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> setContentView(R.layout.activity_deskripsi)</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";">
- </span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> if (supportActionBar != null) {</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> (supportActionBar as ActionBar).title = "Deskripsi Varietas Beras"</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> }</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";"> }</span></li>
- <li style="text-align: justify;"><span style="color: orange; font-family: "calibri";">}</span></li>
- </ol>
- </div>
- Pada source code tersebut terdapat nama variabel "Interpreter" yang tidak dikenal sehingga menjadi berwarna merah seperti pada gambar berikut.
- Hal tersebut disebabkan oleh tidak adanya library yang cocok untuk ditambahkan. Sehingga diperlukannya penambahan modul dependency pada "build.gradle (Module: app)". Library yang dibutuhkan adalah TensorFlow Lite versi 1.13.1. Cara menambahkannya seperti berikut.
- //menambah library tensorflowlite
- implementation 'org.tensorflow:tensorflow-lite:1.13.1'
- Setelah itu kita dapat mengimpor library TensorFlow Lite pada class Klasifikasi dengan cara menekan Alt+Enter seperti pada langkah sebelumnya.
- Buat lagi class baru dengan nama "Deteksi". Class ini berfungsi untuk menyimpan nilai data hasil deteksi dari class "Pendeteksi" dan mengubah nilai probabilitasnya ke dalam bentuk persentase (%). Pada class "Deteksi" tidak perlu ditambahkan ke manifest karena class ini tidak akan ditampilkan pada layout aplikasi Android.
- data class Deteksi(
- val name: String,
- val probability: Float
- ) {
- override fun toString() =
- "$name : ${probability*100}%"
- }
- Selanjutnya buat class "Pendeteksi.kt" yang digunakan untuk mendeteksi obyek beras menggunakan fitur kamera pada smartphone Android. Setelah dibuat class-nya lalu tambahkan ke manifest yang sama seperti pada langkah2 sebelumnya.
- class Pendeteksi : AppCompatActivity() {
- private lateinit var classifier: Klasifikasi
- //mendeklarasikan komponen TextView resultbar yang akan dimanipulasi
- private lateinit var resultbar: TextView
- private lateinit var processtime: TextView
- //deklarasi variabel lastprocessingtimems bertipe data long
- private var lastProcessingTimeMs: Long = 0
- @SuppressLint("MissingPermission")
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_pendeteksi)
- //mengganti nama title bar tiap activity
- if (supportActionBar != null) {
- (supportActionBar as ActionBar).title = "Pendeteksi Varietas Beras"
- }
- //obyek TextView resultbar disesuaikan (cast) dengan komponen TextView ber-ID result_bar di layout activity_pendeteksi.xml melalui metode findViewById().
- resultbar = findViewById(R.id.result_bar)
- processtime = findViewById(R.id.process_time_bar)
- classifier = Klasifikasi(assets)
- if (!canUseCamera()) {
- requestCameraPermissions()
- } else {
- setupCamera()
- }
- }
- private fun requestCameraPermissions() {
- ActivityCompat.requestPermissions(
- this,
- arrayOf(Manifest.permission.CAMERA),
- REQUEST_CAMERA_CODE
- )
- }
- @SuppressLint("MissingPermission", "SetTextI18n")
- private fun setupCamera() {
- camera.addPictureTakenListener {
- AsyncTask.execute {
- val startTime = SystemClock.uptimeMillis()//menghitung waktu awal
- val recognitions = classifier.recognize(it.data)
- val txt = recognitions.joinToString(separator = "\n")
- lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime//menghitung lamanya proses
- val waktu = lastProcessingTimeMs.toString()//konversi ke string
- runOnUiThread {
- /*menampilkan hasil pada UI*/
- //Toast.makeText(this, txt, Toast.LENGTH_LONG).show()
- /*menampilkan hasil pada layout TextView*/
- resultbar.text = txt
- processtime.text = "$waktu ms "
- }
- }
- }
- capturePhoto.setOnClickListener {
- camera.capture()
- }
- }
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array<out String>,
- grantResults: IntArray
- ) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (REQUEST_CAMERA_CODE == requestCode) {
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- setupCamera()
- } else {
- Toast.makeText(this, "App needs camera in order to work.", Toast.LENGTH_LONG).show()
- requestCameraPermissions()
- }
- }
- }
- @SuppressLint("MissingPermission")
- override fun onResume() {
- super.onResume()
- if (canUseCamera()) {
- camera.start()
- }
- }
- override fun onPause() {
- if (canUseCamera()) {
- camera.stop()
- }
- super.onPause()
- }
- override fun onDestroy() {
- if (canUseCamera()) {
- camera.destroy()
- }
- super.onDestroy()
- }
- private fun canUseCamera() =
- ActivityCompat.checkSelfPermission(
- this,
- Manifest.permission.CAMERA
- ) == PackageManager.PERMISSION_GRANTED
- companion object {
- private const val REQUEST_CAMERA_CODE = 1
- }
- }
- Kemudian buat juga layout "activity_pendeteksi.xml" dengan jenis layout "ConstraintLayout" yang digunakan untuk class "Pendeteksi".
- Source code layout "activity_pendeteksi.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"
- android:background="@drawable/gradient_pendeteksi"
- tools:context=".Pendeteksi">
- <com.priyankvasa.android.cameraviewex.CameraView
- android:id="@+id/camera"
- android:layout_width="480dp"
- android:layout_height="640dp"
- android:layout_marginBottom="0dp"
- android:adjustViewBounds="true"
- android:keepScreenOn="true"
- app:aspectRatio="4:3"
- app:autoFocus="continuous_picture"
- app:awb="auto"
- app:cameraMode="single_capture"
- app:facing="back"
- app:flash="auto"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:opticalStabilization="true"
- app:outputFormat="jpeg"
- app:pinchToZoom="true"
- app:shutter="short_time"
- app:touchToFocus="true"
- app:zsl="true" />
- <TextView
- android:id="@+id/process_time_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#CCFF6633"
- android:padding="2dp"
- android:fontFamily="times"
- android:gravity="right"
- android:text="@string/process_time"
- android:textAlignment="gravity"
- android:textColor="#FFFF00"
- android:textSize="18sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- tools:layout_editor_absoluteX="0dp" />
- <TextView
- android:id="@+id/process_name_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="2dp"
- android:fontFamily="times"
- android:gravity="left"
- android:text=" Waktu Proses Deteksi : "
- android:textAlignment="gravity"
- android:textColor="#FFFF00"
- android:textSize="18sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- tools:layout_editor_absoluteX="0dp" />
- <TextView
- android:id="@+id/info_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#80000000"
- android:fontFamily="times"
- android:gravity="center"
- android:padding="2dp"
- android:text="@string/info"
- android:textAlignment="gravity"
- android:textColor="#FFFFFF"
- android:textSize="16sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.05"
- tools:layout_editor_absoluteX="0dp" />
- <TextView
- android:id="@+id/capturePhoto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="84dp"
- android:background="#8000CC33"
- android:padding="7dp"
- android:shadowColor="@android:color/darker_gray"
- android:text="Deteksi"
- android:textColor="@android:color/black"
- android:textSize="30sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent" />
- <TextView
- android:id="@+id/prediction_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="63dp"
- android:background="#9000CC33"
- android:fontFamily="times"
- android:text="HASIL PREDIKSI :"
- android:textAlignment="center"
- android:textColor="#000099"
- android:textSize="16sp"
- app:layout_constraintBottom_toBottomOf="parent" />
- <TextView
- android:id="@+id/result_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="0dp"
- android:background="#9000CC33"
- android:fontFamily="times"
- android:text="@string/hasil_1_nhasil_2_nhasil_3"
- android:textAlignment="center"
- android:textColor="#000099"
- android:textSize="16sp"
- app:layout_constraintBottom_toBottomOf="parent" />
- </androidx.constraintlayout.widget.ConstraintLayout>
- Kemudian buat drawable resource file "gradient_pendeteksi.xml" yang digunakan untuk menambah warna latar belakang layout. Anda dapat melewati langkah ini jika tidak ingin menambahkan warna background (default warna putih). Jika ingin melewatinya anda tidak perlu membuat "gradient_pendeteksi.xml" dan hanya perlu menghapus baris program "android:background="@drawable/gradient_pendeteksi"". Jika ingin menambahkan warna background gradasi silahkan mengikutin langkah berikut.
- Setelah itu maka akan dibuat secara otomatis file resource-nya pada direktori app → res → drawable. Kemudian ubah source code-nya menjadi seperti ini. Untuk pengaturan warna dan efek gradasinya Anda bisa mengubahnya sendiri sesuai selera.
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:type="linear"
- android:startColor="@android:color/holo_blue_bright"
- android:centerColor="#FFFF66"
- android:endColor="@android:color/holo_green_light"
- android:angle="315"/>
- </shape>
- Selanjutnya buat nilai dari resources string untuk "process_time".
- Masukkan nilai string-nya seperti berikut dan klik "OK". Nilai string ini nantinya akan terus diupdate nilai waktunya seiring dilakukannya pendeteksian.
- Selanjutnya nilai string yang sudah dibuat pada direktori app → res → values → strings.xml diubah sesuai karakter yang disarankan oleh Android Studio.
- Selanjutnya buat nilai string untuk text "info". string ini digunakan untuk menampilkan informasi berupa karakter pada layout aplikasi Android.
- Selanjutnya buat string teks lagi untuk menampilkan Text View nilai probabilitas hasil deteksi.
- Kemudian menuju nilai string resources-nya dengan cara tekan tombol Ctrl+Klik kiri pada keyboard untuk mengubah nilai string-nya secara manual.
- Setelah itu ubah string-nya sesuai pada gambar.
- <string name="hasil_1_nhasil_2_nhasil_3"><b>Hasil 1</b>\nHasil 2\nHasil 3</string>
- Kembali lagi ke "activity_pendeteksi.xml" di sini terdapat baris kode yang jenis layout untuk kamera menggunakan library cameraview-ex milik google yang dimodifikasi oleh priyankvasa. Namun sumbernya masih tidak dikenal. Library tersebut sangat cocok digunakan untuk keperluan image processing menggunakan fitur kamera pada Android.
- Agar dapat menggunakannya, maka perlu ditambahkan implementation di dalam dependencies pada "build.gradle (Module: app)" seperti pada langkah 27 sebelumnya.
- //menambah tampilan kamera
- implementation "com.priyankvasa.android:cameraview-ex:3.5.1-alpha"
- Setelah ditambahkan dan disinkronisasi, maka library sudah dikenal.
- Sesudah itu kembali lagi menuju class "Pendeteksi.kt" untuk mengimpor library tersebut.
- Buat kelas baru lagi dengan nama "KlasifikasiDariGaleri.kt".
- class KlasifikasiDariGaleri(assetManager: AssetManager, modelPath: String, labelPath: String, inputSize: Int) {
- private var interpreter: Interpreter
- private var labellist: List<String>
- private val inputsize: Int = inputSize
- private val pixelsize: Int = 3
- private val imagemean = 0
- private val imagestd = 255.0f
- private val maxresults = 3
- private val threshold = 0.4f
- data class Recognition(
- var id: String = "",
- var title: String = "",
- var confidence: Float = 0F,
- val percent: Float = confidence*100
- ) {
- override fun toString(): String {
- return "Title = $title, Hasil Prediksi = $percent)"
- }
- }
- init {
- interpreter = Interpreter(loadModelFile(assetManager, modelPath))
- labellist = loadLabelList(assetManager, labelPath)
- }
- private fun loadModelFile(assetManager: AssetManager, modelPath: String): MappedByteBuffer {
- val fileDescriptor = assetManager.openFd(modelPath)
- val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
- val fileChannel = inputStream.channel
- val startOffset = fileDescriptor.startOffset
- val declaredLength = fileDescriptor.declaredLength
- return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
- }
- private fun loadLabelList(assetManager: AssetManager, labelPath: String): List<String> {
- return assetManager.open(labelPath).bufferedReader().useLines { it.toList() }
- }
- fun recognizeImage(bitmap: Bitmap): List<Recognition> {
- val scaledBitmap = Bitmap.createScaledBitmap(bitmap, inputsize, inputsize, false)
- val byteBuffer = convertBitmapToByteBuffer(scaledBitmap)
- val result = Array(1) { FloatArray(labellist.size) }
- interpreter.run(byteBuffer, result)
- return getSortedResult(result)
- }
- private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
- val byteBuffer = ByteBuffer.allocateDirect(4 * inputsize * inputsize * pixelsize)
- byteBuffer.order(ByteOrder.nativeOrder())
- val intValues = IntArray(inputsize * inputsize)
- bitmap.getPixels(intValues, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
- var pixel = 0
- for (i in 0 until inputsize) {
- for (j in 0 until inputsize) {
- val `val` = intValues[pixel++]
- byteBuffer.putFloat((((`val`.shr(16) and 0xFF) - imagemean) / imagestd))
- byteBuffer.putFloat((((`val`.shr(8) and 0xFF) - imagemean) / imagestd))
- byteBuffer.putFloat((((`val` and 0xFF) - imagemean) / imagestd))
- }
- }
- return byteBuffer
- }
- private fun getSortedResult(labelProbArray: Array<FloatArray>): List<Recognition> {
- Log.d("KlasifikasiDariGaleri", "List Size:(%d, %d, %d)".format(labelProbArray.size,labelProbArray[0].size,labellist.size))
- val pq = PriorityQueue(
- maxresults,
- Comparator<Recognition> {
- (_, _, confidence1), (_, _, confidence2)
- -> confidence1.compareTo(confidence2) * -1
- })
- for (i in labellist.indices) {
- val confidence = labelProbArray[0][i]
- if (confidence >= threshold) {
- pq.add(Recognition("" + i,
- if (labellist.size > i) labellist[i] else "Unknown", confidence)
- )
- }
- }
- Log.d("KlasifikasiDariGaleri", "pqsize:(%d)".format(pq.size))
- val recognitions = ArrayList<Recognition>()
- val recognitionsSize = pq.size.coerceAtMost(maxresults)
- for (i in 0 until recognitionsSize) {
- recognitions.add(pq.poll())
- }
- return recognitions
- }
- }
- Buat lagi class "DeteksiDariGaleri.kt" dan tambahkan class tersebut ke manifest.
- class DeteksiDariGaleri : AppCompatActivity() {
- private lateinit var mClassifier: KlasifikasiDariGaleri
- private lateinit var mBitmap: Bitmap
- private val mGalleryRequestCode = 2
- private val mInputSize = 224
- private val mModelPath = "Klasifikasi_3_Varietas_Beras_MobileNet.tflite"
- private val mLabelPath = "Klasifikasi_3_Varietas_Beras_MobileNet.txt"
- private val mSamplePath = "basmathi.jpg"
- private var lastProcessingTimeMs: Long = 0
- @SuppressLint("SetTextI18n")
- @RequiresApi(Build.VERSION_CODES.O)
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- if (supportActionBar != null) {
- (supportActionBar as ActionBar).title = "Pendeteksi Varietas Beras"
- }
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- setContentView(R.layout.activity_import_gallery)
- mClassifier = KlasifikasiDariGaleri(assets, mModelPath, mLabelPath, mInputSize)
- resources.assets.open(mSamplePath).use {
- mBitmap = BitmapFactory.decodeStream(it)
- mBitmap = Bitmap.createScaledBitmap(mBitmap, mInputSize, mInputSize, true)
- mPhotoImageView.setImageBitmap(mBitmap)
- }
- mGalleryButton.setOnClickListener {
- val callGalleryIntent = Intent(Intent.ACTION_PICK)
- callGalleryIntent.type = "image/*"
- startActivityForResult(callGalleryIntent, mGalleryRequestCode)
- }
- mDetectButton.setOnClickListener {
- val startTime = SystemClock.uptimeMillis()//menghitung waktu awal
- val results = mClassifier.recognizeImage(mBitmap).firstOrNull()
- mResultTextView.text= results?.title+"\n Probabilitas: "+results?.percent+"%"
- lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime//menghitung lamanya proses
- val waktu = lastProcessingTimeMs.toString()//konversi ke string
- delaytime.text = "$waktu ms "
- }
- }
- @SuppressLint("MissingSuperCall", "SetTextI18n")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if(requestCode == mGalleryRequestCode) {
- if (data != null) {
- val uri = data.data
- try {
- mBitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, uri)
- } catch (e: IOException) {
- e.printStackTrace()
- }
- println("Selesai!")
- mBitmap = scaleImage(mBitmap)
- mPhotoImageView.setImageBitmap(mBitmap)
- }
- } else {
- Toast.makeText(this, "Unrecognized request code", Toast.LENGTH_LONG).show()
- }
- }
- fun scaleImage(bitmap: Bitmap?): Bitmap {
- val orignalWidth = bitmap!!.width
- val originalHeight = bitmap.height
- val scaleWidth = mInputSize.toFloat() / orignalWidth
- val scaleHeight = mInputSize.toFloat() / originalHeight
- val matrix = Matrix()
- matrix.postScale(scaleWidth, scaleHeight)
- return Bitmap.createBitmap(bitmap, 0, 0, orignalWidth, originalHeight, matrix, true)
- }
- }
- Kemudian buat layout-nya dengan nama "activity_import_gallery".
- Atur jenis layout-nya menjadi "ConstraintLayout" seperti pada gambar.
- Tambahkan source code berikut pada text editor di bawah "android:layout_height="match_parent"" kemudian tekan Alt+Enter untuk menambahkan library tools yang dibutuhkan.
- android:background="@drawable/gradient_pendeteksi"
- tools:context=".DeteksiDariGaleri"
- Selanjutnya tambahkan source code berikut di bawah "tools:context=".DeteksiDariGaleri">".
- <TextView
- android:id="@+id/delaytime"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#CCFF6633"
- android:padding="2dp"
- android:fontFamily="times"
- android:gravity="right"
- android:text="@string/delay_time"
- android:textAlignment="gravity"
- android:textColor="#FFFF00"
- android:textSize="18sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- tools:layout_editor_absoluteX="0dp" />
- <TextView
- android:id="@+id/process_name_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="2dp"
- android:fontFamily="times"
- android:gravity="left"
- android:text=" Waktu Proses Deteksi : "
- android:textAlignment="gravity"
- android:textColor="#FFFF00"
- android:textSize="18sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- tools:layout_editor_absoluteX="0dp" />
- <TextView
- android:id="@+id/hints_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#80000000"
- android:fontFamily="times"
- android:gravity="center"
- android:padding="2dp"
- android:text="@string/hints"
- android:textAlignment="gravity"
- android:textColor="#FFFFFF"
- android:textSize="16sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.05"
- tools:layout_editor_absoluteX="0dp" />
- <Button
- android:id="@+id/mGalleryButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="0dp"
- android:text="@string/buttonSelectPhoto"
- android:textColor="@android:color/holo_green_dark"
- android:textSize="18sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/hints_bar" />
- <ImageView
- android:id="@+id/mPhotoImageView"
- android:layout_width="360dp"
- android:layout_height="360dp"
- android:contentDescription="@string/descriptionImage"
- app:layout_constraintBottom_toTopOf="@+id/mDetectButton"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/mGalleryButton"
- app:layout_constraintVertical_chainStyle="packed"
- app:srcCompat="@android:color/darker_gray" />
- <Button
- android:id="@+id/mDetectButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:background="#8000CC33"
- android:padding="7dp"
- android:shadowColor="@android:color/darker_gray"
- android:text="Deteksi"
- android:textColor="@android:color/black"
- android:textSize="24sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintBottom_toTopOf="@+id/mResultTextView"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent" />
- <TextView
- android:id="@+id/mResultTextView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="0dp"
- android:background="#9000CC33"
- android:fontFamily="times"
- android:text="@string/display_result"
- android:textAlignment="center"
- android:textColor="#000099"
- android:textSize="16sp"
- android:textStyle="bold"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent" />
- Tambahkan lagi library skema layout untuk baris program "app".
- Selanjutnya buat nilai string untuk "delay_time" dan isi value-nya "… ms ".
- Buat string untuk "hints" dan isikan value-nya dengan "Pilih Gambar Lalu Klik DETEKSI".
- Buat string untuk "buttonSelectPhoto", kemudian isi value-nya "Pilih Gambar".
- Buat string untuk "descriptionimage", isikan value-nya "Lihat untuk Menampilkan Gambar".
- Buat string untuk "display_result" dan isikan secara manual "<b>Hasil Prediksi</b>\n".
- Kembali ke "DeteksiDariGaleri.kt" untuk meingimpor library import gallery.
- Setelah itu buat class baru "SplashScreen" yang digunakan untuk menampilkan layout awal memulai ketika aplikasi Android baru dijalankan.
- class SplashScreen : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- //menghilangkan ActionBar
- this.requestWindowFeature(Window.FEATURE_NO_TITLE)
- setContentView(R.layout.activity_splash_screen)
- //tvSplash = findViewById(R.id.tvSplash) as TextView
- //setelah loading maka akan langsung berpindah ke MainActivity
- val handler = Handler()
- handler.postDelayed(Runnable {
- startActivity(Intent(applicationContext, MainActivity::class.java))
- finish()
- }, 3000L) //3000 L = 3 detik
- }
- }
- Kemudian tambahkan layout-nya "activity_splash_screen".
- Atur jenis layout-nya seperti pada gambar di bawah 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"
- android:id="@+id/activity_splash_screen"
- android:background="@drawable/gradient_splash_screen"
- android:gravity="center"
- tools:context=".SplashScreen">
- <TextView
- android:id="@+id/tvSplash"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_marginTop="280dp"
- android:fontFamily="sans-serif-smallcaps"
- android:text="Klasifikasi Varietas Beras"
- android:textAlignment="center"
- android:textColor="@android:color/holo_green_dark"
- android:textSize="24sp"
- android:textStyle="bold" />
- <ImageView
- android:id="@+id/logo_kvb"
- android:layout_width="match_parent"
- android:layout_height="100dp"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_marginTop="176dp"
- android:scaleType="centerInside"
- android:src="@drawable/logo_kvb" />
- <ImageView
- android:id="@+id/logo_tflite"
- android:layout_width="match_parent"
- android:layout_height="20dp"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_marginTop="320dp"
- android:scaleType="centerInside"
- android:src="@drawable/tflite" />
- <ProgressBar
- android:id="@+id/progresBar"
- style="@style/Widget.AppCompat.ProgressBar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="60dp" />
- </RelativeLayout>
- Tambahkan warna latar belakangnya jika Anda mau, jika tidak lewati saja.
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:type="linear"
- android:startColor="@android:color/holo_blue_bright"
- android:endColor="@android:color/holo_green_light"
- android:angle="-90"/>
- </shape>
- Tambahkan juga logonya jika Anda mau, jika tidak hapus baris program ini.
- Untuk menambahkan logo maka copy gambar dari Windows Explorer.
- Kemudian paste-kan di direktori app → res → drawable → (di sini).
- Selanjutnya pilih yang hanya drawable saja dan klik "OK".
- Setelah itu samakan nama logonya dengan yang di layout.
- Jika sukses maka tampilannya akan seperti pada gambar di bawah.
- Jika Anda ingin menambahkan logo lain di bawahnya misal "TensorFlow Lite" maka langkahnya sama saja seperti pada langkah 63-68 di atas.
- Sesudah itu buat class "Tentang.kt" dan tambahkan juga ke manifest. Class pada aktivitas ini berfungsi untuk menampilkan halaman informasi aplikasi Android.
- Buat layout-nya dengan nama "activity_tentang".
- Atur jenis layout-nya menjadi "ScrollView" agar dapat digulir ke atas/bawah.
- Kemudian lengkapi source code-nya seperti di bawah ini :
- <ScrollView 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:background="@drawable/gradient_tentang"
- android:textAlignment="center"
- tools:context=".Tentang">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="24sp"
- android:fontFamily="times"
- android:text="Klasifikasi Varietas Beras"
- android:background="#20000000"
- android:textAlignment="center"
- android:layout_marginLeft="0dp"
- android:layout_marginRight="0dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
- android:textColor="@android:color/holo_blue_dark"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Versi 2.0"
- android:fontFamily="times"
- android:background="#80FFFFFF"
- android:textSize="16sp"
- android:textAlignment="center"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:textColor="@android:color/holo_green_dark"/>
- <TextView
- android:textSize="14dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/update"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:lineSpacingMultiplier="1"
- android:textColor="#000099"/>
- <TableLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp">
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#000099"
- android:text="1. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_update_1"
- android:textColor="#000099"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#000099"
- android:text="2. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_update_2"
- android:textColor="#000099"/>
- </TableRow>
- </TableLayout>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/content_about"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:lineSpacingMultiplier="1"
- android:textColor="@android:color/black"
- android:autoLink="all"
- android:linksClickable="true"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- android:text="Author : Vidi Fitriansyah Hidarlan"
- android:layout_marginTop="50dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- android:textColor="@android:color/black"/>
- </LinearLayout>
- </ScrollView>
- Buat warna latar belakangnya seperti berikut jika diinginkan.
- Ubah source code "gradient_tentang.xml"nya menjadi seperti berikut :
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:type="linear"
- android:startColor="@android:color/holo_blue_bright"
- android:centerColor="#FFFF66"
- android:endColor="@android:color/holo_green_light"
- android:angle="225"/>
- </shape>
- Selanjutnya buat string untuk "update" kemudian edit value-nya pada strings.xml.
- Lakukan hal yang sama untuk membuat nilai resources string dari "content_update_1","content_update_2","content_about" dan isikan nilai string-nya masing-masing dengan source code di bawah seperti pada gambar berikut.
- <string name="content_about">Aplikasi ini dibuat hanya untuk klasifikasi tiga varietas beras (Basmathi, IR-64, dan Ketan) menggunakan sebuah metode CNN (<i>Convolutional Neural Network</i>) dengan model arsitektur MobileNetV1. Arsitektur tersebut dilatih pada <i>Google Colaboratory</i>. Setelah dilatih disimpan modelnya dan dikonversi menggunakan <i>TensorFlow Lite</i>. Setelah dikonversi diunduh <i>file</i> modelnya dan di-<i>import</i> untuk diimplementasikan pada <i>smartphone Android</i> agar dapat melakukan klasifikasi varietas beras secara langsung menggunakan sebuah perangkat Android.\n\n
- Pada aplikasi ini juga diatur secara otomatis penggunaan <i>flash</i> kameranya agar perubahan cahaya yang ada tidak berbeda signifikan ketika berada di luar ruangan yang banyak cahaya maupun di dalam ruangan yang minim cahaya karena cahaya <i>flash</i> kamera <i>smartphone</i> dapat menyala dan mati secara otomatis berdasarkan intensitas cahaya yang ada. Hal itu dapat mempermudah pengguna untuk mendeteksi varietas berasnya.\n\n
- <i>Sourcecode</i> program pada <i>Google Colaboratory</i> dan <i>Android Studio</i> : https://github.com/Soedirman-Machine-Learning/rice-varieties-classification\natau\nhttps://github.com/Vidi005/Klasifikasi-3-Varietas-Beras</string>
- <string name="update"><b>Pembaruan Fitur :</b></string>
- <string name="content_update_1">Versi 1.0\n- Klasifikasi 3 varietas beras dengan kamera\n- Memperbaiki <i>bug force closed</i>\n- Mengubah aspek rasio kamera menjadi 4:3\n- Menambah fitur waktu respon deteksi</string>
- <string name="content_update_2">Versi 2.0\n- Menambah fitur deteksi dengan impor galeri</string>
- Sehingga tampilan preview pada layout tentang akan seperti ini.
- Selanjutnya kita mengedit tampilan layout untuk "activity_bantuan.xml". Buat gambar untuk background-nya jika Anda mau dan letakan di dalam folder drawable.
- Lengkapi source code-nya seperti di bawah ini :
- <?xml version="1.0" encoding="utf-8"?>
- <ScrollView 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:background="@drawable/background"
- tools:context=".Bantuan">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:textStyle="bold"
- android:fontFamily="times"
- android:text="1. Cara Kerja Aplikasi"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
- android:textColor="@android:color/holo_green_dark"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/content_work"
- android:background="#80FF6600"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:lineSpacingMultiplier="1"
- android:textColor="#FFFFFF"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:textStyle="bold"
- android:fontFamily="times"
- android:text="2. Cara Menggunakan"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
- android:textColor="@android:color/holo_green_dark"/>
- <TableLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp">
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="1. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_1"
- android:textColor="#FFFFFF"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="2. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_2"
- android:textColor="#FFFFFF"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="3. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_3"
- android:textColor="#FFFFFF"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="4. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_4"
- android:textColor="#FFFFFF"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="5. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_5"
- android:textColor="#FFFFFF"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="6. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_6"
- android:textColor="#FFFFFF"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_marginRight="16dp"
- android:textColor="#FFFFFF"
- android:text="7. "/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="14sp"
- android:layout_weight="1"
- android:text="@string/content_step_7"
- android:textColor="#FFFFFF"/>
- </TableRow>
- </TableLayout>
- </LinearLayout>
- </ScrollView>
- Buat string untuk "content_work", masukkan value-nya kosong saja karena nanti akan dimasukkan secara manual ke dalam "strings.xml".
- Lakukan hal yang sama pada string "content_step_1" sampai dengan "content_step_7" seperti pada langkah 81. Kemudian isi masing-masing nilai value-nya seperti ini.
- <string name="content_work">Aplikasi ini bekerja dengan cara mengambil citra/gambar obyek dari varietas beras menggunakan kamera <i>smartphone</i> atau mengimpor gambar dari galeri foto yang sudah tersimpan. Setelah itu diekstrak fitur dari piksel obyek tersebut dan diubah ke dalam bentuk <i>array</i> dengan ukuran piksel 224 x 224 dengan format gambar berwarna (RGB). Kemudian dilatih dan diproses menggunakan model yang sudah dilatih sebelumnya sehingga dapat diprediksi hasil keluaran dari varietas beras yang dideteksi.</string>
- <string name="content_step_1">Buka Aplikasi Klasifikasi Varietas Beras kemudian pilih menu "DETEKSI BERAS DENGAN KAMERA" untuk klasifikasi secara langsung</string>
- <string name="content_step_2">Setelah terbuka menu tersebut arahkan kamera ke obyek varietas beras yang ingin dideteksi (jarak kamera dengan obyek sekitar 10 cm), kemudian klik/tap tombol "Deteksi"</string>
- <string name="content_step_3">Hasil prediksi akan muncul beberapa saat setelah menekan tombol "Deteksi" di bagian bawah layar dalam persentase (% diurutkan dari yang tertinggi). Persentase tertinggi merupakan hasil prediksi yang paling mendekati/mirip</string>
- <string name="content_step_4">Buka menu "DETEKSI BERAS DARI GALERI" jika ingin mengklasifikasi secara manual dengan mengimpor gambar yang sudah tersimpan di galeri foto</string>
- <string name="content_step_5">Klik tombol "PILIH GAMBAR" dan cari gambar obyek beras yang ingin dideteksi</string>
- <string name="content_step_6">Selanjutnya tekan tombol DETEKSI dan hasil prediksinya akan muncul beberapa saat setelah menekannya di bagian bawah layar</string>
- <string name="content_step_7">Jika Anda ingin mengetahui penjelasan singkat tiap varietas beras pilih menu "DESKRIPSI VARIETAS BERAS, Menu "BANTUAN" untuk mengetahui cara penggunaan aplikasi ini, dan Menu "TENTANG APLIKASI" sebagai informasi pembuatan aplikasi ini.</string>
- Maka tampilan layout-nya akan seperti berikut.
- Kemudian kembali lagi ke "activity_deskripsi.xml" untuk mengedit layout-nya. Lengkapi source code programnya seperti di bawah ini.
- <?xml version="1.0" encoding="utf-8"?>
- <ScrollView 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:background="@drawable/background"
- tools:context=".Deskripsi">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="26sp"
- android:fontFamily="times"
- android:text="Deskripsi Tiga Varietas Beras (Basmathi, IR-64, dan Ketan)"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
- android:textColor="@android:color/holo_blue_dark"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:text="1. Varietas Beras Basmathi"
- android:textSize="20sp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:textColor="@android:color/holo_green_dark"/>
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- android:scaleType="fitCenter"
- android:src="@drawable/basmathi" />
- </FrameLayout>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/content_basmathi"
- android:background="#60FFFFFF"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:lineSpacingMultiplier="1"
- android:textColor="#FFFFFF"
- android:autoLink="all"
- android:linksClickable="true"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:text="2. Varietas Beras IR-64"
- android:textSize="20sp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:textColor="@android:color/holo_green_dark"/>
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- android:scaleType="fitCenter"
- android:src="@drawable/ir64" />
- </FrameLayout>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/content_ir64"
- android:background="#60FFFFFF"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:lineSpacingMultiplier="1"
- android:textColor="#FFFFFF"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:text="3. Varietas Beras Ketan"
- android:textSize="20sp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:textColor="@android:color/holo_green_dark"/>
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:adjustViewBounds="true"
- android:scaleType="fitCenter"
- android:src="@drawable/ketan" />
- </FrameLayout>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/content_ketan"
- android:background="#60FFFFFF"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginBottom="16dp"
- android:lineSpacingMultiplier="1"
- android:textColor="#FFFFFF"
- android:autoLink="all"
- android:linksClickable="true"/>
- </LinearLayout>
- </ScrollView>
- Kemudian masukkan gambar masing-masing jenis berasnya ke dalam folder drawable.
- Selain itu juga buat nilai string-nya masing-masing jenis beras.
- Isikan masing-masing nilai string-nya pada "string.xml".
- <string name="content_basmathi">Beras basmathi bersal dari negara india/pakistan, beras basmathi berasal dari bahasa sanskerta ‘basmathi’ artinya berarti harum atau wangi dalam bahasa india ia juga mempunyai maksud ”soft rice” yaitu lembut.\n
- Beras ini akan melar memanjang butiran, sedikit pera, mudah terurai butiran berasnya dan aromanya sangat harum, beras ini memang aromanya sangat harum. Keistimewaan lainnya, butiran atau buliran beras ini panjang dan kecil melebihi ukuran beras yang biasanya agak pendek dan bulat.\n
- Sumber : https://www.asshidiqaqiqah.com/kandungan-kandungan-beras-basmati/</string>
- <string name="content_ir64">Beras IR 64 merupakan salah satu jenis varietas padi sawah yang memiliki bentuk tegak, tinggi sekitar 115-126 cm. Gabahnya berbentuk ramping dan panjang dan berwarna kuning bersih. Padi jenis ini cocok ditanam di daerah lahan sawah irigasi dataran rendah sampai sedang.\n
- Beras IR 64 adalah jenis beras yang pulen jika dimasak menjadi nasi karena memiliki kadar amilosa sebanyak 23%. Bobot beras per seribu butir beras IR64 adalah 24,1 gram.\n
- Sumber : Suprihatno, B, Daradjat, dkk. 2010. Deskripsi Varietas Padi. Balai Besar Penelitian Tanaman Padi.\n</string>
- <string name="content_ketan">Beras ketan putih (Oryza sativa glutinosa) merupakan salah satu varietas padi yang termasuk dalam famili Graminae.\n
- Butir beras sebagian besar terdiri dari zat pati sekitar 80-85% yang terdapat dalam endosperma yang tersusun oleh granula-granula pati yang berukuran 3-10 milimikron. Beras ketan juga mengandung vitamin (terutama pada bagian aleuron), mineral dan air. Dari komposisi kimiawinya diketahui bahwa karbohidrat penyusun utama beras ketan adalah pati. Pati merupakan karbohidrat polimer glukosa yang mempunyai dua struktur yakni amilosa dan amilopektin.\n
- Sumber : http://www.alatcetakrengginang.com/2012/02/beras-ketan-sifat-fisika-kimianya.html</string>
- Sehingga tampilan layout-nya akan seperti berikut.
- Sesudah itu kita masuk ke tahap pembuatan intent untuk membuat tombol menu yang dapat berpindah menuju ke aktivitas lainnya. Untuk membuat intent pada Android perlu ditambahkan "View.OnClickListener" pada class "MainActivity.kt" yang berfungsi mendeteksi adanya penekanan tombol pada suatu aktivitas yang sudah dibuat.
- Kemudian tambahkan pada fungsi "onCreate" untuk memperkenalkan setiap tombol yang dibuat sesuai dengan nama id-nya masing-masing yang dipanggil pada layout "activity_main.xml". Selain itu juga dibuat fungsi untuk menambahkan kejadian "onClick" pada masing-masing tombol yang sudah dibuat. Selain itu tambahkan juga pada fungsi "onClick" untuk memindahkan suatu intent ketika adanya kondisi penekanan tombol.
- class MainActivity : AppCompatActivity(), View.OnClickListener {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- //memperkenalkan button yang sudah ditambahkan di layout activity_main.xml
- val btnPendeteksi: Button = findViewById(R.id.btn_move_detection)
- //menambahkan event onClick pada button btnMoveDetection
- btnPendeteksi.setOnClickListener(this)
- val btnImporGaleri: Button = findViewById(R.id.btn_move_import)
- btnImporGaleri.setOnClickListener(this)
- val btnDeskripsi: Button = findViewById(R.id.btn_move_description)
- btnDeskripsi.setOnClickListener(this)
- val btnBantuan: Button = findViewById(R.id.btn_move_help)
- btnBantuan.setOnClickListener(this)
- val btnTentang: Button = findViewById(R.id.btn_move_about)
- btnTentang.setOnClickListener(this)
- }
- override fun onClick(v: View) {
- when (v.id) {
- R.id.btn_move_detection -> {
- //menambahkan suatu Intent pada method onClick()
- val moveIntent = Intent(this@MainActivity, Pendeteksi::class.java)
- startActivity(moveIntent)
- }
- R.id.btn_move_import -> {
- val moveIntent = Intent(this@MainActivity, DeteksiDariGaleri::class.java)
- startActivity(moveIntent)
- }
- R.id.btn_move_description -> {
- val moveIntent = Intent(this@MainActivity, Deskripsi::class.java)
- startActivity(moveIntent)
- }
- R.id.btn_move_help -> {
- val moveIntent = Intent(this@MainActivity, Bantuan::class.java)
- startActivity(moveIntent)
- }
- R.id.btn_move_about -> {
- val moveIntent = Intent(this@MainActivity, Tentang::class.java)
- startActivity(moveIntent)
- }
- }
- }
- }
- Selanjutnya menuju layout "activity_main.xml" ganti jenis layout-nya menjadi "LinearLayout" dan lengkapi source code-nya seperti di bawah ini.
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout 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"
- tools:context=".MainActivity"
- android:orientation="vertical"
- android:background="@drawable/background"
- android:padding="16dp">
- <Button
- android:id="@+id/btn_move_detection"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="64dp"
- android:layout_marginBottom="32dp"
- android:textColor="@android:color/holo_green_dark"
- android:text="@string/pendeteksi"
- android:textStyle="bold"/>
- <Button
- android:id="@+id/btn_move_import"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="32dp"
- android:textColor="@android:color/holo_green_dark"
- android:text="@string/import_gallery"
- android:textStyle="bold"/>
- <Button
- android:id="@+id/btn_move_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="32dp"
- android:textColor="@android:color/holo_green_dark"
- android:text="@string/deskripsi"
- android:textStyle="bold"/>
- <Button
- android:id="@+id/btn_move_help"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="32dp"
- android:textColor="@android:color/holo_green_dark"
- android:text="@string/bantuan"
- android:textStyle="bold"/>
- <Button
- android:id="@+id/btn_move_about"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="32dp"
- android:textColor="@android:color/holo_green_dark"
- android:text="@string/tentang"
- android:textStyle="bold"/>
- </LinearLayout>
- Setelah itu buat masing-masing nama string yang ditampilkan pada tombol.
- <string name="pendeteksi">Deteksi Beras dengan Kamera</string>
- <string name="import_gallery">Deteksi Beras dari Galeri</string>
- <string name="deskripsi">Deskripsi Varietas Beras</string>
- <string name="bantuan">Bantuan</string>
- <string name="tentang">Tentang Aplikasi</string>
- Berikutnya menuju "styles.xml" yang terletak tepat di bawah "strings.xml" pada folder values. Kemudian ubah source code-nya menjadi seperti ini.
- <resources>
- <!-- Base application theme. -->
- <style name="AppThemeSplashScreen" parent="Theme.AppCompat.Light.NoActionBar">
- <!-- Customize your theme here. -->
- <item name="colorPrimary">@color/colorPrimary</item>
- <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
- <item name="colorAccent">@color/colorAccent</item>
- </style>
- <!-- Main application theme. -->
- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
- <!-- Customize your theme here. -->
- <item name="colorPrimary">@android:color/holo_blue_dark</item>
- <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
- <item name="colorAccent">@color/colorAccent</item>
- </style>
- </resources>
- Kembali lagi ke "AndroidManifest.xml" dan lengkapi source code-nya seperti berikut untuk mengatur tampilan aktivitas awal dan orientasi tiap aktivitas.
- <activity android:name=".SplashScreen"
- android:label="@string/app_name"
- android:noHistory="true"
- android:screenOrientation="portrait"
- android:theme="@style/AppThemeSplashScreen">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <activity android:name=".Bantuan" />
- <activity android:name=".Deskripsi" />
- <activity android:name=".Pendeteksi"
- android:screenOrientation="portrait"
- android:colorMode="wideColorGamut">
- </activity>
- <activity android:name=".DeteksiDariGaleri"
- android:screenOrientation="portrait">
- </activity>
- <activity android:name=".MainActivity"
- android:screenOrientation="portrait">
- </activity>
- <activity android:name=".Tentang" />
- Sesudah itu membuat folder "assets" pada direktori "app" di dalam project tersebut. Folder tersebut digunakan untuk menyimpan model hasil pelatihan dari arsitektur CNN dalam format "*.tflite" beserta labelnya berformat "*.txt" dan untuk menyimpan gambar contoh untuk pengujian varietas beras dengan menggunakan fitur import gallery (mengambil gambar dari galeri foto). Cara pembuatannya dapat dilihat langsung pada gambar berikut.
- Pilih target sumber "main" kemudian klik "Finish".
- Tunggu sinkronisasi gradle pada project Android hingga selesai.
- Selanjutnya copy contoh gambar pengujian yang berformat (.jpg), model hasil pelatihan arsitektur CNN berformat (.tflite), dan labelnya berformat (.txt).
- Paste-kan di dalam direktori folder "assets" dan samakan namanya.
- Anda juga dapat membuat/mengedit isi labelnya secara langsung.
- Kembali ke "build.gradle (Module: app)", tambahkan implementasi untuk penampil gambar di dalam tag dependencies seperti pada gambar.
- //menampilkan sebuah custom ImageView dalam bentuk lingkaran
- implementation 'de.hdodenhof:circleimageview:3.0.0'
- Kemudian tambahkan perintah sesuai pada gambar di bawah untuk mencegah terjadinya kompresi pada file ".tflite" dan mencegah duplikasi saat proses build. Setelah itu lakukan sinkronisasi kembali pada "build.gradle" untuk menerapkan perunbahan.
- aaptOptions {
- noCompress "tflite"
- noCompress "lite"
- }
- packagingOptions {//mencegah duplikasi modul kotlin saat build aplikasi
- pickFirst 'META-INF/kotlinx-io.kotlin_module'
- pickFirst 'META-INF/atomicfu.kotlin_module'
- pickFirst 'META-INF/kotlinx-coroutines-io.kotlin_module'
- }
- Langkah terakhir adalah menambahkan ikon launcher pada aplikasi Android-nya. Caranya cukup mudah hanya perlu menyalin gambar ke dalam folder mipmap.
- Kemudian samakan namanya dengan yang ada pada "AndroidManifest.xml".
- Terakhir jalankan aplikasinya pada perangkat Android Anda. Untuk menjalankannya hubungkan HP Android Anda ke Laptop/Komputer dan aktifkan mode USB Debugging-nya yang terletak pada menu pengaturan "Developer Options". Kemudian tunggu nama device Anda sampai terdeteksi di Android Studio. Selanjutnya tekan tombol "Run".
- Tunggu sampai proses build dan instalasi aplikasi selesai.
- Berikut contoh tampilan aplikasinya setelah dijalankan di perangkat Android.
- Jika sudah berhasil dijalankan dan ingin meng-install-nya di perangkat lain, maka dapat dibuat juga file "*.apk"-nya dengan cara seperti berikut.
- Tunggu prosesnya hingga selesai, dan klik "locate" untuk menuju direktori dimana file aplikasi Android tersebut disimpan setelah di-build.
- File .apk sudah berhasil dibuat, sekarang Anda dapat mengirim/menyalinnya ke perangkat Android lainnya yang ingin Anda coba untuk di-install.
- Selesai !!
Tambahkan Source code program berikut tepat di atas tag "<application":
Jika muncul jendela seperti gambar di bawah maka pilih saja semua library yang dibutuhkan kemudian klik "OK". Hal tersebut biasanya terjadi ketika Anda menyalin potongan source code program dari luar yang tidak disertakan library-nya.
Lengkapi source code pada class tersebut seperti berikut :
Source code dependency untuk TensorFlow Lite :
Source code kelas Deteksi :
Source code class Pendeteksi.kt :
Source code "gradient_pendeteksi.xml" :
Source code untuk string hasil prediksi deteksi :
Source code dependency untuk cameraview-ex :
Lengkapi source code-nya seperti berikut :
Source code class "DeteksiDariGaleri.kt" :
Source code "SplashScreen.kt" :
Lengkapi source code-nya seperti berikut :
Kemudian ubah source code-nya seperti ini :
Nilai string yang diperlukan :
Nilai string untuk "content_step_1" sampai "content step_7" :
Nilai masing-masing string yang diperlukan :
Source code "MainActivity.kt" :
Source code "activity_main.xml" :
Nilai untuk tiap string-nya adalah :
Source code untuk "styles.xml" :
Source code ditempatkan di dalam tag "<application ...>(di sini)</application>" :
Source code dependency untuk menampilkan custom ImageView :
Perintah yang perlu ditambahkan pada "build.gradle (Module: app)" :
- Bingkai textbox yang berwarna biru merupakan program Bahasa Kotlin, sedangkan yang berwarna oranye adalah program dalam bahasa ".xml".
- Gambar logo dan contoh pengujian bisa Anda buat atau cari sendiri.
- Untuk source code lengkapnya dapat diunduh di sini.
Video tutorial hasil pelatihannya dapat dilihat di bawah.
Terima kasih atas kunjungannya semoga bermanfaat.