5. ünite / 12
Kenar algılama: gradyan, Sobel ve Canny
Türev olarak kenar, Sobel çekirdekleri, büyüklük ve yön; Canny'nin dört adımı ve histerezis eşikleri.
Bu ünitenin sonunda
- Kenarı parlaklığın hızlı değiştiği yer olarak tanımlamak ve Sobel ile ölçmek
- Gradyan büyüklüğü ve yönünü hesaplamak
- Canny'nin dört adımını sırasıyla açıklamak
- Alt ve üst eşiği sonuca göre ayarlamak
Ders notları
Kenar, parlaklığın kısa mesafede çok değiştiği yerdir: nesne sınırı, gölge sınırı, doku çizgisi. Değişim türevle ölçülür. Görüntü kesikli olduğundan türev bir evrişimle yaklaşık hesaplanır; Sobel çekirdeği türevi bir yönde alırken öbür yönde hafifçe yumuşatır:
Gradyan büyüklüğü kalın ve gürültülü bir kenar haritası verir. Türev gürültüyü büyüttüğü için önce bulanıklaştırmak gerekir.
Canny'nin dört adımı
- Gauss yumuşatma: gürültüyü bastır.
- Sobel gradyanı: büyüklük ve yön.
- Maksimum olmayanı bastırma (NMS): her pikselde gradyan yönündeki iki komşuya bak; en büyük değilsen sıfırlan. Kalın kenar tek piksele iner.
- Çift eşik ve histerezis: üst eşikten büyükler kesin kenar; alt ile üst arasındakiler yalnız kesin bir kenara bağlıysa kalır. Kopuk kenarlar böyle birleşir, tek başına gürültü atılır.
Pratik bir kural: üst eşik alt eşiğin 2–3 katı. Ortanca parlaklığın 0,66 ve 1,33 katı da iyi bir başlangıçtır.
Oyun alanı
Kenar algılama: Sobel ve Canny
Sobel yatay ve dikey türevi verir; büyüklük ve yön kenarı tarif eder. Canny dört adımdır: Gauss yumuşatma, Sobel gradyanı, maksimum olmayanı bastırma ve iki eşikli histerezis. Eşikleri değiştir, kopuk kenarların nasıl birleştiğine bak.
Kendin dene
Bloklar sahnesinde Sobel büyüklüğüne bak, sonra Canny'ye geç. Alt eşiği 20'den 120'ye kaydır (üst 150): kenar sayısı nasıl değişti? Alt = üst yaparak histerezisi kapat ve yıldızın kenarlarında kopuklukları say. Bulanıklık çekirdeğini 1'e (yok) indir ve dokulu zeminde neler çıktığını açıkla.
Kod
import cv2
import numpy as np
img = cv2.imread('girdi.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 1.4)
gx = cv2.Sobel(blur, cv2.CV_32F, 1, 0, ksize=3) # negatif değerler için float
gy = cv2.Sobel(blur, cv2.CV_32F, 0, 1, ksize=3)
mag, ang = cv2.cartToPolar(gx, gy, angleInDegrees=True)
print('en büyük gradyan:', round(float(mag.max()), 1))
med = float(np.median(blur)) # ortancaya göre otomatik eşikler
lo, hi = int(max(0, 0.66 * med)), int(min(255, 1.33 * med))
edges = cv2.Canny(blur, lo, hi) # içeride: NMS + histerezis
print('Canny', lo, hi, 'kenar pikseli:', int((edges > 0).sum()))
mag8 = cv2.convertScaleAbs(mag, alpha=255.0 / max(float(mag.max()), 1.0))
cv2.imwrite('cikti.png', np.hstack([mag8, edges]))Sık yapılan hatalar
- Sobel sonucunu uint8'de istemek (cv2.CV_8U): negatif türevler 0'a kırpılır, kenarların yarısı kaybolur. CV_16S ya da CV_32F kullan.
- Canny'den önce bulanıklaştırmayı atlamak: dokulu bölgelerde binlerce kısa kenar çıkar.
- Eşikleri her görüntüde elle değiştirmek: ortancaya bağlı otomatik eşik ya da önce eşitleme düşün.
- Canny çıktısını kapalı kontur sanmak: kenarlar kopuk olabilir; kontur bulmadan önce genişletme (6. ünite) gerekebilir.
Yazılım mühendisliği bağlantısı
Canny bir boru hattıdır: her adım bir öncekinin çıktısını alır. Hata ayıklarken ara çıktıları görmek (yukarıdaki araçta olduğu gibi) en hızlı yoldur.
- Her aşamayı ayrı fonksiyon yap ve ara sonuçları isteğe bağlı döndür (debug=True).
- Sentetik girdilerle test et: kare bir nesnenin her satırında tam iki kenar pikseli olmalı.
Kendini sına
Canny'de maksimum olmayanı bastırma (NMS) adımının amacı nedir?
Alt eşik 50, üst eşik 150. Gradyanı 100 olan bir piksel ne zaman kenar sayılır?
Sobel sonucu neden float (ya da 16 bit işaretli) tutulmalıdır?
Dikey bir kenarda (solda koyu, sağda açık) hangi gradyan bileşeni büyüktür?
