我有视频文件,我可以将每帧的
cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
转换为RGB空间。
然后我想计算红色和蓝色颜色值的平均值和标准偏差,在 100 秒的时间内进行平均和标准偏差。我的视频长度 >100 秒,因此希望每 100 秒重复一次此操作并将值分配给向量。 因此,对于每第 n 个 100 秒的剪辑,我都有值 R(ave)、R(sd)、B(ave)、B(sd)。
我对简历和视频处理相当陌生,所以希望能在这方面的任何部分提供帮助。
我终于写了。整个程序将在长度超过 1 分钟的视频上运行足够长的时间。如果你的电脑很弱,那我也不羡慕你。但一般来说它运作良好。这是:
import cv2
def calc_sd(arr: list, mean_val: float):
prev_dis = 0
for k in arr:
prev_dis += (k - mean_val) ** 2
dis = prev_dis / len(arr)
return dis ** (1 / 2)
def calc_mean(arr: list):
return sum(arr) / len(arr)
# list of your videos here
lst_of_videos = ['vid_test.mkv', 'signs.mkv', 'signs_ml.mkv']
lst_of_all_videos_data = []
for i in lst_of_videos:
cap = cv2.VideoCapture(i)
# list for data every 100 sec
# data there will be like:
# [['mean_red', 'mean_green', 'mean_blue', 'sd_red', 'sd_green', 'sd_blue'], 'and every 100 sec like this']
lst_of_data = []
lst_of_red = []
lst_of_green = []
lst_of_blue = []
# getting video fps
fps = cap.get(cv2.CAP_PROP_FPS)
abstract_seconds = 0 # for counting frames
print('video: ', i)
while True:
ret, frame = cap.read()
if abstract_seconds >= 100 or not ret:
print(' video: ', i, ', 100 secs, ret: ', ret)
mean_red = calc_mean(lst_of_red)
mean_green = calc_mean(lst_of_green)
mean_blue = calc_mean(lst_of_blue)
print(' mean counted')
sd_red = calc_sd(lst_of_red, mean_red)
sd_green = calc_sd(lst_of_green, mean_green)
sd_blue = calc_sd(lst_of_blue, mean_blue)
print(' sd counted')
lst_of_data.append([mean_red, mean_green, mean_blue, sd_red, sd_green, sd_blue])
lst_of_red.clear()
lst_of_green.clear()
lst_of_blue.clear()
print(' arrays cleared')
if not ret:
break
b, g, r = cv2.split(frame)
lst_of_red.append(r.sum(axis=0).sum(axis=0) / r.size)
lst_of_green.append(g.sum(axis=0).sum(axis=0) / g.size)
lst_of_blue.append(b.sum(axis=0).sum(axis=0) / b.size)
abstract_seconds += 1 / fps
print(lst_of_data)
lst_of_all_videos_data.append(lst_of_data)
lst_of_data.clear()
我将使用类似以下代码的内容来计算 1 小时视频的全局统计数据。
我直接在stackoverflow框中修改了它以满足您的“时间窗口”要求,因此可能会有一些小错误。 我放弃了最后一段,因为你说你想要 100 秒的窗口,但如果你真的需要它,你可以轻松调整它。
我保留了 BGR 格式,因为它和其他格式一样好。
我比其他答案更喜欢这个选项,因为你没有保留很多列表,并且平均值和标准计算是一次性完成 3 个通道的。
还有一个重要的点就是开放视频的发布。
from docopt import docopt
from contextlib import contextmanager
import cv2
import numpy as np
import sys
@contextmanager
def VideoCapture(input_video):
# findFileOrKeep allows more searching paths
capture = cv2.VideoCapture(cv2.samples.findFileOrKeep(input_video))
if not capture.isOpened():
print('Unable to open: ' + input_video, file=sys.stderr)
exit(0)
try:
yield capture
finally:
# Release the video capture object at the end
capture.release()
DOCTEXT = f"""
Usage:
name_of_the_script.py <video_path>... [--windows=<w>]
Options:
--windows=<w> Windows seconds [default: 100]
"""
if __name__ == '__main__':
args = docopt(DOCTEXT, argv=sys.argv[1:], help=True, version=None, options_first=False)
video_pathes = args['<video_path>']
windows = float(args['--windows'])
output = []
for i_video, video_path in enumerate(video_pathes):
with VideoCapture(video_path) as cap:
fps = cap.get(cv2.CAP_PROP_FPS)
i = 0
psum = np.array([0.0, 0.0, 0.0]) # BGR
psum_sq = np.array([0.0, 0.0, 0.0]) # BGR
num_px = windows * fps * int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) * cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
while True:
ret, frame = cap.read()
if not ret: # frame is None
break
fr += 1
if fr % int(windows * fps) == 0:
print(f'Processed {fr / fps}s\tVideo {i_video} / len(video_pathes)\t{video_path}')
total_mean = psum / num_px
total_var = (psum_sq / num_px) - (total_mean ** 2)
total_std = np.sqrt(total_var)
output.append([total_mean, total_std])
psum = np.array([0.0, 0.0, 0.0]) # BGR
psum_sq = np.array([0.0, 0.0, 0.0]) # BGR
psum = frame.sum(axis=(0, 1))
psum_sq = (frame ** 2).sum(axis=(0, 1))
print(f'[mean (BGR), std (BGR)]:\n{output}') # NOTE: or use it as you want. You should make it as a function if you need it to do another thing.