Question: I would like to seperate the fringes (the red curved lines) that I have calculated with scipy findpeaks how can I achive it. I would like to seperate them and store in the text file.  import numpy as np from scipy.signal import find_peaks import matplotlib.pyplot as plt X = np.load('X.npy') Y = np.load('Y.npy') P_new = np.load('P_new.npy') # Example data: Replace with your actual data T = np.real(P_new) # Simulating a 2D matrix # Plot the original image plt.figure() plt.imshow(T, cmap='jet', aspect='auto') plt.colorbar() plt.title('Original Image') plt.show() # Peak detection parameters min_peak_dist = 3 # Minimum distance between peaks min_peak_h = 3e-5 # Minimum peak height x_coords = [] y_coords = [] # Process all rows from top to bottom for k in range(T.shape[0]): tex = T[k, :] peaks, _ = find_peaks(tex, distance=min_peak_dist, height=min_peak_h) if peaks.size > 0: x_coords.extend(X[k, peaks]) y_coords.extend(Y[k, peaks]) # Plot detected peaks plt.figure() plt.scatter(x_coords, y_coords, color='r', s=2) # 's' controls marker size plt.xlabel('X Coordinate') plt.ylabel('Y Coordinate') plt.title('Detected Fringes in Real-World Coordinates') plt.colorbar() plt.show()  data for plotting is here Want I want see is just seperate fringes like here:  previously I could do it with cv2 method contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) but this is from finding the edges which is not very rigorous for my case as with finding peaks of the actual data. Can someone help with this?

Answer: I had reasonable success using HDBSCAN. I first ran find_peaks to find peaks along y (rather than along x) - these are the black lines. I then clipped the image to within the blue square, and clustered the points using HDBSCAN. The final clusterings are coloured.  To plot a particular cluster, you could use: view_cluster = 20 plt.figure(figsize=(3, 5)) plt.scatter(*clustered[view_cluster].T, marker='.', s=1) plt.xlabel('x') plt.ylabel('y') plt.title(f'Fringe {view_cluster}') plt.grid(linestyle=':')   Solution Load data and preprocess: import numpy as np from scipy.signal import find_peaks import matplotlib.pyplot as plt from matplotlib.patheffects import withTickedStroke # # Example data: Replace with your actual data # X_orig = np.load('X.npy') Y_orig = np.load('Y.npy') P_new = np.load('P_new.npy') n_rows, n_cols = P_new.shape T = np.real(P_new) # Simulating a 2D matrix #Resample X and Y to match resolution of P_new # NB. I use the min/max values for simplicity x_axis = np.linspace(X_orig.min(), X_orig.max(), num=n_cols) y_axis = np.linspace(Y_orig.min(), Y_orig.max(), num=n_rows) X_grid, Y_grid = np.meshgrid(x_axis, y_axis) # Peak detection parameters min_peak_dist = 3 # Minimum distance between peaks min_peak_h = 3e-5 # Minimum peak height coords = [] # Process all cols (x axis) for col_ix in range(n_cols): peaks, _ = find_peaks(T[:, col_ix], distance=min_peak_dist, height=min_peak_h) if not len(peaks): continue x_coord = x_axis[col_ix] peaks_y = y_axis[peaks] coords.extend([(x_coord, peak_y) for peak_y in peaks_y]) coords = np.array(coords) x_coords, y_coords = coords.T # #Clip unwanted regions # ignore_left_of = 5 ignore_below = 25 # ignore_shorter_than = 10 coords_clipped = coords[(x_coords > ignore_left_of) & (y_coords > ignore_below)]  Cluster and visualise: # # HDBSCAN # from sklearn.cluster import HDBSCAN coord_labels = HDBSCAN().fit_predict(coords_clipped) clustered = [ coords_clipped[coord_labels==cluster_id] for cluster_id in np.unique(coord_labels) ] # # Visualise # plt.figure() #Original image plt.matshow( T, extent=[x_axis.min(), x_axis.max(), y_axis.min(), y_axis.max()], cmap='Greys_r', origin='lower', vmax=5e-4, aspect='auto' ) plt.gcf().set_size_inches(12, 6) plt.colorbar(extend='both') #Detected peaks plt.scatter(x_coords, y_coords, color='black', marker='.', s=0.2, alpha=0.4) # #Clip regions plt.axvline(ignore_left_of, path_effects=[withTickedStroke(angle=135)]) plt.axhline(ignore_below, path_effects=[withTickedStroke(angle=-135)]) #Clusters clustered = sorted(clustered, key=lambda members: members[:, 1].min()) for cluster_members in clustered: plt.plot(*cluster_members[::30].T, alpha=0.2, zorder=0, lw=8) #Formatting plt.xlabel('x') plt.ylabel('y') plt.show()