Perbandingan CPU dan CUDA Pada Gamma Correction

⏱️7 min read

Intro

Beberapa waktu yang lalu saya dan tim berhasil menyelesaikan tugas besar komputasi tersebar dan parallel dimana tim/kelompok saya memilih topik perbandingan performa operasi gamma correction menggunakan CPU dan CUDA. Jika ingin meliha full code untuk project ini dapat dilihat di Google Colaboratory pada link berikut : Link Google Colab

CPU vs GPU CUDA

Fungsi GPU (Graphics processing unit) hampir sama dengan CPU, yang membedakan CPU hanya memiliki beberapa core saja, sedangkan untuk GPU memiliki beberapa ribu core. Lebih tepatnya, fungsi utama GPU yaitu mengolah data grafis atau gambar. GPU biasanya didukung oleh adanya panel yang berfungsi sebagai antarmuka antara user dengan perangkat tersebut.

CUDA singkatan dari Compute Unified Device Architecture merupakan arsitektur komputer paralel yang dikembangkan NVIDIA. CUDA memiliki kemampuan melakukan komputasi yang rumit secara bersamaan sehingga prosess komputai bisa berjalan lebih cepat karena mempunyai bandwidth memori yang lebih besar dan GPU menggunakan Parallelization.

Untuk lebih jelas tentang perbedaan CPU dengan GPU simak video berikut :

Gamma Corection??

Menurut wikipedia:

Gamma correction or gamma is a nonlinear operation used to encode and decode luminance or tristimulus values in video or still image systems.


Gamma Correction merupakan salah satu operasi pada Image Processing yang digunakan untuk mengubah nilai Gamma pada gambar.Gamma dapat digambarkan sebagai hubungan antara input dan output yang dihasilkan. Untuk ruang lingkup percobaan ini inputnya adalah nilai intensitas RGB dari suatu gambar.

Gamma correction juga dikenal sebagai Power Law Transform. Pertama, intensitas piksel gambar kita harus diskalakan dari kisaran [0, 255]. Dari sana, kami mendapatkan gambar output gamma correction dengan menerapkan persamaan berikut:

formula

kemudian output diskalakan kembali ke kisaran [0, 255]. Pada percobaan kali ini kami menggunakan library openCV, numpy, numba, matplotlib, Pillow, dan timeit.

Referensi

https://thecryptmag.com/Online/57/imgproc_6.html

Pseudo code pada link referensi

  gammaCorrection = 1 / gamma
  colour = GetPixelColour(x, y)
  newRed = 255 * (Red(colour) / 255) ^ gammaCorrection
  newGreen = 255 * (Green(colour) / 255) ^ gammaCorrection
  newBlue = 255 * (Blue(colour) / 255) ^ gammaCorrection
  PutPixelColour(x, y) = RGB(newRed, newGreen, newBlue)

Implementasi

Gambar diambil dari web https://thispersondoesnotexist.com/ sama seperti nama websitenya, gambar orang yang muncul dalam website tersebut merupakan gambar buatan yang dibuat dengan menggunakan GAN (generative adversarial network). Gambar yang kita gunakan dalam percobaan ini berukuran

256x256 pixel
512x512 pixel
1024x1024 pixel
2048x2048 pixel
4096x4096 pixel
8192x8192 pixel

Import Library

import cv2
import numpy as np
import numba
import matplotlib.pyplot as plt
from PIL import Image
from timeit import default_timer as timer

Download Gambar dengan Menggunakan Perintah Linux Wget

#Download gambar

!wget https://raw.githubusercontent.com/mrizkitriyanto/Numba-Gamma-Correction/main/Images/thispersondoesntexist_256.jpg
!wget https://raw.githubusercontent.com/mrizkitriyanto/Numba-Gamma-Correction/main/Images/thispersondoesntexist_512.jpg
!wget https://raw.githubusercontent.com/mrizkitriyanto/Numba-Gamma-Correction/main/Images/thispersondoesntexist_1024.jpg
!wget https://raw.githubusercontent.com/mrizkitriyanto/Numba-Gamma-Correction/main/Images/thispersondoesntexist_2048.jpg
!wget https://raw.githubusercontent.com/mrizkitriyanto/Numba-Gamma-Correction/main/Images/thispersondoesntexist_4096.jpg
!wget https://raw.githubusercontent.com/mrizkitriyanto/Numba-Gamma-Correction/main/Images/thispersondoesntexist_8192.jpg

1

Load Gambar

Ketikkan kode berikut dan ulangi hingga sampai ukuran 8192 piksel

img_256 = cv2.imread('thispersondoesntexist_256.jpg')
img_256 = cv2.cvtColor(img_256, cv2.COLOR_BGR2RGB)
plt.imshow(img_256)

Output akan seperti berikut: 2

Pembuatan Dictionary Image

Dictionary digunakan untuk menyimpan nilai data dalam pasangan key:value. Penggunaan dictionary ini bertujuan untuk mempermudah dalam mengeksekusi. Masukkan kode sebagai berikut:

#Pembuatan dictionary supaya mudah dalam melakukan eksekusi secara langsung dengan looping

dictsImages = {} #dictionary kosong yang akan diisi dengan key (angka 0 - 3) dan values (gambar 256 hingga 8192)
keys = range(6)
values = [img_256, img_512, img_1024, img_2048, img_4096, img_8192]
for i in keys:
        dictsImages[i] = values[i]
print(dictsImages)

Output akan seperti berikut: 3

Akan terbentuk dictionary dengan pasangan key dan value. key mulai dari angka 0 hingga 3. Sedangkan value akan berisi array 3 dimensi yang merepresentasikan nilai tiap piksel.

Pembuatan Fungsi Untuk Operasi Gamma Correction Menggunakan CPU

def gamma_correction_seq (image, gamma):
  init_image = np.zeros(image.shape, np.uint8) #inisialisasi variabel init_image dengan value setiap index pada matrix = 0 menggunakan numpy.zeros
  for i in range(image.shape[0]):  # baris
    for j in range(image.shape[1]):  # kolom
      init_image[i][j] = 255.0 * (image[i][j] / 255.0)**(1 / gamma) #mengisi setiap index matrix dengan formula gamma correction yang didapat dari https://thecryptmag.com/Online/57/imgproc_6.html
  return init_image # mengembalikan matrix init_image

Pembuatan Fungsi Untuk Operasi Gamma Correction Menggunakan Cuda

Disini kami menggunakan Numba Vectorize Decorator. Apa itu Numba Vectorize Decorator? Vektor Numba memungkinkan fungsi Python mengambil argumen input skalar untuk digunakan sebagai ufuncs NumPy. Menggunakan vectorize(), Kita dapat menulis fungsi sebagai operasi pada skalar input, bukan array. Numba akan menghasilkan loop (atau kernel) di sekitarnya yang memungkinkan iterasi yang efisien atas input aktual.

Dokumentasi numba

@numba.vectorize('uint8(uint8,float64)',target='cuda')     #menggunakan numba vectorize decorator dengan target cuda (GPU)

#######
#Arti dari fungsi ini adalah gamma_correction_par yang menggunakan numba vectorize(numba telah membuatkan loop(atau kernel) yang  memungkonkan iterasi atas inputan kita)
#kemudian fungsi gamma_correction_par akan mengembalikan nilai setiap index matrix yang valuenya adalah hasil dari formula gamma correction
#######
def gamma_correction_par(image,gamma):
  return 255.0 * (image / 255.0)**(1 / gamma)

Menghitung Waktu Runtime CPU

time_seq = {} #dictionary untuk waktu runtime
output_seq = {} #dictionary untuk output matrix
for keys,values in dictsImages.items():
  start = timer()
  output_seq[keys]=gamma_correction_seq(values, 2)
  time_seq[keys] =  timer() - start
time_seq #waktu runtime untuk setiap key dan value

Output akan menampilkan waktu runtime untuk setiap key dan value 4

Menghitung Waktu Runtime CUDA

time_par = {} # dictionary untuk waktu runtime
output_par = {} # dictionary untuk output matrix
for keys,values in dictsImages.items():
  start = timer()
  output_par[keys]=gamma_correction_par(values, 2)
  time_par[keys] =  timer() - start
time_par # waktu runtime untuk setiap key dan value

5

Perbandingan Gambar Hasil Operasi

Original
CPU
CUDA

Gambar Original

Gambar CPU

Gambar CUDA

Grafik Perbandingan

#Grafik Perbandingan Waktu Runtime CPU vs CUDA

def graph(dictionary_sequential,dictionary_parallel,title):
  plt.figure(figsize=(20,12))
  x = sorted(list(map(int,dictionary_sequential.keys()))) #Mengambil dan mengurutkan Key yang diambil dari dictionary time_seq
  y_seq= [dictionary_sequential[i] for i in x] # mengambil waktu eksekusi sequential
  y_par= [dictionary_parallel[i] for i in x] # mengambil waktu eksekusi parallel
  labels = ["256x256", "512x512", "1024x1024", "2048x2048", "4096x4096", "8192x8192"] # Label untuk sumbu X
  plt.plot(x,y_seq,marker='o', c="r") # plot grafix sequential dengan warna merah
  plt.plot(x,y_par,marker='o', c="g") # plot grafik parallel dengan warna hijau
  plt.ylim(ymin=-1)
  plt.xticks(x,labels) # https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.xticks.html?highlight=xtick Mengubah untuk setiap koordinat yang semulanya berisi key dari dictionary [0, 1, 2, 3, 4, 5] menjadi label ["256x256", "512x512", "1024x1024", "2048x2048", "4096x4096", "8192x8192"]
  plt.xlabel('Resolusi gambar (px)')
  plt.ylabel('Waktu dalam satuan detik (s)')
  plt.legend(['Single CPU','CUDA'])
  plt.title(title)
  plt.show

Jalankan fungsi dengan memanggilnya

graph(time_seq,time_par,"Perbandingan Waktu Runtime Gamma Correction")

9

Berdasarkan proses Gamma Correction dengan mengguankan pendekatan sekuensial dan paralal dapat disimpulkan bahwa performa waktu terbaik berhasil dicapai oleh proses yang dilakukan dengan menggunakan pendekatan paralelism.

Cara Menjalankan Semua Cell Pada Google Colab

  1. Ubah runtime type terlebih dahulu menjadi CUDA caranya pilih menu Runtime -> Change Runtime Type -> pilih GPU -> Save.
  2. Kemudian jalankan cellnya