2

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) : Plot of Y in function of X ;of X in function of Frame and of Y in function of Frame.

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.

Mathieu
  • 5,410
  • 6
  • 28
  • 55
  • It might be helpful to look at the Fourier representation of X and Y and try to adapt the formulae to non-uniform time steps. If you compute the DFT of these values, the period should be a local maximum im phase space. EDIT: What I described is called [Trigonometric interpolation](https://en.wikipedia.org/wiki/Trigonometric_interpolation), but nevermind, this assumes that you already know *some multiple* of the period (two distinct times when the object is at the same rotation state), which I assume is not the case for you. – Tobias Ribizel Aug 11 '17 at 12:49
  • The problem is not that I can't find this period, it's that it isn't constant. My "object" is a mouse, and when it decides to take a break, well there is nothing to do about it... Moreover this drug induce comportmental test is done on 40 mouse, and they don't react the same way. Some we'll turn with a constant period at 10 rpm, some won't even move, and we'll do maybe one or 2 rotation in 2 or 3 minutes. – Mathieu Aug 11 '17 at 12:59
  • My current solution that seems to give better result is to count the local maximum on the sum of X and Y (Basicly same algorithm than above, but with the sum). Not perfect, and I hope someone got a better idea. – Mathieu Aug 11 '17 at 17:53
  • why not substract sliding average from the `x,y` functions ... that will "center" the coordinates around the actual center of rotation. From that you can count zero angle crossings (`x` is near max and `y` is near zero) – Spektre Aug 18 '17 at 08:04

0 Answers0