Python对接海康威视机器视觉工业相机

一、下载MVS客户端

海康机器人-机器视觉-下载中心

二、解压并安装MVS客户端

三、找到MVS示例代码(代码在MVS的安装位置)

工业相机只允许单条连接,也就是说MVS如果连接了相机,python代码就无法获取数据,此时必须退出MVS客户端

四、使用Pycharm打开Python代码示例文件夹

如果觉得爆红难受,将MvImport文件夹中的五个文件,拷贝到BasicDemo文件夹下即可。

博主这里出现了一个警告:获取不到相机参数。忽略掉即可。

五、获取相机图像

安装一下opencv

pip3 install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

相机工具类(读者直接复制)HKCamera_class.py

import sys
from ctypes import *
import numpy as np
import cv2


# 这里要根据个人情况进行修改 TODO
sys.path.append("E:\MVS_STD_4.2.1_230921\MVS\Development\Samples\Python\MvImport")  # 打开MVS中的MvImport文件,对于不同系统打开的文件路径跟随实际文件路径变化即可
from MvCameraControl_class import *  # 调用了MvCameraControl_class.py文件

class HKCamera():
    def __init__(self, CameraIdx=0, log_path=None, CameraIp=""):
        self.name = "HKCamera"
        deviceList = self.enum_devices()
        strModeName = ""
        TargetCamera = None
        for i in range(0, deviceList.nDeviceNum):
            mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
            nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
            nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
            nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
            nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
            Devip = "%d.%d.%d.%d" % (nip1, nip2, nip3, nip4)
            if Devip == CameraIp:
                TargetCamera = i
        self.camera = self.open_camera(deviceList=deviceList, CameraIdx=TargetCamera, log_path=log_path)
        # self.start_camera()

    def __del__(self):
        if self.camera is None:
            return

        # 停止取流
        # ret = self.camera.MV_CC_StopGrabbing()
        # if ret != 0:
        #     raise Exception("stop grabbing fail! ret[0x%x]" % ret)

        # 关闭设备
        ret = self.camera.MV_CC_CloseDevice()
        if ret != 0:
            raise Exception("close deivce fail! ret[0x%x]" % ret)

        # 销毁句柄
        ret = self.camera.MV_CC_DestroyHandle()
        if ret != 0:
            raise Exception("destroy handle fail! ret[0x%x]" % ret)

    @staticmethod
    def enum_devices(device=0, device_way=False):
        """
        device = 0  枚举网口、USB口、未知设备、cameralink 设备
        device = 1 枚举GenTL设备
        """
        if device_way == False:
            if device == 0:
                cameraType = MV_GIGE_DEVICE | MV_USB_DEVICE | MV_UNKNOW_DEVICE | MV_1394_DEVICE | MV_CAMERALINK_DEVICE
                deviceList = MV_CC_DEVICE_INFO_LIST()
                # 枚举设备
                ret = MvCamera.MV_CC_EnumDevices(cameraType, deviceList)
                if ret != 0:
                    raise Exception("enum devices fail! ret[0x%x]" % ret)
                return deviceList
            else:
                pass
        elif device_way == True:
            pass

    def open_camera(self, deviceList, CameraIdx, log_path):
        # generate a camera instance
        camera = MvCamera()

        # 选择设备并创建句柄
        stDeviceList = cast(deviceList.pDeviceInfo[CameraIdx], POINTER(MV_CC_DEVICE_INFO)).contents
        if log_path is not None:
            ret = self.camera.MV_CC_SetSDKLogPath(log_path)
            if ret != 0:
                raise Exception("set Log path  fail! ret[0x%x]" % ret)

            # 创建句柄,生成日志
            ret = camera.MV_CC_CreateHandle(stDeviceList)
            if ret != 0:
                raise Exception("create handle fail! ret[0x%x]" % ret)
        else:
            # 创建句柄,不生成日志
            ret = camera.MV_CC_CreateHandleWithoutLog(stDeviceList)
            if ret != 0:
                raise Exception("create handle fail! ret[0x%x]" % ret)

        # 打开相机
        ret = camera.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
        if ret != 0:
            raise Exception("open device fail! ret[0x%x]" % ret)

        return camera

    def start_camera(self):
        stParam = MVCC_INTVALUE()
        memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))

        ret = self.camera.MV_CC_GetIntValue("PayloadSize", stParam)
        if ret != 0:
            raise Exception("get payload size fail! ret[0x%x]" % ret)

        self.nDataSize = stParam.nCurValue
        self.pData = (c_ubyte * self.nDataSize)()
        self.stFrameInfo = MV_FRAME_OUT_INFO_EX()
        memset(byref(self.stFrameInfo), 0, sizeof(self.stFrameInfo))

        self.camera.MV_CC_StartGrabbing()

    def get_Value(self, param_type, node_name):
        """
        :param cam:            相机实例
        :param_type:           获取节点值得类型
        :param node_name:      节点名 可选 int 、float 、enum 、bool 、string 型节点
        :return:               节点值
        """
        if param_type == "int_value":
            stParam = MVCC_INTVALUE_EX()
            memset(byref(stParam), 0, sizeof(MVCC_INTVALUE_EX))
            ret = self.camera.MV_CC_GetIntValueEx(node_name, stParam)
            if ret != 0:
                raise Exception("获取 int 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))
            return stParam.nCurValue

        elif param_type == "float_value":
            stFloatValue = MVCC_FLOATVALUE()
            memset(byref(stFloatValue), 0, sizeof(MVCC_FLOATVALUE))
            ret = self.camera.MV_CC_GetFloatValue(node_name, stFloatValue)
            if ret != 0:
                raise Exception("获取 float 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))
            return stFloatValue.fCurValue

        elif param_type == "enum_value":
            stEnumValue = MVCC_ENUMVALUE()
            memset(byref(stEnumValue), 0, sizeof(MVCC_ENUMVALUE))
            ret = self.camera.MV_CC_GetEnumValue(node_name, stEnumValue)
            if ret != 0:
                raise Exception("获取 enum 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))
            return stEnumValue.nCurValue

        elif param_type == "bool_value":
            stBool = c_bool(False)
            ret = self.camera.MV_CC_GetBoolValue(node_name, stBool)
            if ret != 0:
                raise Exception("获取 bool 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))
            return stBool.value

        elif param_type == "string_value":
            stStringValue = MVCC_STRINGVALUE()
            memset(byref(stStringValue), 0, sizeof(MVCC_STRINGVALUE))
            ret = self.camera.MV_CC_GetStringValue(node_name, stStringValue)
            if ret != 0:
                raise Exception("获取 string 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))
            return stStringValue.chCurValue

        else:
            return None

    def set_Value(self, param_type, node_name, node_value):
        """
        :param cam:               相机实例
        :param param_type:        需要设置的节点值得类型
            int:
            float:
            enum:     参考于客户端中该选项的 Enum Entry Value 值即可
            bool:     对应 0 为关,1 为开
            string:   输入值为数字或者英文字符,不能为汉字
        :param node_name:         需要设置的节点名
        :param node_value:        设置给节点的值
        :return:
        """
        if param_type == "int_value":
            ret = self.camera.MV_CC_SetIntValueEx(node_name, int(node_value))
            if ret != 0:
                raise Exception("设置 int 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))

        elif param_type == "float_value":
            ret = self.camera.MV_CC_SetFloatValue(node_name, float(node_value))
            if ret != 0:
                raise Exception("设置 float 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))

        elif param_type == "enum_value":
            ret = self.camera.MV_CC_SetEnumValueByString(node_name, node_value)
            if ret != 0:
                raise Exception("设置 enum 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))

        elif param_type == "bool_value":
            ret = self.camera.MV_CC_SetBoolValue(node_name, node_value)
            if ret != 0:
                raise Exception("设置 bool 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))

        elif param_type == "string_value":
            ret = self.camera.MV_CC_SetStringValue(node_name, str(node_value))
            if ret != 0:
                raise Exception("设置 string 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name, ret))

    def set_exposure_time(self, exp_time):
        self.set_Value(param_type="float_value", node_name="ExposureTime", node_value=exp_time)

    def get_exposure_time(self):
        return self.get_Value(param_type="float_value", node_name="ExposureTime")

    def get_image(self, width=None):
        """
        :param cam:     相机实例
        :active_way:主动取流方式的不同方法 分别是(getImagebuffer)(getoneframetimeout)
        :return:
        """
        # ret= self.camera.MV_CC_SaveImageEx2()
        ret = self.camera.MV_CC_GetOneFrameTimeout(self.pData, self.nDataSize, self.stFrameInfo, 5000)
        if ret == 0:
            image = np.asarray(self.pData).reshape((self.stFrameInfo.nHeight, self.stFrameInfo.nWidth, 3))
            #    if width is not None:
            #    image = cv2.resize(image, (width, int(self.stFrameInfo.nHeight * width / self.stFrameInfo.nWidth)))
            #    pass
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            return image
        else:
            return None

    def show_runtime_info(self, image):
        exp_time = self.get_exposure_time()
        cv2.putText(image, ("exposure time = %1.1fms" % (exp_time * 0.001)), (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                    255, 1)

获取照片数据

Main.py
import os
import cv2
from numpy import ndarray

from HKCamera_class import HKCamera

if __name__ == '__main__':
    camera = HKCamera(CameraIp='192.168.1.191')
    # 对摄像头配置进行设置
    try:
        camera.set_Value(param_type="enum_value", node_name="PixelFormat",
                         node_value='RGB8Packed')
        camera.set_Value(param_type="enum_value", node_name="ExposureAuto",
                         node_value='Off')
        camera.set_Value(param_type="float_value", node_name="ExposureTime",
                         node_value='400')
    except Exception as e:
        print(e)
        os._exit(0)
    camera.start_camera()


    while True:
        try:
            image: ndarray = camera.get_image()
            if image is not None:
                # 这里拿到图片的ndarray数据后,可以转Image、可以保存图片....等等操作
                print(image.shape)
            key = cv2.waitKey(50) & 0xFF
            if key == ord('e') or key == ord('E'):
                cv2.destroyAllWindows()
                break
        except Exception as e:
            print(e)

六、补充2025年1月24日

错误一:AttributeError: 'HKCamera' object has no attribute 'camera'

原因:IP错误,未找到相关相机。确保IP正确,确保IP与本机在同一个网段。

错误二:设置 enum 型数据节点 PixelFormat 失败 !报错码 ret[0x80000004]

这是因为相机的图像配置,跟代码的摄像机配置不一致引起的。

代码配置的是:RGB8Packed格式。你需要用MVS将你的摄像机配置成RGB8Packed格式。保持一致。

有些工业相机不支持RGB8Packed格式,那我们就需要去MVS上查看一下默认的图像格式,然后将代码里的格式改成相机支持的格式

七、一些常见错误码

1.MV_OK 0x00000000 成功,无错误

解析:-2147483648/0x80000000正常状态返回,执行成功

2.MV_E_HANDLE 0x80000000 错误或无效的句

解析:-2147483648/0x80000000 无效句柄,常见问题如下:

用户没有申请句柄,直接调用接口,新手常犯的错误,要引导去学习我们接口使用流程

用户创建了句柄,但是其他地方销毁了句柄,用户没有注意到,需要仔细排查代码

3.MV_E_SUPPORT 0x80000001 不支持的功能

解析:-2147483647/0x80000001

SDK接口的bayer空域降噪、无损压缩、色彩矫正等ISP功能,需要配合CS-Pro系列相机支持

格式转化时,不同格式的相互转化,超出了算法能力集,详情请仔细查阅SDK接口说明

4.MV_E_BUFOVER 0x80000002 缓存已满

解析:-2147483646/0x80000002

常见于gige驱动启动时报错,低版本SDK在某些网卡上面易发,驱动启动失败后,走socket协议发送接收相机数据,效率低,CPU负载大 推荐使用SDK3.5版本解决此问题

5.MV_E_CALLORDER 0x80000003 函数调用顺序有误

解析:-2147483645/0x80000003

sdk接口调用,有一定流程顺序,例如getimagebuffer在startgrabing之前调用,就违反了接口流程,就会报错顺序调用错误 还比如,没有调用startgrabing接口,就去调用频繁调用stopgrabing接口,也会报此错误

6.MV_E_PARAMETER 0x80000004 错误的参数

解析:-2147483644/0x80000004

常见问题: 常见于格式转化、图像保存等需要补充数据结构的接口调用,部分参数传入错误,或者没有传入,这个时候,要去仔细检查参数的传入是否正确

7.MV_E_NODATA 0x80000007 无数据

解析:-2147483641/0x80000007无数据

相机帧率低,用户调用主动取流接口getimagebuffer/getoneframetimout频率高于相机出图频率,且超时时间短,没有拿到图片,此时应该打印相机帧号,如帧号连续则为正常现象

相机处于触发模式,没有触发信号给到相机,此时,应该排查用户是否给了软触发或者硬触发信号

相机停流,此时,建议打开MVS,观察相机状态

耐心寻找规律,看看是否跟packsize、scpd、取流超时时间不合理所致

8.MV_E_NOENOUGH_BUF 0x8000000A 传入的内存空间不足

解析:-2147483638/0x8000000A

1.用户自行开辟的内存大小,小于当前相机图像所需要的图像大小,例如用mono8的图像大小,接收RGB的图像

2.用户定义的内存大小,中途更换了分辨率更高的相机,导致所需内存较多

3.相机开启了chunk功能,用户开辟缓存大小,仅考虑了图像宽高像素格式,没有考虑到chunk

解决方法:

排查内存开辟大小,建议使用相机payloadsize大小

检查相机图像格式

关闭相机chunk等功能

9.MV_E_UNKNOW 0x800000FF 未知的错误

解析:-2147483137/0x800000FF GenICam未知错误

未知错误,形成原因较难分析,建议如下

更新最新版本的sdk

开启sdk日志等级,通过日志分析形成原因

10.MV_E_GC_GENERIC 0x80000100 通用错误

解析:-2147483392/0x80000100

通用接口调用,关键词写错,例如曝光:ExposureTime,拼写错误就会报错

第三方相机链接,例如迈德威视相机链接MVS,也会报通用错误,这是因为迈德威视不是标准的genicam协议的相机导致的

接口类型用错,例如曝光时float型节点,我们使用了一个int型的节点接口进行读写,访问,那么也会报通用错误

11.MV_E_GC_RANGE 0x80000102 值超出范围

解析:-2147483390/0x80000102

接口传入的参数值,超出相机接受范围,例如,曝光存在上下限,图像宽高存在步进,没有按照步进进行设置等等

12.MV_E_GC_ACCESS 0x80000106 节点访问条件有误

解析:-2147483386/0x80000106

常见的问题类型有: 相机节点不存在或者无法访问,例如,自动曝光,在手动曝光情况下,自动曝光节点会被隐藏,或者其他相机存在这个参数,而使用的相机无此参数,例如线扫相机的行频参数等等,还比如说部分相机无此功能,例如event参数,部分相机固件暂不支持用户调用

13.MV_E_GC_TIMEOUT 0x80000107 超时

解析:-2147483385/0x80000107 GVCP命名包回复超时,一般出现在网络环境不好的情况下,此时应该调用接口加大gvcp命令包等待时间(MV_GIGE_GetGvcpTimeout()),持续出现该报错,应该排查网络环境问题

分析:根据海康SDK提示文件:更新官网最新SDK时,设备网络SDK开发包【库文件】里的HCNetSDK.dll、HCCore.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll、HCNetSDKCom文件夹等文件均要加载到程序里面,HCNetSDKCom文件夹(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。

解决方法:将海康SDK的头文件放到Debug文件夹.exe文件的存放处:

14.MV_E_ACCESS_DENIED 0x80000203 设备无访问权限

解析:-2147483133/0x80000203常见问题有:

相机被其他软件打开占用,关闭其他软件,检查设备管理器中,可能存在的残留进程

代码debug下,心跳时间问题,等待60s后,可以重新打开(此问题参考心跳问题解决方法,可缩短打开时间)

其余问题,例如一上电就打不开相机,需要重新插拔网线、USB线,就是其他问题,需要具体问题,具体分析

15.MV_E_NETER 0x80000206 网络相关错误

解析:-2147483130/0x80000206

此类报错非常常见,主要分以下集中

相机掉线,能够在日志中发现大量的206报错,此时需要去区分掉线原因,结合相机上电时间、心跳时间、相机权限,日志等信息,综合判断

网线异常,导致相机掉线,也需要具体问题分析

16.MV_E_IP_CONFLICT 0x80000221 设备IP冲突

解析:-2147483103/0x80000221,常见于IP设置时,当前ip已经被其他设备使用,需要更换ip重新设置

17.MV_E_USB_READ 0x80000300 读USB出错

解析:-2147482880/0x80000300

USB读取相机信息失败,此类问题较为复制,与USB接口稳定性、线缆长度、电磁环境相关,往往不好分析,可以尝试插拔一下,或者更换USB接口尝试

18.MV_E_USB_WRITE 0x80000301 写USB出错

解析:-2147482879/0x80000301

同0x80000300一样,不好分析,可以尝试插拔一下,或者更换USB接口尝试

19.MV_E_USB_DRIVER 0x80000305 驱动不匹配或者未装驱动

解析: -2147482875/0x80000305

同0x80000300一样,问题复杂,除了更换USB接口外,还可以尝试更换sdk版本

另外一种场景,比较好解决的是,卸载第三方的usb驱动,比如说halcon驱动

20、错误号64:NET_DVR_LOADPLAYERSDKFAILED 64 载入当前目录下 Player Sdk 出错
分析:该问题为解决错误107时间引出,问题应该在文件放置目录不对或者库文件内容有缺失导致;解决方法:与错误107相同,将其库文件放置到指定目录;必要时:检查调用Play的文件中是否引入头文件,可引入头文件如“PlayM4.h”或“plaympeg4.h”尝试解决。

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值