目錄
- 1、相機硬件準備
- 1.1、相機
- 1.2、線纜
- 1.3、鏡頭
- 1.4、光源及控制器
- 2、MVS軟件準備
- 2.1、MVS下載
- 2.2、MVS安裝
- 2.3、MVS示例
- 3、PyCharm工程代碼
- 3.1、導入Python示例文件
- 3.2、創建主程序.py
- 3.3、pip下載pyzbar庫
- 3.4、解碼功能代碼
- 3.5、解碼效果展示
- 3.6、完整代碼展示
1、相機硬件準備
1.1、相機
這裏使用的是海康機器人的 MV-CH120-60UM USB面陣相機。
注意:僅支持USB3.0接口
|
產品型號
|
MV-CH120-60UM
|
|
傳感器型號
|
堆棧式BSI
|
|
靶面尺寸
|
1.1"
|
|
像元尺寸
|
3.45 μm
|
|
快門類型
|
Global(全局)
|
|
分辨率
|
4096 × 3000
|
|
最大幀率
|
30 fps
|
|
曝光時間
|
USE:10μs~19μs NE:20μs~10sec
|
|
典型功耗
|
3.0 W@12 VDC
|
|
鏡頭接口
|
C-Mount
|
|
尺寸圖
|
A
|
1.2、線纜
這裏使用的是Micro USB3.0(B型)線纜,請自配。
1.3、鏡頭
選用合適鏡頭,這裏使用的是25mm焦距鏡頭,請自配。
1.4、光源及控制器
若環境光有限,請調高曝光或供外部光。
2、MVS軟件準備
2.1、MVS下載
下載鏈接:海康官網MVS下載
2.2、MVS安裝
- 下載下來exe安裝包,Windows為例;
- 雙擊運行;
- 點擊開始安裝;
- 選擇安裝路徑,所有都勾選上後點擊下一步;
- 等待安裝……
- 安裝好後,取消勾選完成後打開軟件和打開發行説明,點擊完成。
2.3、MVS示例
MVS提供了很多二次開發的接口,通過下面路徑可以找到有關Python開發的庫文件和接口,先 Ctrl C 複製一份出來(共5個py文件)。
3、PyCharm工程代碼
3.1、導入Python示例文件
- 新建一個PyCharm項目,這裏Python版本用的是3.8.2。
- 輸入項目名稱、項目路徑,選擇Python解釋器,點擊創建。
將前面複製的5個py文件,拷貝粘貼到工程下。
3.2、創建主程序.py
新建一個py文件,作為控制的主程序,後續的代碼編寫都在主程序內。
記得要安裝下面兩個庫
1. pip install opencv
2. pip install numpy
主程序 HiK_CV.py的代碼如下:
import cv2
import sys
import copy
import msvcrt
import numpy as np
from ctypes import *
sys.path.append("./MvImport")
from MvCameraControl_class import *
def enum_devices():
# 枚舉所有連接的相機設備
devicelist = MV_CC_DEVICE_INFO_LIST() # 設備列表
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE # 設備類型
# 枚舉設備
ret = MvCamera.MV_CC_EnumDevices(tlayerType, devicelist)
if ret != 0:
# 在 SDK 約定中,返回值 0 表示 操作成功
print("枚舉設備失敗!ret[0x%x]" % ret)
return None
if devicelist.nDeviceNum == 0:
print("未發現設備!")
return None
print("找到 %d 個設備:" % devicelist.nDeviceNum)
# 輸出設備信息
for i in range(devicelist.nDeviceNum):
# 設備信息
mvcc_dev_info = cast(devicelist.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
# 如果是GIG設備
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print("\nGigE 設備 [%d]" % i)
model_name = ''.join(chr(c) for c in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName if c != 0)
# 設備IP
ip = mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp
ip_str = '%d.%d.%d.%d' % (
(ip & 0xff000000) >> 24,
(ip & 0x00ff0000) >> 16,
(ip & 0x0000ff00) >> 8,
(ip & 0x000000ff)
)
print("型號名稱:%s" % model_name)
print("IP地址:%s" % ip_str)
# USB設備
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print("\nUSB 設備 [%d]" % i)
model_name = ''.join(chr(c) for c in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName if c != 0)
serial_number = ''.join(chr(c) for c in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber if c != 0)
print("型號名稱:%s" % model_name)
print("序列號:%s" % serial_number)
return devicelist
def create_camera_handle(device_list, index=0):
# 創建相機句柄
if index >= device_list.nDeviceNum:
print("選擇的設備序號超出範圍!")
return None
# 創建相機句柄
cam = MvCamera()
dev_info = cast(device_list.pDeviceInfo[index], POINTER(MV_CC_DEVICE_INFO)).contents
# 創建句柄
ret = cam.MV_CC_CreateHandle(dev_info)
if ret != 0:
print("創建相機句柄失敗!ret[0x%x]" % ret)
return None
ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
if ret != 0:
print("打開設備失敗!ret[0x%x]" % ret)
cam.MV_CC_DestroyHandle()
return None
if dev_info.nTLayerType == MV_GIGE_DEVICE:
# 設置包大小
packet_size = cam.MV_CC_GetOptimalPacketSize()
if int(packet_size) > 0:
ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", packet_size)
if ret != 0:
print("設置包大小失敗!ret[0x%x]" % ret)
else:
print("獲取最佳包大小失敗!ret[0x%x]" % packet_size)
# 關閉觸發模式
ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print("關閉觸發模式失敗!ret[0x%x]" % ret)
return cam
return cam
def grab_and_show_image(cam):
# 獲取一幀圖像並顯示
# 獲取數據包大小
stParam = MVCC_INTVALUE()
memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
if ret != 0:
print ("get payload size fail! ret[0x%x]" % ret)
return False
nPayloadSize = stParam.nCurValue
# 開始取流
ret = cam.MV_CC_StartGrabbing()
if ret != 0:
print ("start grabbing fail! ret[0x%x]" % ret)
return False
stDeviceList = MV_FRAME_OUT_INFO_EX()
memset(byref(stDeviceList), 0, sizeof(stDeviceList))
data_buf = (c_ubyte * nPayloadSize)()
ret = cam.MV_CC_GetOneFrameTimeout(byref(data_buf), nPayloadSize, stDeviceList, 1000)
if ret == 0:
print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" %
(stDeviceList.nWidth, stDeviceList.nHeight, stDeviceList.nFrameNum))
width = stDeviceList.nWidth
height = stDeviceList.nHeight
pixel_type = stDeviceList.enPixelType
# 將 c_ubyte 緩衝區轉為 numpy 數組
data_array = np.ctypeslib.as_array(data_buf)
# 根據不同像素格式處理圖像
if pixel_type == PixelType_Gvsp_Mono8:
# 灰度圖直接 reshape
frame = data_array.reshape(height, width)
elif pixel_type == PixelType_Gvsp_BayerRG8:
# Bayer RG 轉 BGR
frame = data_array.reshape(height, width)
frame = cv2.cvtColor(frame, cv2.COLOR_BAYER_RG2BGR)
else:
print("Unsupported pixel format: 0x%x" % pixel_type)
return False
# 使用 OpenCV 顯示圖像
frame = cv2.resize(frame, (640, 480))
cv2.imshow("Camera Frame", frame)
cv2.waitKey(0) # 按任意鍵關閉窗口
return True
if __name__ == "__main__":
# 枚舉設備
device_list = enum_devices()
if not device_list:
sys.exit()
# 創建相機句柄
cam = create_camera_handle(device_list, 0)
if not cam:
sys.exit()
nConnectionNum = 0
if int(nConnectionNum) >= device_list.nDeviceNum:
print ("intput error!")
sys.exit()
# 獲取圖像
success = grab_and_show_image(cam)
if not success:
print("圖像採集/顯示失敗")
# 清理資源
cam.MV_CC_StopGrabbing()
cam.MV_CC_CloseDevice()
cam.MV_CC_DestroyHandle()
print("資源已釋放")
3.3、pip下載pyzbar庫
pip install pyzbar #Python第三方庫,用於解條形碼
3.4、解碼功能代碼
import pyzbar.pyzbar as pyzbar
# 解碼功能
# 圖像為彩色(3通道)
if len(frame.shape) == 2:
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
# 解碼並繪製四邊形框
if frame is not None:
decoded_objects = pyzbar.decode(frame)
for i, obj in enumerate(decoded_objects):
# 獲取原始頂點座標
polygon = obj.polygon
points = np.array(polygon, dtype=np.int32).reshape((-1, 2))
# 確保頂點數量為四個
if len(points) != 4:
# 使用boundingRect獲取矩形區域
x, y, w, h = cv2.boundingRect(points)
points = np.array([[x, y], [x + w, y], [x + w, y + h], [x, y + h]], dtype=np.int32)
# 繪製綠色四邊形框
cv2.polylines(frame, [points], isClosed=True, color=(0, 255, 0), thickness=10)
# 顯示序號
cv2.putText(frame, f"{i + 1}", tuple(points[0]),
cv2.FONT_HERSHEY_DUPLEX, 8, (0, 255, 0), 5)
# 打印解碼信息
print(f"條形碼{i + 1}:\t 類型:{obj.type}\t 數據:{obj.data.decode('utf-8')}\t 頂點座標: {obj.polygon}")
3.5、解碼效果展示
運行HiK_CV.py,在鏡頭下方放置一個條形碼。
通過PyCharm的終端控制枱可以看到解碼成功並返回解碼內容和數據。
3.6、完整代碼展示
添加了解析條形碼功能後的 HiK_CV.py代碼如下:
import cv2
import sys
import copy
import msvcrt
import numpy as np
import pyzbar.pyzbar as pyzbar
from ctypes import *
sys.path.append("./MvImport")
from MvCameraControl_class import *
def enum_devices():
# 枚舉所有連接的相機設備
devicelist = MV_CC_DEVICE_INFO_LIST() # 設備列表
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE # 設備類型
# 枚舉設備
ret = MvCamera.MV_CC_EnumDevices(tlayerType, devicelist)
if ret != 0:
# 在 SDK 約定中,返回值 0 表示 操作成功
print("枚舉設備失敗!ret[0x%x]" % ret)
return None
if devicelist.nDeviceNum == 0:
print("未發現設備!")
return None
print("找到 %d 個設備:" % devicelist.nDeviceNum)
# 輸出設備信息
for i in range(devicelist.nDeviceNum):
# 設備信息
mvcc_dev_info = cast(devicelist.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
# 如果是GIG設備
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print("\nGigE 設備 [%d]" % i)
model_name = ''.join(chr(c) for c in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName if c != 0)
# 設備IP
ip = mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp
ip_str = '%d.%d.%d.%d' % (
(ip & 0xff000000) >> 24,
(ip & 0x00ff0000) >> 16,
(ip & 0x0000ff00) >> 8,
(ip & 0x000000ff)
)
print("型號名稱:%s" % model_name)
print("IP地址:%s" % ip_str)
# USB設備
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print("\nUSB 設備 [%d]" % i)
model_name = ''.join(chr(c) for c in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName if c != 0)
serial_number = ''.join(chr(c) for c in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber if c != 0)
print("型號名稱:%s" % model_name)
print("序列號:%s" % serial_number)
return devicelist
def create_camera_handle(device_list, index=0):
# 創建相機句柄
if index >= device_list.nDeviceNum:
print("選擇的設備序號超出範圍!")
return None
# 創建相機句柄
cam = MvCamera()
dev_info = cast(device_list.pDeviceInfo[index], POINTER(MV_CC_DEVICE_INFO)).contents
# 創建句柄
ret = cam.MV_CC_CreateHandle(dev_info)
if ret != 0:
print("創建相機句柄失敗!ret[0x%x]" % ret)
return None
ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
if ret != 0:
print("打開設備失敗!ret[0x%x]" % ret)
cam.MV_CC_DestroyHandle()
return None
if dev_info.nTLayerType == MV_GIGE_DEVICE:
# 設置包大小
packet_size = cam.MV_CC_GetOptimalPacketSize()
if int(packet_size) > 0:
ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize", packet_size)
if ret != 0:
print("設置包大小失敗!ret[0x%x]" % ret)
else:
print("獲取最佳包大小失敗!ret[0x%x]" % packet_size)
# 關閉觸發模式
ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print("關閉觸發模式失敗!ret[0x%x]" % ret)
return cam
return cam
def grab_and_show_image(cam):
# 獲取一幀圖像並顯示
# 獲取數據包大小
stParam = MVCC_INTVALUE()
memset(byref(stParam), 0, sizeof(MVCC_INTVALUE))
ret = cam.MV_CC_GetIntValue("PayloadSize", stParam)
if ret != 0:
print("get payload size fail! ret[0x%x]" % ret)
return False
nPayloadSize = stParam.nCurValue
# 開始取流
ret = cam.MV_CC_StartGrabbing()
if ret != 0:
print("start grabbing fail! ret[0x%x]" % ret)
return False
stDeviceList = MV_FRAME_OUT_INFO_EX()
memset(byref(stDeviceList), 0, sizeof(stDeviceList))
data_buf = (c_ubyte * nPayloadSize)()
ret = cam.MV_CC_GetOneFrameTimeout(byref(data_buf), nPayloadSize, stDeviceList, 1000)
if ret == 0:
print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" %
(stDeviceList.nWidth, stDeviceList.nHeight, stDeviceList.nFrameNum))
width = stDeviceList.nWidth
height = stDeviceList.nHeight
pixel_type = stDeviceList.enPixelType
# 將 c_ubyte 緩衝區轉為 numpy 數組
data_array = np.ctypeslib.as_array(data_buf)
# 根據不同像素格式處理圖像
if pixel_type == PixelType_Gvsp_Mono8:
# 灰度圖直接 reshape
frame = data_array.reshape(height, width)
elif pixel_type == PixelType_Gvsp_BayerRG8:
# Bayer RG 轉 BGR
frame = data_array.reshape(height, width)
frame = cv2.cvtColor(frame, cv2.COLOR_BAYER_RG2BGR)
else:
print("Unsupported pixel format: 0x%x" % pixel_type)
return False
# 解碼功能
# 圖像為彩色(3通道)
if len(frame.shape) == 2:
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
# 解碼並繪製四邊形框
if frame is not None:
decoded_objects = pyzbar.decode(frame)
for i, obj in enumerate(decoded_objects):
# 獲取原始頂點座標
polygon = obj.polygon
points = np.array(polygon, dtype=np.int32).reshape((-1, 2))
# 確保頂點數量為四個
if len(points) != 4:
# 使用boundingRect獲取矩形區域
x, y, w, h = cv2.boundingRect(points)
points = np.array([[x, y], [x + w, y], [x + w, y + h], [x, y + h]], dtype=np.int32)
# 繪製綠色四邊形框
cv2.polylines(frame, [points], isClosed=True, color=(0, 255, 0), thickness=10)
# 顯示序號
cv2.putText(frame, f"{i + 1}", tuple(points[0]),
cv2.FONT_HERSHEY_DUPLEX, 8, (0, 255, 0), 5)
# 打印解碼信息
print(f"條形碼{i + 1}:\t 類型:{obj.type}\t 數據:{obj.data.decode('utf-8')}\t 頂點座標: {obj.polygon}")
# 調整圖像窗口大小
frame = cv2.resize(frame, (640, 480))
cv2.imshow("Camera Frame", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
return True
if __name__ == "__main__":
# 枚舉設備
device_list = enum_devices()
if not device_list:
sys.exit()
# 創建相機句柄
cam = create_camera_handle(device_list, 0)
if not cam:
sys.exit()
nConnectionNum = 0
if int(nConnectionNum) >= device_list.nDeviceNum:
print("intput error!")
sys.exit()
# 獲取圖像
success = grab_and_show_image(cam)
if not success:
print("圖像採集/顯示失敗")
# 清理資源
cam.MV_CC_StopGrabbing()
cam.MV_CC_CloseDevice()
cam.MV_CC_DestroyHandle()
print("資源已釋放")