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,89 @@
|
||||
"""
|
||||
UTS INF301 Pengolahan Citra Digital - Soal 2
|
||||
Memecah teks menjadi karakter individual menggunakan regionprops,
|
||||
lalu menyimpan setiap karakter sebagai gambar terpisah.
|
||||
"""
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from skimage import io, color, filters, measure, morphology, transform, util
|
||||
|
||||
# Input dan output relatif terhadap folder program ini, sehingga program
|
||||
# bisa dijalankan dari folder mana pun.
|
||||
FOLDER = os.path.dirname(os.path.abspath(__file__))
|
||||
INPUT = os.path.join(FOLDER, "Problem 2 Image.png")
|
||||
OUTPUT_DIR = os.path.join(FOLDER, "karakter")
|
||||
MIN_AREA = 20 # region lebih kecil dari ini dianggap noise
|
||||
# Radius dilasi untuk menggabungkan huruf menjadi kata. Pada citra ini celah
|
||||
# antar-huruf dalam satu kata <= 87 piksel dan celah antar-kata >= 104 piksel,
|
||||
# sehingga disk radius 48 (menutup celah ~96 piksel) memisahkan keduanya.
|
||||
WORD_RADIUS = 48
|
||||
# Huruf satu goresan (seperti I) sangat memanjang. Untuk huruf ini sumbu
|
||||
# utamanya sendiri lebih tepat dipakai daripada sudut lengkung kata. Pada
|
||||
# citra ini eccentricity I = 0.97, huruf lain <= 0.88.
|
||||
ELONGATED_ECC = 0.95
|
||||
|
||||
# 1. Baca citra lalu ubah ke grayscale (citra asli RGBA)
|
||||
img = io.imread(INPUT)
|
||||
if img.ndim == 3 and img.shape[2] == 4:
|
||||
img = color.rgba2rgb(img)
|
||||
gray = color.rgb2gray(img) if img.ndim == 3 else util.img_as_float(img)
|
||||
|
||||
# 2. Segmentasi: threshold Otsu memisahkan teks putih dari latar hitam
|
||||
thresh = filters.threshold_otsu(gray)
|
||||
binary = gray > thresh
|
||||
binary = morphology.remove_small_objects(binary, max_size=MIN_AREA)
|
||||
|
||||
# 3. Labeling: setiap komponen terhubung (8-connectivity) = satu karakter
|
||||
labels = measure.label(binary, connectivity=2)
|
||||
|
||||
# 4. regionprops: ambil bounding box setiap karakter
|
||||
regions = measure.regionprops(labels, intensity_image=gray)
|
||||
print(f"Jumlah karakter terdeteksi: {len(regions)}")
|
||||
|
||||
# 5. Kelompokkan karakter menjadi kata: dilasi menyatukan huruf yang
|
||||
# berdekatan, lalu labeling memberi satu label per kata.
|
||||
words = measure.label(morphology.dilation(binary, morphology.disk(WORD_RADIUS)))
|
||||
word_of = [words[tuple(region.coords[0])] for region in regions]
|
||||
|
||||
# 6. Sudut kemiringan setiap huruf. Baseline kata bisa melengkung
|
||||
# (contoh: TWINPEAK), jadi sudut dihitung per huruf dari garis singgung
|
||||
# lengkung yang melalui centroid huruf-huruf dalam katanya.
|
||||
char_angle = [0.0] * len(regions)
|
||||
for w in set(word_of):
|
||||
idx = [i for i, k in enumerate(word_of) if k == w]
|
||||
pts = np.array([regions[i].centroid for i in idx])
|
||||
pts = pts - pts.mean(axis=0)
|
||||
# Sumbu utama (PCA) kumpulan centroid = arah umum kata
|
||||
_, _, vt = np.linalg.svd(pts)
|
||||
u_dir, v_dir = vt[0], vt[1] # vektor (baris, kolom)
|
||||
if u_dir[1] < 0: # teks dibaca dari kiri ke kanan
|
||||
u_dir = -u_dir
|
||||
u, v = pts @ u_dir, pts @ v_dir
|
||||
# Lengkung kuadratik v = a u^2 + b u + c, kemiringan lokal dv/du = 2au + b
|
||||
a, b, _ = np.polyfit(u, v, 2)
|
||||
for i, ui in zip(idx, u):
|
||||
dr, dc = u_dir + (2 * a * ui + b) * v_dir
|
||||
# baris citra bertambah ke bawah, maka -dr agar sudut berlawanan jarum jam positif
|
||||
char_angle[i] = np.degrees(np.arctan2(-dr, dc))
|
||||
|
||||
# 7. Crop, luruskan, dan simpan setiap karakter.
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
for i, (region, w) in enumerate(zip(regions, word_of), start=1):
|
||||
angle = char_angle[i - 1]
|
||||
if region.eccentricity > ELONGATED_ECC:
|
||||
# orientation = sudut sumbu utama terhadap sumbu vertikal (baris);
|
||||
# putar sebesar itu agar goresan menjadi vertikal
|
||||
angle = np.degrees(region.orientation)
|
||||
# image_intensity = piksel karakter ini saja; karakter tetangga yang
|
||||
# masuk ke dalam kotak tidak ikut tersimpan
|
||||
# Putar balik sebesar sudut kata agar huruf tegak; resize=True agar
|
||||
# ujung huruf tidak terpotong.
|
||||
upright = transform.rotate(region.image_intensity, -angle, resize=True)
|
||||
# Kotak baru mengikuti huruf yang sudah tegak
|
||||
rows, cols = np.nonzero(upright > thresh)
|
||||
upright = upright[rows.min():rows.max() + 1, cols.min():cols.max() + 1]
|
||||
char_img = util.img_as_ubyte(np.clip(upright, 0, 1))
|
||||
filename = os.path.join(OUTPUT_DIR, f"karakter_{i:02d}.png")
|
||||
io.imsave(filename, char_img)
|
||||
print(f"{filename}: kata={w}, sudut={angle:.1f} derajat, area={region.area}")
|
||||
Reference in New Issue
Block a user