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.
90 lines
4.1 KiB
Python
90 lines
4.1 KiB
Python
"""
|
|
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}")
|