# Soal 2 — Memecah Teks Menjadi Karakter Individual dengan `regionprops` Program: `soal2.py` · Input: `Problem 2 Image.png` · Output: folder `karakter/` (18 file PNG) Cara menjalankan: `python soal2.py` (library: `numpy`, `scikit-image`) Referensi teori: Gonzalez & Woods, *Digital Image Processing*, edisi ke-4 (Pearson, 2018). ## 1. Tujuan Citra input berisi tiga kata putih di atas latar hitam: **ZEUS** (horizontal), **TWINPEAK** (miring naik), dan **ACOSTA** (miring tajam). Program harus memisahkan setiap huruf dan menyimpannya sebagai gambar tersendiri. Huruf pada hasil harus tegak, yaitu kotak potongannya mengikuti kemiringan tulisan, lalu huruf diputar balik hingga lurus. ![Citra input](Problem%202%20Image.png) ## 2. Alur Program 1. Baca citra, ubah ke grayscale. 2. Segmentasi dengan threshold Otsu menjadi citra biner. 3. Buang noise kecil. 4. Labeling komponen terhubung: satu komponen = satu karakter. 5. `regionprops`: ambil properti setiap karakter. 6. Dilasi untuk mengelompokkan karakter menjadi kata. 7. Hitung sudut kemiringan setiap huruf dari lengkung yang melalui centroid huruf-huruf dalam katanya. Huruf satu goresan (I) memakai sumbu utamanya sendiri. 8. Putar setiap karakter sebesar sudutnya, potong ulang, simpan. ## 3. Penjelasan Setiap Langkah ### 3.1 Representasi citra dan konversi grayscale (Bab 2 dan Bab 7) Citra digital adalah fungsi dua dimensi f(x, y) yang disimpan sebagai matriks piksel. File input berformat RGBA berukuran 762 × 860 × 4. Kanal alpha dibuang (`rgba2rgb`), lalu citra RGB diubah menjadi satu kanal intensitas (`rgb2gray`) dengan kombinasi linear: ``` Y = 0.2125 R + 0.7154 G + 0.0721 B ``` Hasilnya matriks bernilai [0, 1]. Tulisan bernilai mendekati 1 (putih), latar mendekati 0 (hitam). ### 3.2 Segmentasi dengan threshold Otsu Thresholding mengubah citra grayscale menjadi citra biner: ``` g(x, y) = 1 jika f(x, y) > T g(x, y) = 0 jika f(x, y) ≤ T ``` Nilai T dipilih otomatis dengan metode Otsu. Otsu mencoba setiap nilai k dan memilih k yang memaksimalkan varians antar-kelas: ``` σ²_B(k) = [m_G · P₁(k) − m(k)]² / [P₁(k) · (1 − P₁(k))] ``` dengan P₁(k) = proporsi piksel ≤ k, m(k) = rata-rata kumulatif intensitas sampai k, dan m_G = rata-rata intensitas global. Histogram citra ini bimodal (tulisan dan latar), jadi Otsu memisahkan keduanya dengan baik. Pada citra ini T = 0,342 (skala 0–1). Catatan: metode Otsu dibahas di Bab 10 (Segmentasi), setelah materi UTS. Konsep dasarnya, yaitu fungsi thresholding sebagai transformasi intensitas, sudah dibahas di Bab 3. ### 3.3 Pembuangan noise `remove_small_objects` menghapus komponen dengan luas ≤ 20 piksel. Komponen sekecil itu bukan huruf, melainkan noise sisa kompresi citra. Pada citra ini tidak ada komponen yang terbuang, tetapi langkah ini menjaga agar noise tidak tersimpan sebagai "karakter". ### 3.4 Labeling komponen terhubung (Bab 2 dan Bab 9) **Ketetanggaan (Bab 2).** Piksel p di (x, y) memiliki: - 4-tetangga N₄(p): (x±1, y) dan (x, y±1). - 8-tetangga N₈(p): N₄(p) ditambah 4 tetangga diagonal. Dua piksel foreground termasuk satu komponen jika ada jalur piksel foreground di antara keduanya. Program memakai **8-connectivity** (`connectivity=2`), sehingga goresan diagonal huruf seperti A, K, dan W tetap tersambung sebagai satu komponen. **Ekstraksi komponen terhubung (Bab 9).** Secara morfologi, komponen yang memuat piksel awal X₀ diperoleh dengan iterasi: ``` X_k = (X_{k−1} ⊕ B) ∩ I, k = 1, 2, 3, ... ``` dengan I = citra biner dan B = elemen penstruktur 3 × 3 berisi 1 (untuk 8-connectivity). Iterasi berhenti saat X_k = X_{k−1}. `measure.label` menjalankan proses yang setara untuk setiap komponen dan memberi label 1, 2, 3, dan seterusnya. Hasilnya **18 label**, sesuai jumlah huruf: ZEUS (4) + TWINPEAK (8) + ACOSTA (6). Pemisahan ini berhasil karena setiap huruf tidak bersentuhan dengan huruf lain. Huruf "I" juga tidak bermasalah karena kapital dan tidak memiliki titik. ### 3.5 Cara `regionprops` digunakan `measure.regionprops(labels, intensity_image=gray)` menghasilkan satu objek per label. Properti yang dipakai program: | Properti | Arti | Kegunaan di program | |---|---|---| | `coords` | Daftar koordinat (baris, kolom) piksel region | Mencari label kata dari region (langkah 3.6) | | `centroid` | Titik pusat region: rata-rata koordinat piksel | Menghitung lengkung kata dan sudut setiap huruf (langkah 3.7) | | `eccentricity` | Seberapa memanjang region: 0 = lingkaran, mendekati 1 = garis | Mengenali huruf satu goresan seperti I (langkah 3.7) | | `orientation` | Sudut sumbu utama region terhadap sumbu vertikal | Sudut putar huruf satu goresan (langkah 3.7) | | `image_intensity` | Potongan citra grayscale seukuran bounding box; piksel di luar region bernilai 0 | Gambar karakter yang diputar dan disimpan (langkah 3.8) | | `area` | Jumlah piksel region | Informasi pada output | `image_intensity` dipakai alih-alih memotong citra langsung. Pada huruf yang miring, bounding box satu huruf bisa memuat sebagian huruf tetangga. Dengan `image_intensity`, hanya piksel milik huruf itu sendiri yang ikut tersimpan. Centroid dihitung dari momen orde nol dan orde satu region R: ``` area = M₀₀ = Σ 1 r̄ = M₁₀ / M₀₀ = (1/area) Σ r c̄ = M₀₁ / M₀₀ = (1/area) Σ c ``` Catatan: deskriptor region seperti area, centroid, momen, dan komponen utama dibahas lebih lanjut di Bab 11. Program memakainya lewat `regionprops`, sesuai petunjuk soal. ### 3.6 Pengelompokan karakter menjadi kata dengan dilasi (Bab 9) Huruf harus diputar sesuai kemiringan **katanya**, jadi program perlu tahu setiap huruf milik kata yang mana. Dilasi A oleh elemen penstruktur B didefinisikan: ``` A ⊕ B = { z | (B̂)_z ∩ A ≠ ∅ } ``` Dilasi menebalkan setiap huruf. Dengan B berupa disk radius r, dua huruf menyatu jika celah di antaranya kurang dari sekitar 2r. Hasil pengukuran jarak terdekat antar-piksel huruf pada citra ini: - Celah antar-huruf dalam satu kata: paling besar **87 piksel** (pada ZEUS). - Celah antar-kata: paling kecil **104 piksel** (antara E pada TWINPEAK dan A pada ACOSTA). Radius r = 48 (menutup celah sekitar 96 piksel) berada di antara kedua nilai itu. Dilasi menyatukan huruf dalam satu kata tetapi tidak menyatukan kata yang berbeda. Labeling ulang pada citra hasil dilasi menghasilkan **3 label**, satu per kata. Label kata sebuah huruf dibaca dari citra label kata pada salah satu koordinat (`coords`) huruf tersebut. ### 3.7 Sudut kemiringan setiap huruf Huruf-huruf dalam satu kata tersusun di sepanjang garis dasar (baseline). Baseline tidak selalu lurus. Pada TWINPEAK, kemiringan antar-centroid naik dari sekitar 2° (T–W) menjadi sekitar 27° (A–K), jadi baseline-nya melengkung. Satu sudut rata-rata per kata akan membuat huruf di ujung kata diputar terlalu jauh atau kurang jauh. Karena itu sudut dihitung **per huruf**, dari garis singgung lengkung baseline di posisi huruf tersebut. **Langkah 1: arah umum kata (PCA).** Arah umum sebaran centroid dicari dengan analisis komponen utama: 1. Kurangi setiap centroid dengan rata-ratanya: **p**ᵢ − **p̄**. 2. Bentuk matriks kovarians C = (1/n) Σ (**p**ᵢ − **p̄**)(**p**ᵢ − **p̄**)ᵀ. 3. Eigenvektor C dengan eigenvalue terbesar = arah umum kata **u**. Eigenvektor kedua **v** tegak lurus terhadap **u**. Program menghitungnya dengan SVD, yang memberi vektor yang sama. Arah **u** dipilih agar komponen kolomnya positif (teks dibaca dari kiri ke kanan). **Langkah 2: lengkung baseline.** Setiap centroid diproyeksikan ke sumbu **u** dan **v**, lalu dicocokkan lengkung kuadratik dengan metode kuadrat terkecil: ``` v = a·u² + b·u + c ``` Kemiringan lengkung di posisi huruf ke-i adalah turunannya, dv/du = 2a·uᵢ + b. Arah garis singgung dalam koordinat citra: ``` (Δr, Δc) = u + (2a·uᵢ + b) · v ``` **Langkah 3: sudut.** Sudut terhadap sumbu horizontal: ``` θ = arctan2(−Δr, Δc) ``` Tanda minus diperlukan karena indeks baris citra bertambah ke bawah. Dengan begitu, θ positif berarti tulisan naik (berlawanan arah jarum jam). Untuk kata yang lurus seperti ZEUS, a ≈ 0 dan semua huruf mendapat sudut ≈ 0°. **Pengecualian: huruf satu goresan.** Huruf I pada TWINPEAK di citra asli condong sekitar 10,7°. Nilai ini hampir sama dengan kemiringan italic font (sekitar 9,5°, diukur dari batang huruf E pada ZEUS). Artinya, I di citra asli praktis tidak diputar mengikuti lengkung, padahal lengkung memberi sudut 6,2°. Jika diputar 6,2°, I justru menjadi semakin condong (sekitar 17°). Untuk huruf yang berupa satu goresan, arah huruf bisa dibaca langsung dari bentuknya. `regionprops` menghitung elips yang memiliki momen pusat orde dua sama dengan region: ``` μ_pq = Σ (r − r̄)^p (c − c̄)^q ``` Dari matriks kovarians [μ₂₀ μ₁₁; μ₁₁ μ₀₂] / M₀₀ diperoleh eigenvalue λ₁ ≥ λ₂. Panjang sumbu elips sebanding dengan √λ, sehingga: - `eccentricity` = √(1 − λ₂/λ₁). Nilainya 0 untuk lingkaran dan mendekati 1 untuk garis. - `orientation` = sudut eigenvektor λ₁ (sumbu utama) terhadap sumbu vertikal. Eccentricity huruf I = 0,97, sedangkan huruf lain paling tinggi 0,88. Program memakai batas 0,95: huruf dengan eccentricity di atas batas itu diputar sebesar `orientation`-nya sendiri sehingga goresannya tepat vertikal. Huruf lain memakai sudut lengkung. ### 3.8 Rotasi dan pemotongan ulang (Bab 2) Rotasi adalah transformasi geometri affine. Dalam koordinat homogen: ``` [x'] [cos θ −sin θ 0] [x] [y'] = [sin θ cos θ 0] [y] [1 ] [ 0 0 1] [1] ``` Setiap huruf diputar sebesar **−θ** (searah jarum jam sebesar θ) agar baseline-nya kembali horizontal. `transform.rotate` memakai *inverse mapping*: setiap piksel output (x', y') dipetakan balik ke posisi asal (x, y) dengan matriks invers. Posisi asal umumnya bukan bilangan bulat, sehingga nilainya dihitung dengan **interpolasi bilinear** dari 4 piksel terdekat: ``` v(x, y) = ax + by + cxy + d ``` Opsi `resize=True` memperbesar kanvas agar ujung huruf tidak terpotong saat diputar. Setelah rotasi, kotak dipotong ulang ke baris dan kolom yang masih memuat piksel huruf (intensitas > T Otsu). Hasilnya kotak rapat di sekitar huruf yang sudah tegak, lalu disimpan sebagai PNG 8-bit. ## 4. Hasil Penamaan file mengikuti urutan label dari `regionprops`, yaitu urutan scan dari atas ke bawah. Karena itu urutan file tidak sama dengan urutan baca untuk TWINPEAK dan ACOSTA. | File | Hasil | Huruf | Kata | Sudut | Sumber sudut | |---|---|---|---|---|---| | karakter_01.png | ![](karakter/karakter_01.png) | Z | ZEUS | −0,3° | lengkung kata | | karakter_02.png | ![](karakter/karakter_02.png) | E | ZEUS | −0,1° | lengkung kata | | karakter_03.png | ![](karakter/karakter_03.png) | U | ZEUS | 0,1° | lengkung kata | | karakter_04.png | ![](karakter/karakter_04.png) | S | ZEUS | 0,3° | lengkung kata | | karakter_05.png | ![](karakter/karakter_05.png) | K | TWINPEAK | 26,9° | lengkung kata | | karakter_06.png | ![](karakter/karakter_06.png) | A | TWINPEAK | 22,9° | lengkung kata | | karakter_07.png | ![](karakter/karakter_07.png) | E | TWINPEAK | 18,6° | lengkung kata | | karakter_08.png | ![](karakter/karakter_08.png) | P | TWINPEAK | 14,4° | lengkung kata | | karakter_09.png | ![](karakter/karakter_09.png) | N | TWINPEAK | 10,1° | lengkung kata | | karakter_10.png | ![](karakter/karakter_10.png) | I | TWINPEAK | −10,7° | sumbu utama huruf | | karakter_11.png | ![](karakter/karakter_11.png) | W | TWINPEAK | 2,1° | lengkung kata | | karakter_12.png | ![](karakter/karakter_12.png) | T | TWINPEAK | −2,1° | lengkung kata | | karakter_13.png | ![](karakter/karakter_13.png) | A | ACOSTA | 55,5° | lengkung kata | | karakter_14.png | ![](karakter/karakter_14.png) | T | ACOSTA | 56,1° | lengkung kata | | karakter_15.png | ![](karakter/karakter_15.png) | S | ACOSTA | 56,8° | lengkung kata | | karakter_16.png | ![](karakter/karakter_16.png) | O | ACOSTA | 57,4° | lengkung kata | | karakter_17.png | ![](karakter/karakter_17.png) | C | ACOSTA | 58,0° | lengkung kata | | karakter_18.png | ![](karakter/karakter_18.png) | A | ACOSTA | 58,6° | lengkung kata | ## 5. Batasan - **Font italic.** Setelah diluruskan, huruf masih tampak sedikit condong (sekitar 8–10°) karena fontnya italic. Ini bentuk huruf, bukan rotasi: ZEUS (sudut 0°) juga italic pada citra asli. Pengecualiannya huruf I: karena diluruskan dengan sumbu utamanya sendiri, I menjadi tepat vertikal. - **Parameter disesuaikan dengan citra ini.** Radius dilasi 48 dan batas eccentricity 0,95 dipilih dari nilai yang diukur pada citra ini. Citra lain bisa membutuhkan nilai lain. - **Kata minimal tiga huruf.** Lengkung kuadratik membutuhkan minimal tiga centroid. - **Huruf tidak boleh bersentuhan.** Jika dua huruf bersentuhan, keduanya menjadi satu komponen dan tersimpan sebagai satu gambar.