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的参数即可。
后续将会更新算法处理的相关内容。