Halo semua! Sekarang kita akan lanjut lagi nih pembahasan mengenai algoritma Deep Learning YOLO versi keempat, setelah kita mengetahui keunggulan YOLOv4 dibandingkan versi sebelumnya, dan sudah mencoba berbagai aplikasi dengan YOLOv4, sekarang kita akan coba implementasikan proses klasifikasi dan menghitung kendaraan. Contohnya kita bisa lihat aplikasi Vehicle Counting Classification yang dibuat oleh perusahaan nodeflux.
Pada artikel ini lebih membahas bagaimana konsep logic dan penjelasan code, aplikasi ini kita akan buat dengan menggunakan bahasa C. Kenapa bukan python? menurutku, dalam pengaplikasian project yang skala besar, framework Darknet lebih efektif dibandingkan Darkflow, sehingga kita menggunakan bahasa C, alasan lengkapnya bisa cek disini.
Dalam pembuatan aplikasi ini dibutuhkan beberapa software yang harus diinstall pada PC (Personal Computer). Software tersebut adalah OpenCV sebagai aplikasi penunjang machine learning, CUDA Toolkit sebagai penyedia untuk mengembangkan aplikasi dengan kinerja GPU, CuDNN sebagai library untuk deep learning, Darknet sebagai framework untuk machine learning, YOLO sebagai algoritma pendeteksian dan pengklasifikasian objek dan Visual Studio sebagai text editor untuk file C language. Jika kalian ingin mencoba framework Darknet, kalian bisa liat tutorial instalasinya disini.
Setelah semua package sudah ter-install, pastikan juga semua file pendukung berupa file source dan file header untuk OpenCV C sudah terdapat di folder src. File source dan file header ini dipakai saat pembuatan aplikasi di file utama dengan memanggil semua file source dan file header ini untuk proses machine learning, seperti activation layer, detection layer, normalization layer dll. Pastikan juga kalian sudah mendownload 3 file penting dalam YOLO, yaitu coco.data, YOLOv4.cfg dan YOLOv4.weights.
Selanjutnya kita masuk ke code, pertama inisialisasi library-library pada OpenCV C Language yang digunakan untuk proses deep learning. Kita juga bisa menambahkan library MySQL sebagai database.
#include "image . h"
#include " utils.h"
#include " blas.h"
#include "cuda.h"
#include <stdio.h>
#include <math.h>
#include <opencv / cv.h>
#include <opencv / highgui.h>
#include <mysql.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include " stb_image_write.h"
#include " opencv2/core/version.hpp"
#include <opencv/cv.h>
#include <opencv/highgui.h>
#ifndef CV_VERSION_EPOCH
#include "opencv2/videoio/ videoio_c.h"
#include "opencv2/imgcodecs/imgcodecs_c.h"
#include "http_stream.h"
#endif
#include "h
Kemudian kita akan coba deklarasikan variabel-variabel dan beberapa function untuk melakukan proses object classification dan counting. Fungsi ini nantinya akan dipanggil sesuai kebutuhan.
int windows = 0 ;
int globalbot = 0 ;
int globalpoint1 ;
int globalpoint2 ;
int globalpoint3 ;int globalstart ;
int globalend ;
int sumCar ;
int sumMotor ;
int sumBus ;
int sumTruck ;
int sumLawanarus ;float colors[6][3] = {{1,0,1},{0,0,1},{0,1,1},{0,1,0},{1,1,0},{1,0, 0}};float get_color(int c,int x,int max){float ratio = ((float) x/max) * 5 ;
int i = floor(ratio) ;
int j = ceil(ratio) ;
ratio -= i ;
float r = (1-ratio) * colors[i][c] + ratio * colors[j][c];return r ;
}
Variabel windows = 0 digunakan untuk menampilkan hasil output, globalbot = 0 adalah variable nilai awal untuk proses membuat garis ROI Line bagian bawah (bottom). Terdapat 3 variabel penting yaitu globalpoint1, globalpoint2 dan globalpoint3. Point1 dan point2 digunakan untuk menentukan proses counting di hitung di area mana. Point1 dan point2 adalah jarak lebar, jadi proses counting kendaraannya nanti akan dihitung diantara value dari point 1 dan point 2. Point3 adalah untuk menentukan Panjang kordinat, karena setiap object yang terdeteksi memiliki kordinat atas, bawah, kanan dan samping. Value dari point3 ini dapat diatur sesuai Panjang area yang diinginkan.
Variabel selanjutnya adalah sumCar, sumMotor, sumBus dan sumTruck. Keempat variable ini digunakan untuk proses counting dengan masing-masing klasifikasi objeknya. Kemudian terdapat variable array colors, ini digunakan untuk mengaplikasikan warna untuk digunakan di label, di tampilan background dan tentunya di bagian bounding box.
void draw_detections_cv (Ipl Image*show_img , int num, float thresh ,box *boxes , float **probs , char **names , image **alphabet,
int classes){int i;for (i=0; i<num; ++i ) {
int class_id = max_index(probs[i], classes);
float prob = probs[i][class_id];if(prob > thresh) {
int width = show_img->height *.012;if(0) {
width = pow(prob, 1./2.) * 10+1 ;
alphabet = 0 ;}int offset = class_id * 123457 % classes;
float red = get_color(2,offset, classes);
float green = get_color(1 , offset, classes);
float blue = get_color (0, offset, classes);
float rgb [3];
rgb[0] = red;
rgb[1] = green;
rgb[2] = blue;
box b = boxes[i];
int left = (b.x - b.w / 2.) * show_img->width;
int right = (b.x + b.w/ 2.) * show_img->width;
int top = (b.y - b.h/ 2.) * show_img->height;
int bot = (b.y + b.h / 2.) * show_img->height;
Fungsi draw_detections_cv memanggil banyak parameter seperti fungsi dari IplImage, nilai thresh, variable alphabet dll. Pertama terdapat perulangan for dimana digunakan untuk menampilkan 2 variabel, yaitu class_id dan prob. int class_id = max_index(probs[i], classes); merupakan perintah untuk menampilkan class dari setiap objek dengan fungsi array dari probs sehingga variable prob akan menampilkan hasil dari probabilitas dan nama class dari object yang terdeteksi. Misalnya saat mobil terdeteksi, maka akan muncul output “80% Car“.
Kemudian terdapat sebuah kondisi dimana terdapat syarat object tersebut terdeteksi. if (prob > thresh) merupakan suatu kondisi suatu object akan terdeteksi jika nilai probabilitasnya diatas dari nilai thresh. Pada YOLO nilai default dari threshold adalah 0.6 atau 60%, maka dari itu jika prediksinya dibawah 60%, object tidak akan terdeteksi dan tidak akan muncul bounding boxnya. Kemudian disini mendeklarasikan variabel untuk menyimpan nilai koordinat dari masing-masing sisi bounding box (left, right, top, bottom).
globalbot = 0 ;if (bot > globalpoint1 && top < globalpoint2 && left > globalstart && right < globalend) {printf ("terdeteksi : %s,%d,%d,%d,%dn" , names[class_id],bot,top,
left,right);if (strcmp(names[class_id], "car") == 0) {
sumCar = sumCar + 1 ;
}else if(strcmp(names[class_id], "motorbike") == 0) {
sumMotor = sumMotor + 1 ;
}else if(strcmp(names[class_id], "bus") == 0) {
sumBus = sumBus + 1 ;
}else if(strcmp(names[class_id], "truck") == 0) {
sumTruck = sumTruck + 1 ;
}
Script diatas adalah validasi untuk perhitungan kendaraan yang melewati garis ROI. Jika ada bounding box dari suatu objek yang tersentuh dengan garis ROI, maka proses perhitungan akan berjalan. Kemudian terdapat validasi jenis kendaraan apa yang terdeteksi, maka akan terhitung secara otomatis +1 dan disimpan di variabel masing-masing.
MYSQL conn ;
MYSQL_RES res ;
char server = "localhost" ;
char user = "haiqal";
char password = " haiqal123!!"
char database = "counting" ;
conn = mysql_init(NULL);if(!mysql_real_connect(conn, server,
user, password, database, 0, NULL, 0)){
fprintf(stderr,"%sn", mysql_error(conn));
exit(1);
Script diatas digunakan untuk proses connecting ke MySQL dengan Bahasa C. Jika keempat variable tersebut memiliki value yang cocok, maka program sudah terkoneksi dengan database. Jika program tidak dapat terkoneksi dengan database, maka akan mencetak output berupa peringatan mysql_error kemudian akan keluar dari program aplikasi.
char query[1024];sprintf(query ,"UPDATE total SET total = total + 1 WHERE class =’%s ’;",names[class_id]);if(mysql_query(conn,query)){
fprintf(stderr,"%sn",mysql_error(conn));
exit(1);
}res = mysql_use_result(conn);
mysql_free_result(res);mysql_close(conn);}
Kemudian pada query database akan menjalankan perintah Update. sprintf(query, “UPDATE total SET total = total + 1 WHERE class =‘%s‘;“, names[class_id]); merupakan perintah untuk merubah field dari Total didalam database counting.
intpoint1 = 530; // garis hijau
intpoint2 = 540; // garis merah/panjang garisint start = 300; // titik mulai
int end = 780; // titik akhirglobalpoint1 = point1;
globalpoint2 = point2;
globalstart = start;
globalend = end;
ROI (Region of Interest) adalah salah satu fitur di pengolahan citra dimana terkadang user hanya menginginkan pengolahan hanya pada daerah/bagian tertentu dari citra. Daerah yang kita inginkan tersebut disebut dengan Region of Interest (ROI). Proses dalam membuat sebuah ROI ada banyak macam, bisa dalam membentuk sebuah line (garis), circle (lingkaran) atau rectangle (persegi Panjang). Pada aplikasi ini menggunakan ROI jenis line (garis), dimana garis tersebut akan digunakan untuk proses counting atau menghitung kendaraan di area tertentu.
Terdapat 2 garis yang akan dibuat, dimana proses counting akan dikerjakan diantara 2 titik kordinat dari point1 dan point2. Jadi misal value dari point1 = 530 dan value dari point2 = 540, maka proses counting akan terjadi di titik kordinat antara 531–539. User bisa mengubah titik kedua kordinat sesuai kebutuhan .Kemudian terdapat 2 variabel lagi yaitu start dan end. Kedua variable ini digunakan untuk membuat dan mengatur Panjang dari ROI line. Panjang dari ROI Line dimulai dari titik 0 pada garis kordinat x
cvLine (show_img, cvPoint(start, point1), cvPoint(end,point1), green_color, 2, CV_AA, 0);CvScalar red_color = CV_RGB(255 ,0, 0);cvLine(show_img, cvPoint(start, point2), cvPoint(end , point2), red_color,2 ,CV_AA, 0);
Perintah cvLine adalah sebuah fitur yang disediakan oleh OpenCV untuk menggambar sebuah garis. Pada OpenCV dengan Bahasa C bentuk umum dari cvLine adalah CvLine(CvArr*img, cvPoint pt1, cvPoint pt2, cvScalar color, int thickness=1, int line_type = 8, int shift = 0
char text[255];
sprintf(text, "Car: %d",(int)sumCar);
cvPutText(show_img, text, cvPoint(330,35), &font, black_clr);char text2[255];
sprintf(text2, "Motorbike : %d",(int)sumMotor);
cvPutText(show_img, text2, cvPoint(10,35), &font, black_clr);char text3[255];
sprintf(text3, "Bus : %d",(int)sumBus);
cvPutText(show_img, text3, cvPoint(650,35), &font, black_clr);char text4[255];
sprintf(text4, "Truck : %d",(int)sumTruck);
cvPutText(show_img, text4, cvPoint(970,35), &font ,black_clr);
Perintah yang dapat digunakan untuk membuat sebuah text string pada OpenCV adalah cvPutText. Bentuk umum cvPutText pada Bahasa C adalah cvPut- Text(CvArr* img, const char* text, CvPoint org, const CvFont* font, CvScalar color). Nantinya kita akan menampilkan jumlah perhitungan setiap kendaraan diatas screen.
darknet.exe detector demo cfg/coco.data cfg/yolov4.cfg weights/yolov4.weights [namavideo].mp4
Program bisa dijalankan dengan menggunakan Git CMD dengan perintah diatas.
Dari percobaan diatas, perbedaan yang paling mendasar dari Darknet dan Darkflow adalah kecepatan pemrosesan dan tingkat akurasi yang berbeda. Kita bisa menyesuaikan perhitungan kendaraan yang lebih spesifik, misalnya dengan men-training data untuk Bajaj, Becak maupun kendaraan lainnya.