OpenCVMiddleAlgorithms

В чём разница между 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):

  1. Gaussian blur — подавление шума
  2. Sobel — вычисление градиента по X и Y
  3. Non-maximum suppression (NMS) — утончение рёбер до 1 пикселя
  4. Double threshold — два порога: threshold1 (слабые рёбра) и threshold2 (сильные рёбра)
  5. 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-пайплайном.

Sources

Related topics