如何使用OpenCV和Python对视频进行逐帧图像处理,在视频流中实现。

59

我是OpenCV的初学者,想对上传到我的服务器的视频帧进行一些图像处理。我只想读取可用的帧并将它们写入目录中。然后,等待视频的另一部分上传完成并将帧写入目录。并且,我应该等待每一帧完全上传后再将其写入文件。

您能告诉我如何使用OpenCV(Python)实现吗?

编辑1: 我编写了此代码以从文件中捕获视频,同时新数据被附加到文件末尾。换句话说,out.mp4 文件不是完整的视频,另一个程序正在其上写入新帧。我要做的是等待另一个程序写入新帧,然后读取并显示它们。

这是我的代码:

import cv2
cap = cv2.VideoCapture("./out.mp4")

while True:
    if cap.grab():
        flag, frame = cap.retrieve()
        if not flag:
            continue
        else:
            cv2.imshow('video', frame)
    if cv2.waitKey(10) == 27:
        break

所以问题在于cap.grab()调用!当没有帧时,它会返回False!即使我等很长时间,它也不会捕获帧。


请添加更详细的描述。在上传期间还是之后对帧进行一些图像处理?“可用帧”在哪里以及如何使其可用?“等待视频的其他部分”,所以只需追加吗?... - Robert Caspary
@RobertCaspary 感谢您的评论。 “对帧进行一些图像处理”:假设我想将该帧写入目录中的文件中。 “可用帧”:假设视频有100帧,但只上传了其中的40帧,因此可用帧是前40帧。到目前为止,我已经将它们保存到特定目录中的文件中。 “等待视频的其他部分”:等待第41帧上传,然后将其保存到另一个文件中,然后是第42帧,以此类推。 清楚吗? - Mehran
7个回答

64
在阅读了VideoCapture的文档后,我发现可以在下一次调用VideoCapture.read()(或VideoCapture.grab())时,告诉VideoCapture处理哪一帧。
问题是当你想要读取一个还没有准备好的帧时,VideoCapture对象会卡在该帧上并且永远无法继续。因此,你必须强制它从上一帧重新开始。
这是代码:
import cv2

cap = cv2.VideoCapture("./out.mp4")
while not cap.isOpened():
    cap = cv2.VideoCapture("./out.mp4")
    cv2.waitKey(1000)
    print "Wait for the header"

pos_frame = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
while True:
    flag, frame = cap.read()
    if flag:
        # The frame is ready and already captured
        cv2.imshow('video', frame)
        pos_frame = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
        print str(pos_frame)+" frames"
    else:
        # The next frame is not ready, so we try to read it again
        cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, pos_frame-1)
        print "frame is not ready"
        # It is better to wait for a while for the next frame to be ready
        cv2.waitKey(1000)

    if cv2.waitKey(10) == 27:
        break
    if cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) == cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT):
        # If the number of captured frames is equal to the total number of frames,
        # we stop
        break

这个可以工作,但是在某些随机点上,帧停止了,尽管输入流继续。这里仍然有一些东西被挂起了。 - Gulzar
6
从OpenCV 3.0开始,将 cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) 改为 cap.get(cv2.CAP_PROP_POS_FRAMES) - Rohan Pillai

27

使用这个:

import cv2
cap = cv2.VideoCapture('path to video file')
count = 0
while cap.isOpened():
    ret,frame = cap.read()
    cv2.imshow('window-name', frame)
    cv2.imwrite("frame%d.jpg" % count, frame)
    count = count + 1
    if cv2.waitKey(10) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows() # destroy all opened windows

1
我认为你最后一行应该是:cv2.destroyAllWindows() - Captain Fantastic
如果没有返回值: 继续执行 - Bahae El Hmimdi

10
根据OpenCV 3.0及更高版本的最新更新,您需要按照以下方式在Mehran代码中更改属性标识符
cv2.cv.CV_CAP_PROP_POS_FRAMES

为了

cv2.CAP_PROP_POS_FRAMES

同样适用于 cv2.CAP_PROP_POS_FRAME_COUNT

希望能对你有所帮助。


这并没有回答问题。 - JohnAllen
包括附加进一步信息的链接将是非常感激的。 - Richard

7
在OpenCV的文档中,有一个获取逐帧视频的示例。它是用C++编写的,但很容易将示例移植到Python - 您可以搜索每个函数文档以查看如何在Python中调用它们。
#include "opencv2/opencv.hpp"

using namespace cv;

int main(int, char**)
{
    VideoCapture cap(0); // open the default camera
    if(!cap.isOpened())  // check if we succeeded
        return -1;

    Mat edges;
    namedWindow("edges",1);
    for(;;)
    {
        Mat frame;
        cap >> frame; // get a new frame from camera
        cvtColor(frame, edges, CV_BGR2GRAY);
        GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
        Canny(edges, edges, 0, 30, 3);
        imshow("edges", edges);
        if(waitKey(30) >= 0) break;
    }
    // the camera will be deinitialized automatically in VideoCapture destructor
    return 0;
}

非常感谢您的回答。 我用Python做了这个。但问题是,当您调用cap>>frame;时, 它不会等待帧完全准备好!因此,如果帧没有准备好,它将返回null。之后,它就不会捕获任何其他帧了。这有意义吗? - Mehran
5
Mehran,你能否请友善地分享Python版本? - user2239318

2

我找到的唯一解决方案不是将索引设置为以前的帧并等待(然后OpenCV停止读取帧),而是再次初始化捕获。因此,代码看起来像这样:

cap = cv2.VideoCapture(camera_url)
while True:
    ret, frame = cap.read()

    if not ret:
        cap = cv.VideoCapture(camera_url)
        continue

    # do your processing here

它运行得非常完美!


不错的解决方案。但是,重新初始化 Capture 的开销是多少?我们需要确保销毁对象和创建对象不会降低性能。 - Mehran
它不会降低性能 - 只会掉几帧。没有卡顿或类似的问题,所以这对我们很有效。 - Alexander Lysenko
这个解决方案适用于流(在本例中为相机)始终从当前帧开始的情况。在我的情况下,文件正在上传到服务器并保存到out.mp4。因此,如果重新初始化视频捕获,它将从视频的第一帧而不是最后可用帧开始。 - Mehran

2
这是我开始解决此问题的方法:
  1. Create a video writer:

    import cv2.cv as cv
    videowriter = cv.CreateVideoWriter( filename, fourcc, fps, frameSize)
    

    Check here for valid parameters

  2. Loop to retrieve[1] and write the frames:

    cv.WriteFrame( videowriter, frame )
    

    WriteFrame doc

[1] zenpoy已经指出了正确的方向。您只需要知道可以从网络摄像头或文件中检索图像 :-)

希望我理解要求是正确的。


0
这是一个例子,其中a.mp4是视频文件。
import cv2
cap = cv2.VideoCapture('a.mp4')
count = 0
while cap.isOpened():
    ret,frame = cap.read()
    if not ret:
        continue
    cv2.imshow('window-name', frame)
    #cv2.imwrite("frame%d.jpg" % count, frame)
    count = count + 1
    if cv2.waitKey(10) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

你的计数器在这里做什么? - Batselot
计算已处理的帧数。 - Bahae El Hmimdi
哦,好的,你没有使用它们(打印或返回它们),所以我想也许你在对计数做其他的事情。 - Batselot

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