opencv打开摄像头_网络摄像头控制(上)

本篇介绍如何控制监控摄像头。文中所涉及到的摄像头型号为海康PTZmini,不同品牌的网络摄像头SDK接口不同,但控制方式类似。
主要有以下内容:
1. OpenCV读取摄像头视频流及OpenCV操作 2. 用SDK接口方式控制摄像头  3. 用访问网页的爬虫方式控制摄像头 1 OpenCV操作

OpenCV作为计算机视觉开源库,功能很多,其中可以读取摄像头视频流或者本地文件的视频流,从而进行图像处理,图像编辑。另外有可以用图片生成视频文件等功能。

写完这篇觉得挺枯燥的,然后就利用OpenCV上述的功能做了一个表情包倒放‍的沙雕视频。代码实现在1.1节中。

原图是这样的:

进行了倒放处理: 至于为什么处理之后是视频而不是动图,因为OpenCV只能保存成视频,生成GIF可以 使用imageio。 1.1 OpenCV分解合成视频
 1# -*- encoding: utf-8 -*- 2import cv2 as cv 3import os 4 5#如果不存在建立一个路径 6images = './image/' 7if not os.path.exists(images): 8    os.mkdir(images) 910cap = cv.VideoCapture("forword.gif")11time = 012while True:13    success, frame = cap.read()14    if success:15        time = time + 116        cv.imwrite(images + str(time) + '.jpg', frame)17        print(time)18    else:19        break20cap.release()2122font = cv.FONT_HERSHEY_SIMPLEX #字体23fps = 12.024size = (328, 640)25#建立一个视频写入器, 解释点126videowriter = cv.VideoWriter("backup.avi", cv.VideoWriter_fourcc('X', 'V', 'I', 'D'), fps, size)27for i in range(time, 0, -1):28    img = cv.imread(images + str(i) + '.jpg')29    img = cv.resize(img, (328, 640))30    cv.putText(img, str('made by fangyiwei'), (20, 20), font, 0.5, (0, 255, 0), 1)31    videowriter.write(img)32    print(i)3334videowriter.release()35print('done')
这里使用到的都是OpenCV基础的功能,文件读取视频写入。做视频倒放的逻辑就是读取GIF中每一帧图片,存入文件夹中。然后再逆序读入每张图片,写成视频。 opencv打开摄像头_网络摄像头控制(上)_第1张图片

    解释点

  • 1.cv.VideoWriter的参数。依次是:生成的文件名,解码方式(这个很奇特,经常会解码失败,多换几种试试),帧率,视频尺寸。


1.2
OpenCV读取视频流
 1import cv2 as cv 2 3class ReadVideo(): 4    def __init__(self, DisplayMood): 5        if DisplayMood: 6            self.path = "./saving/vid/1.mp4" #本地视频地址 7        else: 8            self.path = "rtsp://admin:" + str(password) + "@" + str(address) \ 9                        + ":554/h264/ch33/main/av_stream?tcp"  #一般的网络摄像头解析地址10        self.cam = cv.VideoCapture(self.path) #读取摄像头路径上的视频流(解释点1)11        #测试摄像头(解释点2)12        if not self.cam.isOpened():13            print('无法打开该路径')1415        ret, img = self.cam.read()16        if ret == False:17            print("无法读取该路径视频流")1819    def run(self):20        ret, img = self.cam.read()21        #解释点322        if not ret:23            self.cam = cv.VideoCapture(self.fn)24            ret, img = self.cam.read()25        #解释点426        cv.putText(img, str('图上的文字'), (123,456), font, 2, (0,255,0), 3)2728        return img

针对上述代码解释:
  • 1.此处设置了播放模式。同一个封装函数中既能播放网络视频流,又能播放本地视频流。通过传入DisplayMood这个参数控制,1代表播放本地视频,0代表播放网络视频
  • 2.路径检查。在函数初始化过程中,会对视频流路径和该视频流进行检查,如果无法读取视频则给出提示信息。
  • 3.视频播放结束后操作。这里主要针对本地视频,如果一个视频文件读取结束则重新从头再读取。
  • 4.在读取的每一帧图片上放置文字。各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细。
1.3 PyQt5多线程播放视频流 上一篇中有在Tkinter界面中播放视频,这里是用PyQt5的多线程在界面上播放视频流。在Python中有Thread的多线程模块,但是在实践中发现在Qt框架下使用Python自带的Thread并不稳定,而且无法在子线程更改控件的状态,使用起来的体验并不友好。 所以尽量使用QT框架自带的QThread。QThread运用Qt中信号与槽的功能特点,子线程发送信号,然后主线去接受信号,将该内容反馈于主线程。
 1#直接播放的视频线程 2class DirectPlayTheard(QThread): 3    img_signal = pyqtSignal(np.ndarray, np.ndarray) #解释点1 4    close_signal = pyqtSignal(int)  5    def __init__(self): 6        super(DirectPlayTheard, self).__init__() 7        self.vid_list = [] #解释点2 8        self.vid_list.append(ReadVideo(0)) 9        self.vid_list.append(ReadVideo(0))1011    def run(self):12        global VidOpenFlag, vidPauseFlag, StageFlag, VidNum, lock13        #这里是一个线程锁,保持数据的一致性14        lock.acquire()15        VidNum += 216        lock.release()17        while True:18            start = time.time()19            img1 = self.vid_list[0].run()20            img2 = self.vid_list[1].run()21            self.img_signal.emit(img1, img2)2223            #这里是视频暂停参数,按下按钮更改这个参数为1,就能让线程休眠,相当于视频暂停24            while vidPauseFlag:25                self.sleep(1)2627            #这里是控制读取速度,一般为12帧每秒28            while time.time() - start 0.08:29                time.sleep(0.001)3031            #这里是视频结束播放标志,按下按钮,更改这个参数为0,就能发射信号,并退出循环32            if VidOpenFlag == 0:33                self.close_signal.emit(5)34                break3536        return3738class My_MainWindow(GUI.Ui_MainWindow, QtWidgets.QWidget):39    def __init__(self, MainWindow):40        super(My_MainWindow,self).__init__() #这样能继承QtWidgets.QWidget41        super().setupUi(MainWindow) #继承HJPG中的setupUI函数4243    def open_cam(self):44        #检查网络摄像头是否介入局域网中,解释点345        if self.DisplayMood == 0:46            for IP_address in self.IP_address_list:47                cmd = ["ping", "-n", "1", IP_address]48                print(cmd)49                output = os.popen(" ".join(cmd)).readlines()50                print(output)51                for line in output:52                    if str(line).upper().find("TTL") >= 0:53                        self.textBrowser.append(IP_address + '网络连接良好\n')54                        self.VidNum += 155                        break56            if self.VidNum == 0:57                QMessageBox.warning(self, '警告', '未检测到摄像头接入网络')58                self.textBrowser.append(" + '未检测到网络中有摄像头连接,请检查摄像头连接或所处的网络' + "")5960        #建立视频播放线程61        self.directplay = DirectPlayTheard()62        self.directplay.img_signal.connect(self.DirectVidPlay)63        self.directplay.close_signal.connect(self.CloseDirectVid)64        self.directplay.start()6566    #视频直接播放    67    def DirectVidPlay(self, img5, img6):68        show5 = cv.cvtColor(img5, cv.COLOR_BGR2RGB)  # 视频色彩转换回RGB,这样才是现实的颜色69        # show5 = cv.resize(img5_color, (300, 164))  # 把读到的帧的大小重新设置尺寸, 解释点470        showImage5 = QImage(show5.data, show5.shape[1], show5.shape[0],71                                 QImage.Format_RGB888)  # 把读取到的视频数据变成QImage形式72        self.label1.setPixmap(QtGui.QPixmap.fromImage(showImage5))73        show6 = cv.cvtColor(img6, cv.COLOR_BGR2RGB)  # 视频色彩转换回RGB,这样才是现实的颜色74        # show6 = cv.resize(img6_color, (300, 164))  # 把读到的帧的大小重新设置尺寸75        showImage6 = QImage(show6.data, show6.shape[1], show6.shape[0],76                                 QImage.Format_RGB888)  # 把读取到的视频数据变成QImage形式77        self.label2.setPixmap(QtGui.QPixmap.fromImage(showImage6))7879    #结束视频线程,放置一个空图片80    def CloseDirectVid(self, idx):81        self.label1.setPixmap(QtGui.QPixmap(""))82        self.label2.setPixmap(QtGui.QPixmap(""))

上面代码继承了QThread中的方法,然后用信号与槽的方式,针对解释点说明如下:
  • 1.PyQt5的多线程信号发射方式。这里要注意OpenCV读取的img的格式为np.array.可以通过执行下面语句来查看。
   print(type(img))
整个流程: 建立信号
img_signal = pyqtSignal(np.ndarray, np.ndarray)连接信号
self.directplay = DirectPlayTheard()
self.directplay.img_signal.connect(self.DirectVidPlay)发射信号
self.img_signal.emit(img1, img2)
接收信号
def DirectVidPlay(self, img5, img6):
  • 2.优化代码结构。这里建立一个列表来存放视频流读取的方法函数,这样有利于提前初始化方法,并且如果摄像头个数太多,方便建立函数优化代码结构
  • 3.摄像头IP地址检查。由于实际运用的需要,这里在建立视频播放子线程之前,先去检查每个摄像头的IP地址是否存在。运用的原理就是调用系统去ping摄像头的IP地址,如果摄像头都连接在局域网内,那么差不多0.05S就能完成操作,但是如果不在该局域网内需要接近2S去ping地址。摄像头个数多了之后可能等待时间就比较长。
  • 4.图片自适应label大小。这里调用cv中resize的方法,更改图片的尺寸,但是有个问题,如果长和宽均小于300,那么放在QT界面上就会变形(这个问题搞得我很懵逼)。后来发现QT的label有一个属性,让图片自适应大小,再也不用resize了,对于界面有十几种尺寸的视频图像的就很舒服,小于300也不会变形了。
    label.setScaledContents(True)

你可能感兴趣的:(opencv打开摄像头,opencv读取usb摄像头,opencv读取摄像头全白,控制网页frame,vba,杂牌网络摄像头ip更改工具)