Implement a Hough accumulator array for line detection given a list of edge points. The goal is to build a 2D voting array indexed by θ and ρ to detect lines in an image.
The Hough transform is a feature extraction technique used in image processing to detect lines, circles, and other shapes. It works by transforming image points into a parameter space, where each point in the image votes for all possible lines that could pass through it. The Hough accumulator is a 2D array where each cell represents a possible line in the image, parameterized by θ (angle) and ρ (distance from origin).
Here are the steps to build the accumulator:
For each edge point (x,y)
For each θ bin (0° to 180°)
Compute ρ=x⋅cos(θ)+y⋅sin(θ)
Map ρ to a bin index and increment that cell
ρ=x⋅cos(θ)+y⋅sin(θ)
This technique is widely used in computer vision for line detection and image processing.
The task is to implement the Hough line transform accumulator: for each edge point, vote in a 2D array over angle \theta and distance ρ, counting how many points support each possible line.
1. Background Knowledge (Concepts & Theory)
A straight line in image (Cartesian) coordinates (x,y) can be written in Hough form:
ρ=xcosθ+ysinθ
where:
ρ is the perpendicular distance from the origin to the line.
\theta is the angle of the line’s normal vector with the x-axis.
The Hough transform maps points in image space to curves in parameter space(\theta,ρ).
One image point lies on infinitely many lines → in (\theta,ρ) space this is represented as a sinusoidal curve.
If many points lie on the same geometric line, their curves intersect at a common (\theta,ρ). Counting votes in a 2D accumulator lets you find peaks that correspond to actual lines in the image.
Because images are discrete and bounded, you:
Discretize \theta into bins in [0∘,180∘).
Discretize ρ into bins in [−ρmax,ρmax], where
ρmax=width2+height2
Use a 2D array acc[theta_idx][rho_idx] to count votes.
2. Algorithm / General Approach
Pattern for this problem:
Precompute discretization:
Choose number of \theta bins (numTheta).
Derive number of ρ bins (numRho) from image size and desired resolution.
Precompute trigonometry:
For each \theta-bin, precompute cos(theta) and sin(theta).
Iterate over all edge points:
For each point (x,y) and for every \theta-bin:
Compute continuous ρ.
Map ρ to a discrete bin index.
Increment the corresponding accumulator cell.
Optionally, peak detection on the accumulator gives detected lines (not required for just building the accumulator).
This is a nested-loop voting algorithm: outer loop over points, inner loop over parameter bins, updating a shared 2D histogram.
3. Step-by-Step Strategy (Implementation Outline)
Assume you’re given:
Image width, height
List of edge points edges = [(x1, y1), (x2, y2),...]
numTheta and numRho (or at least numTheta and you derive numRho)
1) Define parameter ranges
import math
max_rho = math.sqrt(width**2 + height**2)
# theta in [0, 180)
num_theta = N_theta # e.g. 180 or 360 depending on resolution
theta_step = math.pi / num_theta # radians per bin
# rho in [-max_rho, max_rho]
num_rho = N_rho # choose resolution; e.g. int(2 * max_rho) or so
rho_step = (2 * max_rho) / num_rho
2) Precompute sin/cos for each θ-bin
cos_t = [math.cos(i * theta_step) for i in range(num_theta)]
sin_t = [math.sin(i * theta_step) for i in range(num_theta)]
3) Initialize the accumulator
# 2D array: theta index × rho index
acc = [[0 for _ in range(num_rho)] for _ in range(num_theta)]
4) Voting loop
Core idea: map continuous ρ to an integer bin:
rho_idx=⌊rho_stepρ+ρmax⌋
for (x, y) in edges:
for t_idx in range(num_theta):
rho = x * cos_t[t_idx] + y * sin_t[t_idx] # continuous
rho_idx = int((rho + max_rho) / rho_step) # shift to [0, 2*max_rho]
if 0 <= rho_idx < num_rho:
acc[t_idx][rho_idx] += 1
At the end, acc is your Hough accumulator.
4. Common Pitfalls
Wrong coordinate origin:
Make sure (x,y) are defined consistently with your Hough equation.
Many implementations use origin at the image center instead of top-left; if the problem assumes one, stick to that consistently.
Incorrect ρ indexing / negative ρ:
ρ can be negative. You must shift by +max_rho before dividing by rho_step to get a non-negative index.
Check boundary conditions so rho_idx never goes outside [0, num_rho-1].
Angle units:
Use radians for sin/cos in code, even if conceptual description uses degrees.
Ensure the mapping from theta_idx to actual angle is correct:
theta = theta_idx * theta_step # theta_step in radians
Too coarse or too fine bin sizes:
Very coarse bins → accumulator too “blurry”, peaks are weak or merged.
Extremely fine bins → accumulator huge and sparse, expensive and noisy.
Performance issues:
Complexity is O(N_\text{points} \times N_\theta). With many points and large numTheta, this is expensive; precomputing sin/cos helps, and sometimes you limit theta range using gradient orientation (not required here but common in practice).
5. Time & Space Complexity
Let:
P = number of edge points
T = number of \theta-bins (numTheta)
R = number of ρ-bins (numRho)
Time Complexity:
Outer loop over all points: P
Inner loop over all angles: T
Constant work inside (a few arithmetic ops, one array increment).