使用opencv-python读取多个(海康\大华)网络摄像头的视频流,解决实时读取延迟问题
在上一篇博客中,主要介紹了python之opencv按幀提取視頻中的圖片,但是,由于最近在做人臉識(shí)別的項(xiàng)目,用的是大華的監(jiān)控?cái)z像頭,我發(fā)現(xiàn)大華的攝像頭實(shí)時(shí)讀取延遲問題特別嚴(yán)重,尤其是主碼流,這個(gè)問題困擾了我好久,最終想到的方式就是自己實(shí)時(shí)推流,經(jīng)過實(shí)踐,終于解決了實(shí)時(shí)讀取延遲問題。
前言
同樣需要準(zhǔn)備對(duì)應(yīng)的python開發(fā)環(huán)境,具體參考上一篇python之opencv按幀提取視頻中的圖片,里面介紹了詳細(xì)的需要的庫(kù)文件。
好了,既然是自己實(shí)現(xiàn)實(shí)時(shí)預(yù)覽推流,那就要確定使用推流方式,我這里使用的是RTSP地址和格式實(shí)現(xiàn)推流的,下面是我歸納的各大監(jiān)控?cái)z像頭廠商的RTSP具體推流格式。
各大攝像頭廠商RTSP推流格式
rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
說明:
username: 用戶名。例如admin。
password: 密碼。例如123456。
ip: 為設(shè)備IP。例如 192.168.0.224。
port: 端口號(hào)默認(rèn)為554,若為默認(rèn)可不填寫。
codec:有h264、MPEG-4、mpeg4這幾種。
channel: 通道號(hào),起始為1。例如通道1,則為ch1。
subtype: 碼流類型,主碼流為main,子碼流為sub。
舉個(gè)栗子:
例如,請(qǐng)求海康攝像機(jī)通道1的主碼流,Url如下 rtsp://admin:123456@192.168.0.224:554/h264/ch1/main/av_stream rtsp://admin:123456@192.168.0.224:554/MPEG-4/ch1/main/av_stream rtsp://admin:123456@192.168.0.224:554/h264/ch33/main/av_stream //ipc例如,請(qǐng)求海康攝像機(jī)通道1的子碼流,Url如下: rtsp://admin:123456@192.168.0.224/mpeg4/ch1/sub/av_stream rtsp://admin:123456@192.168.0.224/h264/ch1/sub/av_stream【新版本】URL:rtsp://username:password@<address>:<port>/Streaming/Channels/<id>(?parm1=value1&parm2-=value2…)注:VLC可以支持解析URL里的用戶名密碼,實(shí)際發(fā)給設(shè)備的RTSP請(qǐng)求不支持帶用戶名密碼。 舉例:DS-9632N-ST的模擬通道01主碼流:rtsp://admin:123456@192.168.0.224:554/Streaming/Channels/101?transportmode=unicastDS-9016HF-ST的IP通道01主碼流:rtsp://admin:123456@192.168.0.224:554/Streaming/Channels/1701?transportmode=unicastDS-9016HF-ST的模擬通道01子碼流:rtsp://admin:123456@192.168.0.224:554/Streaming/Channels/102?transportmode=unicast (單播)rtsp://admin:123456@192.168.0.224:554/Streaming/Channels/102?transportmode=multicast (多播)rtsp://admin:123456@192.168.0.224:554/Streaming/Channels/102 (?后面可省略,默認(rèn)單播)注:前面老URL,NVR(>=64路的除外)的IP通道從33開始;新URL,通道號(hào)全部按順序從1開始。rtsp://username:password@ip:port/cam/realmonitor?channel=1&subtype=0
說明:
username: 用戶名,例如admin。
password: 密碼,例如admin。
ip: 為設(shè)備IP,例如192.168.0.224。
port: 端口號(hào)默認(rèn)為554,若為默認(rèn)可不填寫。
channel: 通道號(hào),起始為1;例如通道2,則為channel=2。
subtype: 碼流類型,主碼流為0(即subtype=0);子碼流為1(即subtype=1)。
舉個(gè)栗子:
例如,請(qǐng)求某設(shè)備的通道2的子碼流,Url如下 rtsp://admin:admin@192.168.0.224:554/cam/realmonitor?channel=2&subtype=1默認(rèn)IP地址:192.168.0.224
用戶名: admin
密碼空:123456
端口:TCP端口:34567 和 HTTP端口:80,onvif端口是8899
舉個(gè)栗子:
RTSP地址:rtsp://192.168.0.224 :554/user=admin&password=123456&channel=1&stream=0.sdp?real_stream
192.168.0.224 這個(gè)是被連接的設(shè)備的IP
554這個(gè)是RTSP服務(wù)的端口號(hào),可以在設(shè)備的網(wǎng)絡(luò)服務(wù)里面更改
user=admin這個(gè)是設(shè)備的登錄用戶名
password= 123456
channel=1 第一通道
stream=0.sdp?主碼流
stream=1.sdp?副碼流
默認(rèn)IP地址:192.168.0.224
用戶名admin
密碼123456
端口:http端口80 數(shù)據(jù)端口8091 RTSP端口554 ONVIF端口 80
舉個(gè)栗子:
RTSP地址(不需要密碼):
-
主碼流地址:rtsp://192.168.0.224 :554/mpeg4
-
子碼流地址:rtsp://192.168.0.224 :554/mpeg4cif
RTSP地址(需要密碼): -
主碼流 rtsp://admin:123456@192.168.0.224 :554/mpeg4
-
子碼流 rtsp://admin:123456@192.168.0.224 :554/mpeg4cif
默認(rèn)IP地址:DHCP 默認(rèn)(0.0.0.0)
用戶名admin 默認(rèn)
密碼 空
舉個(gè)栗子:
RTSP地址:rtsp://0.0.0.0:8554/live1.264(次碼流) rtsp://0.0.0.0:8554/live0.264 (主碼流)九安
RTSP地址:rtsp://IP:port(website port)/ch0_0.264(主碼流)
rtsp://IP:port(website port)/ch0_1.264(子碼流)
技威/YOOSEE
默認(rèn)IP地址:DHCP 用戶名admin 密碼123456
RTSP地址:主碼流:rtsp://IPadr:554/onvif1
次碼流:rtsp://IPadr:554/onvif2
onvif端口是5000
設(shè)備發(fā)現(xiàn)的端口是3702
V380
默認(rèn)IP地址:DHCP 用戶名admin 密碼空/admin
onvif端口8899
RTSP地址:主碼流rtsp://ip//live/ch00_1
子碼流rtsp://ip//live/ch00_0
宇視
默認(rèn)IP地址: 192.168.0.13/DHCP 默認(rèn)用戶名 admin 和默認(rèn)密碼 123456
端口:HTTP 80/RTSP 554/HTTPS 110(443)/onvif端口 80
RTSP地址:rtsp://用戶名:密碼@ip:端口號(hào)/video1/2/3,分別對(duì)應(yīng)主/輔/三碼流;
舉個(gè)栗子:
天地偉業(yè)
默認(rèn)IP地址:192.168.1.2 用戶名“Admin”、密碼“1111”
onvif端口號(hào)“8080”
RTSP地址:rtsp://192.168.1.2
巨龍/JVT
默認(rèn)IP地址:192.168.1.88 默認(rèn)用戶名 admin 默認(rèn)密碼admin
RTSP地址:
主碼流地址:rtsp://IP地址/av0_0
次碼流地址:rtsp://IP地址/av0_1
onvif端口 2000
海清
RTSP地址:rtsp://用戶名:密碼@ip:端口號(hào)/av0_0
D-Link
rtsp://[username]:[password]@[ip]:[port]/[channel].sdp
說明:
username:用戶名。例如admin
password:密碼。例如12345,如果沒有網(wǎng)絡(luò)驗(yàn)證可直接寫成rtsp:// [ip]:[port]/[channel].sdp
ip:為設(shè)備IP。例如192.168.0.108。
port:端口號(hào)默認(rèn)為554,若為默認(rèn)可不填寫。
channel:通道號(hào),起始為1。例如通道2,則為live2。
舉個(gè)栗子:
例如,請(qǐng)求某設(shè)備的通道2的碼流,URL如下 rtsp://admin:12345@192.168.200.201:554/live2.sdprtsp://[username]:[password]@[ip]/axis-media/media.amp?[videocodec]&[resolution]
說明:
username:用戶名。例如admin
password:密碼。例如12345,如果沒有網(wǎng)絡(luò)驗(yàn)證可省略用戶名密碼部分以及@字符。
ip:為設(shè)備IP。例如192.168.0.108。
videocodec:支持MPEG、h.264等,可缺省。
resolution:分辨率,如resolution=1920x1080,若采用默認(rèn)分辨率,可缺省此參數(shù)。
舉個(gè)栗子:
例如,請(qǐng)求某設(shè)備h264編碼的1280x720的碼流,URL如下: rtsp:// 192.168.200.202/axis-media/media.amp?videocodec=h264&resolution=1280x720好了。支持,市場(chǎng)上主流的監(jiān)控?cái)z像頭RTSP推流就介紹完畢了,接下來就實(shí)戰(zhàn)RTSP實(shí)時(shí)推流吧。這里一大華攝像頭為栗子。
實(shí)戰(zhàn)
在上一篇,我們知道了,開啟實(shí)時(shí)預(yù)覽的方式,需要開啟opencv VideoCapture,細(xì)心一點(diǎn)你會(huì)發(fā)現(xiàn),在上一篇中有這樣的代碼如下:
# 導(dǎo)入所需要的庫(kù) import cv2 import numpy as np # 讀取視頻文件 videoCapture = cv2.VideoCapture("test.mp4") # 通過攝像頭的方式 # videoCapture=cv2.VideoCapture(1)經(jīng)過分析,你會(huì)發(fā)現(xiàn),我們只需要把cv2.VideoCapture(“test.mp4”)這里做成實(shí)時(shí)推流的即可。
一:開啟RTSP:
在前面,我們知道了大華攝像頭的RTSP推流方式,那好,第一步就先實(shí)現(xiàn)RTSP推流吧。代碼如下:
import cv2import time import multiprocessing as mpdef image_put(q, name, pwd, ip, channel=1)://使用占位符,動(dòng)態(tài)的代替ip地址,用戶名,密碼,預(yù)覽通道等參數(shù)cap = cv2.VideoCapture("rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel))if cap.isOpened():print('HIKVISION')else:cap = cv2.VideoCapture("rtsp://%s:%s@%s/cam/realmonitor?channel=%d&subtype=0" % (name, pwd, ip, channel))print('DaHua')while True:q.put(cap.read()[1])q.get() if q.qsize() > 1 else time.sleep(0.01)def image_get(q, window_name):cv2.namedWindow(window_name, flags=cv2.WINDOW_FREERATIO)while True:frame = q.get()cv2.imshow(window_name, frame)cv2.waitKey(1)def run_multi_camera():# user_name, user_pwd = "admin", "password"user_name, user_pwd = "admin", "admin123456"camera_ip_l = ["192.168.35.121", # ipv4"[fe80::3aaf:29ff:fed3:d260]", # ipv6# 把你的攝像頭的地址放到這里,如果是ipv6,那么需要加一個(gè)中括號(hào)。]二:多線程隊(duì)列解決實(shí)時(shí)閱覽延遲問題:
上面,我們知道了,如何實(shí)現(xiàn)實(shí)時(shí)預(yù)覽,下面就解決一下核心問題,實(shí)時(shí)讀取延遲問題,代碼如下:
import multiprocessing as mp ... img_queues = [mp.Queue(maxsize=2) for _ in camera_ip_l] # queue ... q.put(frame) if is_opened else None # 線程A不僅將圖片放入隊(duì)列 q.get() if q.qsize() > 1 else time.sleep(0.01) # 線程A還負(fù)責(zé)移除隊(duì)列中的舊圖 ...好了,完成了,這倆步,就可以解決實(shí)時(shí)讀取延遲問題了,最后附上完整代碼。
完整代碼:
import cv2import time import multiprocessing as mpdef image_put(q, name, pwd, ip, channel=1):cap = cv2.VideoCapture("rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel))if cap.isOpened():print('HIKVISION')else:cap = cv2.VideoCapture("rtsp://%s:%s@%s/cam/realmonitor?channel=%d&subtype=0" % (name, pwd, ip, channel))print('DaHua')while True:q.put(cap.read()[1])q.get() if q.qsize() > 1 else time.sleep(0.01)def image_get(q, window_name):cv2.namedWindow(window_name, flags=cv2.WINDOW_FREERATIO)while True:frame = q.get()cv2.imshow(window_name, frame)cv2.waitKey(1)def run_multi_camera():# user_name, user_pwd = "admin", "password"user_name, user_pwd = "admin", "admin123456"camera_ip_l = ["192.168.35.121", # ipv4"[fe80::3aaf:29ff:fed3:d260]", # ipv6# 把你的攝像頭的地址放到這里,如果是ipv6,那么需要加一個(gè)中括號(hào)。]mp.set_start_method(method='spawn') # initqueues = [mp.Queue(maxsize=4) for _ in camera_ip_l]processes = []for queue, camera_ip in zip(queues, camera_ip_l):processes.append(mp.Process(target=image_put, args=(queue, user_name, user_pwd, camera_ip)))processes.append(mp.Process(target=image_get, args=(queue, camera_ip)))for process in processes:process.daemon = Trueprocess.start()for process in processes:process.join()if __name__ == '__main__':run_multi_camera()當(dāng)然還有更簡(jiǎn)單的實(shí)現(xiàn)方式,下面看看如何利用OpenCV官網(wǎng)給出的視頻流讀取吧
簡(jiǎn)單版-OpenCV官網(wǎng)給出的視頻流讀取示例
經(jīng)過簡(jiǎn)單修改,如下:
def run_opencv_camera():video_stream_path = 0 # local camera (e.g. the front camera of laptop)cap = cv2.VideoCapture(video_stream_path)while cap.isOpened():is_opened, frame = cap.read()cv2.imshow('frame', frame)cv2.waitKey(1)cap.release()當(dāng) video_stream_path = 0 的時(shí)候,電腦會(huì)開啟默認(rèn)攝像頭,比如筆記本電腦的前置攝像頭 。
當(dāng)我們需要讀取網(wǎng)絡(luò)攝像頭的時(shí)候,我們可以對(duì) cap = cv2.VideoCapture(括號(hào)里面的東西進(jìn)行修改),填寫上我們想要讀取的視頻流,它可以是:
具體參考:OpenCV官網(wǎng)給出的視頻流讀取示例代碼
好了,到此,我們就解決實(shí)時(shí)讀取延遲問題,但是,目前我們只是監(jiān)控一路,如何監(jiān)控多路,解決實(shí)時(shí)讀取延遲問題了,其實(shí)很簡(jiǎn)單,因?yàn)槊恳宦肥仟?dú)立,互不干涉,下面就實(shí)戰(zhàn)多個(gè)攝像頭。
實(shí)時(shí)預(yù)覽多路攝像頭
有了單路的思路,你會(huì)發(fā)現(xiàn),多路只要使用多線程隊(duì)列,就能解決延遲卡頓問題,讀取多個(gè)攝像頭。
def image_put(q, user, pwd, ip, channel=1):cap = cv2.VideoCapture("rtsp://%s:%s@%s//Streaming/Channels/%d" % (user, pwd, ip, channel))if cap.isOpened():print('HIKVISION')else:cap = cv2.VideoCapture("rtsp://%s:%s@%s/cam/realmonitor?channel=%d&subtype=0" % (user, pwd, ip, channel))print('DaHua')while True:q.put(cap.read()[1])q.get() if q.qsize() > 1 else time.sleep(0.01)def image_get(q, window_name):cv2.namedWindow(window_name, flags=cv2.WINDOW_FREERATIO)while True:frame = q.get()cv2.imshow(window_name, frame)cv2.waitKey(1)def run_single_camera():user_name, user_pwd, camera_ip = "admin", "admin123456", "192.168.35.121"mp.set_start_method(method='spawn') # initqueue = mp.Queue(maxsize=2)processes = [mp.Process(target=image_put, args=(queue, user_name, user_pwd, camera_ip)),mp.Process(target=image_get, args=(queue, camera_ip))][process.start() for process in processes][process.join() for process in processes]def run_multi_camera():# user_name, user_pwd = "admin", "password"user_name, user_pwd = "admin", "admin123456"camera_ip_l = ["172.20.114.26", # ipv4"[fe80::3aaf:29ff:fed3:d260]", # ipv6]mp.set_start_method(method='spawn') # initqueues = [mp.Queue(maxsize=4) for _ in camera_ip_l]processes = []for queue, camera_ip in zip(queues, camera_ip_l):processes.append(mp.Process(target=image_put, args=(queue, user_name, user_pwd, camera_ip)))processes.append(mp.Process(target=image_get, args=(queue, camera_ip)))for process in processes:process.daemon = Trueprocess.start()for process in processes:process.join()if __name__ == '__main__':# run_single_camera()run_multi_camera()pass關(guān)鍵部分解釋:
我使用Python3自帶的多線程模塊,創(chuàng)建一個(gè)隊(duì)列,線程A從通過rtsp協(xié)議從視頻流中讀取出每一幀,并放入隊(duì)列中,線程B從隊(duì)列中將圖片取出,處理后進(jìn)行顯示。線程A如果發(fā)現(xiàn)隊(duì)列里有兩張圖片(證明線程B的讀取速度跟不上線程A),那么線程A主動(dòng)將隊(duì)列里面的舊圖片刪掉,換上新圖片。通過多線程的方法:
好了,支持,多路和單路實(shí)時(shí)預(yù)覽效果,便實(shí)現(xiàn)了,下面看一下具體的實(shí)現(xiàn)效果吧:
總結(jié)
以上是生活随笔為你收集整理的使用opencv-python读取多个(海康\大华)网络摄像头的视频流,解决实时读取延迟问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不要再来北上广深了
- 下一篇: python下载网页歌词_利用Pytho