python 调用海康工业相机图像获取方式之回调取流并用 opencv 显示

系列文章目录

第一章 python调用海康工业相机并用opencv显示(整体实现)

第二章 python 调用海康工业相机图像获取方式之回调取流并用 opencv 显示

第三章 python 调用海康工业相机图像获取方式之主动取流(getoneframetimeout )并用 opencv 显示

第四章 python 调用海康工业相机图像获取方式之主动取流( getimagebuffer )并用 opencv 显示

第五章 python 调用海康工业相机调试后出现被占用问题


 


前言

海康工业相机的底层SDK中提供了两种不同方式的图像数据获取方式,一种是回调方式,一种是主动取流方式。但是官方示例中,只提供了相关数据获取到之后的打印信息,对于图像数据的解析并没有给出,基于以上情况,本文对回调取流方式获取到的图像数据进行解析。


一、使用前提

需要先安装海康工业相机的底层SDK,在安装之后,会有相关例程在安装目录下,本文的代码调用需要在该目录下调用(未作移植使用);

基于上一章节(python调用海康工业相机并用opencv显示)对于海康工业相机连接的内容,本文对于海康工业相机的回调取图方式取到的图像解析进行说明。

二、回调取流(官方例程说明)

1.接口说明

在海康提供的底层SDK中,有相关回调取流的接口,并且在安装目录下的工业相机SDK开发指南中,有对该接口的具体介绍,具体如下:

接口:MV_CC_RegisterImageCallBackEx()

对应于 C 语言接口如下:

MV_CAMCTRL_API int __stdcall MV_CC_RegisterImageCallBackEx  ( void *  handle,  
                                                              void(__stdcall *cbOutput)(unsigned char *pData, MV_FRAME_OUT_INFO_EX *pstFrameInfo, void *pUser)  ,  
                                                              void *  pUser ) 

参数:

。handle :设备句柄

。cbOutput:回调函数指针

。pUser:用户自定义变量

返回值:

。调用成功,返回 MV_OK

。调用失败,返回错误码

注:

     1、通过该接口可以设置图像数据回调函数,需要在 MV_CC_CreateHandle() 接口之后调用,并且需要在 MV_CC_StarGrabbing() 接口之前设置,这样采集的图像数据在设置的回调函数中才能返回;

     2、该接口不支持 Cameralink 设备,仅支持 GigE 、USB 设备;

2.python 下接口实现

(1)根据以上说明可知,MV_CC_RegisterImageCallBackEx() 接口需要在打开设备和开启采集接口之间声明调用,即官方例程中的注册抓图回调,如下:

 # ch:注册抓图回调 | en:Register image callback
    ret = cam.MV_CC_RegisterImageCallBackEx(CALL_BACK_FUN,None)
    if ret != 0:
        print ("register image callback fail! ret[0x%x]" % ret)
        sys.exit()

(2)并且在开启取流和停止取流接口调用中间,需要写入如下代码,否则回调取图只能获取到一张图像后会停止获取图像,如下:

 print ("press a key to stop grabbing.")
    msvcrt.getch()

其中 msvcrt 模块时 python 语言标准库中的一个 控制输入输出的库;

(3)实现回调函数功能

winfun_ctype = WINFUNCTYPE             # 声明回调函数指针类型,首先要声明返回值类型
stFrameInfo = POINTER(MV_FRAME_OUT_INFO_EX)   # 使用 POINTER 指向 stFrameInfo 数据的类型为 MV_FRAME_OUT_INFO_EX
pData = POINTER(c_ubyte)       # 使用 POINTER 指向 pData 数据的类型为 c_ubyte
FrameInfoCallBack = winfun_ctype(None, pData, stFrameInfo, c_void_p)   # 定义回调函数类型
# 实现回调函数功能
def image_callback(pData, pFrameInfo, pUser):
        stFrameInfo = cast(pFrameInfo, POINTER(MV_FRAME_OUT_INFO_EX)).contents
        if stFrameInfo:
            print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
CALL_BACK_FUN = FrameInfoCallBack(image_callback)

以上三步均为官方例程中的调用介绍,如上代码中可以看到当取到图像数据后,仅打印图像数据的信息,并没有对图像数据进行解析,接下来将对图像数据进行解析,并作显示;

三、回调取流并对黑白图像数据解析并用 opencv 显示

1.回调数据获取并解析 mono 格式的图像数据

winfun_ctype = WINFUNCTYPE
stFrameInfo = POINTER(MV_FRAME_OUT_INFO_EX)
pData = POINTER(c_ubyte)
FrameInfoCallBack = winfun_ctype(None, pData, stFrameInfo, c_void_p)
def image_callback(pData, pFrameInfo, pUser):
    global img_buff
    img_buff = None
    stFrameInfo = cast(pFrameInfo, POINTER(MV_FRAME_OUT_INFO_EX)).contents
    if stFrameInfo:
        print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
    if img_buff is None:
        img_buff = (c_ubyte * stFrameInfo.nWidth*stFrameInfo.nHeight)()
        cdll.msvcrt.memcpy(byref(img_buff) , pData , stFrameInfo.nWidth*stFrameInfo.nHeight)
    data = np.frombuffer(img_buff , count = int(stFrameInfo.nWidth*stFrameInfo.nHeight) , dtype = np.uint8)
    image_control(data = data , stFrameInfo = stFrameInfo)
CALL_BACK_FUN = FrameInfoCallBack(image_callback)

通过对官方例程中回调获取图像数据的代码修改,获取到相关图像的数据,如上代码中 data 即为回调获取到的图像数据,并通过自定义函数 image_control() 对图像数据进行 reshape 操作,获取到可以显示的数据;

2.图像数据显示

def image_show(image):
    image = cv2.resize(image, (600, 400), interpolation=cv2.INTER_AREA)
    cv2.imshow('fgmask', image)
    k = cv2.waitKey(1) & 0xff

def image_control(data , stFrameInfo):
    image = data.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
    image_show(image = image)

以上代码是对回调获取到的图像数据进行显示的代码,但是以上代码仅在使用海康工业相机黑白模式下使用,因为没有考虑像素格式以及通道等信息,下面对多种像素格式图像数据进行解析并显示;

四、回调取流并对多种像素格式的图像数据解析并用 opencv 显示

1.回调数据获取并解析多种像素格式的数据

基于第一章(python调用海康工业相机并用opencv显示)中,对于常用接口的封装,回调部分也进行相关内容的封装,将回调中的数据解析部分封装为一体,具体代码如下所示:

# 回调取图采集
winfun_ctype = WINFUNCTYPE
stFrameInfo = POINTER(MV_FRAME_OUT_INFO_EX)
pData = POINTER(c_ubyte)
FrameInfoCallBack = winfun_ctype(None, pData, stFrameInfo, c_void_p)
def image_callback(pData, pFrameInfo, pUser):
    global img_buff
    img_buff = None
    stFrameInfo = cast(pFrameInfo, POINTER(MV_FRAME_OUT_INFO_EX)).contents
    if stFrameInfo:
        print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
    if img_buff is None and stFrameInfo.enPixelType == 17301505:
        img_buff = (c_ubyte * stFrameInfo.nWidth*stFrameInfo.nHeight)()
        cdll.msvcrt.memcpy(byref(img_buff) , pData , stFrameInfo.nWidth*stFrameInfo.nHeight)
        data = np.frombuffer(img_buff , count = int(stFrameInfo.nWidth*stFrameInfo.nHeight) , dtype = np.uint8)
        image_control(data=data, stFrameInfo=stFrameInfo)
        del img_buff
    elif img_buff is None and stFrameInfo.enPixelType == 17301514:
        img_buff = (c_ubyte * stFrameInfo.nWidth*stFrameInfo.nHeight)()
        cdll.msvcrt.memcpy(byref(img_buff) , pData , stFrameInfo.nWidth*stFrameInfo.nHeight)
        data = np.frombuffer(img_buff , count = int(stFrameInfo.nWidth*stFrameInfo.nHeight) , dtype = np.uint8)
        image_control(data=data, stFrameInfo=stFrameInfo)
        del img_buff
    elif img_buff is None and stFrameInfo.enPixelType == 35127316:
        img_buff = (c_ubyte * stFrameInfo.nWidth * stFrameInfo.nHeight*3)()
        cdll.msvcrt.memcpy(byref(img_buff), pData, stFrameInfo.nWidth * stFrameInfo.nHeight*3)
        data = np.frombuffer(img_buff, count=int(stFrameInfo.nWidth * stFrameInfo.nHeight*3), dtype=np.uint8)
        image_control(data=data, stFrameInfo=stFrameInfo)
        del img_buff
    elif img_buff is None and stFrameInfo.enPixelType == 34603039:
        img_buff = (c_ubyte * stFrameInfo.nWidth * stFrameInfo.nHeight * 2)()
        cdll.msvcrt.memcpy(byref(img_buff), pData, stFrameInfo.nWidth * stFrameInfo.nHeight * 2)
        data = np.frombuffer(img_buff, count=int(stFrameInfo.nWidth * stFrameInfo.nHeight * 2), dtype=np.uint8)
        image_control(data=data, stFrameInfo=stFrameInfo)
        del img_buff
CALL_BACK_FUN = FrameInfoCallBack(image_callback)

因为工业相机总有不同的像素格式,也就对应有不同的通道数,所以在图像解析过程中就会出现不同分支的解析方式,以上内容是根据海康提供的官方定义中获取到的像素格式对应的数字,进行判定,具体的定义在海康 SDK 目录下:../MVS/Development/Samples/Python/MvImport/PixelType_header.py 文件中,如有需要,请自行查询;

2.图像数据显示

由以上内容可以知道,通过回调获取到的图像数据是多种像素格式类型的,不同像素格式类型图像通道不同,自然也会导致显示时,不同通道配置的代码,以下内容是对不同像素格式类型的图像数据的显示,代码如下:

# 显示图像
def image_show(image):
    image = cv2.resize(image, (600, 400), interpolation=cv2.INTER_AREA)
    cv2.imshow('fgmask', image)
    k = cv2.waitKey(1) & 0xff

# 需要显示的图像数据转换
def image_control(data , stFrameInfo):
    if stFrameInfo.enPixelType == 17301505:
        image = data.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
        image_show(image=image)
    elif stFrameInfo.enPixelType == 17301514:
        data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
        image = cv2.cvtColor(data, cv2.COLOR_BAYER_GB2RGB)
        image_show(image=image)
    elif stFrameInfo.enPixelType == 35127316:
        data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
        image = cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
        image_show(image=image)
    elif stFrameInfo.enPixelType == 34603039:
        data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
        image = cv2.cvtColor(data, cv2.COLOR_YUV2BGR_Y422)
        image_show(image = image)

可以看到不同的通道数,对应不同的数据转换,最终将图像显示在桌面上;


总结

在工业相机的使用中,很多场景使用的都是回调方式获取图像,而回调方式获取到的图像数据如何解析,就相对比较重要,以上是对海康工业相机回调获取图像数据的解析;由于现阶段大部分工业相机的SDK底层是以 C 语言和 C# 为主要语言,所以 python 的调用,就选择了使用 C 语言为基础的接口转换后的 python 接口来调用相机,而这种方式在工业界相对比较常见。

### Python调用海康威视工业相机并结合OpenCV显示图像及YOLO目标检测 #### 使用 OpenCV 调用海康威视工业相机 要通过 `cv2.VideoCapture()` 接口调用海康威视工业相机,可以使用 RTSP 协议连接到设备。以下是具体方法: ```python import cv2 url = "rtsp://admin:123456789yang@192.168.31.120/Streaming/Channels/2" cap = cv2.VideoCapture(url) if not cap.isOpened(): print("无法打开摄像头") else: while True: ret, frame = cap.read() if not ret: print("未能读取帧") break # 显示图像 cv2.imshow("Camera Feed", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ``` 上述代码实现了通过 RTSP 地址访问海康威视工业相机,并实时显示其捕获的画面[^2]。 --- #### 结合 YOLO 实现目标检测 为了在获取的视频上应用 YOLO 进行目标检测,需加载预训练模型并对每一帧执行推理操作。以下是一个完整的示例代码: ```python import cv2 import numpy as np def load_yolo_model(weights_path, config_path, labels_path): net = cv2.dnn.readNetFromDarknet(config_path, weights_path) with open(labels_path, 'r') as f: classes = [line.strip() for line in f.readlines()] layer_names = net.getLayerNames() output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()] return net, classes, output_layers def detect_objects(frame, net, output_layers): blob = cv2.dnn.blobFromImage(frame, scalefactor=0.00392, size=(416, 416), mean=(0, 0, 0), swapRB=True, crop=False) net.setInput(blob) outputs = net.forward(output_layers) return outputs def draw_boxes(outputs, frame, classes, confidence_threshold=0.5, nms_threshold=0.4): class_ids = [] confidences = [] boxes = [] height, width = frame.shape[:2] for output in outputs: for detection in output: scores = detection[5:] class_id = np.argmax(scores) confidence = scores[class_id] if confidence > confidence_threshold: center_x = int(detection[0] * width) center_y = int(detection[1] * height) w = int(detection[2] * width) h = int(detection[3] * height) x = int(center_x - w / 2) y = int(center_y - h / 2) boxes.append([x, y, w, h]) confidences.append(float(confidence)) class_ids.append(class_id) indices = cv2.dnn.NMSBoxes(boxes, confidences, confidence_threshold, nms_threshold) for i in indices.flatten(): box = boxes[i] color = (0, 255, 0) label = f"{classes[class_ids[i]]}: {confidences[i]:.2f}" cv2.rectangle(frame, (box[0], box[1]), (box[0]+box[2], box[1]+box[3]), color, thickness=2) cv2.putText(frame, label, (box[0], box[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, thickness=2) return frame # 加载 YOLO 模型 weights_path = "yolov3.weights" config_path = "yolov3.cfg" labels_path = "coco.names" net, classes, output_layers = load_yolo_model(weights_path, config_path, labels_path) # 打开摄像头 url = "rtsp://admin:123456789yang@192.168.31.120/Streaming/Channels/2" cap = cv2.VideoCapture(url) while True: ret, frame = cap.read() if not ret: print("未能读取帧") break outputs = detect_objects(frame, net, output_layers) detected_frame = draw_boxes(outputs, frame, classes) cv2.imshow("YOLO Detection", detected_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ``` 此代码片段展示了如何加载 YOLOv3 模型、处理每帧数据以提取边界框和类别标签,并绘制结果于原始图像之上[^3]。 --- #### 注意事项 - **RTSP 配置**:确保 RTSP URL 正确无误,用户名密码与实际设备匹配。 - **性能优化**:对于高分辨率视频,可考虑降低输入尺寸或调整推理频率来提升效率。 - **依赖安装**:运行前确认已安装必要库 (`opencv-python` 和 `numpy`) 及下载对应权重文件。 ---
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值