2020-02-28 17:25:37 Singular__point 阅读数 76

一般的海康摄像头是25fps,opencv在while循环中取流播放的话,完全没有问题。但是,如果加入一些目标检测等等图像操作会增加处理时间,由于硬件水平的限制,可能就达不到25fps了,这样就会堆积一部分帧,导致了延迟和花屏的现象。

解决办法:我们自己定义一个缓冲区(用vector模拟),由连个线程取维护它。read线程每次从缓冲区中取最新的帧,write线程每次取流将帧压入缓冲区。但是缓冲区数据入的速度大于出的速度,这样就需要清一下,清的时候不能全清也不能不清,隔帧抽取清理,保证画面的顺序。上代码 :

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <thread>
#include <queue>
#include <chrono>
#include <functional>
#include <X11/Xlib.h>

using namespace cv;
using namespace std;
using namespace std::chrono; // calc fps

double fps()
{
    static double fps = 0.0;
    static int frameCount = 0;
    static auto lastTime = system_clock::now();
    static auto curTime = system_clock::now();

    curTime = system_clock::now();

    auto duration = duration_cast<microseconds>(curTime - lastTime);
    double duration_s = double(duration.count()) * microseconds::period::num / microseconds::period::den;

    if (duration_s > 2)//2秒之后开始统计FPS
    {
        fps = frameCount / duration_s;
        frameCount = 0;
        lastTime = curTime;
    }
    ++frameCount;
    return fps;
}

void frame_write(vector<Mat>& frame_buffer){
    cout << "this is write" << endl;
    Mat input, blob;
    VideoCapture capture;
    capture.open("rtsp://admin:yfzx2019@192.168.10.6/h264/main/ch1/av_stream");
    if(capture.isOpened())
    {
        cout << "Capture is opened" << endl;
        for(;;)
        {
            capture >> input;
            if(input.empty())
                continue;
            if(frame_buffer.size() < 100)
                frame_buffer.push_back(input);
            else{
                cout << "thread ==============> after read stop, frame_buffer.size() > 100 , write stop";
                return;
            }
        }
    } else{
        cout << "open camera failed" << endl;
    }
}
void frame_read(vector<Mat>& frame_buffer){
    cout << "this is read" << endl;
    Mat frame;
    while(1){
        if (!frame_buffer.empty()){
            frame = frame_buffer.front();
            // 在这里加上目标检测算法
            /*
             *
             */
            if (frame_buffer.size() > 10){ // 隔帧抽取一半删除
                auto iter = frame_buffer.begin();
                for(int inde = 0; inde < frame_buffer.size()/2 ; inde++)
                    frame_buffer.erase(iter++);
            }
            cout << "FPS:" << fps() << endl;
            imshow("Thread Sample", frame);
            if(waitKey(10) == 113) // ’q‘ ASCII == 113
                break;
        }
    }
    cout << "thread ==============> read stop" <<endl;
}

int main(int argc, char** argv)
{
    vector<Mat> frame_buffer;
    XInitThreads();
    std::thread tw(frame_write,ref(frame_buffer)); // pass by value
    std::thread tr(frame_read, ref(frame_buffer)); // pass by value

    tw.join();
    tr.join();

    return 0;
}

 

2019-10-03 17:06:59 javastart 阅读数 141

 

opencv处理视频速度跟不上视频送过来的速度,会导致资源积压!如何设置一个缓冲区,在一定时间里只取最新的资源,剩下资源的清空?

在使用opencv处理视频的过程中,通常我们会读取视频帧,读取出来的视频帧就相当于一幅图像,我们只要读取到了图像就可以对图像进行各种各样的操作。

例如,行人检测,汽车检测这些算法,目前非常火的目标识别算法就是yolo3,无论是在速度上还是精度上,yolo3都要比他的前辈,RCNN,FASTRCNN,YOLOv1要好很多。

但是就算是这样的背景下,我们在实际运用yolo的过程中,也是会遇到这样那样的问题,例如尽管网上对于yolo的速度大加赞赏,但是我在使用过程中,用cpu进行一次80类别数据集的检测却要耗费0.6秒,普通的视频fps大约25-30,对于每帧0.6秒的处理速度来说是远远不够的,那么性能好的gpu呢,在使用gpu的情况下,一次检测也需要将近0.08秒,接近0.1秒,在将算法应用到视频中时,会发现明显的卡顿,但是卡顿都是可以接受的,令人无法接受的是,opencv自带的帧缓冲区,这个就是cap函数读取视频时存放帧的地方,在实测过程中发现,cap函数无法获取最新帧,而是按照cap缓冲区中顺序一帧一帧的读取,这导致了我算法的延时,会导致延时越来越严重,我们期望cap能够跳过算法的处理时间,直接读取当前帧而抛掉算法运行过程中的帧,但是事实显示,opencv并不是这样做的,在和同学的讨论中,一位同学告诉我,cap能够读取当前帧,但是我在实验过程中发现,事实并不是这样,在研究中发现,opencv的确会在一定时间清空帧缓冲区,但这个清空的时机并不是我们能够控制的,也就是说我没有发现一个api可以让我们手动清空帧缓冲区。

方法一:编写我们自己的帧缓冲区就很重要。

大致思路就是,创建一个自定义的帧缓冲区,开启一个线程使用cap函数读取视频帧,将读取到的视频帧存入我们自定义的缓冲区,这个缓冲区可以设计成固定大小,每次新增新的帧进入缓冲区,将挤掉旧的帧。代码可以参考:

class Stack:
 
    def __init__(self, stack_size):
        self.items = []
        self.stack_size = stack_size
 
    def is_empty(self):
        return len(self.items) == 0
 
    def pop(self):
        return self.items.pop()
 
    def peek(self):
        if not self.isEmpty():
            return self.items[len(self.items) - 1]
 
    def size(self):
        return len(self.items)
 
    def push(self, item):
        if self.size() >= self.stack_size:
            for i in range(self.size() - self.stack_size + 1):
                self.items.remove(self.items[0])
        self.items.append(item)
另外,开启一个线程,这里用主线程也可以,读取视频缓冲区中的帧并进行算法,然后显示。

def capture_thread(video_path, frame_buffer, lock):
    print("capture_thread start")
    vid = cv2.VideoCapture(video_path)
    if not vid.isOpened():
        raise IOError("Couldn't open webcam or video")
    while True:
        return_value, frame = vid.read()
        if return_value is not True:
            break
        lock.acquire()
        frame_buffer.push(frame)
        lock.release()
        cv2.waitKey(25)
def play_thread(frame_buffer, lock):
    print("detect_thread start")
    print("detect_thread frame_buffer size is", frame_buffer.size())
 
    while True:
        if frame_buffer.size() > 0:
            lock.acquire()
            frame = frame_buffer.pop()
            lock.release()
            # TODO 算法
            cv2.imshow("result", frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
主函数可以参考这里:

from thread import capture_thread, play_thread
from stack import Stack
import threading
 
 
path = ''
rtsp_url = ''
frame_buffer = Stack(3)
lock = threading.RLock()
t1 = threading.Thread(target=capture_thread, args=(path, frame_buffer, lock))
t1.start()
t2 = threading.Thread(target=play_thread, args=(frame_buffer, lock))
t2.start()
这样,我们就可以控制自己的帧缓冲区,并且每次处理耗时算法以后,能够获取到最新的一帧进行处理,虽然画面可以能会掉帧的样子,但是不会再有延时的情况。

后来又在github上,看到一个完整代码,网址如下:https://github.com/cxstdio/pycv4rtsp

方法二:采用网络摄像头的辅码或者调低摄像的分辨率和图像贞率

参考资料: https://www.jianshu.com/p/2b79012c0228 验证摄像头分辨率的方法

没有更多推荐了,返回首页