Soal 1: selisih Laplacian of Gaussian (sigma 0,1 dan 0,2). Soal 2: memecah teks menjadi karakter individual dengan regionprops. Soal 3: menghitung jumlah koin per ukuran dengan regionprops.
86 lines
3.3 KiB
Python
86 lines
3.3 KiB
Python
"""Membuat gambar uji koin_100.png dari koin.png.
|
|
|
|
Soal mengandaikan gambar berisi 100 koin berbagai ukuran yang tidak saling
|
|
bersentuhan di atas latar belakang seragam. koin.png hanya berisi satu koin,
|
|
jadi koin tersebut diperkecil ke beberapa ukuran lalu ditempel 100 kali pada
|
|
posisi acak di atas latar seragam. Setiap koin diputar acak dan seluruh gambar
|
|
diberi sedikit noise agar koin sejenis tidak identik piksel per piksel, seperti
|
|
pada foto asli. Jumlah koin per ukuran diketahui, sehingga hasil
|
|
pengolahan_citra.py dapat diperiksa.
|
|
|
|
Cara menjalankan:
|
|
python buat_gambar_koin.py
|
|
"""
|
|
|
|
import os
|
|
|
|
import cv2
|
|
import numpy as np
|
|
|
|
|
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
SOURCE_PATH = os.path.join(BASE_DIR, "koin.png")
|
|
OUTPUT_PATH = os.path.join(BASE_DIR, "koin_100.png")
|
|
|
|
CANVAS_SIZE = 3000
|
|
BACKGROUND_BGR = (40, 40, 40) # latar abu-abu gelap yang seragam
|
|
MIN_GAP = 10 # jarak minimum antar-tepi koin (piksel), agar tidak bersentuhan
|
|
# (diameter koin dalam piksel, jumlah koin); total 100 koin
|
|
COIN_SIZES = [(140, 30), (170, 27), (205, 25), (245, 18)]
|
|
NOISE_STD = 3.0 # simpangan baku noise Gaussian (tingkat keabuan)
|
|
SEED = 0
|
|
|
|
|
|
def load_coin() -> np.ndarray:
|
|
coin = cv2.imread(SOURCE_PATH, cv2.IMREAD_UNCHANGED)
|
|
if coin is None or coin.shape[2] != 4:
|
|
raise FileNotFoundError(f"{SOURCE_PATH} harus berupa PNG dengan kanal alpha.")
|
|
# Potong ke area koin saja (alpha > 0)
|
|
rows, cols = np.nonzero(coin[:, :, 3])
|
|
return coin[rows.min():rows.max() + 1, cols.min():cols.max() + 1]
|
|
|
|
|
|
def main() -> None:
|
|
rng = np.random.default_rng(SEED)
|
|
coin = load_coin()
|
|
canvas = np.full((CANVAS_SIZE, CANVAS_SIZE, 3), BACKGROUND_BGR, np.float64)
|
|
|
|
diameters = [d for d, n in COIN_SIZES for _ in range(n)]
|
|
rng.shuffle(diameters)
|
|
|
|
# Tempatkan koin besar lebih dulu agar mudah mendapat tempat
|
|
placed = [] # (baris pusat, kolom pusat, jari-jari)
|
|
for d in sorted(diameters, reverse=True):
|
|
r = d / 2
|
|
for _ in range(100000):
|
|
y, x = rng.uniform(r + MIN_GAP, CANVAS_SIZE - r - MIN_GAP, size=2)
|
|
if all(np.hypot(y - py, x - px) >= r + pr + MIN_GAP for py, px, pr in placed):
|
|
placed.append((y, x, r))
|
|
break
|
|
else:
|
|
raise RuntimeError("Tidak cukup ruang untuk menempatkan semua koin.")
|
|
|
|
for y, x, r in placed:
|
|
d = round(2 * r)
|
|
small = cv2.resize(coin, (d, d), interpolation=cv2.INTER_AREA)
|
|
# Putar koin dengan sudut acak; area di luar koin tetap transparan
|
|
rotation = cv2.getRotationMatrix2D(((d - 1) / 2, (d - 1) / 2), rng.uniform(0, 360), 1.0)
|
|
small = cv2.warpAffine(small, rotation, (d, d), flags=cv2.INTER_LINEAR,
|
|
borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0))
|
|
small = small.astype(np.float64)
|
|
top, left = round(y - r), round(x - r)
|
|
region = canvas[top:top + d, left:left + d]
|
|
# Alpha blending: tepi koin menyatu halus dengan latar
|
|
alpha = small[:, :, 3:4] / 255
|
|
region[:] = alpha * small[:, :, :3] + (1 - alpha) * region
|
|
|
|
canvas += rng.normal(0, NOISE_STD, canvas.shape)
|
|
cv2.imwrite(OUTPUT_PATH, np.clip(np.round(canvas), 0, 255).astype(np.uint8))
|
|
print(f"Gambar uji disimpan di: {OUTPUT_PATH}")
|
|
for d, n in COIN_SIZES:
|
|
print(f"Diameter {d} piksel: {n} koin")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|