V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
xiaolinjia
V2EX  ›  Python

请教下 opencv 的问题。

  •  
  •   xiaolinjia · 2019-08-22 16:08:02 +08:00 · 1723 次点击
    这是一个创建于 1918 天前的主题,其中的信息可能已经有所发展或是发生改变。

    场景:公司有个 ip 摄像头在海港那边。然后这边想访问一个 url,然后就返回摄像头那时候的一帧的截图(其实是返回图像识别过的 json )。

    但是每次都去连接那个 ip 摄像头太慢了,于是便想将该 Video 连接持续化,将其放入一个 dict 里存起来,而不是每次连接完之后都 release,这样不用每次访问都去连接一次。大概像下面这样:

    pool = {}
    def create(camId):
        cap = cv2.VideoCapture(baseUrl+camId)
        if camId in pool:
            return True
        if cap.isOpened():
            print("连接摄像头成功")
            capDict = {}
            capDict['time'] = int(time.time() * 1000)
            capDict['camera'] = cap
            pool[camId] = capDict
    

    然后我用 flask 搭建的后台,每次访问 url,便调用下面的 detectBoat()函数,从 dict 中取出对应 id 的 Video 摄像头连接。这样只要最开始创建一次,就可以不用每次访问 url 都连接一次了。

    def detectBoat(camId):
        cap = pool[camId]['camera']
        retryLimit = 3
        if cap.isOpened():
            results = ""
            while True:
                success, frame = cap.read()
                try:
                    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                except cv2.error:
                    print('reading frame failed, retry after 1s...')
                    time.sleep(1)
                    continue
    
                img_name = f'{int(time.time() * 1000)}.jpg'
                cv2.imwrite(f"{image_folder}/{img_name}", frame)
                print(f'截取了图片: {img_name}')
                resp = urlopen(myurl) #让别的分析后台来分析,再返回 json
                results = json.loads(resp.read())
                print(f"船舶识别: {results}")
                break
            del success, frame
            return results
        else:
            return '请先连接摄像头'
    

    然后问题来了,我发现我每次调用那个可持续化的摄像头连接 cap 的时候,他返回给我的永远是这一帧后的下一帧图。就是比如说,我 4 点 1 分调用这个函数,返回给我 4 点 1 分 0 秒的图,然后我 5 点再调用,还是返回给我 4 点 1 分 0 秒的图的下一帧的图给我,我再调用,就返回给我再下一帧图,我怀疑他是不是将每帧图都缓存起来了?我怎样才能让他返回给我当前时间的图呢。

    第 1 条附言  ·  2020-01-15 16:45:20 +08:00

    个人现在来补充下,首先是1楼老哥说的方法是完全可行的。当时我根据他的说法+参考网上一些资料自己写了一个。

    一开始是继承 cv2.VideoCapture 类写了一个新类 class NewVideoCapture(cv2.VideoCapture)

    然后把继承改成写了一个新类,将cv2.VideoCapture()作为类成员,如下:

    import threading
    import cv2
    
    
    class NewVideoCapture:
        def __init__(self, url, *args, **kwargs):
            self.frame_receiver_thread = threading.Thread(target=self.recv_frame)
            self._cur_frame = None
            self._reading = False
            if isinstance(url, str) and url.startswith(("rtsp://", "rtmp://")):
                self._reading = True
            else:
                raise ValueError('url格式不对')
            self.url = url
            self.video = cv2.VideoCapture(url)
    
            self.can_read = False
    
        def isOpened(self):
            return self.video.isOpened()
    
        def recv_frame(self):
            while self.isOpened() and self._reading:
                revtal, frame = self.video.read()
                if not revtal:
                    break
                self._cur_frame = frame
    
                self.can_read = True
    
            self._reading = False
    
        def start_read(self):
            if self.isOpened():
                self.frame_receiver_thread.start()
    
        def read(self, image=None):
            while not self.can_read:
                pass
    
            if self._cur_frame is not None:
                frame = self._cur_frame.copy()
            else:
                frame = None
            revtal = frame is not None
            return revtal, frame
    
        def stop_read(self):
            self._reading = False
            if self.frame_receiver_thread.is_alive():
                self.frame_receiver_thread.join()
    
        def get(self, propId):
            return self.video.get(propId)
    
        def release(self):
            self.stop_read()
            self.video.release()
    

    这个也是没问题的,然后我为啥今天突然补充下,是因为今天看到一个第三方库原来已经实现了这个功能了。

    pip install imutils

    from imutils.video import WebcamVideoStream

    不过我看了源码,其实思路也是一样的,想用哪个就用哪个吧。

    2 条回复    2019-08-23 14:10:13 +08:00
    shuianfendi6
        1
    shuianfendi6  
       2019-08-22 18:20:08 +08:00
    循环读取摄像头,将图片持续写到全局变量中
    xiaolinjia
        2
    xiaolinjia  
    OP
       2019-08-23 14:10:13 +08:00
    @shuianfendi6 我还以为有什么函数可以直接跳过帧的,结果还是要循环吗,那只能开个线程来循环读了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4770 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 05:39 · PVG 13:39 · LAX 21:39 · JFK 00:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.