如何使用 Opencv 从一个 rtsp Url 捕获一个视频?

问题描述

服务器正在使用相同的 RTSP URL(rtsp://192.168.0.2:8554/) 逐个发送视频 我可以使用 opencv 捕获和显示视频。

import numpy as np
import cv2 as cv
os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "rtsp_transport;udp"

cap = cv.VideoCapture('rtsp://192.168.0.2:8554/')
while cap.isOpened():
    ret,frame = cap.read()
    # if frame is read correctly ret is True
    if not ret:
        print("Can't receive frame (stream end?). Exiting ...")
        break
    cv.imshow('frame',frame)
    if cv.waitKey(1) == ord('q'):
        break
cap.release()
cv.destroyAllWindows()

该程序在继续播放下一个视频时返回错误。 我试过了,但没有用。

import cv2 as cv
import os
import time

os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "rtsp_transport;udp"

cap = cv.VideoCapture('rtsp://192.168.0.26:8554/')
if not cap.isOpened():
    print("Cannot open camera")
    exit()
while True:
    try:
        time.sleep(2)
        # Capture frame-by-frame
        ret,frame = cap.read()
        # if frame is read correctly ret is True
        # Our operations on the frame come here
        # display the resulting frame
        cv.imshow('frame',frame)
        if cv.waitKey(1) == ord('q'):
            break
    except:
        print("Exception!!")
        # When everything done,release the capture
        cap.release()
        cv.destroyAllWindows()

我能得到一些帮助吗? 提前致谢!

解决方法

我通过使用多线程程序解决了这个问题。

主文件

from datasets import LoadStreams

import threading
import os
import logging
import cv2
import torch
import time
logger = logging.getLogger(__name__)


def select_device(device='',batch_size=None):
    # device = 'cpu' or '0' or '0,1,2,3'
    cpu_request = device.lower() == 'cpu'
    if device and not cpu_request:  # if device requested other than 'cpu'
        os.environ['CUDA_VISIBLE_DEVICES'] = device  # set environment variable
        assert torch.cuda.is_available(),f'CUDA unavailable,invalid device {device} requested'  # check availablity

    cuda = False if cpu_request else torch.cuda.is_available()
    if cuda:
        c = 1024 ** 2  # bytes to MB
        ng = torch.cuda.device_count()
        if ng > 1 and batch_size:  # check that batch_size is compatible with device_count
            assert batch_size % ng == 0,f'batch-size {batch_size} not multiple of GPU count {ng}'
        x = [torch.cuda.get_device_properties(i) for i in range(ng)]
        s = f'Using torch {torch.__version__} '
        for i,d in enumerate((device or '0').split(',')):
            if i == 1:
                s = ' ' * len(s)
            logger.info(f"{s}CUDA:{d} ({x[i].name},{x[i].total_memory / c}MB)")
    else:
        logger.info(f'Using torch {torch.__version__} CPU')

    logger.info('')  # skip a line
    return torch.device('cuda:0' if cuda else 'cpu')


def detect(rtsp_url):
    dataset = LoadStreams(rtsp_url)
    device = select_device('')
    count = 0
    view_img = True
    # img = torch.zeros((1,3,imgsz,imgsz),device=device)  # init img

    try:
        for frame_idx,(path,img,im0s,vid_cap) in enumerate(dataset):  # for every frame
            count += 1
            im0 = im0s[0].copy()
            if view_img:
                cv2.imshow(str(path),im0)
                # if cv2.waitKey(1) == ord('q'):  # q to quit
                #     raise StopIteration
    except:
        print("finish execption")
        dataset.stop()
    return "good"

if __name__ == '__main__':
    rtsp_url = "rtsp://192.168.0.26:8554/"
    while True:
        for thread in threading.enumerate():
            print(thread.name)
        print(detect(rtsp_url))

数据集类文件

import glob
import logging
import math
import os
import random
import shutil
import time
import re
from itertools import repeat
from multiprocessing.pool import ThreadPool
from pathlib import Path
from threading import Thread

import cv2
import numpy as np
import torch

class LoadStreams:  # multiple IP or RTSP cameras
    def __init__(self,sources='streams.txt',img_size=640):
        self.mode = 'stream'
        self.img_size = img_size
        self.capture = None
        self.my_thread = None
        self.stopFlag = False

        if os.path.isfile(sources):
            with open(sources,'r') as f:
                sources = [x.strip() for x in f.read().strip().splitlines() if len(x.strip())]
        else:
            sources = [sources]

        n = len(sources)
        self.imgs = [None] * n
        self.sources = [clean_str(x) for x in sources]  # clean source names for later
        s = sources[0]
        # for i,s in enumerate(sources):
        # Start the thread to read frames from the video stream
        # print('%g/%g: %s... ' % (i + 1,n,s),end='')
        cap = cv2.VideoCapture(eval(s) if s.isnumeric() else s)
        assert cap.isOpened(),'Failed to open %s' % s
        w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        fps = cap.get(cv2.CAP_PROP_FPS) % 100
        self.ret,self.imgs[0] = cap.read()  # guarantee first frame
        thread = Thread(target=self.update,args=([0,cap]),daemon=True)
        print(' success (%gx%g at %.2f FPS).' % (w,h,fps))
        thread.start()
        self.capture = cap
        self.my_thread = thread
        print('')  # newline

        # check for common shapes
        s = np.stack([letterbox(x,new_shape=self.img_size)[0].shape for x in self.imgs],0)  # inference shapes
        self.rect = np.unique(s,axis=0).shape[0] == 1  # rect inference if all shapes equal
        if not self.rect:
            print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.')

    def update(self,index,cap):
        # Read next stream frame in a daemon thread
        n = 0
        while cap.isOpened() and not self.stopFlag:
            n += 1
            # _,self.imgs[index] = cap.read()
            cap.grab()
            if n == 4:  # read every 4th frame
                _,self.imgs[index] = cap.retrieve()
                n = 0
            time.sleep(0.01)  # wait time

    def stop(self):
        self.stopFlag = True
        try:
            # self.capture.release()
            # self.my_thrsead.join()
            print("stop thread!!")
        except:
            print("ERROR stopping thread!!")

    def __iter__(self):
        self.count = -1
        return self

    def __next__(self):
        self.count += 1
        img0 = self.imgs.copy()
        if cv2.waitKey(1) == ord('q'):  # q to quit
            cv2.destroyAllWindows()
            raise StopIteration

        if not self.ret:
            print("error!!!")
            self.stop()

        # Letterbox
        img = [letterbox(x,new_shape=self.img_size,auto=self.rect)[0] for x in img0]

        # Stack
        img = np.stack(img,0)

        # Convert
        img = img[:,:,::-1].transpose(0,2)  # BGR to RGB,to bsx3x416x416
        img = np.ascontiguousarray(img)

        return self.sources,img0,None

    def __len__(self):
        return 0  # 1E12 frames = 32 streams at 30 FPS for 30 years

    # def stop(self):



def clean_str(s):
    # Cleans a string by replacing special characters with underscore _
    return re.sub(pattern="[|@#!¡·$€%&()=?¿^*;:,¨´><+]",repl="_",string=s)


def letterbox(img,new_shape=(640,640),color=(114,114,114),auto=True,scaleFill=False,scaleup=True):
    # Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232
    shape = img.shape[:2]  # current shape [height,width]
    if isinstance(new_shape,int):
        new_shape = (new_shape,new_shape)

    # Scale ratio (new / old)
    r = min(new_shape[0] / shape[0],new_shape[1] / shape[1])
    if not scaleup:  # only scale down,do not scale up (for better test mAP)
        r = min(r,1.0)

    # Compute padding
    ratio = r,r  # width,height ratios
    new_unpad = int(round(shape[1] * r)),int(round(shape[0] * r))
    dw,dh = new_shape[1] - new_unpad[0],new_shape[0] - new_unpad[1]  # wh padding
    if auto:  # minimum rectangle
        dw,dh = np.mod(dw,32),np.mod(dh,32)  # wh padding
    elif scaleFill:  # stretch
        dw,dh = 0.0,0.0
        new_unpad = (new_shape[1],new_shape[0])
        ratio = new_shape[1] / shape[1],new_shape[0] / shape[0]  # width,height ratios

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    if shape[::-1] != new_unpad:  # resize
        img = cv2.resize(img,new_unpad,interpolation=cv2.INTER_LINEAR)
    top,bottom = int(round(dh - 0.1)),int(round(dh + 0.1))
    left,right = int(round(dw - 0.1)),int(round(dw + 0.1))
    img = cv2.copyMakeBorder(img,top,bottom,left,right,cv2.BORDER_CONSTANT,value=color)  # add border
    return img,ratio,(dw,dh)
        while cap.isOpened() and not self.stopFlag:

这一行特别重要,因为 如果没有这一行,线程将被堆叠并出现内存错误 随着堆栈的堆积。