Mode belajar: tulis kode sendiri. Copy/cut, seleksi teks dan klik kanan dibatasi pada halaman; pembatasan UI bukan jaminan anti-penyalinan mutlak.
1. Tujuan
- Menghasilkan peta tepi Canny dan menjelaskan pengaruh blur serta dua threshold.
- Mengekstrak kontur eksternal dari mask objek dan membedakannya dari pixel tepi.
- Menghitung luas kontur, perimeter, dan bounding box dalam satuan pixel.
- Mengklasifikasikan bentuk sederhana secara heuristik memakai approxPolyDP, circularity, sudut, dan rasio sisi.
Prasyarat: Memahami grayscale, threshold, mask, dan notebook Colab. Sel import/upload tetap mandiri; tidak perlu menjalankan modul lain.
2. Materi Pembahasan

2.1 Tepi sebagai perubahan intensitas
Tepi adalah lokasi perubahan intensitas yang kuat, bukan label objek. Batas daun terhadap latar dapat menimbulkan tepi, tetapi urat daun, bayangan, tulisan, atau refleksi juga dapat menimbulkannya. Canny menggunakan pemrosesan gradien dan keputusan dengan dua threshold untuk menghasilkan peta tepi yang tipis. Threshold bawah membantu menghubungkan kandidat lemah yang tersambung dengan kandidat kuat; threshold atas menentukan kandidat tepi kuat.
Blur Gaussian ringan diterapkan sebelum Canny untuk mengurangi respons terhadap noise. Ambang contoh 50 dan 150 merupakan titik awal, bukan pilihan optimal universal. Jika terlalu rendah, tekstur dan noise dapat dominan; jika terlalu tinggi, batas objek melemah atau terputus. Periksa peta tepi dengan foto sumber dan catat parameter. Peta Canny tidak langsung memberi area objek penuh untuk pengukuran luas.
2.2 Kontur dari mask foreground
Kontur adalah rangkaian titik batas pada citra biner. Praktikum menampilkan Canny sebagai studi tepi, lalu mengekstrak kontur dari mask Otsu yang dipilih polaritasnya. Pemisahan langkah ini mencegah mahasiswa menyamakan kumpulan pixel tepi dengan satu siluet tertutup. RETR_EXTERNAL hanya mengambil batas terluar sehingga lubang dan hubungan hierarki internal tidak dianalisis dalam contoh dasar.
CHAIN_APPROX_SIMPLE mengurangi titik yang berlebihan pada segmen lurus. Kontur kecil disaring berdasarkan luas untuk mengurangi pengaruh bintik. Objek menyentuh bingkai dilewati pada contoh karena batasnya tidak lengkap di dalam foto; keputusan tersebut dilaporkan, bukan disembunyikan. Jika mask salah memilih background, kontur besar di bingkai dapat muncul. Periksa mask terlebih dahulu sebelum mencoba memperbaiki klasifikasi bentuk dengan parameter geometri.
2.3 Luas, perimeter, dan bounding box
contourArea menghitung luas geometris dari rangkaian titik, sedangkan arcLength menghitung panjang lintasan batas. Keduanya menggunakan satuan pixel, bukan ukuran fisik kamera. Jumlah pixel putih pada mask dan luas kontur dapat berbeda karena satu menghitung raster terisi dan yang lain menghitung geometri batas. Tanpa kalibrasi skala dan koreksi perspektif, jangan menyebut hasil luas sebagai sentimeter persegi.
boundingRect menghasilkan koordinat kiri atas serta lebar dan tinggi kotak sejajar sumbu gambar. Kotak ini berguna untuk memberi anotasi dan mengambil crop, tetapi mencakup background di sudut luar objek. Objek berotasi dapat mempunyai kotak sejajar sumbu yang lebih besar. minAreaRect menghasilkan persegi panjang berotasi, sehingga rasio sisinya digunakan untuk membantu menguji persegi tanpa terlalu bergantung pada orientasi foto.
2.4 Pendekatan polygon dan bentuk sederhana
approxPolyDP menyederhanakan batas dengan toleransi epsilon. Contoh menggunakan dua persen perimeter sebagai baseline. Polygon empat vertex tidak otomatis persegi: persegi panjang, belah ketupat, atau bentuk perspektif juga dapat mempunyai empat sudut. Karena itu contoh memeriksa convexity, kedekatan sudut terhadap siku-siku, dan rasio sisi kotak berotasi. Bentuk empat sudut yang gagal pemeriksaan tetap dilabeli segiempat, bukan dipaksa menjadi persegi.
Lingkaran diduga melalui circularity, yaitu 4πA/P², jumlah vertex pendekatan, convexity, dan rasio sisi. Nilai mendekati satu menunjukkan kontur relatif bulat, tetapi noise, perspektif, dan rasterisasi mengubah nilainya. Label lingkaran pada contoh merupakan kandidat heuristik. Benda organik seperti daun dapat memiliki kurvatur halus tanpa berbentuk lingkaran; fitur geometris sederhana tidak cukup untuk pengenalan kategori atau spesies.
2.5 Evaluasi label dan objek tertutup
Tampilkan mask, peta tepi, kontur, polygon pendekatan, dan bounding box agar keputusan dapat ditelusuri. Setiap deteksi diberi nomor dan tabel ringkas yang berisi label, luas, perimeter, jumlah vertex, circularity, serta koordinat kotak. Kontur yang ditemukan library direpresentasikan sebagai lintasan tertutup untuk perhitungan, tetapi itu bukan bukti bahwa benda fisik pada foto mempunyai batas sempurna atau seluruhnya terlihat.
Tugas memakai foto lingkaran, persegi, dan satu objek tertutup lain dengan latar kontras serta posisi tidak saling menempel. Bandingkan label dengan bentuk yang diketahui, lalu dokumentasikan kesalahan akibat sudut pandang, shadow, atau parameter. Untuk testing algoritme, gambar sintetis hanya boleh disebut fixture pengujian, bukan foto nyata. Notebook penyerahan tetap menggunakan foto nyata dan menyimpan analisis serta semua output dalam satu link Google Colab Viewer.
Video pembelajaran sesuai bab
Real time Shape Detection using Contours [9] | OpenCV Python Tutorials for Beginners 2020 — Murtaza's Workshop - Robotics and AI.
Fokus saat menonton: Ikuti preprocessing, contour dan aproksimasi bentuk. Bedakan batas piksel, kontur dan label bentuk.
Referensi dan sumber ilustrasi
3. Praktikum Google Colab
Buat notebook baru, simpan pada Drive milik sendiri, lalu isi identitas. Gunakan sel kode terpisah untuk setiap langkah dan jalankan berurutan dengan Shift + Enter. Tidak perlu mengunduh notebook contoh atau memasang Python lokal.
# Materi 06 — Pengolahan Citra
Nama: [Nama lengkap mahasiswa]
NPM: [NPM mahasiswa]
Kelas: [Kelas, jika diperlukan]- Ambil foto objek kontras pada latar sederhana; objek tidak saling menyentuh dan tidak terpotong bingkai.
- Buat notebook Colab mandiri; upload foto dan jalankan Canny untuk memeriksa tepi eksternal maupun internal.
- Tentukan polaritas mask dan periksa Otsu sebelum mengukur bentuk.
- Jalankan kontur, catat jumlah yang disaring, lalu periksa luas serta bounding box pada rekap.
- Bandingkan kontur dengan polygon approx; ubah epsilon 0.01, 0.02, dan 0.04 kali perimeter untuk eksperimen.
- Gunakan foto yang memuat lingkaran, persegi, serta objek tertutup lain; simpan semua output dan analisis.
- Kumpulkan satu link Google Colab akses Viewer dengan Nama/NPM, kode, hasil, serta pembahasan kesalahan.
Sel 1 — 1. Import library
Ketik di notebook Colab baru setelah sel Markdown Nama dan NPM. Modul ini mandiri; tidak menggunakan variabel dari modul lain.
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import filesSel 2 — 2. Upload satu foto
Pilih satu foto JPEG/PNG uint8, maksimum 10 MiB dan 12 juta pixel. Pembacaan mempertahankan channel asli untuk validasi, lalu menyiapkan BGR tiga channel. PNG transparan tidak dipakai agar tidak ada background tersembunyi.
uploaded = files.upload()
if len(uploaded) != 1:
raise ValueError("Pilih tepat satu foto JPEG/PNG.")
nama, data = next(iter(uploaded.items()))
if not nama.lower().endswith((".jpg", ".jpeg", ".png")):
raise ValueError("Gunakan file JPEG atau PNG.")
if not data or len(data) > 10 * 1024 * 1024:
raise ValueError("File kosong atau melebihi 10 MiB.")
raw = cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_UNCHANGED)
if raw is None:
raise ValueError("File tidak dapat didecode sebagai gambar.")
h, w = raw.shape[:2]
if h * w > 12_000_000 or min(h, w) < 32 or raw.dtype != np.uint8:
raise ValueError("Gunakan gambar uint8, sisi minimal 32 pixel, <=12 MP.")
if raw.ndim == 2:
bgr = cv2.cvtColor(raw, cv2.COLOR_GRAY2BGR)
elif raw.ndim == 3 and raw.shape[2] == 3:
bgr = raw.copy()
else:
raise ValueError("Gunakan foto grayscale/3 channel tanpa alpha.")
print("Foto:", nama, "shape BGR:", bgr.shape)
plt.figure(figsize=(6, 5))
plt.imshow(cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB))
plt.title("Foto unggahan")
plt.axis("off")
plt.show()Sel 3 — 3. Peta tepi Canny
Dua threshold Canny adalah parameter awal. Bandingkan tepi dengan foto sumber agar tekstur internal tidak dianggap batas objek.
gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
LOW, HIGH = 50, 150
if not 0 <= LOW < HIGH:
raise ValueError("Threshold Canny harus 0 <= LOW < HIGH.")
edges = cv2.Canny(blur, LOW, HIGH)
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].imshow(cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)); axes[0].set_title("Foto sumber")
axes[1].imshow(edges, cmap="gray", vmin=0, vmax=255)
axes[1].set_title(f"Canny: {LOW}, {HIGH}")
for ax in axes: ax.axis("off")
plt.tight_layout(); plt.show()Sel 4 — 4. Mask dan kontur eksternal
Kontur dihitung dari mask siluet Otsu, bukan langsung dari tekstur Canny. Gunakan OBJEK_GELAP sesuai foto; morfologi kecil boleh dilewati bila merusak bentuk.
OBJEK_GELAP = True
mode = cv2.THRESH_BINARY_INV if OBJEK_GELAP else cv2.THRESH_BINARY
T_otsu, mask_awal = cv2.threshold(blur, 0, 255, mode | cv2.THRESH_OTSU)
kernel = np.ones((3, 3), dtype=np.uint8)
mask = cv2.morphologyEx(mask_awal, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
MIN_AREA = max(50.0, 0.001 * mask.size)
kontur_valid = []
h, w = mask.shape
for cnt in contours:
if cv2.contourArea(cnt) < MIN_AREA:
continue
x, y, bw, bh = cv2.boundingRect(cnt)
if x <= 0 or y <= 0 or x + bw >= w or y + bh >= h:
print("Dilewati: kontur menyentuh bingkai; cek polaritas atau ambil foto lebih longgar.")
continue
kontur_valid.append(cnt)
kontur_valid.sort(key=lambda c: cv2.boundingRect(c)[0])
print("Threshold Otsu:", T_otsu, "MIN_AREA:", MIN_AREA,
"kontur total:", len(contours), "kontur valid:", len(kontur_valid))
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
for ax, judul, img in zip(axes, ["Mask awal", "Mask untuk kontur"], [mask_awal, mask]):
ax.imshow(img, cmap="gray", vmin=0, vmax=255); ax.set_title(judul); ax.axis("off")
plt.tight_layout(); plt.show()Sel 5 — 5. Luas, bounding box, dan klasifikasi bentuk
Label adalah heuristik geometri, bukan pengenal kategori. Persegi diperiksa dengan sudut serta rasio sisi minAreaRect; lingkaran memerlukan circularity dan bentuk cukup bulat.
def dugaan_bentuk(cnt):
area = float(cv2.contourArea(cnt))
perimeter = float(cv2.arcLength(cnt, True))
approx = cv2.approxPolyDP(cnt, 0.02 * perimeter, True)
n = len(approx)
circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter > 0 else 0.0
sisi = cv2.minAreaRect(cnt)[1]
rasio = max(sisi) / min(sisi) if min(sisi) > 0 else float("inf")
convex = bool(cv2.isContourConvex(approx))
label = "objek tertutup lain"
if n == 3:
label = "segitiga"
elif n == 4:
pts = approx.reshape(-1, 2).astype(np.float32)
cos_sudut = []
for i in range(4):
u = pts[(i - 1) % 4] - pts[i]
v = pts[(i + 1) % 4] - pts[i]
pembagi = float(np.linalg.norm(u) * np.linalg.norm(v))
cos_sudut.append(abs(float(np.dot(u, v))) / pembagi if pembagi else 1.0)
siku = max(cos_sudut) < 0.25
if convex and siku:
label = "persegi" if rasio <= 1.10 else "persegi panjang"
else:
label = "segiempat lain"
elif n >= 5 and convex and circularity >= 0.80 and rasio <= 1.20:
label = "kandidat lingkaran"
return label, approx, area, perimeter, float(circularity)
anotasi = bgr.copy()
rekap = []
for nomor, cnt in enumerate(kontur_valid, start=1):
label, approx, area, perimeter, circ = dugaan_bentuk(cnt)
x, y, bw, bh = cv2.boundingRect(cnt)
cv2.drawContours(anotasi, [cnt], -1, (0, 255, 0), 2)
cv2.polylines(anotasi, [approx], True, (255, 0, 255), 2)
cv2.rectangle(anotasi, (x, y), (x + bw - 1, y + bh - 1), (255, 0, 0), 2)
cv2.putText(anotasi, f"{nomor}: {label}", (x, max(15, y - 8)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
item = {"nomor": nomor, "label": label, "luas_pixel2": round(area, 2),
"perimeter_pixel": round(perimeter, 2), "vertex": len(approx),
"circularity": round(circ, 3), "bbox_xywh": (x, y, bw, bh)}
rekap.append(item)
print(item)
if not rekap:
print("Tidak ada kontur valid. Periksa mask, polaritas, MIN_AREA, dan posisi objek.")
plt.figure(figsize=(12, 8))
plt.imshow(cv2.cvtColor(anotasi, cv2.COLOR_BGR2RGB))
plt.title("Hijau: kontur; magenta: approx; biru: bounding box")
plt.axis("off"); plt.show()Jika terjadi error
- Banyak kontur kecil muncul.
- Periksa mask dan noise; atur prapemrosesan/MIN_AREA secara terdokumentasi, jangan hanya menaikkan batas tanpa melihat objek kecil.
- Tidak ada kontur valid.
- Periksa polaritas, mask kosong/penuh, objek menyentuh bingkai, dan apakah MIN_AREA terlalu besar.
- Persegi miring dilabeli bentuk lain.
- Periksa polygon, sudut, dan rasio minAreaRect; perspektif dapat mengubah persegi menjadi trapezoid, sehingga label harus dianggap heuristik.
- Luas dilaporkan sebagai cm².
- Gunakan pixel²; ukuran fisik memerlukan objek kalibrasi, skala, dan koreksi perspektif.
- Satu benda menghasilkan banyak garis Canny.
- Canny mendeteksi tekstur; gunakan mask foreground yang diperiksa untuk kontur siluet, bukan mengasumsikan semua tepi adalah batas objek.
Runtime Colab dan file upload bersifat sementara. Simpan output penting pada notebook/Drive sebelum sesi berakhir. Jika runtime direset, jalankan ulang sel dari awal. Gunakan media berizin dan hindari unggahan data sensitif tanpa izin.
4. Tugas 6
Deteksi lingkaran, persegi, dan satu objek tertutup lain dari foto nyata, tampilkan kontur, label dugaan, serta bounding box. Kumpulkan hanya SATU link Google Colab berakses Viewer dengan identitas, output, dan analisis.
Ketentuan pengerjaan
- Gunakan satu atau beberapa foto nyata yang bersama-sama memuat lingkaran, persegi, dan satu objek tertutup lain; objek tidak saling menempel.
- Tampilkan peta Canny, mask objek, kontur, polygon approx, dan bounding box.
- Catat threshold Canny, polaritas, nilai Otsu, MIN_AREA, serta epsilon approximation.
- Laporkan luas pixel², perimeter pixel, jumlah vertex, circularity, dan bbox (x,y,w,h) untuk tiap kontur valid.
- Jangan menyebut empat vertex saja sebagai bukti persegi; sertakan pemeriksaan sudut/rasio sisi dan jelaskan label heuristik.
- Bandingkan minimal tiga epsilon dan analisis satu kasus salah label atau pengaruh perspektif.
- Gambar sintetis hanya untuk fixture pengujian berlabel, bukan pengganti foto tugas; semua hasil, Nama/NPM, dan analisis berada dalam satu link Viewer.
Pertanyaan analisis
- Mengapa peta Canny dan batas kontur mask tidak selalu sama?
- Bagaimana epsilon mengubah jumlah vertex dan label bentuk?
- Apa perbedaan luas kontur, jumlah pixel foreground, dan luas bounding box?
- Bagaimana rotasi dan perspektif mempengaruhi deteksi persegi atau lingkaran?
# Materi 06 — Pengolahan Citra
Nama: [Nama lengkap mahasiswa]
NPM: [NPM mahasiswa]
Kelas: [Kelas, jika diperlukan]Pengumpulan
Kumpulkan satu link Google Colab saja. Nama dan NPM wajib berada pada sel Markdown paling atas. Sertakan kode, output yang tersimpan/terlihat serta analisis di notebook; tidak perlu mengumpulkan file .ipynb, ZIP, PDF atau screenshot terpisah.
- Jalankan sel dengan konfigurasi valid dan simpan notebook beserta output. Pastikan opsi menghilangkan output saat menyimpan tidak aktif untuk versi pengumpulan.
- Bagikan sebagai Viewer/Pelihat kepada dosen. Gunakan akses link jika diizinkan institusi; bila dibatasi, beri akses khusus akun dosen. Jangan memberi izin edit publik.
- Periksa akses dari akun penerima. Kirim URL notebook Colab, bukan folder Drive atau runtime sementara.
| Aspek | Bobot | Indikator |
|---|---|---|
| Input, Canny, dan mask | 25% | Foto nyata sesuai objek, peta tepi serta mask diperiksa, parameter lengkap. |
| Kontur dan bounding box | 30% | Kontur valid, bbox, luas, dan perimeter ditampilkan serta satuannya benar. |
| Shape detection dan eksperimen | 25% | Approx, pemeriksaan bentuk, dan variasi epsilon dilakukan; label tidak overclaim. |
| Analisis dan notebook | 20% | Kesalahan dijelaskan dari output; identitas, semua hasil, dan satu link Viewer lengkap. |