将RGB视频转换为灰度视频以减小文件大小

4

我正在使用OpenCV创建彩色视频(RGB)并将生成的视频文件上传到服务器。由于彩色视频文件大小较大,在当前可用带宽下上传会成为瓶颈。因此,我试图通过将其转换为OpenCV中的灰度视频来减小文件大小。

以下是我目前工作的OpenCV实现:

cap = cv2.VideoCapture(RGB_video_filepath)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print("Input Video FPS: ".format(fps))
    outputfilepath = "gray_video_output.avi"

    mjpg_forcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')
    divx_forcc = cv2.VideoWriter_fourcc(*'DIVX')
    xvid_forcc = cv2.VideoWriter_fourcc(*'XVID')
    fmpp4_codec = cv2.VideoWriter_fourcc('F','M','P','4')
    mp4v_codec = cv2.VideoWriter_fourcc(*'MP4V')
    vid_writer = cv2.VideoWriter(outputfilepath, mjpg_codec, 2, (640, 480), 0)

    while cv2.waitKey(1) < 0:
        # get frame from the video
        hasFrame, frame = cap.read()


        # Stop the program if reached end of video
        if not hasFrame:
            print("Done processing !!!")
            print("Output file is stored as ", outputfilepath)
            break

        gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        vid_writer.write(gray_frame)
        print("Frame shape: {} {}".format(frame_count, frame.shape))
        cv2.imshow("Camera frame", frame)
        cv2.waitKey(1)

    print("Total frames: {}".format(frame_count))
    vid_writer.release()
    cap.release()

使用以上的工作流程,我创建了一个灰度视频,但是我发现视频文件大小几乎相同(RGB视频文件大小:25 MB,灰度视频文件大小:23 MB)。
在深入OpenCV之后,我发现尽管OpenCV在Linux操作系统上使用FFMPEG进行视频文件编写,但它将灰度(单通道)帧复制3次并将其作为3个通道写入视频中。
我尝试使用下面的方式将RGB视频文件转换为灰度视频文件:
ffmpeg -i inputvideofile -vf hue=s=0 outputvideofile

在这里,我将色调和饱和度通道保留为空白,令人惊讶的是RGB视频文件(25 MB)转换为灰度图像,文件大小缩小到6 MB。 我很好奇是否可以使用OpenCV实时将RGB转换为灰度,从而实现视频文件大小的减小?
感谢任何帮助/更新。谢谢!!

1
可能有一种正确的方法来实现它,但你可以在开始时将ffmpeg(使用有效参数)作为子进程启动,并通过管道将其输入连接到Python程序的输出。然后,您只需将原始帧写入stdoutffmpeg将制作您的视频,并且您可以免费获得多处理器,因为视频压缩将在单独的进程中进行。我曾经用过CImg而不是OpenCV,并且是用C语言编写的,但您可以理解这个想法... https://dev59.com/aoDba4cB1Zd3GeqPG6eU#46710797 - Mark Setchell
显示您的ffmpeg命令的完整日志。 - llogan
@llogan,FFmpeg在多行中提供有关输入文件和输出文件的信息。我不确定是否需要在此处描述该问题。 - flamelite
@MarkSetchell 您的方法似乎是正确的,也可以使用 ffmpeg-python 模块来实现,就像 iDilip 回答的那样。 - flamelite
@flamelite 这正是我在寻找的,但总有其他问题需要回答。 - llogan
1个回答

3

OpenCV使用FFMPEG。使用ffmpeg-python尝试以下代码将每个帧的numpy数组保存为视频,并比较文件大小。

import numpy as np
import cv2
import ffmpeg

def save_video(cap,saving_file_name,fps=33.0):

    if cap.isOpened():
        ret, frame = cap.read()
        if ret:
            i_width,i_height = frame.shape[1],frame.shape[0]

    process = (
    ffmpeg
        .input('pipe:',format='rawvideo', pix_fmt='rgb24',s='{}x{}'.format(i_width,i_height))
        .output(saved_video_file_name,pix_fmt='yuv420p',vcodec='libx264',r=fps,crf=37)
        .overwrite_output()
        .run_async(pipe_stdin=True)
    )

    return process

if __name__=='__main__':

    cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
    cap.set(3,1920)
    cap.set(4,1080)
    saved_video_file_name = 'output.avi'
    process = save_video(cap,saved_video_file_name)

    while(cap.isOpened()):
        ret, frame = cap.read()
        if ret==True:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Convert frame/img to gray
            frame = cv2.flip(frame,0)
            process.stdin.write(
                cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    .astype(np.uint8)
                    .tobytes()
                    )

            cv2.imshow('frame',frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                process.stdin.close()
                process.wait()
                cap.release()
                cv2.destroyAllWindows()
                break
        else:
            process.stdin.close()
            process.wait()
            cap.release()
            cv2.destroyAllWindows()
            break

我在上述方法的基础上进行了一些更改,包括ffmpeg视频文件写入器初始化和灰度文件转换。这似乎减小了文件大小,但是ffmpeg正在消耗大量CPU资源。 - flamelite
经过快速的研究,我发现这个链接 https://dev59.com/Z1gQ5IYBdhLWcg3wvGYY#42301503 提到 ffmpeg 在写入时重新编码帧,可以使用“-c:v copy -c:a copy”参数禁用。我不确定如何将这个 ffmpeg 命令行参数与 ffmpeg-python 模块集成。 - flamelite
@flamelite,也许你可以尝试将pix_fmt=gray而不是rgb24进行设置,并将灰度发送到ffmpeg,这应该允许你删除-vf hue=s=0,这应该会降低CPU负载。 - Mark Setchell
1
@iDilip,我撤回之前的建议并点赞你改进后的答案。 - Mark Setchell
感谢@iDilip的回答。它对我有用,但我仍然在寻找优化ffmpeg帧写入函数的方法,因为它占用了我设备大部分的CPU。 - flamelite
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接