下载安装MVS
首先在海康机器人官网 海康机器人-机器视觉-下载中心 下载安装MVS,不同的版本,调用会有区别,本文基于这个版本:

默认路径安装完成后,在路径C:\Program Files (x86)\MVS\Development\Samples\Python\BasicDemo下的BasicDemo.py中,海康威视官方提供了基本的调用方法,本文也是基于这个修改的。
BasicDemo.py提供了通过枚举选择相机的方法,在官方的另一个DEMO:ConnectSpecCamera.py中提供了另一种不通过枚举,直接访问特定IP相机的方法,也可以参考。
创建环境和项目
创建conda环境以及在pycharm中创建环境和安装必要的支持包,这些属于基操了,不赘述。
将MVS所在路径下的\Development\Samples\Python\BasicDemo的CamOperation_class.py文件拷贝到创建好的空项目的文件夹中;
将MVS所在路径下的\Development\Samples\Python\MvImport下的几个文件拷贝到创建好的空项目的文件夹中;(也可以通过在代码中添加路径的方法实现这一步)

拷贝好文件之后的项目文件目录:

初始化和创建相机的实例
官方的demo中,将相机初始化和创建为实例,这是一个标准套路。
python
# 初始化SDK
MvCamera.MV_CC_Initialize()
# 创建相机实例
cam = MvCamera()
并通过下面的代码获取所有相机的设备信息列表:
python
deviceList = MV_CC_DEVICE_INFO_LIST() # 所有相机信息的列表
然后可以通过mvcc_dev_info对列表中的相机进行设备序号、产品型号、用户定义名、IP地址等信息的查询。
python
deviceList.nDeviceNum # 相机列表中的数量
user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) # 用户自定义的相机名称
decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) # 相机型号
# 相机的IP:
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)
然后选择一个列表中的相机,定义操作实例:
python
obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex) # 操作的实例
接下来就可以使用定义好的操作实例进行常用操作:
python
obj_cam_operation.Open_device() # 打开设备
obj_cam_operation.Close_device() # 关闭设备
obj_cam_operation.Trigger_once() # 软触发一次
obj_cam_operation.Start_grabbing() # 开始取流
obj_cam_operation.Stop_grabbing() # 停止取流
obj_cam_operation.Set_trigger_mode() # 设置触发模式
obj_cam_operation.Get_parameter() # 获取参数
obj_cam_operation.Set_parameter() # 设置参数
obj_cam_operation.Save_jpg() # 保存jpg文件
obj_cam_operation.Save_bmp() # 保存bmp文件
选择相机的方法
可以通过上面提供的设备序号方法选择特定的相机。
python
obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex)
在工程应用中,由于相机的在线数量可能是不确定的,那么通过设备序号的方法有可能会有不确定性。而IP地址具有唯一性和不可变性(需要在MVS软件中将相机的IP设为固定模式),用IP地址来对相机进行定义和操作比较具有实用性。
python
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents # 设备信息
# 相机的IP地址
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)
# 如果相机在线,则获取相机在设备列表中的序号,并定义它的操作类
if nip1 == self.nip1 and nip2 == self.nip2 and nip3 == self.nip3 and nip4 == self.nip4:
self.obj_cam_operation = CameraOperation(cam, deviceList, i)
self.n_gide_device = i # GidE设备序号
self.user_defined_name = self.decoding_char(
mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) # 设备的用户定义名称(支持中文名称)
self.model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) # 相机的型号
break
if i == deviceList.nDeviceNum - 1:
print("未发现指定的相机!")
另外,如果每台相机都定义了唯一的用户定义名,也可以通过用户定义名来区分相机。
python
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents # 设备信息
# 相机的用户自定义名称
user_defined_name = self.decoding_char(
mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) # 设备的用户定义名称(支持中文名称)
# 如果相机在线,则获取相机在设备列表中的序号,并定义它的操作类
if user_defined_name == self.user_defined_name:
self.obj_cam_operation = CameraOperation(cam, deviceList, i)
self.n_gide_device = i # GidE设备序号
nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24) # IP1
nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16) # IP2
nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8) # IP3
nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff) # IP4
self.model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) # 相机的型号
break
if i == deviceList.nDeviceNum - 1:
print("未发现指定的相机!")
完整代码
下面一段代码,演示了如何通过IP地址定义一台相机,并将其设置为软触发后获取3帧图像,然后将获取到的图像保存。
python
# -*- coding: utf-8 -*-
import time
from CamOperation_class import CameraOperation
from MvCameraControl_class import *
from MvErrorDefine_const import *
from CameraParams_header import *
import ctypes
# 示例代码(基于MVS SDK)
# 将海康的GidE相机定义为类
class HiKGidECamera(MvCamera):
def __init__(self, nip1, nip2, nip3, nip4):
super().__init__()
self.n_gide_device = -1 # GidE设备序号
self.user_defined_name = "" # 用户自定义名称
self.model_name = "" # 相机型号
self.nip1 = nip1 # IP地址1
self.nip2 = nip2 # IP地址2
self.nip3 = nip3 # IP地址3
self.nip4 = nip4 # IP地址4
self.obj_cam_operation = None # 相机操作类
self.isOpen = False # 相机是否打开
self.isGrabbing = False # 相机是否正在采集图像
self.is_trigger_mode = False # 是否触发模式
# 注册相机
def log_on(self):
"""
注册相机,如果相机在线,则获取相机在设备列表中的序号
:return: 0表示成功,其他表示失败
"""
deviceList = MV_CC_DEVICE_INFO_LIST() # 所有相机的设备信息列表
# ######如果需要枚举GigE设备、USB设备、Gentl设备、CXP设备、XOF设备##############
# n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE
# | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE)
##################################################################
# 本例只使用GigE相机
n_layer_type = MV_GIGE_DEVICE # 只使用GigE相机
ret = MvCamera.MV_CC_EnumDevices(n_layer_type, deviceList) # 返回值为0表示成功
if ret != 0:
print("查找设备失败。 错误码[0x%x]" % ret)
return -1
if deviceList.nDeviceNum == 0: # 在线设备数量
print("未发现设备!")
return -1
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents # 设备信息
# 相机的IP地址
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)
# 如果相机在线,则获取相机在设备列表中的序号,并定义它的操作类
if nip1 == self.nip1 and nip2 == self.nip2 and nip3 == self.nip3 and nip4 == self.nip4:
self.obj_cam_operation = CameraOperation(cam, deviceList, i) # 定义相机操作的实体类
self.n_gide_device = i # GidE设备序号
self.user_defined_name = self.decoding_char(
mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName) # 设备的用户定义名称(支持中文名称)
self.model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName) # 相机的型号
return 0
if i == deviceList.nDeviceNum - 1:
print("未发现指定的相机!")
return -1
# 打开相机
def open_device(self):
if self.isOpen == True:
print("相机已打开!")
return MV_E_CALLORDER # 返回错误信息
ret = self.obj_cam_operation.Open_device()
if ret != 0:
print("打开设备失败,错误码:", ret)
self.isOpen = False
else:
self.isOpen = True
# 关闭相机
def close_device(self):
if self.isOpen == False:
print("相机已关闭!")
return MV_E_CALLORDER # 返回错误信息
ret = self.obj_cam_operation.Close_device()
if ret != 0:
print("关闭设备失败,错误码:", ret)
self.isOpen = True
else:
self.isOpen = False
# 设置连续取流模式
def set_continue_mode(self):
ret = self.obj_cam_operation.Set_trigger_mode(False)
if ret != 0:
print("设置连续模式失败:" , self.ToHexStr(ret))
# ch:设置软触发模式 | en:set software trigger mode
# 设置软触发模式
def set_software_trigger_mode(self):
ret = self.obj_cam_operation.Set_trigger_mode(True)
if ret != 0:
print("设置软触发模式失败::", self.ToHexStr(ret))
# 软触发一次
def trigger_once(self):
ret = self.obj_cam_operation.Trigger_once()
if ret != 0:
print("单次软触发失败:",self.ToHexStr(ret))
# 开始取流
def start_grabbing(self):
# ret = cam.MV_CC_StartGrabbing()
ret = self.obj_cam_operation.Start_grabbing(0)
if ret != 0:
print("开始取流失败:" + self.ToHexStr(ret))
else:
self.isGrabbing = True
# time.sleep(5)
# 停止取流
def stop_grabbing(self):
ret = self.obj_cam_operation.Stop_grabbing()
if ret != 0:
print("停止取流失败:", self.ToHexStr(ret))
else:
self.isGrabbing = False
# 解码操作
def decoding_char(self, c_ubyte_value):
c_char_p_value = ctypes.cast(c_ubyte_value, ctypes.c_char_p)
try:
decode_str = c_char_p_value.value.decode('gbk') # Chinese characters
except UnicodeDecodeError:
decode_str = str(c_char_p_value.value)
return decode_str
# 将返回的错误码转换为十六进制显示
def ToHexStr(self, num):
chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'}
hexStr = ""
if num < 0:
num = num + 2 ** 32
while num >= 16:
digit = num % 16
hexStr = chaDic.get(digit, str(digit)) + hexStr
num //= 16
hexStr = chaDic.get(num, str(num)) + hexStr
return hexStr
if __name__ == "__main__":
# 初始化SDK
MvCamera.MV_CC_Initialize()
# 创建相机实例
cam = HiKGidECamera(192,168,100,100) # 创建基于IP地址的相机实例
ret = cam.log_on() # 注册相机
if ret == 0:
cam.open_device() # 打开相机
ret = cam.obj_cam_operation.Set_trigger_mode(True) # 设置软触发模式
cam.start_grabbing() # 开始取流
i = 0
while cam.isGrabbing:
cam.trigger_once() # 软触发一次
cam.obj_cam_operation.b_save_bmp = True # 将保存图像标志置位
print("保存成功", ret)
time.sleep(1)
i += 1
if i == 3:
cam.stop_grabbing() # 停止取流
cam.close_device() # 关闭相机
else:
print("相机注册失败或不在线")
# 反初始化SDK
MvCamera.MV_CC_Finalize()
需要注意的是,厂家提供的SDK中,CamOperation_class.py的这两行代码有点小bug,修改一下才能正常保存。

修改后的代码:
python
# 释放缓存
# self.obj_cam.MV_CC_FreeImageBuffer(stOutFrame)
# 在释放缓存之前先保存
if self.b_save_bmp: # 如果有保存需求
self.Save_Bmp() # 执行保存
self.b_save_bmp = False # 复位保存标志
if self.b_save_jpg:
self.Save_jpg()
self.b_save_jpg = False
# 释放缓存
self.obj_cam.MV_CC_FreeImageBuffer(stOutFrame)
原代码的问题在于:获取到帧以后立即就将缓存释放了,所以无法进行保存操作。