I developed an algorithm detecting headlight cutoff line, can be seen in the following figure: 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: Binary threshold limit: 250 here Initial guess of the tanh model fit cv2.getStructuringElement(cv2.MORPH_RECT, (75, 1)) , size of the structured filter, 75 Continue reading...