Python low-pass filter on list of Time/Position

时间:2017-08-04 13:11:53

标签: python scipy frequency lowpass-filter

Let me first explain my goal and my problem. I have an object moving in circle inside of a bocal (a mice to be precise), and I need to know how many rotation it does in one minute. The movement isn't a perfect circle, and can be chaotic. It can stopped for several seconds, and start again moving.

But what I know, is that I have between 3 and 20 rotation per minute => Low frequency (between 0.05 Hz and 0.33 Hz).

I'm filming this object with a 30 fps camera during 30 minutes. With Python and OpenCV, I manage to extract the (X,Y) coordinates. Since 30 fps is a bit high for the frequency I'm looking for, I selected one frame on 15 in order to have a sampling frequency of 2 Hz.

The first problem, is that sometimes, I'm missing a point because OpenCV couldn't find the object. This is a problem I can't solve, since the point I'm tracking with an HSV color mask is sometimes hidden.

In fact, the data I have to filter are :

  • Frame, a list of the frame number. At the indice [i], I have the frame number corresponding to the X[i] and Y[i]
  • X, a list of the X coordinates
  • Y, a list of the Y coordinates

For instance, I have this kind of list :

  • Frame = [15, 90, 165, 180, 195, 210, 225, 300, 315, 375 ,405]
  • X = [395, 487, 389, 389, 358, 382, 373, 389, 397, 403, 446]
  • Y = [167, 211, 166, 173, 180, 185, 191, 223, 241, 231, 238]

This is the king of movement I get, as you can see, I need to filter the high frequency.

And now my problem : Even if I have some basics in filtering and coding, I don't know how to do it, and which library to use. I've read that scipy should have all the required function, but I don't know which one to use, and how.

I'm not sure, but I think I should do something like this :

  • Fourrier transformation
  • lowpass filtering
  • Inverse transformation

Could you please tell me if I'm right, and point me in the right direction for the coding part ?

Thanks for the help,

Mathieu

2 个答案:

答案 0 :(得分:0)

在深入研究傅立叶变换之前,您可以应用一阶或二阶低通滤波器。您可以先对数据进行线性插值,以便频率恒定为2Hz。然后,您可以将一阶低通滤波器应用于数据点。

y_k = a * x_k + (1-a) * y_km1, a in [0,1]

x_k是您的观察结果,y_k是您的过滤估算值。

然后,如果第一个原型产生一些有用的结果,您可能可以使用一些物理模型来获得更好的估算。卡尔曼滤波器可以更好地利用您的潜在物理现实。但为此,您首先需要了解如何模拟物理现实。

https://en.wikipedia.org/wiki/Kalman_filter

在这里,您可以在跟踪计算机视觉中的移动方面找到更接近的示例: http://www.diss.fu-berlin.de/docs/servlets/MCRFileNodeServlet/FUDOCS_derivate_000000000473/2005_12.pdf

答案 1 :(得分:0)

所以这里是我用你告诉我的代码编写的代码: 我创建了两个函数:

  • 如果是帧,将使用三次多项式插值的插值 间隔不超过6个间隔。否则它将插入 按细分。
  • 正在对细分进行过滤的Filtrage。

目前,我还没有尝试更改a的值来找到正确的值,但它确实给了我很好的结果。

Preliminary results

def Interpolation(Frame, X, Y, Frequence = 2):
    """Fonction réalisant l'interpolation cubique des points manquants si possible, avec le découpage en segment si nécessaire."""

    # Détermination de la séparation entre les Frames pour la fréquence d'échantillonage choisie
    FPS = 30
    DT = FPS / Frequence

    # Initialisation
    i = 0

    # Détermination du début de l'interpolation
    try:
        while Frame[i+2] - Frame[i] != 2 * DT:
            i += 1
            continue
    except:
        print ("Erreur : Il est impossible de commencer l'interpolation.")

    # Suppresion des points antérieurs
    Frame = Frame[i:]
    X = X[i:]
    Y = Y[i:]

    k = 0
    Frame_id = list()
    # Recherche des segments
    for i in range(len(Frame) - 1):
        if Frame[i+1] - Frame[i] > 6 * DT:
            Frame_id.append((k, i))
            k = i + 1

    # Ajout du dernier segment
    if k != len(Frame) - 1:
        Frame_id.append((k, len(Frame)-1))

    # Suppresion des segments de moins de 3 points
    for k in reversed(range(len(Frame_id))):
        i = Frame_id[k][0]
        j = Frame_id[k][1]

        if j - i < 2:
            del Frame_id[k]
            continue

    Abs_inter, X_inter, Y_inter = [], [], []
    for index in Frame_id:
        i = index[0]
        j = index[1]

        # Génération des abscisses sur chaque segment, ainsi que des fonctions interpolés
        Abscisse = np.arange(Frame[i], Frame[j] + DT, DT)

        fX = interp1d(Frame[i:j+1], X[i:j+1], kind = 'cubic')
        fY = interp1d(Frame[i:j+1], Y[i:j+1], kind = 'cubic')

        # Génération des nouvelles coordonnées sur le segment
        Xnew = fX(Abscisse)
        Ynew = fY(Abscisse)

        Abs_inter += Abscisse.tolist()
        X_inter += Xnew.tolist()
        Y_inter += Ynew.tolist()

    # Création des listes résultats finaux
    Frame_final, X_final, Y_final = [], [], []

    Time = 0
    while Time <= Abs_inter[len(Abs_inter)-1]:
        if Time in Frame:
            Frame_final.append(Time)
            X_final.append(X[Frame.index(Time)])
            Y_final.append(Y[Frame.index(Time)])
            Time += DT
            continue
        elif Time in Abs_inter:
            Frame_final.append(Time)
            X_final.append(X_inter[Abs_inter.index(Time)])
            Y_final.append(Y_inter[Abs_inter.index(Time)])
            Time += DT
            continue
        else:
            Time += DT

    return (Frame_final, X_final, Y_final)

def Filtrage2(Frame, X, Y, DT = 15, a = 0.1):
    """Fonction réalisant un filtrage passe-bas d'ordre 1 du signal."""
    # Initialisation
    X_temp, Y_temp = [], []
    X_filter, Y_filter = [], []
    i = 1
    X_temp.append(X[0])
    Y_temp.append(Y[0])

    # Filtrage par morceau
    while i < len(Frame):

        while Frame[i] - Frame[i-1] == DT:
            Xnew = a * X[i-1] + (1 - a) * X_temp[len(X_temp)-1]
            Ynew = a * Y[i-1] + (1 - a) * Y_temp[len(Y_temp)-1]
            X_temp.append(Xnew)
            Y_temp.append(Ynew)
            if i < len(Frame)-1:
                i += 1
            else:
                break

        X_filter += X_temp
        Y_filter += Y_temp
        X_temp, Y_temp = [], []
        X_temp.append(X[i])
        Y_temp.append(Y[i])
        i += 1

    return (X_filter, Y_filter)

我也尝试过使用带有scipy库的butterworth过滤器,但我无法使用它。这是我无法解决的代码:

def Filtrage(Frame, X, Y, DT = 15, fc = 0.5, frequence = 2, order = 5):
    """Fonction réalisant un filtrage passe-bas du signal."""
    # Détermination du filtre
    nyq = 0.5 * frequence
    normal_cutoff = fc / nyq
    b, a = butter(order, normal_cutoff, btype = 'low', analog = False)

    # Filtrage par morceau
    X_temp, Y_temp = [], []
    X_filter, Y_filter = [], []

    # Initialisation
    X_temp.append(X[0])
    Y_temp.append(Y[0])
    i = 1

    while i < len(Frame):

        while Frame[i] - Frame[i-1] == DT:
            X_temp.append(X[i])
            Y_temp.append(Y[i])
            if i < len(Frame)-1:
                i += 1
            else:
                break

        X_filter += lfilter(b, a, X_temp).tolist()
        Y_filter += lfilter(b, a, Y_temp).tolist()
        X_temp, Y_temp = [], []
        X_temp.append(X[i])
        Y_temp.append(Y[i])
        i += 1

    return (X_filter, Y_filter)

如果你对如何使这个butterworth工作有任何想法,我会很高兴听到它。