I want to draw the centerline of a contour I made for the cable in the middle of the video I linked at the bottom. I also want to document the points of the centerline drawn in an array. How do I do this?
This is my code:
import cv2
from PIL import Image
from function import get_limits, get_limits_black
import numpy as np
black = [0,0,0]
cap = cv2.VideoCapture("/Users/samayjain/PycharmProjects/PythonProject/2025-06-07 11-24-40 Side.mp4")
while True:
ret, frame = cap.read()
roi = frame[0:1500, 0:1400]
hsvImage = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
lowerBlack, upperBlack = get_limits_black(color=black)
mask_black = cv2.inRange(hsvImage, lowerBlack, upperBlack)
contours, _ = cv2.findContours(mask_black, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
i = 0
max_width = 0
index = 0
c_x,c_y,c_w,c_h= 0,0,0,0
for contour in contours:
min_area = 1000
if cv2.contourArea(contour) > min_area:
x, y, w, h = cv2.boundingRect(contour)
if x + w > max_width:
index = i
max_width = x + w
c_x,c_y,c_w,c_h=x,y,w,h
M = cv2.moments(contour)
if M["m00"] != 0:
centroid_x = int(M["m10"] / M["m00"])
centroid_y = int(M["m01"] / M["m00"])
i += 1
cv2.rectangle(frame, (c_x, c_y), (c_x + c_w, c_y + c_h), (0, 255, 0), 2)
cv2.drawContours(frame, contours, index, (0, 255, 0), 2)
cv2.circle(frame, (centroid_x, centroid_y), 10, (0, 0, 255), -1)
cv2.imshow("Original Frame with Contours", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
This is part of the video when I run the code: https://somup.com/cTijVkLRr7
This is the original video: https://youtu.be/Mx3RXRGzu4Q
Thanks for the help.
I tried dividing the amount of points of the contour in two and then addressing one half to the upper_limit
list which was the top part of the contour and the other half to the lower_limit
list which was the bottom part of the contour. Then I would create a new array called average_points
which took the x values of the upper limit and then took the average of the y values of the upper and lower limit. After I created a line which was extremely inaccurate and was not even complete. This did not work because in actuality at different times of the video, either the upper or lower part had more points than the other since it is uneven due to constant movement of the cable. What I hoped to do is create a midline of the contour that ran through the middle of the contour.
You'll have to pip install opencv-contrib-python
first.
The outputs of this code:
A window showing the video with blue dots along the cable’s centerline.
centerlines.pkl
: A saved list of lists of (x, y)
points per frame.
import cv2
import numpy as np
import cv2.ximgproc as xip # for thinning
from function import get_limits_black
import pickle # optional, for saving centerline data
black = [0, 0, 0]
cap = cv2.VideoCapture("/Users/samayjain/PycharmProjects/PythonProject/2025-06-07 11-24-40 Side.mp4")
# to store centerline points from each frame
all_frames_centerlines = []
while True:
ret, frame = cap.read()
if not ret:
break
roi = frame[0:1500, 0:1400]
hsvImage = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
lowerBlack, upperBlack = get_limits_black(color=black)
mask_black = cv2.inRange(hsvImage, lowerBlack, upperBlack)
# clean noise from mask
mask_black = cv2.morphologyEx(mask_black, cv2.MORPH_OPEN, np.ones((3, 3), np.uint8))
# find contours to optionally draw bounding box (not needed for centerline but nice visual)
contours, _ = cv2.findContours(mask_black, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
largest_contour = max(contours, key=cv2.contourArea)
if cv2.contourArea(largest_contour) > 1000:
x, y, w, h = cv2.boundingRect(largest_contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.drawContours(frame, [largest_contour], -1, (0, 255, 0), 2)
# skeletonization
skeleton = xip.thinning(mask_black)
# extract centerline points from skeleton
centerline_points = cv2.findNonZero(skeleton)
centerline = [tuple(pt[0]) for pt in centerline_points] if centerline_points is not None else []
# draw centerline on frame
for pt in centerline:
cv2.circle(frame, pt, 1, (255, 0, 0), -1) # tiny blue dot
# store centerline for this frame
all_frames_centerlines.append(centerline)
# display result
cv2.imshow("Frame with Centerline", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
# optional, save centerline data
with open("centerlines.pkl", "wb") as f:
pickle.dump(all_frames_centerlines, f)
print("centerlines saved to centerlines.pkl")