Draf

Klasterisasi Warna Foto

Manipulasi Foto dengan Klasterisasi K-Rerata

Bagaimana mengurangi variasi warna dalam sebuah foto? Salah satu caranya adalah dengan menerapkan klasterisasi k-rerata. Dengan metode ini, foto tersebut akan tersusun hanya dalam k warna. Ide dasar dan cara detailnya dijelaskan dalam artikel ini.
klasterisasi
tutorial
fotografi
Pengarang
Afiliasi
Terbit

October 1, 2025

Kata kunci

fotografi, klasterisasi, klasterisasi k-rerata, machine learning, pemelajaran mesin, pemelajaran statistik, statistical learning

Apa yang kamu lihat dari sebuah foto? Mungkin kamu melihat kenangan yang tersimpan. Mungkin kamu juga ingin menggali pesan yang tersirat. Oke, mari kita melihat sebuah foto atau gambar dari sudut pandang yang berbeda! Kita dapat melihat sebuah gambar sebagai matriks. Perhatikan Gambar 1!

Meme orang yang menatap selembar kertas mungil secara intens
Gambar 1: Meme seseorang yang menatap selembar kertas mungil secara intens

Apa yang dilihat oleh orang dalam meme pada Gambar 1? Gambar tersebut terlalu kecil. Mari kita memperbesar gambar tersebut dan menaruhnya ke bidang koordinat Kartesius. Kita lakukan hal itu dengan pemrograman R. Untuk itu, kita panggil paket-paket yang kita perlukan.

Paket-paketnya telah siap. Kita lanjut memperbesar gambar yang dilihat oleh orang dalam meme tersebut dan memposisikannya dalam bidang koordinat Kartesius. Perhatikan Gambar 2!

Gambar 2: Gambar epsilon berukuran 8 piksel × 8 piksel pada bidang koordinat Kartesius

Sekarang, apa yang kamu amati dari Gambar 2? Ternyata itu adalah simbol epsilon. Meskipun tampak kabur, piksel-piksel epsilon tersebut tampak jelas. Setiap piksel berkorespondensi dengan satu warna. Warna tersebut dapat dinyatakan ke dalam tiga warna dasarnya, yaitu RGB (red, green, dan blue). Dengan demikian, kita dapat menyatakan gambar tersebut sebagai tiga matriks warna dasar: \(R\), \(G\), dan \(B\).

[1] "Matriks R"
       1   2   3   4   5   6   7   8
[1,] 171 177 181 183 183 181 177 172
[2,] 177 183 156  53 117  67 138 177
[3,] 181 189  90  73 201 129 128 182
[4,] 183 192 163  58 140 174 192 184
[5,] 183 148  30 133 208 201 192 184
[6,] 181  91  23 197 149  49 189 182
[7,] 177 153  50 119  77 122 183 177
[8,] 172 177 182 184 184 182 177 172
[1] "Matriks G"
       1   2   3   4   5   6   7   8
[1,] 200 204 207 209 208 207 204 201
[2,] 204 208 184  98 151 110 170 204
[3,] 207 212 129 114 220 161 161 208
[4,] 209 214 188 101 169 198 214 209
[5,] 209 177  78 163 225 220 214 209
[6,] 207 130  73 217 177  94 212 208
[7,] 204 183  96 153 118 156 208 204
[8,] 201 204 208 209 209 208 204 201
[1] "Matriks B"
       1   2   3   4   5   6   7   8
[1,] 209 212 215 216 216 215 212 209
[2,] 212 216 195 121 166 131 183 212
[3,] 215 219 148 135 225 174 175 215
[4,] 216 220 198 124 182 206 220 216
[5,] 216 189 104 177 229 225 220 216
[6,] 215 148 100 222 188 118 219 215
[7,] 212 194 119 168 138 171 216 212
[8,] 209 212 215 216 216 215 212 209

Okay, kita telah dapat melihat sebuah gambar dengan menggunakan matriks-matriks \(R\), \(G\), dan \(B\). Kita dapat memasangkan nilai-nilai yang bersesuaian dari matriks tersebut sebagai triplet. Misalnya, komponen pada baris pertama dan kolom pertama dalam matriks-matriks \(R\), \(G\), dan \(B\), secara berturut-turut adalah 171, 200, dan 209. Dengan demikian, kita mendapatkan satu triplet \(\left( 171,200,209 \right)\). Dengan cara ini, kita juga dapat membuat 63 triplet lainnya.

Sekarang kita memiliki 64 triplet. Semua triplet yang merepresentasikan warna tersebut dapat kita plot dalam ruang dimensi tiga. Hasilnya ditunjukkan pada Gambar 3.

Gambar 3: Titik-titik dalam ruang yang merepresentasikan warna dalam gambar epsilon

Nah, sekarang kita dapat melihat sebuah gambar atau foto dengan menggunakan matriks, yaitu tiga matriks warna dasarnya. Dari tiga matriks tersebut, kita dapat mengkonstruksi titik-titik yang merepresentasikan warna tersebut ke dalam ruang dimensi tiga. Kita gunakan perspektif seperti ini untuk memanipulasi sebuah foto sehingga variasi warnanya tereduksi. Tujuan kita berikutnya adalah untuk membuat foto di sebelah kiri pada Gambar 4 menjadi seperti yang sebelah kanan. Hal ini telah dijanjikan pada pos sebelumnya.

Gambar 4: Foto asli (kiri) dan foto dengan reduksi warna (kanan)

Bagaimana cara mencapai tujuan tersebut? Mari kita bahas gambaran umumnya terlebih dahulu pada bagian berikutnya.

Ide Dasar

Untuk menyerhanakan warna foto sebelah kiri pada Gambar 4, kita gunakan perspektif yang telah dibahas pada bagian sebelumnya. Setelah itu, kita terapkan klasterisasi \(k\)-rerata terhadap titik-titik warna yang dihasilkan. Secara umum, tahapan-tahapan utama yang nanti kita lakukan adalah sebagai berikut.

  • Mengekstrak data warna R, G, dan B dari foto.

  • Klasterisasi \(k\)-rerata terhadap data data warna.

  • Mengganti setiap warna pada foto asli dengan pusat klasternya.

Dengan cara seperti itu, kita akan mendapatkan sebuah foto yang hanya memuat \(k\) warna. Warna-warna itu diperoleh dari pusat klaster-klaster yang terbentuk. Tak perlu berlama-lama, mari kita mulai tahapan pertamanya.

Mengekstrak Data Warna

Tahapan pertamanya adalah mengestrak data warna dari sebuah foto. Foto awal yang kita gunakan adalah foto sebelah kiri pada Gambar 4. Untuk melakukannya, kita gunakan kode berikut.

Kode
# Memuat dan menskala gambar
foto_ku <- image_read(
  "aset/karuna_donau.png"
) |> 
  image_scale("256x256!")

# Ekstrak data mentah piksel
data_mentah_foto <- image_data(foto_ku)

# Ukuran foto
lebar_foto  <- dim(data_mentah_foto)[2]
tinggi_foto <- dim(data_mentah_foto)[3]

# Konversi ke data frame dan membalik koordinat y
rgb_foto_df <- as.data.frame.table(data_mentah_foto, responseName = "nilai") |>
  mutate(
    kanal = as.integer(Var1),
    koord_x = as.integer(Var2),
    koord_y = as.integer(Var3),
    nilai   = as.integer(nilai)
  ) |>
  select(kanal, koord_x, koord_y, nilai) |>
  pivot_wider(
    names_from = kanal,
    values_from = nilai,
    names_prefix = "ch"
  ) |>
  transmute(
    koord_x,
    koord_y = tinggi_foto - koord_y + 1,
    R = ch1,
    G = ch2,
    B = ch3,
    R_norm = R / 255,
    G_norm = G / 255,
    B_norm = B / 255,
    warna_hex = rgb(R_norm, G_norm, B_norm)
  )
 head(rgb_foto_df)
# A tibble: 6 × 9
  koord_x koord_y     R     G     B R_norm G_norm B_norm warna_hex
    <int>   <dbl> <int> <int> <int>  <dbl>  <dbl>  <dbl> <chr>    
1       1     256   117   171   233  0.459  0.671  0.914 #75ABE9  
2       2     256   117   173   234  0.459  0.678  0.918 #75ADEA  
3       3     256   116   172   233  0.455  0.675  0.914 #74ACE9  
4       4     256   116   172   233  0.455  0.675  0.914 #74ACE9  
5       5     256   115   170   234  0.451  0.667  0.918 #73AAEA  
6       6     256   120   171   234  0.471  0.671  0.918 #78ABEA  

Dari kode di atas, kita mendapatkan data warna rgb_foto_df. Data tersebut memuat koordinat piksel-pikselnya (koord_x dan koord_y), warna dasar setiap pikselnya (R, G, dan B), dan warna-warna dasar tersebut yang telah diskala agar minimumnya 0 dan maksimumnya 1 (R_norm, G_norm, dan B_norm). Selain itu, kita juga membuat kolom warna_hex untuk merangkum informasi warna pada kolom-kolom R, G, dan B.

Mari kita selami rgb_foto_df lebih mendalam. Untuk itu, kita gambarkan diagram pencar untuk variabel-variabel warnanya, yaitu R, G, dan B. Perhatikan Gambar 5!

Gambar 5: Titik-titik dalam ruang yang merepresentasikan warna dalam foto

Klasterisasi \(K\)-Rerata

Selanjutnya, kita lakukan klasterisasi terhadap rgb_foto_df dengan menggunakan variabel-variabel R_norm, G_norm, dan B_norm. Untuk melakukannya, kita menggunakan fungsi kmeans(). Fungsi ini menggunakan input sebuah matriks. Oleh karena itu, terlebih dahulu kita membuat matriks tersebut. Matriks tersebut memuat tiga kolom, yaitu R_norm, G_norm, dan B_norm.

Kode
matriks_foto <- rgb_foto_df |> 
  select(R_norm, G_norm, B_norm) |> 
  as.matrix()

Kita telah memperoleh matriks_foto. Kita siap untuk melakukan klasterisasi \(k\)-rerata dengan fungsi kmeans().

Kode
set.seed(123)

# Pilih nilai k
k <- 4

# Lakukan klasterisasi k-rerata
hasil_krerata <- kmeans(
  x = matriks_foto,
  centers = k
)

Hasil klasterisasi tersebut kita simpan sebagai sebuah objek dengan nama hasil_krerata. Terdapat informasi penting yang terkandung dalam hasil_krerata, yaitu cluster (klaster) dan centers (pusat setiap klaster). Kita gunakan kedua informasi tersebut untuk menyederhanakan warna. Kita lakukan pada Bagian 4.

Menyederhanakan Warna

Tiba saatnya kita menyederhanakan warna. Caranya adalah dengan mengganti setiap warna dengan pusat klasternya. Oleh karena itu, kita akan menggunakan informasi cluster dan centers dari hasil_krerata. Hasil penyederhanaan warna ini kemudian kita namai dengan rgb_foto_klaster_df. Kita tampilkan juga baris-baris awal data tersebut agar kita dapat melihat hasilnya.

Kode
rgb_foto_klaster_df <- rgb_foto_df |> 
  mutate(
    klaster = hasil_krerata$cluster,
    pusat_R = hasil_krerata$centers[klaster, "R_norm"],
    pusat_G = hasil_krerata$centers[klaster, "G_norm"],
    pusat_B = hasil_krerata$centers[klaster, "B_norm"]
  ) |> 
  mutate(
    pusat_hex = rgb(pusat_R, pusat_G, pusat_B)
  ) |> 
  select(koord_x, koord_y, R, G, B, klaster, pusat_hex)

head(rgb_foto_klaster_df)
# A tibble: 6 × 7
  koord_x koord_y     R     G     B klaster pusat_hex
    <int>   <dbl> <int> <int> <int>   <int> <chr>    
1       1     256   117   171   233       3 #84B4DE  
2       2     256   117   173   234       3 #84B4DE  
3       3     256   116   172   233       3 #84B4DE  
4       4     256   116   172   233       3 #84B4DE  
5       5     256   115   170   234       3 #84B4DE  
6       6     256   120   171   234       3 #84B4DE  

Dari hasil di atas, kita dapat melihat bahwa rgb_foto_klaster_df lebih sederhana. Data ini hanya memuat \(k=4\) warna, seperti yang ditunjukkan sebagai berikut.

Kode
rgb_foto_klaster_df |> 
  group_by(pusat_hex) |> 
  summarise(
    n = n()
  )
# A tibble: 4 × 2
  pusat_hex     n
  <chr>     <int>
1 #47403B   27742
2 #5E7481   12175
3 #84B4DE   19353
4 #D0B78E    6266

Kita dapat melihat kembali titik-titik warnanya ke dalam ruang dimensi tiga agar dapat mengamati hasil klasterisasi warnanya. Perhatikan Gambar 6!

Gambar 6: Titik-titik dalam ruang yang merepresentasikan warna dalam foto setelah klasterisasi

Sekarang, kita gambarkan kembali piksel-piksel dalam rgb_foto_klaster_df pada bidang koordinat Kartesius. Hasilnya ditunjukkan pada Gambar 7.

Kode
rgb_foto_klaster_df |> 
  ggplot(
    aes(x = koord_x, y = koord_y, fill = pusat_hex)
  ) +
  geom_tile() +
  scale_fill_identity() +
  coord_equal() +
  theme_minimal() + 
  theme(
    axis.title = element_blank()
  )
Gambar 7: Penyederhanaan foto asli yang awalnya memiliki hampir 40 ribu warna menjadi \(k=4\).

Sampai di sini kita telah berhasil mereduksi variasi warna dari sebuah foto. Teknik yang telah kita lakukan tersebut tentu juga dapat diterapkan dengan \(k\) yang berbeda. Misalnya, kita juga dapat melakukannya dengan \(k\) = 16 dan 2. Hasilnya disajikan pada Gambar 8.

Gambar 8: Foto asli dan hasil reduksi variasi warna foto dengan klasterisasi k-rerata dengan k = 16, 4, dan 2

Mungkin kamu penasaran bagaimana komposisi warna dalam foto yang variasi warnanya sudah berkurang tersebut. Mari kita selidiki dan hasilnya disajikan pada Gambar 9.

Gambar 9: Komposisi warna pada foto-foto yang warnanya tereduksi menjadi k = 2, 4, dan 16

Catatan Akhir

Kita telah mereduksi variasi warna dalam sebuah foto dengan menggunakan klasterisasi \(k\)-rerata. Untuk melakukannya, kita perlu mengetahui bagaimana sebuah foto atau gambar dapat direpresentasikan dengan matriks-matriks warna dasarnya. Setelah itu, kita ekstrak data warna piksel-piksel dalam gambar tersebut. Hal ini kita lakukan pada Bagian 2. Setelah kita mendapatkan data warna tersebut, kita gunakan data tersebut untuk melakukan klasterisasi \(k\)-rerata (lihat Bagian 3). Di akhir, kita ubah warna tiap-tiap pikselnya dengan warna pusat klasternya. Dengan cara ini, kita mendapatkan gambar yang hanya tersusun dari \(k\) warna. Hasil akhirnya dapat dilihat pada Gambar 8.

Kembali ke atas

Penggunaan Kembali