Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

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: image description

the results are: image description

image description

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)
    inlier_mask = ransac.inlier_mask_
    outlier_mask = np.logical_not(inlier_mask)
    line_X1 = np.arange(lineX.min(), lineX.max())[:, np.newaxis]
    line_y_ransac1 = ransac.predict(line_X1)


    #ransac fit for second line
    lineX2 = lineX[outlier_mask]
    lineY2 = lineY[outlier_mask]
    ransac.fit(lineX2, lineY2)
    inlier_mask = ransac.inlier_mask_
    # outlier_mask = np.logical_not(inlier_mask)
    line_X2 = np.arange(np.min(lineX2[inlier_mask]), np.max(lineX2[inlier_mask]))[:, np.newaxis]
    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():

    im = cv2.imread(r'C:\Users\wieser\Pictures\ll.png')

    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: image description

the results are: image description

image description

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)
    inlier_mask = ransac.inlier_mask_
    outlier_mask = np.logical_not(inlier_mask)
    line_X1 = np.arange(lineX.min(), lineX.max())[:, np.newaxis]
    line_y_ransac1 = ransac.predict(line_X1)


    #ransac fit for second line
    lineX2 = lineX[outlier_mask]
    lineY2 = lineY[outlier_mask]
    ransac.fit(lineX2, lineY2)
    inlier_mask = ransac.inlier_mask_
    # outlier_mask = np.logical_not(inlier_mask)
    line_X2 = np.arange(np.min(lineX2[inlier_mask]), np.max(lineX2[inlier_mask]))[:, np.newaxis]
    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():

    im = cv2.imread(r'C:\Users\wieser\Pictures\ll.png')
cv2.imread('laserline.png')

    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()