【工业视觉检测1----基于Python的工业相机算法部署】

0、前言

工业视觉检测相关的项目应用很多,但是相关的资料较为零散,且多为C++部署的项目,Python相关的少之又少。故进行整理,此篇包含Python工业部署的诸多方面,如多线程下的多相机并行处理、相机的IO触发等。
所用相机为海康工业相机,镜头、光源等调试过程均略去,在此仅讨论程序方面。

1、设备枚举及排序

程序主体参考为海康MVS中附带的官方例程,安装MVS软件后,在.\MVS\Development\Samples\Python\路径中,可以找到相应的Python例程GrabImage.py,整体实现的功能包括取流,IO端口的读写等操作。

示例程序会读取与电脑连接的网口设备,并将设备的信息保存在deviceList中,然后会将设备的类型及IP地址打印出来。

为了便于算法指定相应的设备,根据设备的IP地址的末位进行排序,即使用sorted()函数,对nip4排序,重新生成设备列表。
相关程序如下

######对设备使用nip4  IP地址的最后一段进行重新排序
devices_with_nip4 = []
for i in range(0, deviceList.nDeviceNum):
    mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
    if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
        nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
        devices_with_nip4.append((mvcc_dev_info, nip4))
sorted_devices_with_nip4 = sorted(devices_with_nip4, key=lambda x: x[1])
# 重新生成 deviceList.pDeviceInfo
for i, (dev_info, nip4) in enumerate(sorted_devices_with_nip4):
    deviceList.pDeviceInfo[i] = cast(pointer(dev_info), POINTER(MV_CC_DEVICE_INFO))

2、设备实例创建

进一步,根据重新生成的设备列表中的相机序号,对多个相机分别创建相机实例,函数中输入设备号nConnectionNum为使用nip4重新排序后设备列表中的设备序号,函数返回值包含cam, data_buf, nPayloadSize,分别为相机实例、数据缓冲(image)、数据包的尺寸。
创建实例所用到的函数如下

def use_camera_by_index(nConnectionNum):
    if int(nConnectionNum) >= deviceList.nDeviceNum:
        print("设备号错误")
        sys.exit()
    cam = MvCamera()
    stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents
    ret = cam.MV_CC_CreateHandle(stDeviceList)    
    if ret != 0:
        print("create handle fail! ret[0x%x]" % ret)
        sys.exit()
    ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)  
    if ret != 0:
        print("open device fail! ret[0x%x]" % ret)
        sys.exit()
    if stDeviceList.nTLayerType == MV_GIGE_DEVICE:  
        nPacketSize = cam.MV_CC_GetOptimalPacketSize() 
        if int(nPacketSize) > 0:
            ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", nPacketSize)
            if ret != 0:
                print("Warning: Set Packet Size fail! ret[0x%x]" % ret)
        else:
            print("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)
    stBool = c_bool(False)     
    ret = cam.MV_CC_GetBoolValue("AcquisitionFrameRateEnable", stBool)
    if ret != 0:
        print("get AcquisitionFrameRateEnable fail! ret[0x%x]" % ret)
    if ret != 0:
        print("set trigger mode fail! ret[0x%x]" % ret)
        sys.exit()
    stParam = MVCC_INTVALUE()
    ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
    if ret != 0:
        print("get PayloadSize fail! ret[0x%x]" % ret)
        sys.exit()
    nPayloadSize = stParam.nCurValue
    ret = cam.MV_CC_StartGrabbing()
    if ret != 0:
        print("start grabbing fail! ret[0x%x]" % ret)
        sys.exit()
    data_buf = (c_ubyte * nPayloadSize)()
    return cam, data_buf, nPayloadSize

3、IO信号输入

在为相机创建实例的同时,可以一并设置相机的IO输入模式,主要针对不同类型的任务,指定触发模式,根据IO输入信号,控制相机的开闭。

GigE相机的Line0用于接入触发信号,触发信号由控制设备给出,举例:信号触发时为低电平,即为NPN设备,根据文档,相机与PLC的接线如图所示。
在这里插入图片描述

与IO输入模式设置有关的参数如下

cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_ON)   #用于开启触发模式,默认关闭。MV_TRIGGER_MODE_ON:1:on ||MV_TRIGGER_MODE_OFF:0:off
cam.MV_CC_SetEnumValue("TriggerSource", 0)  #用于指定输入源,默认为Line0。(Line2也可指定为输入源),0:Line0|| 1:Line1 ||2:Line2  ||7:Software ||8:FrequencyConverter|| 13:anyway
cam.MV_CC_SetEnumValue("TriggerActivation", 1)  #用于设定触发模式,默认为上升沿触发,0:Rising Edge  ||1:Falling Edge ||2:LevelHigh|| 3:LevelLow
cam.MV_CC_SetFloatValue ("TriggerDelay", 0) #用于设定触发延时时间,接收到触发信号后,延时一段时间开始拍摄,默认为0,单位为us
cam.MV_CC_SetBoolValue ("TriggerCacheEnable", True) #用于缓存信号,开启后,上个信号处理完毕前的信号会进行缓存,上个信号处理完成后,处理缓存的信号;不开启则同时接收的两个触发信号,仅当一个信号进行响应,默认不开启。
cam.MV_CC_SetEnumValue("LineSelector", 0)   #用于对输入的信号进行硬件滤波用于选择输入源。0:Line0  ||2:Line2
cam.MV_CC_SetIntValue ("LineDebouncerTime", 0)  #用于设置滤波时间,单位为us

通过指定排序后的相机序号,可以方便实现对不同相机不同触发模式的设定。

通过调用函数,为每个相机创建实例,并分配好IO输入触发模式,接下来,将每个相机实例分配至相应的算法处理子线程中。

4、多线程

Python中的多线程功能通过调用threading多线程模块实现,进而完成多台工业相机的同时工作,单个线程启动的例程如下:

Thread_task = threading.Thread(target=work_thread, args=(cam, data_buf, nPayloadSize))

threading.Thread()用于创建线程任务,其中target=work_thread指定了子线程的处理函数, args=(cam, data_buf, nPayloadSize)则将参数传递至函数中。调用的处理函数通过True循环,不断等待获取帧信息,对图像帧进行算法识别处理,并根据处理函数返回的结果,控制相机的IO输出信号。
然后通过start()方法,启动线程。

Thread_task.start()
Thread_task.join()

5、IO信号输出

可以通过程序控制相机的IO输出,用于返回软件检测的OK及NG信号至控制端,便于控制端的进一步操作,OK及NG信号两种信号分别连接Line1和Line2,外接设备类型为NPN设备,接线方式分别如图所示。

在这里插入图片描述
在这里插入图片描述

与配置IO输入接口的模式类似,IO输出也是通过调用cam方法,设置相应的参数,使相机产生信号输出,相关方法配置参数如下。

cam.MV_CC_SetEnumValue("LineSelector", 1)   #用于选定端口,0:Line0 ||1:Line1 ||2:Line2
cam.MV_CC_SetEnumValue("LineMode", 8)        #仅Line2在使用时,需要进行额外的参数配置,0:Input ||1:Output|| 8:Strobe
cam.MV_CC_SetEnumValue("LineSource", 5)     #用于设置触发源,0:ExposureStartActive ||5:SoftTriggerActive  6:HardTriggerActive
cam.MV_CC_SetIntValue("StrobeLineDuration", 500000) #用于设定输出信号的持续时间,单位为us。
cam.MV_CC_SetIntValue("StrobeLineDelay", 0)#用于设定延迟输出,单位为us
cam.MV_CC_SetIntValue("StrobeLinePreDelay", 0)#用于设定给定输出后,进行曝光取帧的时间,单位为us。
cam.MV_CC_SetBoolValue("StrobeEnable", True)#使能输出。
cam.MV_CC_SetCommandValue('LineTriggerSoftware')#给出开始输出的指令。

根据需求,分别设置Line1、Line2的参数即可。

后续将会更新算法处理的相关内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值