Estimasi Jarak Menggunakan YOLO dan Mediapipe Pose

2025-03-01

Gambar Utama

Sebelum adanya visi komputer, estimasi jarak objek dari kamera dilakukan dengan menggunakan pengukuran fisik. Namun, dengan kemajuan teknologi, kita dapat menggunakan algoritma deteksi objek dan analisis pose untuk memperkirakan jarak objek secara otomatis. Dalam tutorial ini, kita akan membahas dua metode untuk memperkirakan jarak objek dari kamera menggunakan bounding box dari model YOLO dan menggunakan koordinat dari Mediapipe Pose.

Tutorial ini hanya membutuhkan satu buah kamera dan satu objek yang ukurannya diketahui. Kita akan menggunakan YOLO untuk mendeteksi objek dan mendapatkan ukuran bounding box, serta Mediapipe Pose untuk mendapatkan koordinat dari titik-titik tubuh manusia. Terakhir kita juga membutuhkan niat dan usaha untuk belajar, tanpa banyak yapping lagi ayo kita mulai.

Contoh pertama akan kita mulai dari estimasi jarak menggunakan output bounding box, yang didapatkan dari model yolo. Kita akan menggunakan konsep focal length pixel untuk pendekatan ini. Focal length pixel adalah jarak antara lensa kamera dan sensor gambar dalam satuan piksel. Kita dapat menghitung focal length pixel dengan rumus sebagai berikut :

fp=D×hbHof_p=\frac{D\times h_b}{H_o}

Dimana :

  • fp = focal length pixel
  • D = jarak objek nyata (ex : 100cm).
  • Hb = tinggi bounding box dalam satuan pixel
  • Ho = tinggi objek nyata (ex : 180cm).

Pasti kalian bingung bagaimana caranya mendapatkan tinggi bounding box? Agar tidak bingung kita akan lihat implementasinya dalam kode berikut ini. Dimana kita akan mencoba mencari tinggi bounding box dengan kode berikut. Pastikan sudah setup venv dll.

python
import cv2
import time
import math
from ultralytics import YOLO

# Inisialisasi model YOLOv8
model = YOLO('best100epoch.pt')

# Inisialisasi video capture
cap = cv2.VideoCapture(0)
cap.set(3, 1366)
cap.set(4, 768)

def hitung_tinggi_bounding_box(y1, y2):
    return y2 - y1

while True:
    # Capture frame dari video
    ret, frame = cap.read()
    if not ret:
        break

    # Convert frame ke RGB
    img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # Deteksi objek menggunakan YOLO
    results = model.predict(img_rgb, stream=True)
    
    # Proses hasil deteksi
    for r in results:
        boxes = r.boxes
        for box in boxes:
            confidence = math.ceil((box.conf[0] * 100)) / 100
            if confidence > 0.7: #Set nilai confidence minimal 0.7 biar biar benda benda goib ga ikutan wkwkwk
                cls = int(box.cls[0])
                if cls == 0:  #Disini kita pakai manusia sebagai objek
                    x1, y1, x2, y2 = map(int, [box.xyxy[0][0], box.xyxy[0][1], box.xyxy[0][2], box.xyxy[0][3]])
                    tinggi_bounding_box = hitung_tinggi_bounding_box(y1, y2) #Jarak dari koordinat y1 y2 dihitung untuk dapat tingginya

                    # Gambar bounding box dan tinggi di frame
                    cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
                    cv2.putText(frame, f'Tinggi: {tinggi_bounding_box}px', (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)

    # Tampilkan frame
    cv2.imshow('Webcam', frame)
    
    # Tekan 'q' untuk keluar
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Release video capture
cap.release()
cv2.destroyAllWindows()

Setelah running kita cukup melihat ke pojok atas maka dapat dilihat nilai tinggi bounding box (px) dalam bentuk pixel yang siap diinputkan kedalam rumus pada paragraf sebelumnya. Kode yang gacor kan? itu merupakan tes1.py pada proyek kursi roda otonom yang saya kembangkan saat berkuliah. Print nilai tinggi tersebut menggunakan library OpenCv (cv2.) yang merupakan library untuk memproses citra dan video. Selain untuk print nilai Bounding box juga digambar dengan menggunakan fungsi cv2.rectangle. Fungsi cv2.rectangle digunakan untuk menggambar kotak persegi panjang pada citra. Intinya mantap.

Selanjutnya setelah kita mendapatkan Fp, kita dapat menghitung jarak objek dari kamera dengan menggunakan rumus berikut :

D=fp×HohbD = \frac{f_p \times H_o}{h_b}

Dimana :

  • D = jarak objek dari kamera dalam satuan cm
  • fp = focal length pixel
  • Ho = tinggi objek nyata (ex : 180cm)
  • hb = tinggi bounding box dalam satuan pixel.

Rumus sangat gacor dan mudah dipahami. Tidak perlu kita beli lidar mahal untuk mendapatkan jarak dari kamera. Cukup lakukan implementasi seperti pada kode berikut, pastikan sudah melakukan instalasi library ultralytics dll.

python
#Contoh penggunaannya (nilai dibawah ini hasil pengujian terdahulu)
focal_length_pixel = 481
tinggi_objek_nyata = 181

def hitung_jarak(tinggi_bounding_box, focal_length_pixel, tinggi_objek_nyata):
   if tinggi_bounding_box == 0:
      return float('inf')  #Handling error mantap
   jarak = (tinggi_objek_nyata * focal_length_pixel) / tinggi_bounding_box
   return jarak / 100

tinggi_bounding_box = y2 - y1 #pastikan sudah konversi integer ex : x1, y1, x2, y2 = map(int, [x1, y1, x2, y2]) 

#Masukan nilai-nilainya
jarak_meter = hitung_jarak(tinggi_bounding_box, focal_length_pixel, tinggi_objek_nyata)

#Tampilkan nilainya dengan OpenCV
cv2.putText(img, f'Distance Yolo: {jarak_meter:.2f}', (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 0), 2)

Namun perlu diingat cara ini memiliki beberapa kelemahan, yang pertama jarak yang dihasilkan dipengaruhi oleh performa model YOLO yang digunakan. Jika model tidak dapat mendeteksi objek dengan baik, maka estimasi jarak juga akan terpengaruh. Selain itu, cara ini juga tidak dapat digunakan untuk objek yang terlalu dekat dengan kamera, Ketika objek terlalu dekat dengan kamera, bounding box yang dihasilkan oleh YOLO dapat melampaui batas frame gambar, hal ini menyebabkan ukuran bounding box tidak akurat karena sebagian objek berada di luar area yang terdeteksi. Pada pengujian kursi roda otonom saya, jarak terdekat yang dapat terdeteksi adalah 1.3 meter. Hal ini disebabkan oleh ukuran objek yang terlalu kecil dalam frame gambar, sehingga bounding box yang dihasilkan tidak dapat memberikan estimasi jarak yang akurat. Untuk mengatasi masalah ini, kita perlu menggunakan metode lain untuk memperkirakan jarak objek dari kamera.

Solusinya ialah menggunakan Mediapipe Pose untuk mendapatkan koordinat dari titik-titik tubuh manusia. Mediapipe Pose adalah library yang digunakan untuk mendeteksi pose tubuh manusia dalam gambar atau video. Dengan menggunakan Mediapipe Pose, kita dapat mendapatkan koordinat dari titik-titik tubuh manusia, seperti bahu, pinggul, lutut, dan lainnya. Dengan mengetahui posisi titik-titik tubuh manusia, kita dapat menghitung jarak antara objek dan kamera dengan lebih akurat. Agar makin gacor kita bisa menyatukan keduanya, walaupun berat namun akurat presisi dan mantap.

Dalam menentukan jarak dengan menggunakan pose terdapat sebuah metode yaitu Euclidean. Euclidean dalam piksel adalah metode untuk mengukur jarak lurus antara dua titik dalam ruang gambar, yang biasanya diukur dalam piksel. Tanpa banyak basa basi berikut merupakan rumusanya :

dp=(x2x1)2+(y2y1)2×sfd_p = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} \times s_f

Dimana :

  • dp = jarak Euclidean dalam piksel
  • x1, y1 = koordinat titik pertama (x1, y1)
  • x2, y2 = koordinat titik kedua (x2, y2)
  • sf = skala faktor yang digunakan untuk mengonversi jarak Euclidean dalam piksel menjadi jarak nyata.

Setelah mendapatkan jarak dalam bentuk piksel kita bisa lanjut untuk mengkonversi jarak tersebut ke dalam satuan nyata. Kita bisa menggunakan rumus berikut :

Dm=(Kdp)D_m = \left(\frac{K}{{d_p}}\right)

Dimana :

  • Dm = jarak nyata dalam satuan cm
  • K = konstanta kalibrasi yang ditentukan berdasarkan pengukuran objek nyata dan jarak Euclidean dalam piksel.
  • dp = jarak Euclidean dalam piksel.

Untuk mendapatkan nilai K kita bisa menggunakan rumus berikut :

K=Jo×UpK = J_o \times U_p

Dimana :

  • Jo = ukuran objek nyata dalam satuan cm
  • Up = jarak Euclidean dalam piksel.

Dengan menggunakan rumus-rumus di atas, kita dapat menghitung jarak objek dari kamera dengan lebih akurat. Berikut adalah contoh implementasi kode untuk menghitung jarak objek menggunakan Mediapipe Pose dan Euclidean.

python
def hitung_jarak_euclidean(landmark1, landmark2, lebar_img):
    jarak_pix = math.sqrt((landmark1.x - landmark2.x) ** 2 + (landmark1.y - landmark2.y) ** 2) * lebar_img
    return jarak_pix

Fungsi ini menghitung jarak dalam piksel antara dua titik pose dari MediaPipe lalu mengalikan dengan lebar gambar untuk mendapatkan jarak dalam piksel. Setelah itu kita bisa mengkonversi jarak dalam piksel menjadi jarak nyata dengan menggunakan rumus yang telah kita bahas sebelumnya. Adapun penerapannya dalam kode adalah sebagai berikut :

python
k = 10.622

Setelah jarak Euclidean didapat, dikalikan dengan faktor konversi untuk memperkirakan lebar atau jarak dalam meter. Mantap kita sudah mendapatkan jarak objek dari kamera dengan menggunakan pose. Tinggal kita pilih saja landmark mana yang akan digunakan, misalnya bahu, pinggul, lutut, atau titik lainnya. Dengan menggunakan metode ini, kita dapat memperkirakan jarak objek dari kamera dengan lebih akurat dan dapat digunakan untuk berbagai aplikasi seperti pengenalan wajah, pelacakan objek, dan banyak lagi. Berikut contohnya menggunakan lengan kanan:

python
siku_kanan = pose_results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW]
pergelangan_kanan = pose_results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST
jarak_pix = hitung_jarak_euclidean(siku_kanan, pergelangan_kanan, lebar_img)


jarak_lengan = (k / jarak_pix) * 10
#cv2.putText jangan lupa
cv2.putText(img, f'MediaPipe Shoulder Distance: {jarak_mediapipebahu:.2f'}, (10 , 210), cv2.FONT_HERSHEY_SIMPLEX,0.6, (0,0,0),2)

Kita sudah berhasil mendapatkan jarak dengan dua metode yang berbeda. Cukup dengan satu kamera kita sudah bisa mendapatkan estimasi jarak objek dari kamera dengan akurasi yang cukup baik. Namun, perlu diingat bahwa hasil estimasi jarak ini dapat bervariasi tergantung pada kondisi pencahayaan, sudut pandang kamera, dan faktor lainnya. Oleh karena itu, penting untuk melakukan pengujian dan kalibrasi lebih lanjut untuk mendapatkan hasil yang lebih akurat. Intinya Mantap.