[Python] Detecting cutoff line of the headlight beam on the wall

Discussão em 'Python' iniciado por Stack, Fevereiro 7, 2025.

  1. Stack

    Stack Membro Participativo

    I developed an algorithm detecting headlight cutoff line, can be seen in the following figure:

    [​IMG]

    Let me ask right from the start what I would normally ask at the end;

    How to make the algorithm robust to the input data? In the presence of the challenges in the following, but not limited to:

    • changing light conditions
    • non easy visibility of the headlight cut-off line

    Here is the Minimum Reproducible Example:

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit

    def curve_fit_tanh(x_data, y_data):

    def tanh_model(x, a, b, c, d):
    """Hyperbolic tangent: a * tanh[b * (x - c)] + d."""
    return a * np.tanh(b * (x - c)) + d

    # Provide initial guesses for parameters based on data range
    initial_guesses = [-max(y_data) + min(y_data), 0.01, np.median(x_data), np.mean(y_data)]

    # print(f'initial_guesses: a={initial_guesses[0]}, b={initial_guesses[1]}, c={initial_guesses[2]}, d={initial_guesses[3]}')

    # Fit the model using the requested tanh function
    params, _ = curve_fit(tanh_model, x_data, y_data, p0=initial_guesses, maxfev=10000)

    # Extract optimized parameters
    a_opt, b_opt, c_opt, d_opt = params

    # Generate fitted values
    y_fit = tanh_model(x_data, a_opt, b_opt, c_opt, d_opt)

    return params,y_fit,initial_guesses

    def clahe_yuv(frame):
    img_yuv = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV)
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(2,2))
    img_yuv[:,:,0] = clahe.apply(img_yuv[:,:,0])
    img = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR)
    return img

    def debug_show_image(window_name, image):
    cv2.imshow(window_name, image)
    if cv2.waitKey(0) & 0xFF == ord('q'):
    cv2.destroyAllWindows()
    exit()
    else:
    cv2.destroyWindow(window_name)

    def hl_cutoff_filter(frame):
    """
    Detects the headlight cutoff shape by scanning rows and tracking edges across columns.
    """
    # Convert to YUV color space and apply CLAHE to enhance contrast
    yuv_frame = clahe_yuv(frame)

    # Apply thresholding to extract bright regions (headlight cutoff)
    _, binary = cv2.threshold(yuv_frame, 250, 255, cv2.THRESH_BINARY)
    debug_show_image('binary YUV', binary)
    b1 = binary[:,:,0]
    b2 = binary[:,:,1]
    b3 = binary[:,:,2]
    binary = cv2.bitwise_or(b1, cv2.bitwise_or(b2, b3))
    debug_show_image('binary YUV Combined', binary)
    # Detect horizontal edges using a horizontal kernel
    horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (75, 1))
    horizontal_edges = cv2.morphologyEx(binary, cv2.MORPH_OPEN, horizontal_kernel)

    # Find first edge point in each column
    _, width = horizontal_edges.shape
    x_vals = np.arange(width)
    y_vals = np.argmax(horizontal_edges > 0, axis=0)

    # Filter valid points
    valid_mask = np.any(horizontal_edges > 0, axis=0)
    x_vals = x_vals[valid_mask]
    y_vals = y_vals[valid_mask]

    if len(x_vals) < 5:
    return frame

    try:
    params, y_fit, _ = curve_fit_tanh(x_vals, y_vals)

    fitdata = np.column_stack((x_vals, y_fit.astype(int)))
    fit_success = True # Set flag to True if fitting is successful

    except Exception as e:
    print(f"[APP] HL CUT-OFF FITTING failed: {e}")
    fit_success = False # Set flag to False if an exception occurs

    # Only draw if fitting was successful
    if fit_success:

    cv2.polylines(frame, [fitdata], isClosed=False, color=(0, 0, 255), thickness=3) # Red curve
    cv2.putText(frame, "HL Cut-off", (fitdata[0][0]-10, fitdata[0][1]-10),
    cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)

    # find the middle point of the curve in y axis
    middle_y = round(np.mean(fitdata[:, 1]), 4)
    cv2.putText(frame, f"HL Center in Y Axis: {middle_y}th Px", (1080//2, int(middle_y*1.1)),
    cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 2)

    return frame,middle_y

    # Test with a sample image
    frame = cv2.imread("./data/original2.png")
    # cv2.rectangle(frame, (100, 250), (700, 280), (255, 255, 255), -1)

    processed_frame,_ = hl_cutoff_filter(frame)

    # plot with opencv
    cv2.imshow("Headlight Cutoff Detection", processed_frame)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # plt.imshow(cv2.cvtColor(processed_frame, cv2.COLOR_BGR2RGB))
    # plt.title("Headlight Cutoff Detection")
    # plt.show()



    it works good, but, it is not robust to the different test images due to non-optimal parameter selections. The parameters which makes the algorithm very sensitive to the input image/video are:

    1. Binary threshold limit: 250 here
    2. Initial guess of the tanh model fit
    3. cv2.getStructuringElement(cv2.MORPH_RECT, (75, 1)) , size of the structured filter, 75

    Continue reading...

Compartilhe esta Página