前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jetson NanoCamera(USB摄像头连接)

jetson NanoCamera(USB摄像头连接)

作者头像
云深无际
发布2021-06-25 15:26:25
3.3K0
发布2021-06-25 15:26:25
举报
文章被收录于专栏:云深之无迹

来自于GitHub的一个开源的Python库,专门用于英伟达Jetson Nano的USB相机驱动。

先放一些要用到的库以及相关用到的链接的出处:

代码语言:javascript
复制
https://zh.snipaste.com/download.html
代码语言:javascript
复制
https://www.python.org/ftp/python/3.9.5/python-3.9.5-amd64.exe
代码语言:javascript
复制
https://codeload.github.com/thehapyone/NanoCamera/zip/refs/heads/master
代码语言:javascript
复制
https://developer.download.nvidia.cn/embedded/L4T/r32_Release_v1.0/Docs/Accelerated_GStreamer_User_Guide.pdf?uIzwdFeQNE8N-vV776ZCUUEbiJxYagieFEqUoYFM9XSf9tbslxWqFKnVHu8erbZZS20A7ADAIgmSQJvXZTb0LkuGl9GoD5HJz4263HcmYWZW0t2OeFSJKZOfuWZ-lF51Pva2DSDtu2QPs-junm7BhMB_9AMQRwExuDb5zIhf_o8PIbA4KKo

简单的说一下链接的作用:

  1. 因为要用到截图的工具,snipaste
  2. 新电脑没有Python的环境,安装一下
  3. 以及就是我们的主角了,这个Python的库
  4. 要封装的命令

因为是在嵌入式的机器上面,所以在我们的本地机器上面布置没有意义

随便的布置一下就好了,这个就选择了最新的3.9

pip一下,报错

代码语言:javascript
复制
C:\Users\109\AppData\Local\Programs\Python\Python39\Scripts

添加一下这个路径就好了

添加到这里

然后可以pip一下

可以把克隆的文件,大致的看一下结构

其实很短,主要的只有一个文件就是NanoCam这个实现的文件

上面就是一些例子了,先看一个

先对代码格式化,快捷键就好

但是没有装过库,扩展提示了要安装

下面是一个自动安装的脚本

全是绝对的路径,第二个路径有意思我们去看看

代码语言:javascript
复制
 C:/Users/109/AppData/Local/Programs/Python/Python39/python.exe
 c:\Users\109\Desktop\AAAAAAAA\VSCode\VSCode-win32-x64-1.56.2\data\extensions\ms-python.python-2021.5.842923320\pythonFiles\pyvsc-run-isolated.py 
 pip install -U autopep8 --user

这个路径

代码语言:javascript
复制
c:\Users\109\Desktop\AAAAAAAA\VSCode\VSCode-win32-x64-1.56.2\data\extensions\ms-python.python-2021.5.842923320\pythonFiles\

在这里

代码语言:javascript
复制
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

if __name__ != "__main__":
    raise Exception("{} cannot be imported".format(__name__))

import os
import os.path
import runpy
import sys


def normalize(path):
    return os.path.normcase(os.path.normpath(path))


# We "isolate" the script/module (sys.argv[1]) by removing current working
# directory or '' in sys.path and then sending the target on to runpy.
cwd = normalize(os.getcwd())
sys.path[:] = [p for p in sys.path if p != "" and normalize(p) != cwd]
del sys.argv[0]
module = sys.argv[0]
if module == "-c":
    ns = {}
    for code in sys.argv[1:]:
        exec(code, ns, ns)
elif module.startswith("-"):
    raise NotImplementedError(sys.argv)
elif module.endswith(".py"):
    runpy.run_path(module, run_name="__main__")
else:
    runpy.run_module(module, run_name="__main__", alter_sys=True)

直接找到,看看就好了。还是看我们的代码才是正事~

先看第一个代码

按照结构简单的划分了

代码语言:javascript
复制
camera = nano.Camera(camera_type=1, device_id=1,
                         width=640, height=480, fps=30)

个人觉得最重要的就是这个代码,对摄像头的初始化:

我们开始读,到这个里面寻找答案

这个库依赖于time,多线程,cv2

只有一个类,其实就是一个封装的变量库

这里是所有的方法,下划线是私有的方法

代码语言:javascript
复制
    def __init__
    (self,
     camera_type=0, 
     device_id=0, 
     source="localhost:8080", 
     flip=0, 
     width=640, 
     height=480, 
     fps=30,
     enforce_fps=False, 
     debug=False):

一开始这个初始化代码需要仔细的研究一下

我们先看支持的相机的种类

是不是很好看,哈哈哈哈

总结一下,nano的这个库支持从以下几个地方要读取视频帧:

  1. CSI的摄像头
  2. RTSP的摄像头
  3. HTTP的摄像头,这里疯狂暗示Tello
  4. 以及我们的USB摄像头

摄像头的id没有什么说的,直接就是这的参数传进来的

其实接下来有两个参数都是一样的传参

代码语言:javascript
复制
        # initialize all variables
        self.fps = fps
        self.camera_type = camera_type
        self.camera_id = device_id
        # for streaming camera only
        self.camera_location = source

这个就是没有对应接受函数的几个参数

这个是不是看不清

代码语言:javascript
复制
    def __csi_pipeline(self, sensor_id=0):
        return ('nvarguscamerasrc sensor-id=%d ! '
                'video/x-raw(memory:NVMM), '
                'width=(int)%d, height=(int)%d, '
                'format=(string)NV12, framerate=(fraction)%d/1 ! '
                'nvvidconv flip-method=%d ! '
                'video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! '
                'videoconvert ! '
                'video/x-raw, format=(string)BGR ! appsink' % (sensor_id,
                                                               self.width, self.height, self.fps, self.flip_method,
                                                               self.width, self.height))

还是不够好看,继续

代码语言:javascript
复制
'nvarguscamerasrc sensor-id=%d ! '
                'video/x-raw(memory:NVMM), '
                'width=(int)%d, height=(int)%d, '
                'format=(string)NV12, framerate=(fraction)%d/1 ! '
                'nvvidconv flip-method=%d ! '
                'video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! '
                'videoconvert ! '
                'video/x-raw, format=(string)BGR ! appsink' % (sensor_id,
                                                               self.width, self.height, self.fps, self.flip_method,
                                                               self.width, self.height)

所有的魔法都是在这里实现,这个是一个底层的解码应用

我找到了一个PDF转WORD的网站

代码语言:javascript
复制
https://smallpdf.com/cn/result#r=fb65dd890902460585b78214673467d6&t=pdf-to-word

就用一下下,完全ok

就完美转换了

又找到一个WORD转HTML的

要钱,还注册麻烦

用自带的吧

然后就ok了,为什么转换多次。

。。。。我想翻译成中文的看而已

是不是真不错,我觉得也是真不错

我们找到第一个参数的作用了,调用了一个应用程序

传感器的id

捕捉时候的硬件参数,自己对照吧

我发现,一直enter是下一个

Tab一下会将焦点放在上一个寻找

队列中的最大内存量,字节为单位

对于具有低内存分配要求的解码用例(例如在Jetson Nano上),

请使用gstomx解码器插件的enable-low-outbuffer属性。

使用GSTREAMER-1.0进行视频格式转换

的NVIDIA专有nvvidconv的GStreamer-1.0插件允许转换OSS之间(原始)视频格式和NVIDIA视频格式。所述nvvidconv插件目前支持在此描述的格式转换部

raw-yuv输入格式

目前nvvidconv支持的I420,UYVY,YUY2,YVYU,NV12,BGRx,和RGBA原始YUV输入格式。

视频格式的转换和缩放

有参数查不到,但是感觉是一个翻转的代码

代码语言:javascript
复制
   'video/x-raw,
    width=(int)%d,
    height=(int)%d, f
    ormat=(string)BGRx ! '
代码语言:javascript
复制
https://github.com/opencv/opencv/issues/11059

关于以上格式的一些讨论

我对编码不熟悉,只能看看

代码语言:javascript
复制
    def __usb_pipeline(self, device_name="/dev/video1"):
        return ('v4l2src device=%s ! '
                'video/x-raw, '
                'width=(int)%d, height=(int)%d, '
                'format=(string)YUY2, framerate=(fraction)%d/1 ! '
                'videoconvert ! '
                'video/x-raw, format=BGR ! '
                'appsink' % (device_name, self.width, self.height, self.fps))

关于USB摄像头的捕捉代码

可以看到是/dev/video1这里捕捉的

可以看到对于USB的读取是,YUV的原生格式

然后会转换到下面几个常用的格式

代码语言:javascript
复制
https://blog.csdn.net/weixin_41944449/article/details/81805164

这篇文章就是比较好的一个解读这个插件的源码

感兴趣的可以去看看

时间有点久远,我们再回忆一下我们的代码:

代码语言:javascript
复制
camera = nano.Camera(camera_type=1, device_id=1,
                         width=640, height=480, fps=30)

相机的类型选择,代码在下面:

代码语言:javascript
复制
    def open(self):
        # open the camera inteface
        # determine what type of camera to open
        if self.camera_type == 0:
            # then CSI camera
            self.__open_csi()
        elif self.camera_type == 2:
            # rtsp camera
            self.__open_rtsp()
        elif self.camera_type == 3:
            # http camera
            self.__open_mjpeg()
        else:
            # it is USB camera
            self.__open_usb()
        return self

有点switch的意思


具体在使用的时候,相机的id不一定是1,需要自己去看设备的根节点:

可以通过在终端上运行:ls / dev / video *来查看已连接的USB摄像机

对于USB摄像机/ dev / video2,device_id将为2,注意切换。

再回顾一下关于我们实现里面的init代码:

代码语言:javascript
复制
    def __init__(self, camera_type=0, device_id=0, source="localhost:8080", flip=0, width=640, height=480, fps=30,
                 enforce_fps=False, debug=False):
        # initialize all variables
        self.fps = fps
        self.camera_type = camera_type
        self.camera_id = device_id
        # for streaming camera only
        self.camera_location = source
        self.flip_method = flip
        self.width = width
        self.height = height
        self.enforce_fps = enforce_fps

        self.debug_mode = debug
        # track error value
        '''
        -1 = Unknown error
        0 = No error
        1 = Error: Could not initialize camera.
        2 = Thread Error: Could not read image from camera
        3 = Error: Could not read image from camera
        4 = Error: Could not release camera
        '''
        # Need to keep an history of the error values
        self.__error_value = [0]

        # created a thread for enforcing FPS camera read and write
        self.cam_thread = None
        # holds the frame data
        self.frame = None

        # tracks if a CAM opened was succesful or not
        self.__cam_opened = False

        # create the OpenCV camera inteface
        self.cap = None

        # open the camera interface
        self.open()
        # enable a threaded read if enforce_fps is active
        if self.enforce_fps:
            self.start()

完整的代码

代码语言:javascript
复制
 self.debug_mode = debug
        # track error value
        '''
        -1 = Unknown error
        0 = No error
        1 = Error: Could not initialize camera.
        2 = Thread Error: Could not read image from camera
        3 = Error: Could not read image from camera
        4 = Error: Could not release camera
        '''

当你的debug开关开启时,以及要输入的等级

代码语言:javascript
复制
       # Need to keep an history of the error values
        self.__error_value = [0]

        # created a thread for enforcing FPS camera read and write
        self.cam_thread = None

这两个就很清晰了,就是实现了你程序里面的一些状态的保存

剩下就是线程的开关,因为视频流不是很简单的就可以使用

要捕获,解码,渲染,还有一些别的操作,得用线程实现

代码语言:javascript
复制
 def start(self):
        self.cam_thread = Thread(target=self.__thread_read)
        self.cam_thread.daemon = True
        self.cam_thread.start()
        return self

在这里,可以看到是在开始这个函数进行了init

我以前的已经说得很详细了,这里不bb了

代码语言:javascript
复制
    def __thread_read(self):
        # uses thread to read
        time.sleep(1.5)
        while self.__cam_opened:
            try:
                self.frame = self.__read()

            except Exception:
                # update the error value parameter
                self.__error_value.append(2)
                self.__cam_opened = False
                if self.debug_mode:
                    raise RuntimeError(
                        'Thread Error: Could not read image from camera')
                break
        # reset the thread object:
        self.cam_thread = None

好家伙儿,看走眼了

这个线程在这里,还是眼花了

这个功能分为初始化,实现,恢复现场

给1.5s的时间来保存系统给资源来运行

接下来是try和except的搭配

首先看这个read的读取情况

你看这个东西,这个读取的标志就是cv2里面的标志

都学的连起来了

这个是一个标志位,注释写的很清楚了

CAM是不是正常的打开,其实这个地方写的有点鬼畜

追踪这个相机是不是成功的打开,应该是这样的翻译

代码语言:javascript
复制
    # Tracks if camera is ready or not(maybe something went wrong)
    def isReady(self):
        return self.__cam_opened

这个代码加上,上面就不呼噜了,#跟踪相机是否准备就绪(可能出了点问题),有问题就会导致下面的东西不能正常的运行,其实也是在保证程序的正常运行。

代码语言:javascript
复制
    def read(self):
        # read the camera stream
        try:
            # check if debugging is activated
            if self.debug_mode:
                # check the error value
                if self.__error_value[-1] != 0:
                    raise RuntimeError(
                        "An error as occurred. Error Value:", self.__error_value)
            if self.enforce_fps:
                # if threaded read is enabled, it is possible the thread hasn't run yet
                if self.frame is not None:
                    return self.frame
                else:
                    # we need to wait for the thread to be ready.
                    return self.__read()
            else:
                return self.__read()
        except Exception as ee:
            if self.debug_mode:
                raise RuntimeError(ee.args)

读取使用的代码

先插一点,如果你成功的将初始化的参数传入

会调用下面的某一个就是你要捕捉的硬件,会使用cv2的VideoCapture

来进行捕捉的

继续看上面说的是什么,四个函数一起看了吧

  1. 是init的代码,在末尾调用了start()
  2. start()里面有实现了线程
  3. 线程里面又实现了是不是正确的读取
  4. 如果上一步正确,就开始读取
  5. 读取的时候又使用了try,except这样的结构
  6. 先try里面判断debug的等级,里面会触发运行时错误

具体的这个有个函数我也没有找到定义,所以可能不是函数,但是看见了蛛丝马迹:

代码语言:javascript
复制
 if self.enforce_fps:
                self.cap = cv2.VideoCapture(self.__usb_pipeline_enforce_fps(
                    self.camera_name), cv2.CAP_GSTREAMER)

看,就是这里,cv2的接口从这个接口处获得数据

这里这样处理可能好看一些

就是在不停的判断各种成功读取的条件而已

也就是我们的两行代码,背后发生的故事~

这些代码都是常规的cv2代码,没有什么意思了

最后还有一个释放的代码

代码语言:javascript
复制
    def release(self):
        # destroy the opencv camera object
        try:
            # update the cam opened variable
            self.__cam_opened = False
            # ensure the camera thread stops running
            if self.enforce_fps:
                if self.cam_thread is not None:
                    self.cam_thread.join()
            if self.cap is not None:
                self.cap.release()
            # update the cam opened variable
            self.__cam_opened = False
        except RuntimeError:
            # update the error value parameter
            self.__error_value.append(4)
            if self.debug_mode:
                raise RuntimeError('Error: Could not release camera')

这里保证各种标志位变为False

然后将线程退出

如果卡住就会弹出运行时错误

代码读的不精细,也没有多少总结,有时间再看吧~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用GSTREAMER-1.0进行视频格式转换
    • raw-yuv输入格式
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档