# Revision history [back]

Hi,

first Question: a standard calibration is necessary (like chessboard or circles grid) because you have to eliminate the distorion in entire image. Afterward do a line detection described below and calculate the translation matrix.

second Question: first, for each column, I would calculate points along line using e.g. center of gravity (also named center of mass). Based on these points do a line fit for the upper and lower laser line. Afterwards you can calculate the distance between the lines or for each point on the lower line the distance to the upper line, what ever you want.

Notice, that a line detection which is only done via Hough Transformation is not accurate enough for such applications.

Best

Hi,

first Question: a standard calibration is necessary (like chessboard or circles grid) because you have to eliminate the distorion in entire image. Afterward do a line detection described below and calculate the translation matrix.

second Question: first, for each column, I would calculate points along line using e.g. center of gravity (also named center of mass). Based on these points do a line fit for the upper and lower laser line. Afterwards you can calculate the distance between the lines or for each point on the lower line the distance to the upper line, what ever you want.

Notice, that a line detection which is only done via Hough Transformation is not accurate enough for such applications.

Best

ok, I will do the job for you

I have used the following image:

the results are:

and the code is below. However I havn't time for test, documentation, and so on... It is only a first attempt and an important step is to separate the lower and upper points (marked in code as a todo). This must be done better and is essential for higly accurate results. But it is a working example

import cv2
import numpy as np
from sklearn import linear_model
import matplotlib.pyplot as plt

ransac = linear_model.RANSACRegressor(residual_threshold=0.5)
class point:
def __init__(self, x, y):
self.x = x
self.y = y

def getCOG(im):

h, w = im.shape
points = []
for colnum in range(3, w-3):
cols = im[:, colnum-3:colnum+3]
M = cv2.moments(cols)
if M["m00"] != 0:
cX = M["m10"] / M["m00"]
cY = M["m01"] / M["m00"]
p = point(colnum+cX, cY)
points.append(p)

return points

def fit_LineRansac(line):

lineX = []
lineY = []
for l in line:
lineX.append(l.x)
lineY.append(l.y)
lineX = np.asarray(lineX).reshape(-1, 1)
lineY = np.asarray(lineY).reshape(-1, 1)

#todo: split of first and second line is not trustable. must be done better
#ransac fit for first line
ransac.fit(lineX, lineY)
line_X1 = np.arange(lineX.min(), lineX.max())[:, np.newaxis]
line_y_ransac1 = ransac.predict(line_X1)

#ransac fit for second line
ransac.fit(lineX2, lineY2)
line_y_ransac2 = ransac.predict(line_X2)

lw = 2
plt.xlabel("X")
plt.ylabel("Y")
plt.scatter(lineX, lineY, color='yellowgreen', marker='.', label='Inliers')
plt.plot(line_X1, line_y_ransac1, color='cornflowerblue', linewidth=lw, label='RANSAC regressor')
plt.plot(line_X2, line_y_ransac2, color='cornflowerblue', linewidth=lw, label='RANSAC regressor')
plt.show()

return line_X1, line_y_ransac1, line_X2, line_y_ransac2

def cross(a, b):
res = [0,0,0]

res[0] = a[1] * b[2] - a[2] * b[1]
res[1] = a[2] * b[0] - a[0] * b[2]
res[2] = a[0] * b[1] - a[1] * b[0]

return res

def distance_to_line(linex, liney, point):
pa = [linex[0], liney[0], 1]
pb = [linex[-1], liney[-1], 1]

l = cross(pa, pb)

res = np.abs((point.x * l[0] + point.y * l[1] + l[2])) * 1.0 / np.sqrt(l[0] * l[0] + l[1] * l[1])

return res

def main():

if im.shape[2] == 3:  # means a colored image
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
else:
imgray = im
thresh_val, imgray = cv2.threshold(imgray, 0, 255, cv2.THRESH_TOZERO + cv2.THRESH_OTSU)

imRGB = cv2.cvtColor(imgray, cv2.COLOR_GRAY2RGB)

points = getCOG(imgray)

l1x, l1y, l2x, l2y = fit_LineRansac(points)

dist=[]
for x,y in zip(l2x, l2y):
p=point(x,y)
dist.append(distance_to_line(l1x, l1y, p))

mean_dist = np.mean(dist)
print(mean_dist)

for p1x, p1y in zip(l1x, l1y):
cv2.circle(imRGB, (int(p1x), int(p1y)), 1, (0, 255, 0), -1)
for p2x, p2y in zip(l2x, l2y):
cv2.circle(imRGB, (int(p2x), int(p2y)), 1, (255, 0, 0), -1)

cv2.namedWindow("anglepairs",cv2.WINDOW_NORMAL)
cv2.imshow("anglepairs", imRGB)
cv2.waitKey()

if __name__ == "__main__":
main()


Hi,

first Question: a standard calibration is necessary (like chessboard or circles grid) because you have to eliminate the distorion in entire image. Afterward do a line detection described below and calculate the translation matrix.

second Question: first, for each column, I would calculate points along line using e.g. center of gravity (also named center of mass). Based on these points do a line fit for the upper and lower laser line. Afterwards you can calculate the distance between the lines or for each point on the lower line the distance to the upper line, what ever you want.

Notice, that a line detection which is only done via Hough Transformation is not accurate enough for such applications.

Best

ok, I will do the job for you

I have used the following image:

the results are:

and the code is below. However I havn't time for test, documentation, and so on... It is only a first attempt and an important step is to separate the lower and upper points (marked in code as a todo). This must be done better and is essential for higly accurate results. But it is a working example

import cv2
import numpy as np
from sklearn import linear_model
import matplotlib.pyplot as plt

ransac = linear_model.RANSACRegressor(residual_threshold=0.5)
class point:
def __init__(self, x, y):
self.x = x
self.y = y

def getCOG(im):

h, w = im.shape
points = []
for colnum in range(3, w-3):
cols = im[:, colnum-3:colnum+3]
M = cv2.moments(cols)
if M["m00"] != 0:
cX = M["m10"] / M["m00"]
cY = M["m01"] / M["m00"]
p = point(colnum+cX, cY)
points.append(p)

return points

def fit_LineRansac(line):

lineX = []
lineY = []
for l in line:
lineX.append(l.x)
lineY.append(l.y)
lineX = np.asarray(lineX).reshape(-1, 1)
lineY = np.asarray(lineY).reshape(-1, 1)

#todo: split of first and second line is not trustable. must be done better
#ransac fit for first line
ransac.fit(lineX, lineY)
line_X1 = np.arange(lineX.min(), lineX.max())[:, np.newaxis]
line_y_ransac1 = ransac.predict(line_X1)

#ransac fit for second line
ransac.fit(lineX2, lineY2)
line_y_ransac2 = ransac.predict(line_X2)

lw = 2
plt.xlabel("X")
plt.ylabel("Y")
plt.scatter(lineX, lineY, color='yellowgreen', marker='.', label='Inliers')
plt.plot(line_X1, line_y_ransac1, color='cornflowerblue', linewidth=lw, label='RANSAC regressor')
plt.plot(line_X2, line_y_ransac2, color='cornflowerblue', linewidth=lw, label='RANSAC regressor')
plt.show()

return line_X1, line_y_ransac1, line_X2, line_y_ransac2

def cross(a, b):
res = [0,0,0]

res[0] = a[1] * b[2] - a[2] * b[1]
res[1] = a[2] * b[0] - a[0] * b[2]
res[2] = a[0] * b[1] - a[1] * b[0]

return res

def distance_to_line(linex, liney, point):
pa = [linex[0], liney[0], 1]
pb = [linex[-1], liney[-1], 1]

l = cross(pa, pb)

res = np.abs((point.x * l[0] + point.y * l[1] + l[2])) * 1.0 / np.sqrt(l[0] * l[0] + l[1] * l[1])

return res

def main():

if im.shape[2] == 3:  # means a colored image
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
else:
imgray = im
thresh_val, imgray = cv2.threshold(imgray, 0, 255, cv2.THRESH_TOZERO + cv2.THRESH_OTSU)

imRGB = cv2.cvtColor(imgray, cv2.COLOR_GRAY2RGB)

points = getCOG(imgray)

l1x, l1y, l2x, l2y = fit_LineRansac(points)

dist=[]
for x,y in zip(l2x, l2y):
p=point(x,y)
dist.append(distance_to_line(l1x, l1y, p))

mean_dist = np.mean(dist)
print(mean_dist)

for p1x, p1y in zip(l1x, l1y):
cv2.circle(imRGB, (int(p1x), int(p1y)), 1, (0, 255, 0), -1)
for p2x, p2y in zip(l2x, l2y):
cv2.circle(imRGB, (int(p2x), int(p2y)), 1, (255, 0, 0), -1)

cv2.namedWindow("anglepairs",cv2.WINDOW_NORMAL)
cv2.imshow("anglepairs", imRGB)
cv2.waitKey()

if __name__ == "__main__":
main()