reading-analog-gauge / classical.py
2up1down's picture
Upload 3 files
d479d7b verified
import cv2
import numpy as np
linelen = lambda l: np.sqrt((l[0]-l[2])**2 + (l[1]-l[3])**2)
import math
class HoughBundler:
def __init__(self,min_distance=5,min_angle=2):
self.min_distance = min_distance
self.min_angle = min_angle
def get_orientation(self, line):
orientation = math.atan2(abs((line[3] - line[1])), abs((line[2] - line[0])))
return math.degrees(orientation)
def check_is_line_different(self, line_1, groups, min_distance_to_merge, min_angle_to_merge):
for group in groups:
for line_2 in group:
if self.get_distance(line_2, line_1) < min_distance_to_merge:
orientation_1 = self.get_orientation(line_1)
orientation_2 = self.get_orientation(line_2)
if abs(orientation_1 - orientation_2) < min_angle_to_merge:
group.append(line_1)
return False
return True
def distance_point_to_line(self, point, line):
px, py = point
x1, y1, x2, y2 = line
def line_magnitude(x1, y1, x2, y2):
line_magnitude = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
return line_magnitude
lmag = line_magnitude(x1, y1, x2, y2)
if lmag < 0.00000001:
distance_point_to_line = 9999
return distance_point_to_line
u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)))
u = u1 / (lmag * lmag)
if (u < 0.00001) or (u > 1):
#// closest point does not fall within the line segment, take the shorter distance
#// to an endpoint
ix = line_magnitude(px, py, x1, y1)
iy = line_magnitude(px, py, x2, y2)
if ix > iy:
distance_point_to_line = iy
else:
distance_point_to_line = ix
else:
# Intersecting point is on the line, use the formula
ix = x1 + u * (x2 - x1)
iy = y1 + u * (y2 - y1)
distance_point_to_line = line_magnitude(px, py, ix, iy)
return distance_point_to_line
def get_distance(self, a_line, b_line):
dist1 = self.distance_point_to_line(a_line[:2], b_line)
dist2 = self.distance_point_to_line(a_line[2:], b_line)
dist3 = self.distance_point_to_line(b_line[:2], a_line)
dist4 = self.distance_point_to_line(b_line[2:], a_line)
return min(dist1, dist2, dist3, dist4)
def merge_lines_into_groups(self, lines):
groups = [] # all lines groups are here
# first line will create new group every time
groups.append([lines[0]])
# if line is different from existing gropus, create a new group
for line_new in lines[1:]:
if self.check_is_line_different(line_new, groups, self.min_distance, self.min_angle):
groups.append([line_new])
return groups
def merge_line_segments(self, lines):
orientation = self.get_orientation(lines[0])
if(len(lines) == 1):
return np.block([[lines[0][:2], lines[0][2:]]])
points = []
for line in lines:
points.append(line[:2])
points.append(line[2:])
if 45 < orientation <= 90:
#sort by y
points = sorted(points, key=lambda point: point[1])
else:
#sort by x
points = sorted(points, key=lambda point: point[0])
p0 = np.array(points[:2]).mean(axis=0)
p1 = np.array(points[-2:]).mean(axis=0)
return np.block([[p0,p1]]).astype(int)
# return np.block([[points[0],points[-1]]])
def process_lines(self, lines):
lines_horizontal = []
lines_vertical = []
for line_i in [l[0] for l in lines]:
orientation = self.get_orientation(line_i)
# if vertical
if 45 < orientation <= 90:
lines_vertical.append(line_i)
else:
lines_horizontal.append(line_i)
lines_vertical = sorted(lines_vertical , key=lambda line: line[1])
lines_horizontal = sorted(lines_horizontal , key=lambda line: line[0])
merged_lines_all = []
# for each cluster in vertical and horizantal lines leave only one line
for i in [lines_horizontal, lines_vertical]:
if len(i) > 0:
groups = self.merge_lines_into_groups(i)
merged_lines = []
for group in groups:
merged_lines.append(self.merge_line_segments(group))
merged_lines_all.extend(merged_lines)
return np.asarray(merged_lines_all)
def get_needle_line(im)->list:
# fn = "./0.png"
# # load grayscale image
# im = cv2.imread(fn)
gray_im = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
sz = 640
h,w,_ = im.shape
_hf = h/sz
_wf = w/sz
gray_im = cv2.resize(gray_im, (sz,sz))
blur = cv2.GaussianBlur(gray_im, (0,0), 5)
# edges = cv2.Canny(blur, 50, 100)
edges = cv2.Canny(blur, 20, 60)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 19))
hat = cv2.morphologyEx(edges, cv2.MORPH_BLACKHAT, rectKernel)
# hat = cv2.morphologyEx(edges, cv2.MORPH_TOPHAT, rectKernel)
# k = cv2.getStructuringElement(cv2.MORPH_ERODE, (13,13))
k = None
hat = cv2.erode(hat,k, iterations=2)
minLineLength = 60
maxLineGap = 10
plines = cv2.HoughLinesP(image=edges, rho=3, theta=np.pi / 180, threshold=10,minLineLength=minLineLength, maxLineGap=maxLineGap) # rho is set to 3 to detect more lines, easier to get more then filter them out later
if len(plines)<=1:
return plines.squeeze() * [_wf, _hf, _wf, _hf]
bundler = HoughBundler(min_distance=120,min_angle=5)
clines = bundler.process_lines(plines)
return clines.squeeze() * [_wf, _hf, _wf, _hf]