Jawaban UTS Pengolahan Citra Digital Kelompok I
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.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user