Пусть есть картинка, и на ней мы уже знаем какие именно пиксели принадлежат границам объекта, или например мы знаем какие пиксели принадлежат горизонту на ландшафтной фотографии.

И пусть теперь мы хотим на базе этих пикселей определить какая прямая проходит через все этих пиксели максимально аккуратно.

Для начала вспомним какой вид имеет прямая (чтобы понять как ее параметризоваться - т.е. какими параметрами ее исчерпывающе описывать):

\(y = k \cdot x + b\) - стандартная формула в декартовых координатах, но у нее есть недостаток - она не умеет описывать вертикальные прямые (\(k\) начинает устремляться к бесконечности), а значит такая формула недостаточно обща и нам не подходит.

Давайте воспользуемся другой формулой в полярных координатах - любая прямая однозначно задается кратчайшим отрезком от начала системы координат до прямой:

Line polar parametrization

А раз прямая задается этим отрезком, то достаточно научиться исчерпывающе описывать этот отрезок - а он задается углом наклона и длиной - т.е. \(r\) (всегда положительное) и \(\theta\) (от \(0\) до \(2 \cdot \pi\)).

Тогда стандартная формула \(y = k \cdot x + b\) в декартовых координатах через эти параметры преобразуется в следующий вид:

\[y = - \frac{cos \theta}{sin \theta} \cdot x + \frac{r}{sin \theta}\]

Эту формулу можно преобразовать:

\[r = x \cdot cos \theta + y \cdot sin \theta\]

Теперь мы можем заметить, что если есть некая точка \(x_0\), \(y_0\) то все прямые содержащие эту точку имеют четкую зависимость между \(r\) и \(\theta\). Если бы мы однозначно знали что у нашей прямой второй параметр \(\theta=\theta_0\), то могли бы легко найти \(r=r_0\):

\[r_0 = x_0 \cdot cos \theta_0 + y_0 \cdot sin \theta_0\]

Давайте посмотрим на пространство всех возможных кобинаций параметров \(r\), \(theta\) и прочертим в этом пространстве множество прямых содержащих такую точку \(x_0\), \(y_0\):

Lines set from point

А что если у нас не одна точка \(x_0\), \(y_0\) а целых три точки лежащих на картинке на одной прямой? Давайте прочерчем для каждой из этих точек множество прямых которые через точку проходят:

Lines set from three points

И оказывается что эти три кривулины, эти три множества прямых - пересекаются, и точка пересечения - это два параметра (\(r\) и \(theta\)) задающие прямую содержащую наши три точки:

Intersection is the line

И из этого возникает идея - если есть точка которая голосует за искомую прямую, например искомый горизонт на фотографии - то эта точка проводит дополнительную кривулину подсказывая нам где находится истинная прямая.

Итого алгоритм:

1) Взяли картинку, оператором Собеля нашли точки-границы

2) Создали вещественную картинку размера \(max(\theta)\) на \(max(r)\) - т.е. 360 на \(\sqrt{width^2+height^2}\) - назовем ее аккумулятор, это пространство Хафа, в котором мы будем накапливать “голоса” за прямые

3) Для каждой точки-границы \(x_0, y_0\) с силой градиента \(strength\) (т.е. результатом оператора Собеля в этом пикселе):

3.1) Пробежим по всей оси \(\theta\) (т.о. от 0 до \(2 \cdot \pi = 360\) градусов) и высчитаем соответствующее \(r_0=x_0 \cdot cos \theta_0 + y_0 \cdot sin \theta_0\)

3.2) Для очередной пары \(\theta_0, r_0\) - добавим в картинку-аккумулятор голос нашего пикселя причем с весом: accumulator.at<float>(r0, theta0) += strength

4) Нарисуем картинку пространства Хафа и посмотрим сколько ярких точек - должно быть столько сколько прямых на картинке, например вот пара картинка и пространство Хафа:

line11

line11 Hough space

Хорошие материалы