python - 如何在两个连续的视频帧中跟踪轮辐?

标签 python opencv image-processing video-processing

考虑视频的两个连续帧,如下所示: fig1

fig2

跟踪其中一根辐条运动的可能方法是什么?

我问这个是因为我没有任何视频处理经验。所以任何建议都会有所帮助!我在追踪什么?根据我的阅读,通常我必须先检测要跟踪的对象。我正在为此使用角点检测算法,例如 goodfeaturestotrack。但是我如何确保检测到正确的辐条等?

一些附加信息:视频的帧率为 30fps。车轮仅沿顺时针方向旋转。当我逐帧单击视频时,很明显辐条移动的角度不超过两个辐条之间角度的一半(从帧到帧)。 另外:轮子的半径是5cm。

我现在已经尝试了 Mark 的回答。我已经在 txt 文件中记录了 Tmax 和帧的时间戳,然后编写了以下代码来计算相应的速度:

ListOfVelocities = []
for idx in range(1,len(ListOfAngles)):
    if ListOfAngles[idx] < ListOfAngles[idx-1]:
        rotation = (360-ListOfAngles[idx]) + ListOfAngles[idx-1]
    else: 
        rotation = ListOfAngles[idx] - ListOfAngles[idx-1]
    timePassed = VideoTimestamp[idx]-VideoTimestamp[idx-1]
    velocity = 2*np.pi/360 * rotation * RADIUS * timePassed
    ListOfVelocities.append(velocity)

最佳答案

我真的不认为这是一个跟踪问题,因为轮子是受限的,所以它不能在整个框架内移动,它只能改变它的角度位置,所以你只需要知道它的某个部分在哪里它在一帧中以及在下一帧中旋转了多少。然后,如您所知的帧率,即帧之间的时间,您可以计算速度。

因此,问题是如何判断哪个辐条与您在上一帧中测量的辐条相同。由于辐条后面的区域很暗,因此您需要一个光线鲜明的辐条,以便您可以轻松找到它。所以,我会把四根辐条漆成黑色,然后你只需要在深色背景上寻找一根浅色的。我还会考虑将轮子的中心涂成红色(或其他饱和色),这样您就可以轻松找到中间部分。

现在,在处理开始时,通过寻找红色找到轮子的中心,并获得图像中的 x,y 坐标。现在选择一个您以后可以更改的以像素为单位的半径,并计算出以红点为中心并围绕红点的圆周上 360 个点(每度 1 个)的 x、y 坐标列表。这些点和所有正弦/余弦在您的整个处理过程中都不会改变,因此请在您的主视频处理循环之外进行。

现在在每一帧,使用索引来获取 360 点中每个点的亮度,并且至少在最初,将最亮的一个作为辐条。

所以,我粗略地在你的图像上画了中心是红色的,只有一根辐条是白色的:

enter image description here

现在代码看起来像这样:

#!/usr/bin/env python3

import math
import numpy as np
from PIL import Image

# Open image and make Numpy version of it too
im = Image.open('wheel.png')
imnp = np.array(im)

# Find centre by looking for red pixels
# See https://stackoverflow.com/a/52183666/2836621
x, y = 193, 168

# Set up list of 360 points on a circle centred on red dot outside main processing loop
radius = 60
# List of X values and Y values on circumference
Xs = []
Ys = []
for theta in range(360):
    thetaRad = math.radians(theta)
    dx = int(radius * math.sin(thetaRad))
    dy = int(radius * math.cos(thetaRad))
    Xs.append(x+dx)
    Ys.append(y+dy)

# Your main loop processing frames starts here

# Make greyscale Numpy version of image
grey = np.array(im.convert('L'))

sum  = 0
Bmax = 0
Tmax = 0
for theta in range(360):
    brightness=grey[Ys[theta],Xs[theta]]
    sum += brightness
    if brightness > Bmax:
        Bmax = brightness
        Tmax = theta
    print(f"theta: {theta}: brightness={brightness}")

# Calculate mean
Mgrey = sum/len(Xs)
print(f"Mean brightness on circumf: {Mgrey}")

# Print peak brightness and matching theta
print(f"Peak brightness: {Bmax} at theta: {Tmax}")

输出是这样的:

theta: 0: brightness=38
theta: 5: brightness=38
theta: 10: brightness=38
theta: 15: brightness=38
theta: 20: brightness=38
theta: 25: brightness=38
theta: 30: brightness=38
theta: 35: brightness=45
theta: 40: brightness=38
theta: 45: brightness=33
theta: 50: brightness=30
theta: 55: brightness=28
theta: 60: brightness=28
theta: 65: brightness=31
theta: 70: brightness=70
theta: 75: brightness=111
theta: 80: brightness=130
theta: 85: brightness=136
theta: 90: brightness=139    <--- peak brightness at 90 degrees to vertical as per picture - thankfully!
theta: 95: brightness=122
theta: 100: brightness=82
theta: 105: brightness=56
theta: 110: brightness=54
theta: 115: brightness=49
theta: 120: brightness=43
theta: 125: brightness=38
theta: 130: brightness=38
theta: 135: brightness=38
theta: 140: brightness=38
theta: 145: brightness=38
theta: 150: brightness=38
theta: 155: brightness=38
theta: 160: brightness=38
theta: 165: brightness=38
theta: 170: brightness=38
theta: 175: brightness=38
theta: 180: brightness=31
theta: 185: brightness=33
theta: 190: brightness=38
theta: 195: brightness=48
theta: 200: brightness=57
theta: 205: brightness=38
theta: 210: brightness=38
theta: 215: brightness=38
theta: 220: brightness=38
theta: 225: brightness=38
theta: 230: brightness=38
theta: 235: brightness=38
theta: 240: brightness=38
theta: 245: brightness=38
theta: 250: brightness=52
theta: 255: brightness=47
theta: 260: brightness=36
theta: 265: brightness=35
theta: 270: brightness=32
theta: 275: brightness=32
theta: 280: brightness=29
theta: 285: brightness=38
theta: 290: brightness=38
theta: 295: brightness=38
theta: 300: brightness=38
theta: 305: brightness=38
theta: 310: brightness=38
theta: 315: brightness=38
theta: 320: brightness=39
theta: 325: brightness=40
theta: 330: brightness=42
theta: 335: brightness=42
theta: 340: brightness=40
theta: 345: brightness=36
theta: 350: brightness=35
theta: 355: brightness=38
Mean brightness on circumf: 45.87222222222222
Peak brightness: 142 at theta: 89

如果在下一帧中峰值亮度现在与垂直方向成 100 度,则您知道轮子在 1/(frames_per_second) 内旋转了 10 度。

您可能需要改变半径以获得最佳效果 - 实验!图像上显示的白色半径对应于代码中的 60 像素半径。

与其取峰值亮度,不如求圆周上 360 个像素亮度的均值和标准差,然后取角度作为亮度大于某个数量的角度的平均值高于平均值的标准偏差。这取决于您需要的分辨率/精度。

您还可以将由 theta 索引的圆周围的所有亮度收集到一个 360 元素数组中,如下所示:

brightnessByTheta = grey[Ys[:],Xs[:]]

你会得到:

array([ 38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  43,  49,  47,  46,  45,  44,  43,  43,
        40,  38,  36,  34,  33,  33,  33,  32,  31,  31,  29,  30,  28,
        29,  29,  29,  28,  28,  27,  29,  28,  28,  27,  28,  28,  29,
        31,  36,  42,  51,  60,  70,  81,  89,  98, 105, 111, 117, 122,
       126, 128, 130, 131, 132, 133, 135, 136, 138, 139, 141, 142, 139,
       136, 133, 129, 124, 122, 119, 113, 104,  93,  82,  72,  65,  60,
        59,  56,  56,  55,  55,  54,  54,  53,  52,  52,  50,  49,  47,
        46,  45,  44,  43,  42,  40,  39,  38,  38,  37,  38,  38,  37,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  34,  31,  31,  31,  31,
        31,  31,  32,  33,  34,  35,  36,  37,  38,  42,  43,  44,  45,
        48,  49,  50,  51,  55,  57,  60,  64,  65,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  52,  56,  46,  46,  47,  47,  38,  39,  40,  40,
        36,  36,  36,  36,  36,  35,  35,  34,  34,  34,  32,  33,  33,
        33,  33,  32,  32,  31,  30,  29,  29,  28,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,  38,
        38,  38,  38,  38,  38,  38,  40,  40,  39,  38,  39,  39,  39,
        40,  40,  41,  41,  42,  42,  42,  41,  41,  42,  42,  41,  40,
        39,  40,  40,  38,  39,  38,  37,  36,  36,  35,  34,  33,  35,
        38,  38,  38,  38,  38,  38,  38,  38,  38], dtype=uint8)

关于python - 如何在两个连续的视频帧中跟踪轮辐?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56317396/

相关文章:

Python urllib.request.urlopen() 返回错误 10061?

python - 将键列表和值列表转换为字典

python - 单击时,tkinter 在按钮上增量显示整数

java - 如何使用在 PC 上运行的 java 代码启动 Android 手机相机?

c++ - 用于 C++ MatND 的 OpenCV cvGetMinMaxHistValue

android - 如何将图像分成两部分?

c++ - 锐化图像 - 访问邻近像素

python - 提取图像的 k 均值聚类的特定成员

python - PEP 8 : library specific imports?

c++ - 使用 Opencv 加载 12 位原始灰度图像