Skip to content

Instantly share code, notes, and snippets.

@thejasonfisher
Forked from arccoder/opencv_hough_lines.py
Created December 8, 2023 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thejasonfisher/9b54b473cc162a3577b3e3d397cfc4e9 to your computer and use it in GitHub Desktop.
Save thejasonfisher/9b54b473cc162a3577b3e3d397cfc4e9 to your computer and use it in GitHub Desktop.
Process the output of cv2.HoughLines from OpenCV using functions that do Polar to Cartesian line conversion, Intersection of Cartesian lines, Line end points on image. https://arccoder.medium.com/process-the-output-of-cv2-houghlines-f43c7546deae
"""
Script contains functions to process the lines in polar coordinate system
returned by the HoughLines function in OpenCV
Line equation from polar to cartesian coordinates
x = rho * cos(theta)
y = rho * sin(theta)
x, y are at a distance of rho from 0,0 at an angle of theta
Therefore,
m = (y - 0) / (x - 0)
Using the values of x, y, and m
b = y - m * x
Python 3.6 was used to compile and test the code.
"""
import numpy as np
def polar2cartesian(rho: float, theta_rad: float, rotate90: bool = False):
"""
Converts line equation from polar to cartesian coordinates
Args:
rho: input line rho
theta_rad: input line theta
rotate90: output line perpendicular to the input line
Returns:
m: slope of the line
For horizontal line: m = 0
For vertical line: m = np.nan
b: intercept when x=0
"""
x = np.cos(theta_rad) * rho
y = np.sin(theta_rad) * rho
m = np.nan
if not np.isclose(x, 0.0):
m = y / x
if rotate90:
if m is np.nan:
m = 0.0
elif np.isclose(m, 0.0):
m = np.nan
else:
m = -1.0 / m
b = 0.0
if m is not np.nan:
b = y - m * x
return m, b
def solve4x(y: float, m: float, b: float):
"""
From y = m * x + b
x = (y - b) / m
"""
if np.isclose(m, 0.0):
return 0.0
if m is np.nan:
return b
return (y - b) / m
def solve4y(x: float, m: float, b: float):
"""
y = m * x + b
"""
if m is np.nan:
return b
return m * x + b
def intersection(m1: float, b1: float, m2: float, b2: float):
# Consider y to be equal and solve for x
# Solve:
# m1 * x + b1 = m2 * x + b2
x = (b2 - b1) / (m1 - m2)
# Use the value of x to calculate y
y = m1 * x + b1
return int(round(x)), int(round(y))
def line_end_points_on_image(rho: float, theta: float, image_shape: tuple):
"""
Returns end points of the line on the end of the image
Args:
rho: input line rho
theta: input line theta
image_shape: shape of the image
Returns:
list: [(x1, y1), (x2, y2)]
"""
m, b = polar2cartesian(rho, theta, True)
end_pts = []
if not np.isclose(m, 0.0):
x = int(0)
y = int(solve4y(x, m, b))
if point_on_image(x, y, image_shape):
end_pts.append((x, y))
x = int(image_shape[1] - 1)
y = int(solve4y(x, m, b))
if point_on_image(x, y, image_shape):
end_pts.append((x, y))
if m is not np.nan:
y = int(0)
x = int(solve4x(y, m, b))
if point_on_image(x, y, image_shape):
end_pts.append((x, y))
y = int(image_shape[0] - 1)
x = int(solve4x(y, m, b))
if point_on_image(x, y, image_shape):
end_pts.append((x, y))
return end_pts
def hough_lines_end_points(lines: np.array, image_shape: tuple):
"""
Returns end points of the lines on the edge of the image
"""
if len(lines.shape) == 3 and \
lines.shape[1] == 1 and lines.shape[2] == 2:
lines = np.squeeze(lines)
end_pts = []
for line in lines:
rho, theta = line
end_pts.append(
line_end_points_on_image(rho, theta, image_shape))
return end_pts
def hough_lines_intersection(lines: np.array, image_shape: tuple):
"""
Returns the intersection points that lie on the image
for all combinations of the lines
"""
if len(lines.shape) == 3 and \
lines.shape[1] == 1 and lines.shape[2] == 2:
lines = np.squeeze(lines)
lines_count = len(lines)
intersect_pts = []
for i in range(lines_count - 1):
for j in range(i + 1, lines_count):
m1, b1 = polar2cartesian(lines[i][0], lines[i][1], True)
m2, b2 = polar2cartesian(lines[j][0], lines[j][1], True)
x, y = intersection(m1, b1, m2, b2)
if point_on_image(x, y, image_shape):
intersect_pts.append([x, y])
return np.array(intersect_pts, dtype=int)
def point_on_image(x: int, y: int, image_shape: tuple):
"""
Returns true is x and y are on the image
"""
return 0 <= y < image_shape[0] and 0 <= x < image_shape[1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment