Deteksi Objek dengan Faster RCNN

Muhammad IhsanMuhammad Ihsan
8 min read

Salah satu contoh studi kasus dalam bidang computer vision adalah untuk memberikan solusi agar bagaimana suatu sistem dapat "melihat" dan "memahami" objek-objek yang ada di dalam gambar sebagaimana bisa dilakukan oleh manusia. Task ini kemudian dikenal dengan deteksi objek (Object Detection). Jika kita lihat pada kasus klasifikasi, model melakukan identifikasi terhadap jenis objek yang ada di dalam gambar (misalnya, apakah ini adalah gambar kucing atau gambar anjing). Pada deteksi objek, penerapannya diperluan sampai pada tahap untuk menentukan lokasi objek tersebut di dalam gambar dengan cara menggambar kotak pembatas (bounding box) di sekitar objek.

Deteksi objek bisa memiliki peranan penting dalam kehidupan sehari-hari, mulai dari aspek keamanan, untuk pengendalian lalu lintas, sampai hingga teknologi kendaraan otonom. Kompleksitas tugas ini terletak pada dua aspek utama: pertama, bagaimana mendeteksi objek dengan berbagai ukuran, bentuk, dan orientasi di dalam gambar; kedua, bagaimana melakukan deteksi ini dengan cepat dan akurat, bahkan dalam kondisi dunia nyata yang sering kali penuh dengan kebisingan dan latar belakang yang rumit.

Mengenal Sekilas Faster R-CNN

Salah satu metode yang paling populer untuk deteksi objek adalah Faster R-CNN (Region-based Convolutional Neural Network). Faster R-CNN merupakan pengembangan dari metode R-CNN dan Fast R-CNN yang sudah dikembangkan sebelumnya, kelebihan dari Faster R-CNN adalah peningkatan signifikan dalam hal kecepatan dan akurasi deteksi objek. Meskipun untuk saat ini, sudah banyak metode lain yang lebih baru dan cepat dibandingkan Faster R-CNN. Mengetahui dan mencoba metode ini tetap layak dilakukan sebagai pengenalan terhadap metode-metode untuk object detection

Faster R-CNN memperkenalkan pendekatan melakukan integrasi Region Proposal Network (RPN) ke dalam arsitektur deteksi objek. Dengan adanya RPN, Faster R-CNN bisa menghasilkan proposal region (wilayah yang kemungkinan mengandung objek) secara lebih efisien dan dari fitur yang diekstraksi oleh jaringan konvolusi. Sehingga dimungkinkan proses deteksi objek secara end-to-end dalam satu jaringan, menghilangkan bottleneck yang ada pada metode sebelumnya yang mengandalkan proses eksternal seperti selective search. Penjelasan lebih lanjut tentang bagaimana Faster RCNN bekerja bisa dilihat lebih lanjut pada paper ini.

Implementasi Faster RCNN untuk Deteksi Objek

Di sini kita tidak melakukan pelatihan model dari awal, melainkan menggunakan pretrained model yang ada di torchvision. Sedangkan untuk gambar-gambar pada artikel ini bisa didapatkan di Kaggle. Pertama-tama kita perlu mengimpor beberapa library untuk projek ini:

import cv2
import torch
import requests
import numpy as np
import torchvision
from PIL import Image
from torch import no_grad
import matplotlib.pyplot as plt
from torchvision import transforms

Library yang kita impor merupakan library yang berhubungan dengan computer vision. Seperti OpenCV cv2 untuk pemrosesan gambar, kemudian numpy np untuk keperluan komputasi, PIL untuk memuat gambar, matplotlib plt untuk visualisasi dan Request untuk mengunduh gambar dari web.

Membuat Helper Function

# Function to get predictions with optional filtering by object and threshold
def get_predictions(pred, threshold=0.8, objects=None):
    """
    Assign a string name to predicted classes and filter out predictions below a given threshold.

    Args:
        pred: List containing tuples with class labels, probabilities, and bounding boxes.
        threshold: Minimum probability required to consider a prediction valid.
        objects: Optional list of object names to filter predictions.

    Returns:
        List of tuples containing class name, probability, and bounding box for each valid prediction.
    """
    predicted_classes = [(COCO_INSTANCE_CATEGORY_NAMES[i], p, [(box[0], box[1]), (box[2], box[3])])
                         for i, p, box in zip(list(pred[0]['labels'].numpy()),
                                              pred[0]['scores'].detach().numpy(),
                                              list(pred[0]['boxes'].detach().numpy()))]
    predicted_classes = [stuff for stuff in predicted_classes if stuff[1] > threshold]

    if objects and predicted_classes:
        predicted_classes = [(name, p, box) for name, p, box in predicted_classes if name in objects]

    return predicted_classes

Fungsi get_predictions dibuat untuk memproses dan menyaring prediksi yang dibuat oleh model deteksi objek. Fungsi ini menerima tiga parameter: pred sebagai output mentah dari model, threshold untuk menyaring hasil prediksi yang memiliki tingkat confidence rendah, dan objects dalam bentuk list yang sifatnya opsional berfungsi berdasarkan kelas tertentu. Pertama, fungsi ini mengubah prediksi mentah menjadi format yang lebih mudah dibaca dengan membuat daftar tuple yang berisi nama kelas, probabilitas deteksi, dan koordinat kotak pembatas. Kemudian, fungsi menyaring prediksi yang tidak memenuhi ambang batas probabilitas dan, jika parameter objects diberikan, hanya menyisakan prediksi yang sesuai dengan nama objek yang ditentukan. Hasil akhirnya adalah daftar tuple yang sudah difilter, yang siap digunakan untuk analisis atau visualisasi lebih lanjut.

# Function to draw bounding boxes around detected objects
def draw_box(predicted_classes, image, rect_th=1, text_size=1, text_th=1):
    """
    Draw bounding boxes and labels around detected objects in an image.

    Args:
        predicted_classes: List of tuples containing class name, probability, and bounding box.
        image: Image tensor on which boxes and labels will be drawn.
        rect_th: Thickness of the rectangle.
        text_size: Font size of the label text.
        text_th: Thickness of the label text.
    """
    img = (np.clip(cv2.cvtColor(np.clip(image.numpy().transpose((1, 2, 0)), 0, 1), cv2.COLOR_RGB2BGR), 0, 1) * 255).astype(np.uint8).copy()
    for predicted_class in predicted_classes:
        label, probability, box = predicted_class
        t, l = box[0]
        r, b = box[1]
        t, l, r, b = [round(item) for item in [t, l, r, b]]
        cv2.rectangle(img, (t, l), (r, b), (0, 255, 0), rect_th)  # Draw Rectangle
        cv2.putText(img, f"{label}: {str(round(probability, 2))}", (t, l), cv2.FONT_HERSHEY_SIMPLEX, text_size, (0, 255, 0), thickness=text_th)
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.show()

Fungsi draw_box dibuat untuk memvisualisasikan hasil model deteksi objek dengan menggambar bounding box dan label di sekitar objek yang terdeteksi. Fungsi ini juga menerima parameter berupa daftar kelas yang diprediksi, gambar yang akan diolah, ketebalan kotak pembatas, serta ukuran dan ketebalan teks label. Fungsi ini mengubah gambar dari format tensor ke array NumPy. Selanjutnya, fungsi ini mengiterasi daftar kelas yang diprediksi, mengekstrak informasi kelas, probabilitas, dan koordinat kotak pembatas untuk digambar di atas gambar menggunakan OpenCV. Setelah itu, label yang berisi nama kelas dan probabilitas ditambahkan di sudut kiri atas bounding box. Akhirnya, gambar dikonversi kembali ke format RGB (dan ditampilkan menggunakan Matplotlib, sehingga kita bisa melihat objek yang terdeteksi beserta bounding box dan labelnya.

# Function to clear GPU memory and delete images to free up RAM
def save_RAM(image_=False):
    """
    Clear GPU memory and delete image variables to free up RAM.

    Args:
        image_: Boolean flag to indicate if the image object should be deleted.
    """
    torch.cuda.empty_cache()
    global image, img, pred
    del img, pred
    if image_:
        image.close()
        del image

Fungsi save_RAM berfungsi untuk mengelola dan membebaskan memori, terutama dalam lingkungan di mana memori GPU terbatas dan perlu dikelola dengan hati-hati, seperti saat melakukan inferensi model deep learning. Fungsi ini berfokus pada pembersihan memori GPU dan secara opsional menghapus variabel gambar dari RAM. Caranya adalah dengan memanggil torch.cuda.empty_cache() untuk membersihkan cache memori GPU yang tidak terpakai, menggunakan del untuk menghapus variabel img dan pred dari memori, serta secara opsional menghapus variabel image jika parameter image_ diatur ke True.

Persiapan Model

# Load Pre-Trained Faster RCNN Model
model_ = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)
model_.eval()  # Set the model to evaluation mode

# Disable gradient computation for all parameters
for name, param in model_.named_parameters():
    param.requires_grad = False

print("Model loaded successfully.")

Selanjutnya adalah memuat model Faster R-CNN (Region-based Convolutional Neural Network) dengan backbone ResNet-50 dan Feature Pyramid Network (FPN) yang sudah dilatih sebelumnya untuk tugas deteksi objek. Model dimuat menggunakan fungsi torchvision.models.detection.fasterrcnn_resnet50_fpn() dengan argumen pretrained=True, yang berarti bobot model diinisialisasi dengan versi yang sudah dilatih sebelumnya. Kemudan model diatur ke mode evaluasi dengan model_.eval() untuk memastikan lapisan-lapisan yang ada berfungsi secara deterministik selama inferensi. Kode kemudian membekukan bobot model dengan mengatur requires_grad = False untuk setiap parameter, mencegah pembaruan selama inferensi.

# Function to get predictions from the model
def model(x):
    with no_grad():
        yhat = model_(x)
    return yhat

Fungsi model di atas digunakan menghasilkan prediksi dari model deteksi objek yang sudah dilatih sebelumnya.

# COCO class names
COCO_INSTANCE_CATEGORY_NAMES = [
    '__background__', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat',
    'traffic light', 'fire hydrant', 'N/A', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse',
    'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'N/A', 'backpack', 'umbrella', 'N/A', 'N/A', 'handbag',
    'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove',
    'skateboard', 'surfboard', 'tennis racket', 'bottle', 'N/A', 'wine glass', 'cup', 'fork', 'knife', 'spoon',
    'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake',
    'chair', 'couch', 'potted plant', 'bed', 'N/A', 'dining table', 'N/A', 'N/A', 'toilet', 'N/A', 'tv', 'laptop',
    'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'N/A', 'book',
    'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'
]

List di atas COCO_INSTANCE_CATEGORY_NAMES berisikan nama-nama kelas yang digunakan dalam dataset COCO (Common Objects in Context).

Deteksi Model

Setelah model siap, selanjutnya kita akan mencoba menggunakan model faster rcnn untuk memprediksi objek dalam berbagai gambar. Berikut beberapa contoh gambar yang akan kita lakukan deteksi objek di dalamnya

  1. Deteksi pada gambar berisikan satu orang.

  2. Deteksi pada gambar berisikan beberapa orang dalam satu gambar.

  3. Deteksi anjing dan kucing.

  4. Deteksi mobil dan pesawat

  5. Deteksi pada gambar yang diunduh dari internet.

# 1. Predicting a Person
img_path = '/kaggle/input/sample-images-for-object-detection/ronaldo.jpg'
image = Image.open(img_path)
image = image.resize([int(0.5 * s) for s in image.size])
plt.imshow(image)
plt.show()

transform = transforms.Compose([transforms.ToTensor()])
img = transform(image)
pred = model([img])
pred_class = get_predictions(pred, objects=["person"])
draw_box(pred_class, img)
save_RAM(image_=True)

# 2. Predicting People
img_path = '/kaggle/input/sample-images-for-object-detection/people.jpg'
image = Image.open(img_path)
image = image.resize([int(0.5 * s) for s in image.size])
plt.imshow(image)
plt.show()

img = transform(image)
pred = model([img])
pred_thresh = get_predictions(pred, threshold=0.9, objects=["person"])
draw_box(pred_thresh, img, rect_th=1, text_size=1, text_th=1)
save_RAM(image_=True)


# 3. Predicting Cat and Dog
img_path = '/kaggle/input/sample-images-for-object-detection/catanddog.jpg'
image = Image.open(img_path)
image = image.resize([int(0.5 * s) for s in image.size])
plt.imshow(image)
plt.show()

img = transform(image)
pred = model([img])
pred_thresh = get_predictions(pred, threshold=0.8)
draw_box(pred_thresh, img, rect_th=10, text_size=10, text_th=10)
save_RAM(image_=True)

# 4. Predicting a Car and a Plane
img_path = '/kaggle/input/sample-images-for-object-detection/carandplane.jpg'
image = Image.open(img_path)
image = image.resize([int(0.5 * s) for s in image.size])
plt.imshow(image)
plt.show()

img = transform(image)
pred = model([img])
pred_thresh = get_predictions(pred, threshold=0.9)
draw_box(pred_thresh, img)
save_RAM(image_=True)

# 5. Predicting on an Uploaded Image
url = 'https://www.plastform.ca/wp-content/themes/plastform/images/slider-image-2.jpg'
image = Image.open(requests.get(url, stream=True).raw).convert('RGB')
plt.imshow(image)
plt.show()

img = transform(image)
pred = model([img])
pred_thresh = get_predictions(pred, threshold=0.95)
draw_box(pred_thresh, img, rect_th=2, text_size=1.5, text_th=2)
save_RAM(image_=True)

Penutup

Pada artikel ini, kita sudah mencoba menggunakan model Faster R-CNN untuk melakukan deteksi objek. Apa yang kita lakukan barusan hanya bagian kecil, sehingga masih banyak eksplorasi lanjutan yang bisa kita coba sendiri nantinya. Seperti mencobanya pada data lain, otak-atik pada nilai threshold yang ada, sampai melakukan fine tuning pada model agar bisa melakukan deteksi objek pada benda-benda yang lebih banyak. Semoga artikel ini bermanfaat, terima kasih dan selamat belajar.


Source Code : Github / Kaggle

0
Subscribe to my newsletter

Read articles from Muhammad Ihsan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Muhammad Ihsan
Muhammad Ihsan

AI, ML and DL Enthusiast. https://www.upwork.com/freelancers/emhaihsan https://github.com/emhaihsan https://linkedin.com/in/emhaihsan