В чём разница между cv2.Canny() и детектированием границ по Sobel/Laplacian?
Sobel/Laplacian — базовые операторы градиента (одна свёртка). Canny — многоэтапный алгоритм: размытие → Sobel → NMS → двойной порог → трассировка рёбер. Canny даёт тонкие связные рёбра; Sobel/Laplacian — сырую карту градиента.
Алгоритмическая разница
Sobel вычисляет градиент по X и Y через свёртку с ядром 3×3 (или 5×5, 7×7). Результат — карта амплитуды градиента: толстые и шумные рёбра, float-значения.
Laplacian — вторая производная (лапласиан): чувствителен к шуму, реагирует на оба края перехода, результат может быть отрицательным. Перед применением нужно размытие.
Canny — многоэтапный детектор (1986):
- Gaussian blur — подавление шума
- Sobel — вычисление градиента по X и Y
- Non-maximum suppression (NMS) — утончение рёбер до 1 пикселя
- Double threshold — два порога:
threshold1(слабые рёбра) иthreshold2(сильные рёбра) - Edge tracking by hysteresis — слабые рёбра включаются, только если связаны с сильными
Рабочий пример
import cv2 as cv
import numpy as np
img = cv.imread("scene.jpg")
if img is None:
raise FileNotFoundError("scene.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# --- Sobel ---
# ksize должен быть нечётным: 1, 3, 5, 7
sobel_x = cv.Sobel(gray, cv.CV_64F, 1, 0, ksize=3) # градиент по X
sobel_y = cv.Sobel(gray, cv.CV_64F, 0, 1, ksize=3) # градиент по Y
magnitude = cv.magnitude(sobel_x, sobel_y) # амплитуда (float64)
# Для отображения — нормализуем в uint8
sobel_vis = cv.normalize(magnitude, None, 0, 255, cv.NORM_MINMAX, cv.CV_8U)
print("Sobel shape:", sobel_vis.shape, "dtype:", sobel_vis.dtype)
# (H, W), uint8
# --- Laplacian ---
# Рекомендуем размытие перед лапласианом
blurred = cv.GaussianBlur(gray, (5, 5), 0)
laplacian = cv.Laplacian(blurred, cv.CV_64F) # float64, содержит отрицательные значения
laplacian_abs = np.uint8(np.absolute(laplacian)) # берём модуль
print("Laplacian shape:", laplacian_abs.shape) # (H, W)
# --- Canny ---
# Правило: threshold2 ≈ 2–3× threshold1
canny = cv.Canny(gray, threshold1=50, threshold2=150)
# dtype=uint8, значения строго 0 или 255
print("Canny shape:", canny.shape, "dtype:", canny.dtype) # (H, W), uint8
# Canny с явным ядром Гаусса (apertureSize — размер Sobel внутри Canny)
canny_fine = cv.Canny(gray, 30, 100, apertureSize=5, L2gradient=True)
# Сравнение плотности рёбер
print(f"Sobel fg: {np.count_nonzero(sobel_vis > 50):6d} пикс.")
print(f"Canny fg: {np.count_nonzero(canny):6d} пикс.") # обычно меньше, рёбра тоньше
Когда что использовать
Canny — стандарт для большинства задач: поиск контуров, предобработка перед findContours, Hough-преобразование. Даёт чистые тонкие рёбра.
Sobel — когда нужна не бинарная маска, а вещественная карта градиента: оценка резкости изображения, текстурные признаки, directional edge detection.
Laplacian — тест на резкость (Laplacian variance), обнаружение blob-структур, иногда в составе LoG (Laplacian of Gaussian) для SIFT-подобных дескрипторов.
Подводные камни
- Sobel с
ddepth=cv.CV_8Uобрезает отрицательные значения (рёбра на переходе тёмный→светлый пропадают). Всегда используйтеCV_64FилиCV_16S, затем берите модуль. - Laplacian без размытия усиливает шум — получите «снег» вместо рёбер. Всегда делайте GaussianBlur перед Laplacian.
- Неправильное соотношение порогов Canny: если
threshold1 ≈ threshold2, гистерезис не работает — рёбра будут прерывистыми. Рекомендованное соотношение 1:2 или 1:3. - Canny принимает только uint8: если передать float32 или float64 —
cv2.error. Конвертируйте заранее. - Sobel с
ksize=1использует ядро Шарра (3-точечное), не стандартный Sobel — результаты могут удивить. - Canny на цветном изображении: надо конвертировать в grayscale вручную. Canny не принимает 3-канальный массив.
- Параметры Canny зависят от масштаба: одни и те же пороги дадут разные результаты на изображениях разного разрешения. Для робастности используйте автоматический выбор порогов (метод Отцу как стартовая точка).
Common mistakes
- Объяснять
canny vs sobel laplacianтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
canny vs sobel laplacianс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.