多线程计算(Python)

多线程读取IP摄像头(Python)-原文链接

      对于IP摄像头,它是以数据流的形式传输,因此当其帧率较高时,本地处理程序会处理不过来,导致卡帧(延时)和程序卡死!利用多线程来精确化需求分布,可以合理的利用计算资源。

1.threading模块(线程)

      线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。当我们使用Threading模块创建线程时或者自定义线程任务时,最好的方法就是建立一个线程类,并继承于threading.Thead,然后重写run方法。

主要有以下几个常用函数:

  • threading.currentThread():返回当前的线程变量
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
  • threading.activeCount(): 返回正在运行的线程数量,与 len(threading.enumerate())有相同的结果
  • run(): 线程活动的函数,自定义时需要重写 start():启动线程活动,必须使用
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止,正常退出或者抛出未处理的异常,或者是可选的超时发生,只有子线程都运行完了,主线程(main)才会退出!
  • isAlive(): 返回线程是否活动的
  • getName(): 返回线程名
  • setName(): 设置线程名

2.队列模块(queue)

      在Python3中,队列模块为queue, 其包括三种队列类,分别为FIFO(先进先出)、LIFO(后入先出)和PriorityQueue。其常用的方法为:

  • queue.qsize() 返回队列的大小
  • queue.empty() 如果队列为空,返回True,反之False
  • queue.full() 如果队列满了,返回True,反之False
  • queue.full 与 maxsize 大小对应
  • queue.get([block[, timeout]])获取队列,timeout等待时间
  • queue.get_nowait() 相当Queue.get(False)
  • queue.put(item) 写入队列,timeout等待时间
  • queue.put_nowait(item) 相当Queue.put(item, False)

3.多线程处理摄像头读取

      如果我们碰到了一个实时性要求不是那么高的,或者自己设备太差处理不过来图像时,我们可以考虑使用多线程读取摄像头画面!比如我们现在需要两个线程,一个用于实时读取视频流,另外一个每隔一秒钟处理一个最新的摄像头画面!

核心思路:我们使用双端队列来缓存数据,当缓存数据满时,我们从队头剔除数据,然后在队尾加入新数据,在获取时只读取队尾数据,这样就会一直处理当前帧!一定要注意线程退出时,需要在关闭摄像头的同时清空队列,而另一个线程进行队列是否为空的判断!

注意:由于这个队列是可读可写,当两个线程同时工作,会出现冲突,因此我们需要对公共变量加锁进行保护,使用threading.Lock()方法创建锁,加锁解锁的方法为acquire()和release()两种!

import cv2
import threading
from collections import deque
lock = threading.Lock()

class RealReadThread(threading.Thread):
    def __init__(self, input):
        super(RealReadThread).__init__()
        self._jobq = input
        # ip_camera_url = 'rtsp://admin:admin@192.168.1.64/'   # rtsp数据流
        # 创建一个窗口
        self.cap = cv2.VideoCapture(0)
        threading.Thread.__init__(self)

    def run(self):
        cv2.namedWindow('ip_camera', flags=cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO)
        if not self.cap.isOpened():
            print('请检查IP地址还有端口号,或者查看IP摄像头是否开启,另外记得使用sudo权限运行脚本')
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            lock.acquire()
            if len(self._jobq) == 10:
                self._jobq.popleft()
            else:
                self._jobq.append(frame)
            lock.release()
            cv2.imshow('ip_camera', frame)
            if cv2.waitKey(1) == ord('q'):
                # 退出程序
                break
        print("实时读取线程退出!!!!")
        cv2.destroyWindow('ip_camera')
        self._jobq.clear()    # 读取进程结束时清空队列
        self.cap.release()

class GetThread(threading.Thread):
    def __init__(self, input):
        super(GetThread).__init__()
        self._jobq = input
        threading.Thread.__init__(self)

    def run(self):
        cv2.namedWindow('get', flags=cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO)
        flag = False
        while(True):
            if len(self._jobq) != 0:
                lock.acquire()
                im_new = self._jobq.pop()
                lock.release()
                cv2.imshow("get", im_new)
                cv2.waitKey(1000)
                flag = True
            elif flag == True and len(self._jobq) == 0:
                break

        print("间隔1s获取图像线程退出!!!!")

if __name__ == "__main__":
    q = deque([], 10)
    th1 = RealReadThread(q)
    th2 = GetThread(q)
    th1.start()
    th2.start()   # 开启两个线程

    # join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止
    th1.join()
    th2.join()
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值