Last active
January 29, 2019 09:03
-
-
Save barahilia/78ad5392218e622f9f8b05cb1e98f411 to your computer and use it in GitHub Desktop.
Lines on image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) | |
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 | |
# i += 1 | |
# quadres = islice(quadruples(lines), 1000) | |
# quadres = list(quadres) | |
# print len(quadres) | |
# for q in quadres[:3]: | |
# print q | |
# print rectangle_angles(q) | |
if __name__ == '__main__': | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
numpy==1.14.1 | |
Pillow==5.0.0 | |
ipython==5.5.0 | |
opencv-python==3.4.0.12 | |
Shapely==1.6.4.post1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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