So I have an object that has a pseudo-ciruclar movement (EDIT : with a non constant period). I'm filming it with a 30 fps camera, and I extract it's position X and Y every 3 frame. Moreover, the center of this circular movement is moving.
Entry Data :
- Frame, a list
- X and Y, 2 lists
To Frame[i], the position of the object is X[i] and Y[i].
If you wanna see what it looks likes (30 mn video) :
Moreover, my data has "gaps", sometime, I can get for instance from frame 600 to 690, because the object wasn't correctly detected in my video. The data is already filter with a first order low-pass filter (after being interpolate by segments).
My problem, is that I can't find a good algorithm to count the number of rotation, without too many variability.
Current algorithm using the pseudo-periodicity of X and Y:
for length = 200, 400, 600, 800, 1000 and 1200 :
I cut my data (X and Y) in segments of size : length.
for each segments cutted :
Mean_X = Mean value of X on the segment
Mean_Y = Mean value of Y on the segment
If I get above Mean_X, X_counter += 1, and then I skip the following points until I get under Mean_X.
Same with Y_counter.
I sum the counted number of rotation on each segments for the current length to get the number of rotation on the complete duration.
In 2 list, ResX and ResY, I store the counted number of rotation for every length. To resX[i] correspond the length i.
For instance resX[2] give the number counted for length : 600.
But the results on X and Y are different, and have an high variability depending of the length chosen.
So how to choose the real number of rotation on the duration ?
Calculation of the difference of counted number between X and Y for every length.
Then I choose the average of counted X and Y, where there is the smaller difference.
In Python :
# Découpage en segment de 200 à 1200 de longueur avec un pas de 200
Segments = range(200, 1400, 200)
ResX, ResY = [], []
for k in range(len(Segments)):
if Segments[k] < len(X):
X_calc, Y_calc = [], []
length = len(X) // Segments[k]
last = len(X) % Segments[k]
for j in range(length):
X_calc.append(X[j:j + Segments[k]])
Y_calc.append(Y[j:j + Segments[k]])
if last > Segments[k] //2:
X_calc.append(X[len(X) - last:])
Y_calc.append(Y[len(Y) - last:])
else:
X_calc[len(X_calc) - 1] = X_calc[len(X_calc) - 1] + X[len(X) - last:]
Y_calc[len(Y_calc) - 1] = Y_calc[len(Y_calc) - 1] + Y[len(Y) - last:]
# Initialisation of the counter
Kx = 0
Ky = 0
for j in range(len(X_calc)):
Counter = 0
b = 0
Moyenne = np.mean(X_calc[j])
while b < len(X_calc[j]):
if X_calc[j][b] <= Moyenne + 10:
b += 1
continue
else:
Counter += 1
while X_calc[j][b] >= Moyenne + 10:
b += 1
try:
X_calc[j][b]
except:
break
Kx += Counter
Counter = 0
b = 0
Moyenne = np.mean(Y_calc[j])
while b < len(Y_calc[j]):
if Y_calc[j][b] <= Moyenne + 10:
b += 1
continue
else:
Counter += 1
while Y_calc[j][b] >= Moyenne + 10:
b += 1
try:
Y_calc[j][b]
except:
break
Ky += Counter
ResX.append(Kx)
ResY.append(Ky)
# Maintenant on détermine le nombre de tour en comparant les résultats locaux en X et en Y
Diff = []
for j in range(len(ResX)):
Diff.append(abs(ResX[j] - ResY[j]))
ID = Diff.index(min(Diff))
Res_F = int((ResX[ID] + ResY[ID]) /2)
This current solution has an high variability depending on :
- The length
- The "Count Above Value" : If I take Mean(X) or Mean(X) + 10, I get completely different results.
Do you know anyway to reduce this variability ? Or an idea on an algorithm to count the rotation ? Or a way to combine X and Y before the counting ?
Since a rotation with (t = 0, X = 1, Y = 0) can be describe by X = cos(t) and Y = sin(t), I thought there could be a way to sum or multiply X and Y in order to analyze a linear combination of X and Y.