Оказывается, что подгонка сплайна невозможна в OpenCV, но я нашел обходной путь с помощью @triiiiista. Для дальнейшего использования (для простоты используется верхняя часть исходного изображения):
import numpy as np
import cv2
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
frame = cv2.imread("./download.png")
blur = cv2.blur(frame,(5,5))
edged = cv2.Canny(blur, 50, 200)
# remove vertical parts
kernel_remove_vertical = cv2.getStructuringElement(cv2.MORPH_RECT, ksize=(3, 1))# remove vertical parts
edged_hor = cv2.erode(edged, kernel_remove_vertical)
cv2.imshow('edge', edged)
cv2.imshow('edge_hor', edged_hor)
cv2.waitKey(1)
# Find coordinates for white pixels
# Hope that there aren't too many outliers :)
pixels = np.argwhere(edged_hor == 255)
x = (pixels[:, 1])
y = (pixels[:, 0])
# Interpolate with scipy
f = interp1d(x, y, kind='cubic')
xnew = np.linspace(10, 630)
plt.figure()
plt.imshow(cv2.cvtColor(edged_hor, cv2.COLOR_BGR2RGB))
plt.plot(xnew, f(xnew))
В результате: ![enter image description here](https://i.stack.imgur.com/FbrGg.png)