『深度学习』固定相机监控视频数据清洗思路和代码

1. 背景描述:

共8-9台设备固定点位,24小时录像,10天~20天不等,共计600G+ video。需要从中找出船只图像,送标,训练。提高检测率,为后续跟踪等模块做支持。

 参考教程:视频局部区域移动检测, 删除相似帧 - 知乎 (zhihu.com)

 上述截图来自于:视频局部区域移动检测, 删除相似帧 - 知乎 (zhihu.com)

2. 清洗流程

如下图所示,清洗流程为:mkv->imgs->avi->dedup->imgs->筛选标注

3. 难点解决
  1. mkv→imgs速度慢:瓶颈在写入图片速度,写入设备的图片/小文件写入速度是制约瓶颈;比如每秒可写入多少个小文件;小文件的写入速度。
    1. 解决办法:①使用高效的小文件写入磁盘 m.2 SSD来做写入设备,速度差异比较;② 脚本应用多开,exe + 解析路径;解析脚本是单线程,多开来同时处理;每个脚本单独所占资源开销小,CPU占用低、内存占用低、磁盘占用低;多开可以充分利用计算资源;备注:没有采用多线程的方式,原因待详细解释。主要是多线程没用。
  2. 场景有远近距离,去冗余去重复帧的参数阈值要差异化。原因:远近距离中的目标大小/长宽/面积大小差异大,在重复帧的筛选过程中,参数需要差异化。
    现象:
    小目标的场景,丢失了小目标;或者重复帧太多,水面的稍微变化全提取出来;要不重复帧提取多了,要不提取少了。
    1. 所以针对每个区域,单独处理;会麻烦一点,但是相比于后续筛选图像,效率大大提高。还是将这个操作前置。
4. 使用教程

1. 先生成ROI区域

from utils import get_rectangle_point

hours_path = 'E:/aomen_data_imgs_20240326_orivideo/a5_y_3/20240312/00'
for index in range(1, 16):
    # index = 1
    file_path = hours_path +'/' + str(index) + '.avi'
 
    min_x, min_y, width, height = get_rectangle_point(file_path)
    print('剪切坐标 ',index,'\t', min_x, min_y, width, height)

coordinates = {
    1: [741, 182, 157, 81],
    2: [4, 173, 891, 126],
    3: [4, 164, 894, 175],
    4: [6, 149, 891, 216],
    5: [6, 142, 889, 224],
    6: [3, 138, 894, 156],
    7: [6, 133, 890, 137],
    8: [3, 125, 637, 68],
    9: [4, 125, 894, 145],
    10: [4, 137, 894, 162],
    11: [5, 141, 893, 167],
    12: [4, 99, 895, 199],
    13: [2, 94, 894, 197],
    14: [4, 101, 887, 105],
    15: [9, 94, 609, 106]
}


from utils import remove_video_dup
import os
device_path_list = [
    'I:/aomen_data_imgs_20240326/a5_y_3']  # 现在只要给定设备路径就可以了
output_rootDir = 'E:/aomen_video_20240326/'

for device in device_path_list:
    subfolders_day = [f.path for f in os.scandir(device) if f.is_dir()]    
    for day_path in subfolders_day:
        subfolders = [f.path for f in os.scandir(day_path) if f.is_dir()]
        # print(subfolders)
        for hours_path in subfolders:
            # for index in range(1, 16):
            for index in [5]: # 都少写了一个
            # 由于距离远近不一样了,所以要分区域来做了,这里要重新给值了
                remove_video_dup(hours_path,coordinates,mpdecimate_lo=64*45, mpdecimate_hi=64*100, index=index, output_rootDir=output_rootDir)

 utils.py

import os
import cv2

def list_last_level_folders(folder_path):
    """
        功能:实现批量Img进入文件夹
        # 替换为你实际的文件夹路径
        folder_path = "E:\\aomen_data_imgs"
        last_level_folders = list_last_level_folders(folder_path)

        # print("Last level folders:")
        # for folder in last_level_folders:
        #     print(folder)

        # 写入到 output.txt 文件
        output_file_path = "all_hours_output.txt"
        with open(output_file_path, 'w') as file:
            # file.write(f"{folder_path}\n")
            for folder in last_level_folders:
                file.write(f"{folder}\n")

        print(f"Last level folders written to {output_file_path}")
    """
    folders = []

    def _list_last_level_folders(current_path):
        nonlocal folders

        try:
            if os.path.exists(current_path) and os.path.isdir(current_path):
                # 判断当前文件夹是否有子文件夹
                subfolders = [entry.name for entry in os.scandir(current_path) if entry.is_dir()]
                if not subfolders:
                    # 没有子文件夹,添加到列表
                    folders.append(current_path)
                else:
                    # 递归获取子文件夹
                    for entry in os.scandir(current_path):
                        if entry.is_dir():
                            _list_last_level_folders(entry.path)
        except Exception as e:
            print(f"Error: {e}")

    _list_last_level_folders(folder_path)
    return folders

def read_path_from_txt(output_file_path):
    """ usage:
        output_file_path = "all_hours_output.txt"
        read_path_from_txt(output_file_path)
    """
    # output_file_path = "all_hours_output.txt"
    f=open(output_file_path)
    line = f.readline().strip() #读取第一行
    txt=[]
    txt.append(line)
    while line: # 直到读取完文件
        line = f.readline().strip() # 读取一行文件,包括换行符
        txt.append(line)
    f.close() # 关闭文件
    # print(txt)
    return txt



def from_picture_2_video(path, index=None):
    """ 给定hours_path 可以生产该小时下的15个路径的img->avi
        org-19:00---0,1,....,15
            |--0
                |--0a0.jpg 1a0.jpg,...,1000a0.jpg
        res-19:00
            |-0.avi, 1.avi,...., 15.avi       
        
        usage:
            hours_path = 'H:/aomen_data_imgs/20231229/19_start/' # hours_path
            for index in range(0,32):
                from_picture_2_video(hours_path, index=index)
    """
    if index is None:
        # image_folder = 'H:/aomen_data_imgs/20231229/19_start/0'
        # video_name = 'H:/aomen_data_imgs/20231229/19_start/0'+'.avi'
        image_folder = path
        video_name = path + '.avi'
    else:
        # image_folder = 'H:/aomen_data_imgs/20231229/19_start/' + str(index)
        # video_name = 'H:/aomen_data_imgs/20231229/19_start/' + str(index) + '.avi'
        image_folder = path + str(index)
        video_name = path + str(index) + '.avi'
    # 获取图像文件列表
    images = [img for img in os.listdir(image_folder) if img.endswith(".jpg")]
    # 获取图像文件列表并按照数字顺序排序
    # images = sorted([img for img in os.listdir(image_folder) if img.endswith(".jpg")], key=lambda x: int(x.split('.')[0]))

    frame = cv2.imread(os.path.join(image_folder, images[0]))

    # 获取图像尺寸
    height, width, layers = frame.shape

    # 视频编解码器,可以根据需要更改
    # fourcc = cv2.VideoWriter_fourcc(*'XVID')
    # # video = cv2.VideoWriter(video_name, fourcc, 1, (width, height))
    # video = cv2.VideoWriter(video_name, fourcc, 1, (width, height))

    fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # 或者使用 -1
    video = cv2.VideoWriter(video_name, fourcc, 1, (width, height))

    # 将图像写入视频
    for image in images:
        video.write(cv2.imread(os.path.join(image_folder, image)))
        

    cv2.destroyAllWindows()
    video.release()


# 新的用法
# hours_path = 'H:/aomen_data_imgs/20231229/19_start/' # hours_path
import re
import os
# image_folder = txt[66]
def getImageFileListFromFolderPath(image_folder):
    """
    image_folder='F:/aomen_data_imgs/20231229/00/0'   没有最后那个斜杠
        获取00小时0,1,...,15视角下的所有*a0.jpg名字,并按照a前面的圈数来进行排序
        排序之后再拼接为avi视频,保证顺序,也许对dup环节有用。
    """
    # 提取数字部分的函数
    def extract_number(filename):
        match = re.search(r'(\d+)', filename)
        return int(match.group()) if match else float('inf')  # 如果没有匹配到数字,返回无穷大

    images = [img for img in os.listdir(image_folder) if img.endswith(".jpg")]
    # 对文件名进行排序
    sorted_filenames = sorted(images, key=extract_number)
    # images = sorted([img for img in os.listdir(image_folder) if img.endswith(".jpg")], key=lambda x: int(x.split('.')[0]))
    output_file_path = image_folder + "\\filelist.txt"  # 前面image_folder='F:/aomen_data_imgs/20231229/00/0'   没有最后那个斜杠 否则这里要出错
    with open(output_file_path, 'w') as file:
        for folder in sorted_filenames:
            file.write(f"{folder}\n")

def ffmpge_imgs2vido(path, isOpenFFmpeg=True):
    """
        使用ffmpeg将imgs->avi
        需要使用powershell命令
    """
    # python执行powershell指令
    import subprocess
    import os
    image_folder = path
    video_name = path + '.avi'
    # filelist_path = image_folder+'/filelist.txt'

    getImageFileListFromFolderPath(image_folder=image_folder)
    powershell_command = fr'(Get-Content "{image_folder}\filelist.txt") | ForEach-Object {{"file $_"}} | Set-Content (Join-Path -Path "{image_folder}" -ChildPath "filelist.txt")'
    
    # 执行PowerShell指令
    result = subprocess.run(['powershell', powershell_command], capture_output=True, text=True)
    # 下面开始执行ffmpeg指令了:和盘符有关,指明mmfpeg.exe到底在哪里
    if isOpenFFmpeg: # 只生产filelinst,不进行操作,不知道为什么会卡在这里,所以会命令行
        ffmpeg_command = f"G:/zhr/ffmpeg6/ffmpeg.exe -r 1 -f concat -i {image_folder}/filelist.txt -vcodec libx264 -crf 25 -pix_fmt yuv420p {video_name}"
        result = subprocess.run(['powershell', ffmpeg_command], capture_output=True, text=True)
        print(ffmpeg_command)

"""
  ,,获取澳门数据每个区域的截断ROI区域信息:只要大桥前面的数据
"""
import cv2
# 鼠标响应函数(opencv画矩形框,并获取左上角的坐标与长宽)
def _rectangular_box(event, x, y, flags, param):
    """
        鼠标响应函数(opencv画矩形框,并获取左上角的坐标与长宽)
        :param event: 鼠标事件
        :param x: 坐标
        :param y: 坐标
        :param flags:
        :param param: 传递进来的参数(这里传入的是视频的第一帧)
        :return:
    """
    global point1, point2, min_x, min_y, width, height
    img = param.copy()
    if event == cv2.EVENT_LBUTTONDOWN:  # 左键点击
        point1 = (x, y)
        cv2.circle(img, point1, 10, (0, 255, 0), 5)
        cv2.imshow('image', img)
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON):  # 按住左键拖曳
        cv2.rectangle(img, point1, (x, y), (255, 0, 0), 5)
        cv2.imshow('image', img)
    elif event == cv2.EVENT_LBUTTONUP:  # 左键释放
        point2 = (x, y)
        cv2.rectangle(img, point1, point2, (0, 255, 255), 4)
        cv2.imshow('image', img)
        min_x = min(point1[0], point2[0])
        min_y = min(point1[1], point2[1])
        width = abs(point1[0] - point2[0])
        height = abs(point1[1] - point2[1])
def get_rectangle_point(file_path):
    """
        opencv画矩形框,并获取左上角的坐标与长宽
    :return: 矩形框 左上角的坐标, 和长宽
    """
    cap_temp = cv2.VideoCapture(file_path)
    if not cap_temp.isOpened():
        print("无法打开视频文件。请检查文件路径。")
        exit()
    ret, frame = cap_temp.read()  # 读取第一帧
    cv2.namedWindow('image', 0)
    # https://stackoverflow.com/questions/50783177/opencv-the-function-is-not-implemented-rebuild-the-library-with-windows
    # cv2.resizeWindow('image', 1920, 1080)   # 设置窗口大小, 否则显示不完全, 不方便画框
    cv2.resizeWindow('image', frame.shape[0], frame.shape[1]) 
    cv2.setMouseCallback('image', _rectangular_box, frame)   # 设置鼠标回调函数
    cv2.imshow('image', frame)
    cv2.waitKey(0)      # 按任意键退出矩形选择
    cap_temp.release()  # 回收资源
    cv2.destroyWindow('image')  # 关闭窗口

    return min_x, min_y, width, height


"""
    环境:
        python: 3.10
        ffmpeg: 6.0-essentials_build
        opencv: 4.8.0
        CPU: i5 9300h
        GPU: GTX 1050(3G)   # 未使用
    使用场景:
        1.只想对感兴趣区域做相似度判断, 删除相似帧
        2.对1产生的结果, 需要保留原视频完整的画面
    使用场景限制:
        1.感兴趣区域不宜过大,影响压缩日志生成速度, 区域越大,日志生成速度越慢
        2.感兴趣区域不宜有经常变化的物体,影响抽帧的速度,变化越多,抽帧越慢. 例:不能把时间水印框入感兴趣区域, 不能把随风而动的东西框入感兴趣区域
        3.以上两点, 需要同时满足, 否则请跳转到尝试二, 下载宝藏up主的工具, 效率更高
    食用方式:
        1.修改 mpdecimate_***开头的几个参数值, 以达到自己想要的效果, 建议用小视频测试参数值,速度快. 提示: hi和lo越大,删除的帧越多
        2.修改 file_path 原视频文件路径
        3.启动程序
"""
import os
import sys
import time
import re
import cv2

def get_dateName_hoursName(hours_path=None):
    """
        hours_path = 'H:/aomen_data_imgs/20231229/19_start'
        get_dateName_hoursName(hours_path)
    """
    if hours_path is None:
        print("path is None:",hours_path)
        return None, None
    path_parts = os.path.split(hours_path) # 使用 os.path.split 分割路径,得到一个包含两个元素的元组
    hours_name = path_parts[-1]
    dateName = os.path.split(path_parts[-2])[-1]
    # folder_name = path_parts[-2] # 取出最后一个元素,即文件夹或文件的名称
    # print(folder_name)
    # path_parts
    return dateName, hours_name
def makedir_in_newRootDir(hours_path=None, output_rootDir=None):   
    """
        hours_path = 'H:/aomen_data_imgs/20231229/19_start/'
        output_rootDir = 'H:/aomen_video_2Annotations/'
        makedir_in_newRootDir(hours_path, output_rootDir)
    """ 
    if hours_path is not None and output_rootDir is not None:
        # 获取驱动器和路径
        drive, path = os.path.splitdrive(hours_path)
        new_dir = os.path.join(output_rootDir, os.path.relpath(hours_path, drive+'/aomen_data_imgs_20240326')).replace('\\', '/') # 使用 replace('/', '\\') 将路径中的斜杠替换为反斜杠。
        # 递归创建目录
        try:
            os.makedirs(new_dir, exist_ok=True)
        except FileExistsError:
            pass
    return new_dir

def remove_video_dup(hours_path, coordinates, mpdecimate_lo,mpdecimate_hi, index=0, output_rootDir=None):
    """
        设置保存之后的文件格式为
        同时每个视频区域的ROI点是固定的,所以可以把所有区域的一次性导出来的。
        20231229_19_0.mp4 # 日期+小时+区域编号+帧号
        20231229_19_0_1.jpg, 20231229_19_0_2.jpg
        usage:
            hours_path = 'H:/aomen_data_imgs/20231229/19_start/'
            remove_video_dup(hours_path, index=4)
                输入路径:日期/小时
                输出路径:给一个根目录,然后mkdir日期+小时
                index为0.avi, 1.avi
                每个小时输出保存名字要修改为:20231229_19_0.mp4  # 这样每个小时就只有32个vidieo;每个video大小不一样。
                    还可以对video进行合并标注。
    """
    if output_rootDir is None:
        output_rootDir = hours_path
    file_path = hours_path +'/' + str(index) + '.avi'
    print(file_path)
    # file_path = 'H:/aomen_data_imgs/20231229/19_start/3.avi'     # 源文件地址
    temp_file_path = ''     # 一开始转码后的临时文件地址, 留空
    global point1, point2, min_x, min_y, width, height  # 在图像上画矩形框,所需的全局变量
    mpdecimate_max = 0      # 官方默认 0
    mpdecimate_keep = 0     # 官方默认 0    这个程序里该参数未启用, 启用会报错, 没细研究
    
    # 64位下的:0~100  65536=FFFF=16位;int类型。
    mpdecimate_hi = 64*50  # 官方默认 64*12  [5,12]太细了,太多了,不合适。
    mpdecimate_lo = 64*12   # 官方默认 64*5  [45,100]又太大了
    
    mpdecimate_frac = 0.33  # 官方默认 0.33
    log_encoding = 'utf8'   # 日志编码格式
    # skip_frame: 两次时间差值相距超过270帧, 则直接跳转到该帧操作, 不再逐帧跳过,
    # skip_frame: 该值由i5 9300h CPU测试而来, 此CPU下cap.grab()连续跳过270帧耗时与cap.set(cv2.CAP_PROP_POS_MSEC, pts_time)耗时相当
    skip_frame = 270

    # 将原视频转码
    def transcoding_video(file_path):
        """
            将原视频快速转码, 以去除海康威视导出视频里的损坏帧, 防止抽帧时按照时间跳转出现灰屏
            40W帧,耗时几秒钟
        """
        # global temp_file_path
        temp_file_path = f'temp_video.{file_path.split(".")[-1]}' # 只取文件后缀名字 .avi
        print(temp_file_path)

        # 一定要删除历史缓存,我靠
        import os
        if(os.path.isfile(temp_file_path)): # 如果存在就删除
            os.remove(temp_file_path)
            print("remove temp_video.avi successfully")
        else:
            print("temp_video.avi do not exit!")
        
        # os.system(f'H:/zhr/video_dup/ffmpeg6/ffmpeg.exe -ss 0 -i {file_path} -map 0:v:0 -c copy -y {temp_file_path}')
        print(f'G:/zhr/ffmpeg6/ffmpeg.exe -ss 0 -i {file_path} -map 0:v:0 -c copy -y {temp_file_path}')
        os.system(f'G:/zhr/ffmpeg6/ffmpeg.exe -ss 0 -i {file_path} -map 0:v:0 -c copy -y {temp_file_path}')
        # 复制一个备份
        # 'H:/zhr/video_dup/ffmpeg6/ffmpeg.exe -ss 0 -i H:/aomen_data_imgs/20231229/19_start/4.avi -map 0:v:0 -c copy -y temp_video.avi'
        time.sleep(1)   # 加上一个睡眠时间, 否则下一步的画矩形框可能无法弹窗

    # 生成视频压缩日志文件
    def crate_mpdecimate_log(file_path, min_x, min_y, width, height):
        temp_file_path = f'temp_video.{file_path.split(".")[-1]}'
        """
            先剪切, 再计算相似度, 将需要保留的帧信息, 写入日志文件(剪切面积越大, 执行速度越慢, 剪切的过程相当于一次转码)
            如果拥有高端显卡, 可以尝试将下面的指令拆分为两部分执行, 先crop生成一个新视频, 再对新视频mpdecimate提取日志, 总耗时可能会减少
        """
        # CPU编解码, 40W帧 耗时: 576秒  'H:/zhr/video_dup/ffmpeg6/ffmpeg.exe -i temp_video.avi -vf crop=890:596:5:416,mpdecimate=max=0:hi=6400:lo=2880:frac=0.33,showinfo -f null - > mpdecimate_log.txt 2>&1'
        instruct = f"G:/zhr/ffmpeg6/ffmpeg.exe -i {temp_file_path} -vf crop={width}:{height}:{min_x}:{min_y}," \
                f"mpdecimate=max={mpdecimate_max}:hi={mpdecimate_hi}:lo={mpdecimate_lo}:frac={mpdecimate_frac}," \
                f"showinfo -f null - > mpdecimate_log.txt 2>&1"
        print(instruct)
        print('压缩日志生成中, 请等待, 该过程没有进度条展示并且有较高CPU占用, 请耐心等待')
        os.system(instruct)

    # 读取需要保留的帧的时间信息
    def read_time_message():
        try:
            with open('./mpdecimate_log.txt', encoding=log_encoding) as f:
                time_message = f.read()
        except UnicodeError:
            print(f'日志格式编码错误, {log_encoding} 无法读取日志文件')
            exit()
        for item in re.finditer(r'n:.*?pts_time: *(?P<pts_time>\d+(\.\d+)?)', time_message):
            yield item.group('pts_time')

    # 视频时间维度压缩
    def video_time_compress(temp_file_path,hours_path, output_rootDir):
        cap = cv2.VideoCapture(temp_file_path)
        print(temp_file_path)
        if not cap.isOpened():
            print("无法打开视频文件。请检查文件路径。")
            exit()
        fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', '2')  # 初始化视频写入器
        # fourcc = cv2.VideoWriter_fourcc('M', 'P', '4')  
            
        # fourcc = cv2.VideoWriter_fourcc(*'XVID')
        fps = cap.get(cv2.CAP_PROP_FPS)  # 帧率
        cv_total_fps = cap.get(7)  # 总帧数
        cv_total_msec = int(cv_total_fps / fps * 1000)  # 总时长, 秒
        print(f'原视频总帧数:{cv_total_fps}, 帧率:{fps}, 总时长:{cv_total_msec / 1000}s')

        # 为了批量处理,这里需要改为
        # save_path
        if True:
            new_dir = makedir_in_newRootDir(hours_path, output_rootDir) # 新路径下创建文件夹
            print(new_dir)
        else:
            try:  # 输出目录
                os.mkdir('compress_file') # 创建文件夹,路径,文件名   这里写根目录和创建文件夹
            except FileExistsError:
                pass
        
        if False:
            new_name = file_path.split('/')[-1].split('.')[0] + '_compress.avi'  # 输出视频名称  前面是文件名   4_compress.avi  这里写文件名
        else:
            dateName, hourName = get_dateName_hoursName(hours_path)
            new_name = new_dir + '/' + dateName + '_' + hourName + '_' + str(index) + '.mp4' # 输出文件名称,日期——小时——序号.avi
        
        # 新路径 + 新文件名
        print(new_name)
        out = cv2.VideoWriter(f'{new_name}', fourcc, fps, (int(cap.get(3)), int(cap.get(4))))
        # out = cv2.VideoWriter(f'./compress_file/{new_name}', fourcc, fps, (int(cap.get(3)), int(cap.get(4))))
        # cap是读取的temp_video.avi,这里面已经是处理好了的相似性;然后去里面拿数据就可以了
        for frame in extraction_frame(cap, cv_total_msec, fps): # 这个函数里面是提取动态目标的
            out.write(frame) # 往out里面写图像帧
        cap.release()
        out.release()

    # 抽帧
    def extraction_frame(cap, cv_total_msec, fps):
        """
            视频抽取指定时间的所有帧
        :param cap: 待处理的原始视频
        :param cv_total_msec: 总时长(毫秒)
        :param fps: 帧率
        :return: 被抽取的每一帧
        """
        pts_time_last = 0
        for pts_time_str in read_time_message():
            pts_time = int(float(pts_time_str) * 1000)  # 毫秒
            # 如果相邻两帧时间差值相距超过 skip_frame 数量的帧, 则直接跳转到该帧操作, 不再逐帧跳过
            # if (pts_time - pts_time_last) / 1000 * fps >= skip_frame:
            #     cap.set(cv2.CAP_PROP_POS_MSEC, pts_time)  # 跳转到指定毫秒数, 比较耗时 ≈0.3秒
            #     ret, frame = cap.retrieve()
            #     pts_time_last = pts_time
            #     sys.stdout.write(f"\r 进度_抽帧:{round(pts_time / cv_total_msec * 100, 2)}")
            #     sys.stdout.flush()
            #     yield frame
            #     continue
            while True:
                ret = cap.grab()  # 逐帧跳过
                if not ret: break
                if cap.get(cv2.CAP_PROP_POS_MSEC) < pts_time: continue  # 逐帧跳过
                ret, frame = cap.retrieve()
                pts_time_last = pts_time
                # print(round(pts_time / cv_total_msec * 100, 2))
                sys.stdout.write(f"\r 进度:{round(pts_time / cv_total_msec * 100, 2)}")
                sys.stdout.flush()
                yield frame
                break

    # def main(file_path):
    transcoding_video(file_path)                                    # step1: org.avi -->   tmp.avi  copy
    # min_x, min_y, width, height = get_rectangle_point(file_path)    # step2: get roi points  可以写成配置文件  单独来获取
    index_1_coordinates = coordinates.get(index)
    min_x, min_y, width, height =  index_1_coordinates
    print('剪切坐标', min_x, min_y, width, height)
    begin = time.time()
    crate_mpdecimate_log(file_path, min_x, min_y, width, height)    # step3: 创建日志信息   关键地方  给ffmpeg下指定,结果保存在log中
    # 这个ROI区域的文件也是可以自己写成配置文件的!!!
    end = time.time()
    print('临时日志文件已生成')
    print(f'生成日志耗时: {end - begin}')
    begin = time.time()
    temp_file_path = f'temp_video.{file_path.split(".")[-1]}'

    # makedir_in_newRootDir(hours_path, output_rootDir)               # step4: 把需要的路径生成好,保存路径
    video_time_compress(temp_file_path, hours_path, output_rootDir) 
    end = time.time()
    print(f'压缩耗时: {end - begin}')   # 逐帧跳过压缩(总共40W帧,读取7300帧)耗时657秒 # 按时间节点跳帧压缩(读取7300帧)耗时: 129秒

    # main(file_path)
    # if __name__ == '__main__':
    #     main()











5. 多个mp4拼接为一个大的MP4文件

1. 查找所有同区域的mp4
2. 多个mp4合并

import os

def find_files(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(pattern):
                yield os.path.join(root, file)

def write_file_paths_to_txt(directory, pattern, output_file):
    with open(output_file, 'w') as f:
        for file_path in find_files(directory, pattern):
            f.write("file '{}'\n".format(file_path))

def merge_videos_from_txt(input_txt, output_file):
    ffmpeg_cmd = "G:/zhr/ffmpeg6/ffmpeg.exe -f concat -safe 0 -i {} -c copy {}".format(input_txt, output_file)
    os.system(ffmpeg_cmd)

# 搜索 *_4.mp4 文件的目录
search_directory = 'E:/aomen_video_20240326/a5_y_3'

# 要搜索的模式
file_pattern = '_5.mp4'

# 输出文本文件的路径
txt_output_path = 'a5_y_3_5.txt'

# 查找文件并将路径写入文本文件
write_file_paths_to_txt(search_directory, file_pattern, txt_output_path)

# 合并文本文件中列出的视频
merge_videos_from_txt(txt_output_path, 'a5_y_3_5.mp4')

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值