是否可以逐步读取视频中的帧(例如,我想要读取视频流的每五帧)。目前我这样做是一种解决方法,但它不是很有效。
bool bSuccess
int FramesSkipped = 5;
for (int a = 0; < FramesSkipped; a++)
bSuccess = cap.read(NextFrameBGR);
任何建议,所以我不必遍历五个帧来获得所需的帧?
答案 0 :(得分:14)
我担心你能做的并不多,而且这不仅仅是OpenCV的缺点。你看,现代视频编解码器通常是复杂的野兽。为了获得更高的压缩率,帧的编码通常取决于先前的帧,有时甚至是连续的帧。
所以,大多数时候你必须在所需的帧之前解码帧,即使你不需要它们。
对视频文件进行专门编码有一些非常重要的技巧,因此获取每个第N帧会很便宜,但在一般情况下它是不可行的。
也就是说,您可以尝试OpenCV提供的搜索功能(参见OpenCV Seek Function/Rewind)。它可能(也可能不会)根据具体情况更快地工作。但是,就个人而言,我不会赌它。
答案 1 :(得分:4)
我让它在Python中工作...请参阅下面的两个示例用例和一些注意事项。
import cv2
import math
import numpy as np
#################### Setting up the file ################
videoFile = "Jumanji.mp4"
vidcap = cv2.VideoCapture(videoFile)
success,image = vidcap.read()
#################### Setting up parameters ################
seconds = 5
fps = vidcap.get(cv2.CAP_PROP_FPS) # Gets the frames per second
multiplier = fps * seconds
#################### Initiate Process ################
while success:
frameId = int(round(vidcap.get(1))) #current frame number, rounded b/c sometimes you get frame intervals which aren't integers...this adds a little imprecision but is likely good enough
success, image = vidcap.read()
if frameId % multiplier == 0:
cv2.imwrite("FolderSeconds/frame%d.jpg" % frameId, image)
vidcap.release()
print "Complete"
#################### Setting up the file ################
videoFile = "Jumanji.mp4"
vidcap = cv2.VideoCapture(videoFile)
success,image = vidcap.read()
#################### Setting up parameters ################
#OpenCV is notorious for not being able to good to
# predict how many frames are in a video. The point here is just to
# populate the "desired_frames" list for all the individual frames
# you'd like to capture.
fps = vidcap.get(cv2.CAP_PROP_FPS)
est_video_length_minutes = 3 # Round up if not sure.
est_tot_frames = est_video_length_minutes * 60 * fps # Sets an upper bound # of frames in video clip
n = 5 # Desired interval of frames to include
desired_frames = n * np.arange(est_tot_frames)
#################### Initiate Process ################
for i in desired_frames:
vidcap.set(1,i-1)
success,image = vidcap.read(1) # image is an array of array of [R,G,B] values
frameId = vidcap.get(1) # The 0th frame is often a throw-away
cv2.imwrite("FolderFrames/frame%d.jpg" % frameId, image)
vidcap.release()
print "Complete"
几乎就是这样。
<小时/> 一些不幸的警告......取决于你的opencv版本(这是为opencv V3构建的),你可能需要以不同的方式设置fps变量。有关详细信息,请参阅here。要查找您的版本,您可以执行以下操作:
(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
major_ver
答案 2 :(得分:2)
我在Python 3中使用简单的计数器并将捕获设置为该计数器的框架,从而获得了成功,如下所示:
import cv2
cap = cv2.VideoCapture('XYZ.avi')
count = 0
while cap.isOpened():
ret, frame = cap.read()
if ret:
cv2.imwrite('frame{:d}.jpg'.format(count), frame)
count += 30 # i.e. at 30 fps, this advances one second
cap.set(1, count)
else:
cap.release()
break
我试图找到一种方法,使用with
语句使它变得更加Python化,但是我不相信CV2库已经为此进行了更新。
答案 3 :(得分:1)
以下是我的建议:
CvCapture* capture = cvCaptureFromFile("input_video_path");
int loop = 0;
IplImage* frame = NULL;
Mat matframe;
char fname[20];
do {
frame = cvQueryFrame(capture);
matframe = cv::cvarrToMat(frame);
cvNamedWindow("video_frame", CV_WINDOW_AUTOSIZE);
cvShowImage("video_frame", frame);
sprintf(fname, "frame%d.jpg", loop);
cv::imwrite(fname, matframe);//save each frame locally
loop++;
cvWaitKey(100);
} while( frame != NULL );
现在你已经在本地保存了所有帧,你可以快速阅读你想要的第n帧 CATUION :我拥有的12秒样本视频由> 200张图片组成。这将占用大量空间。
一个简单但有效的优化是使用您正在使用的方法或@sergie建议的方法读取第n帧。在此之后,您可以使用其索引保存图像,以便稍后在相同索引处查询将返回保存的图像,而不必像您一样跳过帧。通过这种方式,您可以节省您在保存框架时浪费的空间,这些框架您无法查询并且需要花时间阅读和阅读。保存那些不需要的帧也是如此。
答案 4 :(得分:1)
如果有人需要根据Ishan Shah的代码捕获每5帧并将其另存为jpg:
def closest_point_on_path(point:QPointF, path:QPainterPath) -> QPointF:
if path.isEmpty():
return point
vec = QVector2D(point)
poly = path.toFillPolygon()
minDist = sys.float_info.max
for k in range(poly.count()):
p = QVector2D(poly.at(k))
if k == poly.count() - 1:
k = -1
q = QVector2D(poly.at(k+1))
v = q - p
u = v.normalized()
d = QVector2D.dotProduct(u, vec - p)
if d < 0.0:
d = (vec - p).lengthSquared()
if d < minDist:
minDist = d
minVec = p
elif d*d > v.lengthSquared():
d = (vec - q).lengthSquared()
if d < minDist:
minDist = d
minVec = q
else:
u *= d
u += p
d = (vec - u).lengthSquared()
if d < minDist:
minDist = d
minVec = u
if minDist >= sys.float_info.max:
return point
return minVec.toPointF()
答案 5 :(得分:0)
当我为OpenCV设定了相同的目标时,我只是在调整每秒视频所需的“关键帧”数量,而不考虑帧速率或帧总数。因此,根据我的目标KPS,这使我获得了第N个密钥。
# python3.6 code using OpenCV 3.4.2
import cv2
KPS = 5 # Target Keyframes Per Second
VIDEO_PATH = "path/to/video/folder" # Change this
IMAGE_PATH = "path/to/image/folder" # ...and this
EXTENSION = ".png"
cap = cv2.VideoCapture(VIDEO_PATH)
# Set frames-per-second for capture
fps = round(cap.get(cv2.CAP_PROP_FPS))
hop = round(fps / KPS)
curr_frame = 0
while(True):
ret, frame = cap.read()
if not ret: break
if curr_frame % hop == 0:
print('Creating... {0}'.format(name,))
name = IMAGE_PATH + "_" + str(curr_frame) + EXTENSION
cv2.imwrite(name, frame)
curr_frame += 1
cap.release()
请注意,我正在浏览所有帧,但仅使用hop
作为N来写入第N个帧。
答案 6 :(得分:0)
我遇到了同样的问题。 我所做的就是这个:
import cv2
vs = cv2.VideoCapture("<path of your video>.mp4")
print("Showing frames...")
c=1
while True:
grabbed, frame = vs.read()
if c%5==0:
cv2.imshow('Frame',frame)
cv2.waitKey(1)
c+=1
vs.release()
希望这会有所帮助。
答案 7 :(得分:0)
我使用this回购!
主要思想是:
main.py
from camera import VideoCam
SKIPFRAME = 8
url = 0
v1 = VideoCam(url)
v1.check_camera(v1.cap)
ct = 0
while True:
ct += 1
try:
ret = v1.cap.grab()
if ct % SKIPFRAME == 0: # skip some frames
ret, frame = v1.get_frame()
if not ret:
v1.restart_capture(v1.cap)
v1.check_camera(v1.cap)
continue
# frame HERE
v1.show_frame(frame, 'frame')
except KeyboardInterrupt:
v1.close_cam()
exit(0)
camera.py
import cv2
import logging
class VideoCam():
def __init__(self, url=0):
self.url = url
self.cap = cv2.VideoCapture(self.url)
self.get_frame()
self.get_frame_read()
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
def check_camera(self, cap):
logging.info('Camera {} status: {}'.format(self.url, cap.isOpened()))
def show_frame(self, frame, name_fr='NAME'):
cv2.imshow(name_fr, frame)
# cv2.imshow(name_fr, cv2.resize(frame, (0, 0), fx=0.4, fy=0.4))
cv2.waitKey(1)
def get_frame(self):
return self.cap.retrieve()
def get_frame_read(self):
return self.cap.read()
def close_cam(self):
self.cap.release()
cv2.destroyAllWindows()
def restart_capture(self, cap):
cap.release()
self.cap = cv2.VideoCapture(self.url)
答案 8 :(得分:0)
您应该使用grab
功能移至下一帧。并且仅使用retrieve
来解码所需的帧。
bool bSuccess
int FramesSkipped = 5;
for (int a = 0; < FramesSkipped; a++)
bSuccess = cap.grab();
bSuccess = cap.retrieve(NextFrameBGR);
答案 9 :(得分:-1)
由于编码方案通常非常复杂,因此无法提取随机帧。例如,在MPEG-4中,仅存储包含两个帧之间的差异的信息,因此显然需要先前的帧。