Keahlian dan minat saya jauh dari Seni Rupa tapi 'tuntutan' tidak peduli akan hal itu
Saya diamanahi untuk menjadi pembimbing untuk lomba poster digital di acara “Pentas PAI SMP Tingkat Kabupaten Kuningan 2026”. Saya tidak menemukan akun media sosial yang terdedikasi untuk acara resmi ini, tapi ada poster resmi yang didapat dari guru Pendidikan Agama Islam (PAI) di sekolah.
Mengingat latar belakang pendidikan saya yang (mungkin) sedikit atau bahkan tidak sama sekali berhubungan dengan disiplin ilmu Seni Rupa, saya tidak bisa memberikan bimbingan maksimal terkait unsur seni rupa seperti titik, garis, komposisi, temperatur warna, palet warna, gaya seni, dan lain-lain. Hal ini akan menghambat laju perkembangan dan efektifitas proses persiapan siswa peserta lomba. Walau begitu, saya mencoba menawarkan dan memberikan saran yang bisa menjadi panduan dalam membuat poster digital.
Sasaran utama dari proses bimbingan persiapan lomba ini berfokus pada Kriteria Penilaian yang tertera pada Petunjuk Teknis. Ada empat (4) kriteria penilaian dalam lomba poster digital ini.
Keempat kriteria penilaian diatas bisa dipecah kembali menjadi sasaran-sasaran (objectives) yang lebih kecil, granular, dan spesifik. Sasaran kecil ini saya ekspresikan dalam Problem Statement di bawah ini.
Untuk memenuhi setiap kriteria penilaian (granular), perlu langkah-langkah strategis. Disini saya menggunakan pendekatan saintifik yang cenderung dominan menggunakan prinsip-prinsip Statistika dan Sains Data. Berikut ini merupakan linimasa (timeline) mulai dari pengambilan data sampai interpretasi hasil analisis data.
Untuk memperoleh strategi yang matang, saya membutuhkan data poster digital yang ada di media sosial. Saya mendelegasikan pengambilan data ($N = 29$) berupa tangkap layar poster digital kepada siswa bimbingan. Setelah saya menerima datanya, saya menganotasi setiap file. Detail lebih lengkap tentang anotasi saya cantumkan di bagian selanjutnya.
Untuk setiap file yang saya terima, saya menganotasi beberapa metadata berikut. Kebanyakan metadata diekstrak menggunakan metode manual sedangakan sisanya menggunakan script untuk otomatisasi. Tabel di bawah ini menyajikan variabel, deskripsi, dan metode anotasi.
| Variabel | Deskripsi | Metode Anotasi | Tipe Data |
|---|---|---|---|
auth_id | Nomor unik untuk uploader poster digital | Manual | int |
like | Jumlah Suka yang didapat pada saat diambil | Manual | int |
comment | Jumlah Komentar yang didapat pada saat diambil | Manual | int |
save | Jumlah Simpan yang didapat pada saat diambil | Manual | int |
share | Jumlah Bagikan yang didapat pada saat diambil | Manual | int |
date_published | Tanggal poster diunggah | Manual | date |
style | Gaya/aliran artistik umum | Manual | chr |
width | Lebar (estimasi) poster (px) | Script | int |
height | Tinggi (estimasi) poster (px) | Script | int |
poster_area_px | Luas area poster (px$^2$) | Script | int |
msg_area_px | Luas area teks pesan (estimasi) poster (px$^2$) | Script | int |
msg_to_illus_ratio | Rasio teks-visual $\left( \frac{(\sum_{i = 1}^{n} w_i \times h_{i})}{WH} \right)$ | Script | dbl |
message | Pesan utama | Manual | chr |
sub_message | Subtitle atau pendukung dari pesan utama | Manual | chr |
caption | Paragraf pendukung yang mengandung fakta | Manual | chr |
dom_theme | Tema pesan yang diangkat | Manual | chr |
win_compt | Apakah poster mendapat nominasi juara? | Manual | logi |
software | Perangkat lunak yang digunaka untuk memproduksi poster | Manual | chr |
filename | Nama file | Script | chr |
Seperti disebutkan di bagian sebelumnya, saya menganotasi setiap poster yang sudah dikumpulkan. Spesifiknya, saya menganotasi proporsi teks yang ada di dalam masing-masing poster. Setelah setiap teks yang ada di suatu poster tersebut dianotasi, saya menghitung jumlah luas setiap anotasi. Nilai luas anotasi yang didapat dari masing-masing poster kemudian dibagi dengan luas poster itu sendiri. Dari sini saya bisa mendapat rasio atau proporsi teks terhadap poster, yang mana data ini akan dipakai sebagai bagian dari analisis data eksploratif. Di bawah ini adalah diagram alur kerja yang saya gunakan untuk menganotasi teks yang ada di setiap poster.
graph TD
id1[1. Pangkas Dimensi] --> id2[2. Impor ke Canva]
id2 --> id3[3. Susun Petak 5x6]
id3 --> id4[4. Garis Pandu X/Y]
id4 --> id5[5. Anotasi Transparan]
id5 --> id6[6. Tata Ulang Objek]
id6 --> id7[7. Ekspor format PNG]
id7 --> id8[8. Hitung Luas Area]
id8 --> id9[9. Format Baris CSV]
id9 --> id10[10. Simpan File CSV]
%% Style adjustments for clarity (optional)
style id1 stroke:#333,stroke-width:2px
style id10 stroke:#0288d1,stroke-width:2px
Proses anotasi data bisa dilihat secara publik di sini.
Untuk mendapat nilai luas dari anotasi yang ada di masing-masing poster, saya menggunakan script Python untuk data yang lebih akurat dan potensi otomatisasi yang lebih konsisten. Di sini saya menerapkan konsep kalkulus untuk menghitung luas area yang diarsir (anotasi data). Secara singkat, script Python yang telah dibuat akan mengidentifikasi warna merah yang ada lalu merekam titik koordinat awal dan akhir dari anotasi data per baris. Jika diekspresikan melalui pseudo-code, kurang lebih akan seperti ini:
1. Baca dimensi height dan width file lalu simpan dalam variabel.
2. Pecah file menjadi grid pixel.
3. Lakukan pemindaian warna pixel per baris pixel.
4. Jika di koordinat sekarang warna pixel adalah "putih" atau "hitam", maka pindai pixel yang ada di kanan; selain itu rekam titik koordinat sekarang lalu simpan dalam variabel.
5. Jika di satu baris pixel terdapat rekaman dua data koordinat, maka hitung jumlah dua koordinat tersebut lalu simpan dalam variabel dan pindah baris pemindaian ke bawah sebanyak 1 pixel; selain itu pindah baris pemindaian ke bawah sebanyak 1 pixel.
6. Jumlahkan semua luas pixel dari setiap baris lalu simpan sebagai data integer di dalam variabel.
7. Lakukan aritmatika: annotate / height * width
8. Simpan dalam variabel "rasio", di baris tabel yang memiliki filename yang sama.
9. Kembali ke step 1 untuk x kali pengulangan sejumlah file yang diinput.
Atau bisa cek script python di bawah ini. Saya menggunakan bantuan Google Gemini untuk membuat script ini.
import os
import csv
from PIL import Image
def is_red(pixel):
"""
Check if a pixel is red.
Handles RGBA (transparent) and standard RGB.
"""
if len(pixel) == 4:
r, g, b, a = pixel
if a == 0: return False # Completely transparent pixels are ignored
else:
r, g, b = pixel
# A basic threshold for red: High R, low G and B.
return r > 150 and g < 100 and b < 100
def is_white(pixel):
"""Check if a pixel is white (background)."""
r, g, b = pixel[:3]
return r > 240 and g > 240 and b > 240
def process_image(image_path):
"""Scans the image row-by-row to integrate the area of red rectangles."""
try:
# Convert to RGBA to ensure we handle transparency properly
img = Image.open(image_path).convert('RGBA')
except Exception as e:
print(f"Error opening {image_path}: {e}")
return None, None, None
pixels = img.load()
width, height = img.size
# Calculate the custom image area based on the requested formula
img_area = (width - 20) * (height - 20)
red_area_sq2 = 0
red_rows_set = set() # Using a set to keep track of unique rows containing red
# Row-wise pixel check
for y in range(height):
start_x = None
for x in range(width):
current_pixel = pixels[x, y]
if is_red(current_pixel):
if start_x is None:
# 1. Record coordinate of the FIRST identified red pixel
start_x = x
red_rows_set.add(y)
elif is_white(current_pixel) and start_x is not None:
# 2. Pixel to the right is white, record the LAST known red pixel
end_x = x - 1
# 3. Calculate area for this segment (Integral sum)
red_area_sq2 += (end_x - start_x + 1)
# Reset for potential other rectangles in the same row
start_x = None
# Edge case: If the red rectangle touches the right edge of the image
if start_x is not None:
end_x = width - 1
red_area_sq2 += (end_x - start_x + 1)
n_row_red = len(red_rows_set)
return n_row_red, red_area_sq2, img_area
def bulk_process(input_folder, output_csv):
"""Processes all images in a folder and saves results to a CSV."""
# Supported image extensions
valid_extensions = ('.png', '.jpg', '.jpeg', '.bmp', '.tif', '.tiff')
# Prepare CSV file
with open(output_csv, mode='w', newline='') as csv_file:
writer = csv.writer(csv_file)
# Write headers including the new img_area column
writer.writerow(['filename', 'n_row_red', 'red_area_sq2', 'img_area'])
# Iterate through files in the directory
for filename in os.listdir(input_folder):
if filename.lower().endswith(valid_extensions):
filepath = os.path.join(input_folder, filename)
print(f"Processing: {filename}...")
n_row_red, red_area_sq2, img_area = process_image(filepath)
if n_row_red is not None:
# Write the new img_area value to the CSV
writer.writerow([filename, n_row_red, red_area_sq2, img_area])
print(f" -> Rows: {n_row_red}, Red Area: {red_area_sq2}px, Img Area: {img_area}px")
print(f"\nProcessing complete! Results saved to {output_csv}")
if __name__ == "__main__":
# --- CONFIGURATION ---
# Replace these paths with your actual folder path and desired output file
INPUT_DIRECTORY = "./images"
OUTPUT_CSV_FILE = "red_rectangle_areas.csv"
# Create the folder if it doesn't exist to prevent errors on first run
if not os.path.exists(INPUT_DIRECTORY):
print(f"Please create a folder named '{INPUT_DIRECTORY}' and put your images inside.")
else:
bulk_process(INPUT_DIRECTORY, OUTPUT_CSV_FILE)
Untuk mengambil data width, height, dan filename, saya manggunakan package exiftool di WSL (Windows Subsystem Linux) dengan distribusi Ubuntu.
exiftool -csv -r . > metadata.csv
Teks pesan setiap poster saya ekstrak dengan cara konvensional dan manual. Saya menyalin (baca-ketik) teks pesan yang ada di setiap poster dan menyimpannya dalam tabel di file Spreadsheet. Saya memisahkan antara pesan utama, pesan pelengkap (subtitle), dan pesan tambahan (caption). Setiap huruf yang muncul saya rekam tanpa ada perubahan kapitalisasi sedikitpun. Dengan kata lain, jika pesan utama di suatu poster dituliskan “JAgAlAh HutAn”, maka saya merekamnya sebagai JAgAlAh HutAn.
Semua proses pengambilan data direkam dalam 1 Spreadsheet utama dan bisa diakses secara publik di sini.
style mempengaruhi dan/atau memprediksi like?msg_to_illus_ratio mempengaruhi dan/atau memprediksi like?message dan sub_message mempengaruhi dan/atau memprediksi like?artistic_tags) apa yang paling populer?Pada bagian ini saya menguji asumsi-asumsi untuk setiap variabel yang menjadi perhatian utama. Variabel yang menjadi perhatian utama adalah:
| Variabel | Deskripsi | Tipe Data |
|---|---|---|
like | Jumlah Suka yang didapat pada saat diambil | int |
comment | Jumlah Komentar yang didapat pada saat diambil | int |
save | Jumlah Simpan yang didapat pada saat diambil | int |
share | Jumlah Bagikan yang didapat pada saat diambil | int |
style | Gaya/aliran artistik umum | chr |
msg_to_illus_ratio | Rasio teks-visual | dbl |
cnt_message | Jumlah huruf pada pesan utama | int |
cnt_sub_message | Jumlah huruf subtitle atau pendukung dari pesan utama | int |
main_sub_msg | Jumlah huruf pesan utama + subtitle | int |
cnt_caption | Jumlah huruf caption | int |
txt_cnt | Jumlah huruf pesan utama + subtitle + caption | int |
Untuk kepentingan replikasi, bisa mengimpor data dari Spreadsheet yang sudah disediakan atau bisa juga copy-paste data berikut:
library(tidyverse)
likes <- c(
173100, 59800, 59400, 57500, 39900, 26300, 20600, 20500, 18100, 16300,
11000, 9194, 8363, 5882, 5864, 5246, 5032, 4423, 3328, 2935,
2061, 1908, 1182, 978, 577, 462, 302, 230, 218
)
comments <- c(
918, 557, 428, 1135, 244, 182, 265, 721, 236, 118,
108, 177, 74, 67, 54, 43, 53, 54, 63, 62,
47, 21, 67, 19, 8, 10, 16, 16, 11
)
saves <- c(
9670, 16500, 6417, 6871, 5408, 7076, 3211, 2622, 236, 1933,
2584, 1080, 1248, 1153, 1278, 1054, 734, 798, 670, 867,
309, 501, 378, 237, 226, 75, 50, 60, 33
)
shares <- c(
1049, 1140, 403, 1720, 356, 706, 748, 472, 438, 355,
343, 131, 259, 178, 324, 232, 68, 81, 95, 103,
160, 46, 49, 52, 27, 5, 12, 186, 11
)
msg_to_illust_ratio <- c(
0.00, 0.00, 0.10, 0.07, 0.11, 0.14, 0.15, 0.07, 0.15, 0.14,
0.16, 0.12, 0.14, 0.11, 0.08, 0.00, 0.08, 0.00, 0.08, 0.00,
0.00, 0.10, 0.08, 0.08, 0.07, 0.02, 0.00, 0.16, 0.19
)
cnt_message <- c(
35, 0, 32, 31, 18, 33, 26, 21, 30, 14,
22, 44, 17, 49, 38, 0, 14, 44, 47, 0,
0, 30, 18, 32, 27, 0, 46, 11, 39
)
cnt_sub_message <- c(
0, 0, 40, 0, 0, 0, 44, 0, 0, 32,
60, 0, 40, 68, 0, 0, 0, 0, 0, 0,
0, 77, 0, 0, 0, 0, 0, 0, 18
)
main_sub_message <- c(
35, 0, 72, 31, 18, 33, 70, 21, 30, 46,
82, 44, 57, 117, 38, 0, 14, 44, 47, 0,
0, 107, 18, 32, 27, 0, 46, 11, 57
)
cnt_caption <- c(
0, 0, 0, 0, 223, 0, 326, 0, 0, 0,
0, 0, 0, 0, 0, 0, 71, 85, 0, 0,
0, 0, 0, 0, 0, 0, 0, 14, 50
)
txt_cnt <- c(
35, 0, 72, 31, 241, 33, 396, 21, 30, 46,
82, 44, 57, 117, 38, 0, 85, 129, 47, 0,
0, 107, 18, 32, 27, 0, 46, 25, 107
)
poster_metadata <- data.frame(
likes = likes,
comments = comments,
saves = saves,
shares = shares,
msg_to_illust_ratio = msg_to_illust_ratio,
cnt_message = cnt_message,
cnt_sub_message = cnt_sub_message,
main_sub_message = main_sub_message,
cnt_caption = cnt_caption,
txt_cnt = txt_cnt
)
Untuk mempersingkat proses analisis data eksploratif, saya menggunakan otomasi for-loop uji normalitas Shapiro-Wilk dan plotting (Histogram-Density dan Violin) terhadap setiap variabel yang menjadi perhatian utama pada kasus ini. Output yang dihasilkan adalah (1) tabel rangkuman Uji Normalitas Shapiro-Wilk, (2) Histogram-Density Plot, dan (3) Violin-Box and Whiskers Plot.
shapiro_results <- list()
for (col_name in names(poster_metadata)) {
if (is.numeric(poster_metadata[[col_name]])) {
test <- shapiro.test(poster_metadata[[col_name]])
shapiro_results[[col_name]] <- data.frame(
w_stat = round(test$statistic, 5),
p_value = test$p.value,
verdict = if(test$p.value < 0.05) "Tolak Null" else "Gagal Tolak Null",
row.names = NULL
)
}
}
uji_norm_batch <- do.call(rbind, shapiro_results)
uji_norm_batch
| Variabel | w_stat | p_value | Kesimpulan Normalitas |
|---|---|---|---|
| likes | 0.56591 | 4.087087e-08 | Tolak Null |
| comments | 0.67534 | 9.496506e-07 | Tolak Null |
| saves | 0.68064 | 1.123159e-06 | Tolak Null |
| shares | 0.76443 | 2.048239e-05 | Tolak Null |
| msg_to_illust_ratio | 0.90932 | 1.649580e-02 | Tolak Null |
| cnt_message | 0.93692 | 8.328193e-02 | Gagal Tolak Null |
| cnt_sub_message | 0.61606 | 1.611606e-07 | Tolak Null |
| main_sub_message | 0.92246 | 3.520628e-02 | Tolak Null |
| cnt_caption | 0.42569 | 1.418114e-09 | Tolak Null |
| txt_cnt | 0.67933 | 1.077219e-06 | Tolak Null |
Code-snippet berikut membuat dan langsung menyimpan plot (1) Histogram-Density dan (2) Violin-Box and Whiskers Plot di direktori aktif saat ini.
numeric_cols <- names(poster_metadata)[sapply(poster_metadata, is.numeric)]
# For Loop setiap variabel
for (col_name in numeric_cols) {
# Histogram-Density Plot
p_hist <- ggplot(poster_metadata, aes(x = .data[[col_name]])) +
geom_histogram(aes(y = after_stat(density)),
alpha = 0.5,
color = "white",
fill = "steelblue") +
geom_density(linewidth = 0.8, color = "darkblue") +
labs(
title = paste("Distribusi", col_name),
x = col_name,
y = "Densitas"
) +
theme_classic()
ggsave(
filename = paste0("dist_", col_name, "_raw.png"),
plot = p_hist,
width = 7,
height = 4.5,
dpi = 300
)
# Violin Plot
p_violin <- ggplot(poster_metadata, aes(x = .data[[col_name]], y = factor(0))) +
geom_violin(fill = "aquamarine3", alpha = 0.6) +
geom_boxplot(width = 0.1, color = "black", outlier.shape = NA) +
labs(
title = paste("Violin Plot -", col_name),
x = col_name,
y = ""
) +
theme_classic() +
theme(
axis.text.y = element_blank(),
axis.ticks.y = element_blank()
)
ggsave(
filename = paste0("violin_", col_name, ".png"),
plot = p_violin,
width = 7,
height = 3.5,
dpi = 300
)
}
Saya skeptis tarhadap data yang ada sehingga
like summary(poster_metadata$likes)
Min. 1st Qu. Median Mean 3rd Qu. Max.
218 1908 5864 19334 20500 173100
Saya melakukan uji statistik formal dengan data apa ada nya.
shapiro.test(likes)
Shapiro-Wilk normality test
data: poster_metadata$likes
W = 0.56591, p-value = 4.087e-08
Namun begitu, hasil uji statistik formal ini menghasilkan nilai $W = 0.56591; p = 4.08 \times 10^{-8}$ yang mana berarti data likes tidak berdistribusi normal. Dengan hasil ini, saya melakukan triangulasi dengan visualisasi data.
# Hist + Density Plot
ggplot(poster_metadata, aes(likes)) +
geom_histogram(aes(y = after_stat(density),
alpha = .5)) +
geom_density() +
labs(title = "Distribusi 'Likes'",
subtitle = "Data sangat miring (right-skewed)",
y = "Jumlah") +
guides(alpha = "none") +
theme_classic()
# Violin Plot
ggplot(poster_metadata, aes(likes, factor(0))) +
geom_violin() +
labs(title = "Violin 'Likes'",
subtitle = "Outlier terdeteksi. Miring ekstrim kanan (right-skewed)",
y = "") +
theme_classic()
Saya skeptis bahwa data yang lain mungkin berdistribusi tidak normal. Dengan asumsi ini, saya melakukan uji normalitas Shapiro-Wilk secara batch
shapiro_results <- list()
for (col_name in names(poster_metadata)) {
if (is.numeric(poster_metadata[[col_name]])) {
test <- shapiro.test(poster_metadata[[col_name]])
shapiro_results[[col_name]] <- data.frame(
w_stat = round(test$statistic, 5),
p_value = test$p.value,
verdict = if(test$p.value < 0.05) "Tolak Null" else "Gagal Tolak Null",
row.names = NULL
)
}
}
uji_norm_batch <- do.call(rbind, shapiro_results)
uji_norm_batch
| Variabel | w_stat | p_value | Kesimpulan Normalitas |
|---|---|---|---|
| likes | 0.56591 | 4.087087e-08 | Tolak Null |
| comments | 0.67534 | 9.496506e-07 | Tolak Null |
| saves | 0.68064 | 1.123159e-06 | Tolak Null |
| shares | 0.76443 | 2.048239e-05 | Tolak Null |
| msg_to_illust_ratio | 0.90932 | 1.649580e-02 | Tolak Null |
| cnt_message | 0.93692 | 8.328193e-02 | Gagal Tolak Null |
| cnt_sub_message | 0.61606 | 1.611606e-07 | Tolak Null |
| main_sub_message | 0.92246 | 3.520628e-02 | Tolak Null |
| cnt_caption | 0.42569 | 1.418114e-09 | Tolak Null |
| txt_cnt | 0.67933 | 1.077219e-06 | Tolak Null |
style memiliki 3 grup: Anime ($n = 24$), Cartoon ($n = 4$), Semi-realism ($n = 1$); yang mana memiliki proporsi yang tidak seimbang. Oleh karena itu, data ini tidak memenuhi uji asumsi untuk analisis inferensial lebih lanjut.
like memiliki statistik deskriptif sebagai berikut:
Min : 218
1st Qu : 1908
Median : 5864
Mean : 19334
3rd Qu : 20500
Max : 173100
likes <- c(
173100, 59800, 59400, 57500, 39900, 26300, 20600, 20500, 18100, 16300,
11000, 9194, 8363, 5882, 5864, 5246, 5032, 4423, 3328, 2935,
2061, 1908, 1182, 978, 577, 462, 302, 230, 218
)
shapiro.test(likes)
Shapiro-Wilk normality test
data: likes
W = 0.56591, p-value = 4.087e-08
msg_to_illus_ratio = kontinu
Min : 0.00
1st Qu : 0.02
Median : 0.08
Mean : 0.08
3rd Qu : 0.14
Max : 0.19
msg_to_illust_ratio <- c(
0.00, 0.00, 0.10, 0.07, 0.11, 0.14, 0.15, 0.07, 0.15, 0.14,
0.16, 0.12, 0.14, 0.11, 0.08, 0.00, 0.08, 0.00, 0.08, 0.00,
0.00, 0.10, 0.08, 0.08, 0.07, 0.02, 0.00, 0.16, 0.19
)
shapiro.test(msg_to_illust_ratio)
Shapiro-Wilk normality test
data: msg_to_illust_ratio
W = 0.90932, p-value = 0.0165
like = diskrit
Min : 218
1st Qu : 1908
Median : 5864
Mean : 19334
3rd Qu : 20500
Max : 173100
(insert Density plot; Histogram; Boxplot; Q-Q plot; Independency; Shapiro-Wilk; Levene)
(insert code, result, and interpretation here)
(insert code, result, and interpretation here)
(insert verdict)
message + sub_message = diskrit
(insert summary stats message + sub_message)
like = diskrit
(insert summary stats like)
(insert Density plot; Histogram; Boxplot; Q-Q plot; Independency; Shapiro-Wilk; Levene)
(insert code, result, and interpretation here)
(insert code, result, and interpretation here)
(insert verdict)
###
(insert verdict)
Here are some more articles you might like to read next: