Last active
March 5, 2023 14:06
-
-
Save Hermann-SW/56b3ba66938b9891a0d672f08e1c1849 to your computer and use it in GitHub Desktop.
Determine golf club head speed from single raspiraw Raspberry v2 camera high framerate video frame (640x240@383fps, 640x75@1007fps, ...)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# | |
# Determine club head speed of a golf swing (high framerate video) frame: | |
# https://forums.raspberrypi.com/viewtopic.php?t=345368#p2070418 | |
import cv2 | |
import numpy as np | |
from sys import argv, exit | |
# https://www.geeksforgeeks.org/linear-regression-python-implementation/ | |
def estimate_coef(x, y): | |
# number of observations/points | |
n = np.size(x) | |
# mean of x and y vector | |
m_x = np.mean(x) | |
m_y = np.mean(y) | |
# calculating cross-deviation and deviation about x | |
SS_xy = np.sum(y*x) - n*m_y*m_x | |
SS_xx = np.sum(x*x) - n*m_x*m_x | |
# calculating regression coefficients | |
b_1 = SS_xy / SS_xx | |
b_0 = m_y - b_1*m_x | |
return (b_0, b_1) | |
# https://stackoverflow.com/a/70385257/5674289 | |
def imread_url(u): | |
cap = cv2.VideoCapture(u) | |
return cap, cap.read() # Read the image as a video frame | |
# image_url = 'out.1817.png' | |
# image_url = 'https://forums.raspberrypi.com/download/file.php?id=58662' | |
# image_url = 'https://forums.raspberrypi.com/download/file.php?id=58663' | |
# image_url = 'https://forums.raspberrypi.com/download/file.php?id=58664' | |
# image_url = 'out.3360.png' | |
# image_url = 'https://forums.raspberrypi.com/download/file.php?id=58675' | |
# image_url = 'https://forums.raspberrypi.com/download/file.php?id=58676' | |
# image_url = 'https://forums.raspberrypi.com/download/file.php?id=58677' | |
image_url = 'https://stamm-wilbrandt.de/en/forum/golf_out.1817.png' | |
dbg = 1 | |
delay = 0 | |
if len(argv) > 1: | |
if argv[1] in ["-h", "--help"]: | |
print(argv[0],"[-h|--help|dbg [frameurl [delay (in ms, default 0)]]]") | |
print("dbg=0 outputs speed only in frame window") | |
print("dbg=1 in addition to 0, debug pixels in frame window") | |
print("dbg=2 in addition to 0, debug pixels in analysis window") | |
print("dbg=3 in addition to 2, show edge detection window") | |
exit(0) | |
dbg = int(argv[1]) | |
if len(argv) > 2: | |
image_url = argv[2] | |
if len(argv) > 3: | |
delay = int(argv[3]) | |
cap,(success,img) = imread_url(image_url) | |
assert success | |
# https://forums.raspberrypi.com/viewtopic.php?p=2079331#p2079136 | |
b,g,r = cv2.split(img) | |
ball = cv2.addWeighted(r,8.0,b,-8.0,-128) | |
blur = cv2.GaussianBlur(ball, (19,19), 0) | |
edge = cv2.Canny(blur, 50, 100, 7, L2gradient=True) | |
if dbg >= 3: | |
cv2.imshow('edge', edge) | |
# edge detection, golf ball middle slope determination | |
h,w = edge.shape | |
X=[] | |
Y=[] | |
xmin = w | |
d = 0 | |
def high(): | |
return h >= 100 | |
thr = 15 if high() else 5 | |
for y in range(h): | |
l = 0 | |
while l < w and edge[y][l] == 0: | |
l += 1 | |
if l < w: | |
r = w - 1 | |
while edge[y][r] == 0: | |
r -= 1 | |
if l + thr > r: # eliminate row y completely (all pixels black) | |
while l <= r: | |
edge[y][l] = 0 | |
l += 1 | |
continue | |
x = l + 1 # black all pixels between leftmost and rightmost | |
while x < r: | |
edge[y][x] = 0 | |
x += 1 | |
if dbg == 1: | |
img[y][l] = img[y][r] = (255, 255, 0) | |
mx = (l+r)//2 | |
if mx < xmin: | |
xmin = mx | |
if mx <= xmin + thr: | |
edge[y][mx] = 64 | |
if dbg == 1: | |
img[y][mx] = (255, 255, 0) | |
else: | |
edge[y][mx] = 128 | |
X.append(mx) | |
Y.append(y) | |
if r - l > d: | |
d = r - l | |
if dbg == 1: | |
img[y][mx] = (255, 0, 0) | |
if dbg >= 2: | |
cv2.imshow('analysis', edge) | |
b = estimate_coef(np.array(X), np.array(Y)) if len(X) > 2 else (0, 1) | |
D = 0.04267 # golf ball diameter [m] | |
line_time_s = 0.000019517 # Raspberry v2 camera, raspiraw mode7 | |
ms = ((D / d) / b[1]) / line_time_s if d > 0 else 0 | |
# D / d: golf ball diameter divided by measured diameter [m/pixel] | |
# / b[1]: div by middle pixels slope; 1 line time horizontal distance [m] | |
# / line_time_s: speed [m/s] | |
txt = f"speed: {ms:.2f}m/s ({ms * 3.6:.1f}km/h)" | |
# horizontally centered OpenCV text output | |
font = cv2.FONT_HERSHEY_PLAIN | |
thck = font_scale = 2 if high() else 1 | |
txtSize = cv2.getTextSize(txt, font, font_scale, thck) | |
cv2.putText(img, txt, ((w - txtSize[0][0])//2, h//4), font, font_scale, | |
(255, 0, 0), thck, cv2.LINE_AA) | |
# frame window is shown always | |
cv2.imshow('frame', img) | |
cv2.waitKey(delay) | |
print(txt) | |
cap.release() | |
cv2.destroyAllWindows() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
3 windows output with dbg=3 in next comment:
https://forums.raspberrypi.com/viewtopic.php?p=2079384#p2079384