目錄

  • 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接口

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#計算機視覺

產品型號

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

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#計算機視覺_02

1.2、線纜

這裏使用的是Micro USB3.0(B型)線纜,請自配。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#數碼相機_03

1.3、鏡頭

選用合適鏡頭,這裏使用的是25mm焦距鏡頭,請自配。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#計算機視覺_04

1.4、光源及控制器

若環境光有限,請調高曝光或供外部光。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#數碼相機_05

2、MVS軟件準備

2.1、MVS下載

下載鏈接:海康官網MVS下載

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#opencv_06

2.2、MVS安裝

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#python_07

  1. 下載下來exe安裝包,Windows為例;
  2. 雙擊運行;
  3. 點擊開始安裝;
  4. 選擇安裝路徑,所有都勾選上後點擊下一步;
  5. 等待安裝……
  6. 安裝好後,取消勾選完成後打開軟件和打開發行説明,點擊完成。

2.3、MVS示例

MVS提供了很多二次開發的接口,通過下面路徑可以找到有關Python開發的庫文件和接口,先 Ctrl C 複製一份出來(共5個py文件)。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#計算機視覺_08

3、PyCharm工程代碼

3.1、導入Python示例文件

  1. 新建一個PyCharm項目,這裏Python版本用的是3.8.2。
  2. 輸入項目名稱、項目路徑,選擇Python解釋器,點擊創建。

    將前面複製的5個py文件,拷貝粘貼到工程下。

3.2、創建主程序.py

新建一個py文件,作為控制的主程序,後續的代碼編寫都在主程序內。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#python_09

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#數碼相機_10


基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#相機_11

記得要安裝下面兩個庫

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,在鏡頭下方放置一個條形碼。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#相機_12


通過PyCharm的終端控制枱可以看到解碼成功並返回解碼內容和數據。

基於海康機器人工業相機調用SDK(MVIDCodeReader)二次開發條碼識別_機器人工業讀碼器sdk_qq_#opencv_13

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("資源已釋放")