Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@barahilia
Last active January 29, 2019 09:03
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 barahilia/78ad5392218e622f9f8b05cb1e98f411 to your computer and use it in GitHub Desktop.
Save barahilia/78ad5392218e622f9f8b05cb1e98f411 to your computer and use it in GitHub Desktop.
Lines on image
#!/usr/bin/env python
# http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
import cv2
import numpy as np
from rectangles import Line, line_frequencies, rectangles
def lines_to_file(lines):
with open('data/lines.dat', 'w') as f:
for line_data in lines:
line, = line_data
x1, y1, x2, y2 = line
f.write('%s %s %s %s\n' % (x1, y1, x2, y2))
def lines_to_lines(lines):
for line_data in lines:
line, = line_data
yield Line(*line)
img = cv2.imread('data/4.jpg')
# blur = cv2.blur(img, (5, 5))
blur = cv2.blur(img, (2, 2))
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 150, 200, apertureSize=3)
lines = cv2.HoughLinesP(
image=edges,
rho=1,
theta=(2 * np.pi/180),
threshold=100,
minLineLength=100,
maxLineGap=40
)
if lines is None:
print 'lines not found at all, try other parameters'
exit()
print lines.shape
for line_data in lines:
line, = line_data
x1, y1, x2, y2 = line
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 6)
lines = list(lines_to_lines(lines))
# freqs = line_frequencies(lines)
i = 0
for rectangle in rectangles(lines):
i += 1
# if i > 150:
# break
for line in rectangle:
cv2.line(img, (line.x1, line.y1), (line.x2, line.y2), (0, 0, 255), 6)
# break
cv2.imwrite('data/out.jpg', img)
#!/usr/bin/env python
from collections import Counter, namedtuple, defaultdict
from itertools import combinations, islice
from math import atan2, pi
from shapely.geometry import Point, LineString
from shapely.affinity import rotate
EPSILON = 1e-5
half_pi = pi / 2
Line = namedtuple('Line', ['x1', 'y1', 'x2', 'y2'])
def lines_from_file():
with open('data/lines.dat') as f:
lines = f.readlines()
lines = [map(int, line.split()) for line in lines]
lines = [Line(*line) for line in lines]
return lines
def angle(line):
return atan2(line.y2 - line.y1, line.x2 - line.x1)
def degree(radian):
return int(radian / pi * 180)
def _first_quandrant(angle):
assert -pi <= angle <= pi
if 0 <= angle <= half_pi:
return angle
elif half_pi <= angle <= pi:
return angle - half_pi
elif -half_pi <= angle < 0:
return angle + half_pi
elif -pi <= angle < -half_pi:
return angle + pi
else:
print 'warning: stange math condition', angle
return angle
def first_quandrant(angle):
assert -pi - EPSILON <= angle <= pi + EPSILON
angle = _first_quandrant(angle)
if angle < EPSILON:
angle += EPSILON
if angle > half_pi - EPSILON:
angle -= EPSILON
assert 0 <= angle <= half_pi
return angle
def lines_angles(lines):
def line_degree(line):
return degree(first_quandrant(angle(line)))
return {line: line_degree(line) for line in lines}
def angle_frequencies(angles):
c = Counter()
c.update(angles)
c.update([a + 1 for a in angles])
return c
def line_frequencies(lines):
angles = lines_angles(lines)
freqs = angle_frequencies(angles.values())
return {line: freqs[angles[line]] for line in lines}
def lines_with_same_angle(lines):
d = defaultdict(set)
for line, angle in lines_angles(lines).items():
d[angle].add(line)
d[angle + 1].add(line)
return d.values()
def is_rectangle_angles(lines):
def exact_angle(degree):
return (degree + 3) / 90
angles = map(angle, lines)
degrees = map(degree, angles)
min_degree = min(degrees)
degrees = [d - min_degree for d in degrees]
return Counter([exact_angle(d) % 2 for d in degrees]) == Counter([0, 0, 1, 1])
def is_rectangle(lines):
def xs(lines):
for line in lines:
yield line.x1
yield line.x2
def ys(lines):
for line in lines:
yield line.y1
yield line.y2
def move_to(line, x, y):
return Line(line.x1 - x, line.y1 - y, line.x2 - x, line.y2 - y)
def rotate_line(line, angle, x, y):
l = LineString([Point(line.x1, line.y1), Point(line.x2, line.y2)])
l2 = rotate(l, angle, origin=Point(x, y), use_radians=False)
a, b = list(l2.coords)
x1, y1 = a
x2, y2 = b
return Line(int(x1), int(y1), int(x2), int(y2))
def order_line(line):
if abs(line.x1 - line.x2) < 10:
if line.y1 < line.y2:
return line
else:
return Line(x2, y2, x1, y1)
elif abs(line.y1 - line.y2) < 10:
if line.x1 < line.x2:
return line
else:
return Line(x2, y2, x1, y1)
def delta_x(line):
return abs(line.x1 - line.x2)
def delta_y(line):
return abs(line.y1 - line.y2)
def line_like_sample(line, sample_line):
return (delta_x(line) * 2 > delta_x(sample_line)) and \
(delta_y(line) * 2 > delta_y(sample_line))
def any_line_like_sample(lines, sample_line):
return any(line_like_sample(line, sample_line) for line in lines)
assert len(lines) == 4
# for line in lines: print line
min_x = min(xs(lines))
min_y = min(ys(lines))
# print min_x, min_y
rotate_to = -degree(angle(lines[0]))
lines = [move_to(line, min_x, min_y) for line in lines]
lines = [rotate_line(line, rotate_to, min_x, min_y) for line in lines]
min_x = min(xs(lines))
min_y = min(ys(lines))
max_x = max(xs(lines))
max_y = max(ys(lines))
if abs(min_x - max_x) > 15 and abs(min_y - max_y) > 15:
pass
else:
return False
if abs(min_x - max_x) < 600 and abs(min_y - max_y) < 600:
pass
else:
return False
# for line in lines: print line
# print min_x, min_y, max_x, max_y
return \
any_line_like_sample(lines, Line(min_x, min_y, max_x, min_y)) and \
any_line_like_sample(lines, Line(max_x, min_y, max_x, max_y)) and \
any_line_like_sample(lines, Line(max_x, max_y, min_x, max_y)) and \
any_line_like_sample(lines, Line(min_x, max_y, min_x, min_y))
def quadruples(lines):
line_groups = lines_with_same_angle(lines)
for group in line_groups:
if len(group) >= 4:
for quadre in combinations(group, 4):
yield quadre
def rectangles(lines):
for i, quadre in enumerate(quadruples(lines)):
if i % 100000 == 0:
print i
if is_rectangle_angles(quadre) and is_rectangle(quadre):
yield quadre
def main():
lines = lines_from_file()
for line in lines[:2]:
print line, angle(line)
print
all_rs = list(rectangles(lines))
print len(all_rs)
# angles = map(angle, lines)
# angles = map(first_quandrant, angles)
# angles = map(degree, angles)
# c = Counter()
# c.update(angles)
# c.update([a + 1 for a in angles])
# print c
# print c[57], c[35]
# i, max_i = 0, 10
# for quadre in quadruples(lines):
# if i >= max_i:
# break
# for degrees in rectangle_angles(quadre):
# print quadre
# print degrees
# print
# i += 1
# quadres = islice(quadruples(lines), 1000)
# quadres = list(quadres)
# print len(quadres)
# for q in quadres[:3]:
# print q
# print rectangle_angles(q)
# print
if __name__ == '__main__':
main()
numpy==1.14.1
Pillow==5.0.0
ipython==5.5.0
opencv-python==3.4.0.12
Shapely==1.6.4.post1
# http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
import cv2
# import numpy - not sure if needed
img = cv2.imread('data.jpg')
blur = cv2.blur(img, (2, 2)) # or (5, 5)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 150, 200, apertureSize=3)
lines = cv2.HoughLinesP(
image=edges,
rho=1,
theta=(2 * np.pi/180),
threshold=100,
minLineLength=100,
maxLineGap=40
)
for line in lines:
line, = line
x1, y1, x2, y2 = line
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 6)
cv2.imwrite('output.jpg', img)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment