手把手写深度学习(22):视频数据集清洗之过滤静态/运动程度低的数据

news2024/11/15 9:26:05

手把手写深度学习(0):专栏文章导航

前言:当我们训练自己的视频生成模型时,现在大部分基于扩散模型架构都差不多,关键点在数据上!视频数据的预处理远远比图像数据复杂,其中有一点是如果静态数据、运动程度低的数据加入到数据集中,会对模型的效果产生极大的破坏!这篇博客手把手教读者如何清洗掉这些不合格的数据。

目录

理论基础

什么是光流?

稠密光流

稀疏光流

转换成灰度图

缩小分辨率

计算光流平均得分

完整Demo 


理论基础

主流的方案是用光流法衡量运动的程度,光流又分成稠密光流和稀疏光流。

什么是光流?

光流是图像序列中像素点随时间变化的运动矢量场。它是由于观察者、场景或照明条件的移动而引起的图像对象的表观运动的模式。在计算机视觉中,光流技术用于估计两个连续视频帧之间的像素运动。

稠密光流

稠密光流是指为图像中的每个像素点计算光流矢量。这意味着与稀疏光流(只计算图像中某些特征点的光流)相比,稠密光流提供了一幅完整的运动场景,其中包含了图像中所有像素的运动信息。

基于这些假设,可以使用多种算法来计算稠密光流,如:

  • Lucas-Kanade方法:适用于小窗口内的稠密光流估计。
  • Horn-Schunck算法:通过全局平滑性约束来解决运动估计问题。
  • Farneback算法:使用多尺度方法来计算光流。

尽管稠密光流技术非常有用,但在实际应用中也面临着一些挑战:

  • 计算复杂度:为每个像素点计算光流是计算密集型的。
  • 遮挡处理:当一个物体被另一个物体遮挡时,光流的计算会变得复杂。
  • 大运动:当运动超出了算法的小运动假设时,计算准确的光流变得困难。
  • 光照变化:光照的变化可能违反亮度恒定性假设,影响光流的准确性。

稀疏光流

与稠密光流不同,稀疏光流仅关注图像中的某些特征点或兴趣点的运动,而不是图像中的每个像素点。这些特征点通常是图像中容易识别和跟踪的点,例如角点、边缘或明显的纹理。

稀疏光流的计算方法通常涉及以下步骤:

  • 特征检测:首先在第一帧图像中检测出特征点。
  • 特征匹配:然后在下一帧图像中找到这些特征点的对应位置。
  • 运动估计:最后,通过比较两帧之间特征点的位置变化来计算光流矢量。

常见的稀疏光流算法包括:

  • Lucas-Kanade方法:一种基于局部窗口的迭代最小二乘法。
  • Shi-Tomasi角点检测:与Lucas-Kanade方法结合使用,用于特征点的选取。
  • KLT跟踪器:基于Lucas-Kanade方法的特征跟踪算法。

转换成灰度图

在计算光流得分之前转换成灰度图,当然也可以不转换:

cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY)

缩小分辨率

直接计算的话非常耗时!所以建议缩小分辨率后再计算!

    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    # 确定最短边为16像素的新分辨率
    if width < height:
        new_width = 16
        new_height = int((new_width / width) * height)
    else:
        new_height = 16
        new_width = int((new_height / height) * width)

    desired_fps = 2
    skip_frames = int(round(fps / desired_fps))

    frame_idx = 0
    frame_list = []
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # 检查当前帧是否是需要处理的帧
        if frame_idx % skip_frames == 0:
            # cv2.imwrite(f'./results/frame_{frame_idx}.jpg', frame)
            # 缩小分辨率并转换成灰度图
            resized_frame = cv2.resize(frame, (new_width, new_height), interpolation=cv2.INTER_AREA)
            frame_list.append(cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY))
        
        frame_idx += 1

    cap.release()
    cv2.destroyAllWindows()

    if frame_list is []:
        return 0.0

计算光流平均得分

   # 计算稠密光流
    flow_scores = []  # 存放每一对连续帧的光流得分

    # 初始化前一帧的图像(灰度)
    prev_gray =frame_list[0]

    # 遍历frame_list计算光流
    for i in range(1, len(frame_list)):
        # 获取当前帧并转换为灰度
        # curr_gray = cv2.cvtColor(frame_list[i], cv2.COLOR_BGR2GRAY)
        curr_gray = frame_list[i]
        
        # 计算当前帧和前一帧的光流
        flow = cv2.calcOpticalFlowFarneback(prev_gray, curr_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
        
        # 计算光流的大小(速度)和角度(方向)
        magnitude, angle = cv2.cartToPolar(flow[..., 0], flow[..., 1])
        
        # 可以根据需要对光流进行处理,例如计算平均光流大小作为得分
        flow_score = np.mean(magnitude)
        flow_scores.append(flow_score)
        
        # 更新前一帧的图像
        prev_gray = curr_gray

    # flow_scores现在包含了每一对连续帧的光流得分
    print("flow_scores", flow_scores, "np.mean(flow_scores): ", np.mean(flow_scores))

完整Demo 

import cv2
import os
import numpy as np

# 获取文件夹下的所有video
def get_all_videos(path):

    video_files = []  # 用于存储所有的视频文件

    for filename in os.listdir(path):
        base, ext = os.path.splitext(filename)
        if ext in ['.mp4', '.avi', '.flv', '.mkv', '.mov']:  # 根据你需要的视频格式来修改
            video_files.append(os.path.join(path, filename))

    return video_files


def optical_flow(video_path) -> float:
    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    # 确定最短边为16像素的新分辨率
    if width < height:
        new_width = 16
        new_height = int((new_width / width) * height)
    else:
        new_height = 16
        new_width = int((new_height / height) * width)

    desired_fps = 2
    skip_frames = int(round(fps / desired_fps))

    frame_idx = 0
    frame_list = []
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # 检查当前帧是否是需要处理的帧
        if frame_idx % skip_frames == 0:
            # cv2.imwrite(f'./results/frame_{frame_idx}.jpg', frame)
            # 缩小分辨率并转换成灰度图
            resized_frame = cv2.resize(frame, (new_width, new_height), interpolation=cv2.INTER_AREA)
            frame_list.append(cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY))
        
        frame_idx += 1

    cap.release()
    cv2.destroyAllWindows()

    if len(frame_list) == 0:
        return 0.0

    # 计算稠密光流
    flow_scores = []  # 存放每一对连续帧的光流得分

    # 初始化前一帧的图像(灰度)
    prev_gray =frame_list[0]

    # 遍历frame_list计算光流
    for i in range(1, len(frame_list)):
        # 获取当前帧并转换为灰度
        # curr_gray = cv2.cvtColor(frame_list[i], cv2.COLOR_BGR2GRAY)
        curr_gray = frame_list[i]
        
        # 计算当前帧和前一帧的光流
        flow = cv2.calcOpticalFlowFarneback(prev_gray, curr_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
        
        # 计算光流的大小(速度)和角度(方向)
        magnitude, angle = cv2.cartToPolar(flow[..., 0], flow[..., 1])
        
        # 可以根据需要对光流进行处理,例如计算平均光流大小作为得分
        flow_score = np.mean(magnitude)
        flow_scores.append(flow_score)
        
        # 更新前一帧的图像
        prev_gray = curr_gray

    # flow_scores现在包含了每一对连续帧的光流得分
    print("flow_scores", flow_scores, "np.mean(flow_scores): ", np.mean(flow_scores))


if __name__ == "__main__":
    video_files = get_all_videos("/mnt/dolphinfs/ssd_pool/docker/user/hadoop-mtcv/wangqiang121/base_model/PaddleOCR/webvid-1000/")
    for video_path in video_files:
        optical_flow(video_path)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1468518.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python奇幻之旅(从入门到入狱高级篇)——面向对象进阶篇(下)

目录 引言 3. 面向对象高级和应用 3.1. 继承【补充】 3.1.1. mro和c3算法 c3算法 一句话搞定继承关系 3.1.2. py2和py3区别 3.3. 异常处理 3.3.1. 异常细分 3.3.2. 自定义异常&抛出异常 3.3.3. 特殊的finally 3.4. 反射 3.4.1. 一些皆对象 3.4.2. import_modu…

一元函数微分学——刷题(18

目录 1.题目&#xff1a;2.解题思路和步骤&#xff1a;3.总结&#xff1a;小结&#xff1a; 1.题目&#xff1a; 2.解题思路和步骤&#xff1a; 遇到绝对值函数&#xff0c;需要把它转化为分段函数&#xff0c;从而更加方便求导数&#xff1a; 3.总结&#xff1a; 遇到绝对…

算法沉淀——动态规划之斐波那契数列模型(leetcode真题剖析)

算法沉淀——动态规划之斐波那契数列模型 01.第 N 个泰波那契数02.三步问题03.使用最小花费爬楼梯04.解码方法 动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09;是一种通过将原问题分解为相互重叠的子问题并仅仅解决每个子问题一次&#xff0c;将其解存…

Linux日志轮替

文章目录 1. 基本介绍2. 日志轮替文件命名3. logrotate 配置文件4. 把自己的日志加入日志轮替5. 日志轮替机制原理6. 查看内存日志 1. 基本介绍 日志轮替就是把旧的日志文件移动并改名&#xff0c;同时建立新的空日志文件&#xff0c;当旧日志文件超出保存的范围之后&#xff…

深度学习基础(四)医疗影像分析实战

之前的章节我们初步介绍了卷积神经网络&#xff08;CNN&#xff09;和循环神经网络&#xff08;RNN&#xff09;&#xff1a; 深度学习基础&#xff08;三&#xff09;循环神经网络&#xff08;RNN&#xff09;-CSDN博客文章浏览阅读1.2k次&#xff0c;点赞17次&#xff0c;收…

window: C++ 获取自己写的dll的地址

我自己用C写了一个插件,插件是dll形式的,我的插件式在dll的目录下有个config文件夹,里面是我用json写的插件配置文件,当插件运行的时候我需要读取到json配置文件,所有最重要的就是如何获取dll的路径. 大概就是这么个结构, 我自己封装了一个函数.只适用于window编程,因为里面用…

个人博客系列-前端部署-创建框架(4)

项目环境介绍 Vue3 Vite TypeScript 服务器&#xff1a;阿里云contos node版本&#xff1a;v18.18.2 npm版本&#xff1a;v10.2.4 执行下面一行命令&#xff0c;创建vue3框架 npm create vuelatest修改端口&#xff1a;9528&#xff0c; 此步骤可以忽略&#xff08;使用默…

十三、集合进阶——双列集合

集合进阶——双列集合 13.1 双列集合的特点13.2 Map集合13.2.1 Map集合常用的API13.2.2 Map的遍历方式 13.3 HashMap13.4 LinkedHashMap13.5 TreeMap13.6 源码解析HashMap源码解读TreeMap源码解读 13.7 可变参数13.8 Collections13.9综合练习 13.1 双列集合的特点 双列集合一次…

【动态规划专栏】动态规划:似包非包---不同的二叉树

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

SmartX 携手 openGauss 社区发布联合方案评测与性能最佳实践

近日&#xff0c;北京志凌海纳科技有限公司&#xff08;以下简称 “SmartX”&#xff09;携手 openGauss 社区完成了 openGauss 数据库基于 SmartX 超融合平台&#xff08;SMTX OS&#xff09;和 SmartX 分布式存储平台&#xff08;SMTX ZBS&#xff09;的性能测试和调优。 结果…

【C++】模板初阶 | 泛型编程 | 函数模板 | 类模板

目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 【本节目标】 1. 泛型编程 2. 函数模板 3. 类模板 1. 泛型编程 如何实现一…

C语言调试

目录 一.Debug和Release介绍 二.Windows环境调试介绍 三.窗口介绍 &#xff08;1&#xff09;自动窗口和局部变量窗口 &#xff08;2&#xff09;监视窗口 &#xff08;3&#xff09;调用堆栈 &#xff08;4&#xff09;查看汇编信息 &#xff08;5&#xff09;查看寄存…

Java零基础 - 算术运算符

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

Win11网络连接选项和蓝牙选项突然消失的解决办法

在设置或者开始栏里搜索“网络重置” 打开网络重置&#xff1a; 然后点击立即重置&#xff0c;之后按照系统提示操作即可

Mybatis总结--传参二

#叫做占位符 Mybatis是封装的JDBC 增强版 内部还是用的jdbc 每遇到一个#号 这里就会变为&#xff1f;占位符 一个#{}就是对应一个问号 一个占位符 用这个对象执行sql语句没有sql注入的风险 八、多个参数-使用Param 当 Dao 接口方法有多个参数&#xff0c;需要通过名称使…

猫头虎分享已解决Bug || 超时错误:TimeoutError: Request timed out after 30000ms.

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

进程1——进程与线程——day09

今天&#xff0c;主要讲一下进程的一些基本概念和一些接口 首先是进程的基本概念&#xff1a; 1.进程: 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态…

51单片机项目(34)——基于51单片机和esp8266的智能农业检测系统

1.设计要求 可以检测农业大棚中的温度、湿度、气压、光照等信息&#xff0c;可以检测土壤湿度&#xff0c;可以判断当前有没有下雨&#xff0c;能够将相关数据显示在OLED屏幕上。同时&#xff0c;使用esp8266wifi模块&#xff0c;将上述所有信息发送到手机APP显示。当温度、湿度…

SQL-Labs46关order by注入姿势

君衍. 四十六关 ORDER BY数字型注入1、源码分析2、rand()盲注3、if语句盲注4、时间盲注5、报错注入6、Limit注入7、盲注脚本 四十六关 ORDER BY数字型注入 请求方式注入类型拼接方式GET报错、布尔盲注、延时盲注ORDER BY $id 我们直接可以从界面中得知传参的参数为SORT&#x…

可变参数、Collections类

一、可变参数 定义&#xff1a;是一种特殊的形参&#xff0c;定义在方法、构造器的形参列表里 格式&#xff1a;数据类型...参数名称 特点&#xff1a;可以不传数据&#xff0c;也可以传一个或者多个数据给它&#xff0c;也可以传一个数组 好处&#xff1a;可以灵活接收数据…