双相机结合halcon的条码检测

以下是针对提供的C#代码的详细注释和解释,结合Halcon库的功能和代码结构进行说明:


**代码整体结构**

该代码是一个基于Halcon库的条码扫描类`GeneralBarcodeScan`,支持单台或双台相机的条码检测,并通过回调接口返回结果。主要功能包括:

  1. 相机初始化与图像采集。

  2. 条码检测模型配置(静区、容错等)。

  3. 单/双相机图像处理与条码识别。

  4. 资源管理(释放相机、图像对象等)。


**关键代码注释**

**1. 类定义与成员变量**

```csharp

public class GeneralBarcodeScan : IDisposable

{

private string mLastErrorMsg; // 存储最后的错误信息

private ICcdCallback mCallback; // 相机回调接口,用于处理扫描到的条码数据

private HTuple mHvAcqHandle; // 第一台相机的句柄

private HTuple mHvAcqHandle2; // 第二台相机的句柄

private HObject mHoImage; // 存储第一台相机捕获的图像

private HObject mHoImage2; // 存储第二台相机捕获的图像

// ... 其他窗口相关变量 ...

}

```

  • **功能**:定义类的成员变量,包括相机句柄、图像对象、错误信息和回调接口。

  • **关键点**:`HTuple`是Halcon中常用的参数传递类型,`HObject`用于存储图像数据。


**2. 构造函数与资源初始化**

```csharp

public GeneralBarcodeScan()

{

mHvAcqHandle = new HTuple(); // 初始化相机句柄

mHvAcqHandle2 = new HTuple();

HOperatorSet.GenEmptyObj(out mHoImage); // 创建空图像对象

HOperatorSet.GenEmptyObj(out mHoImage2);

// 设置系统默认图像大小为512x512

HOperatorSet.SetSystem("width", 512);

HOperatorSet.SetSystem("height", 512);

// 初始化第二个窗口的尺寸为0

mWndWidth2 = 0;

mWndHeight2 = 0;

}

```

  • **功能**:初始化类成员变量,并设置Halcon的默认图像尺寸。

  • **关键函数**:

  • `GenEmptyObj`:创建空的`HObject`对象。

  • `SetSystem`:设置Halcon系统参数(如默认图像尺寸)。


**3. 相机打开与初始化方法**

```csharp

public bool OpenImageAcq(HWindow hHalconWnd, int wndWidth, int wndHeight, int deviceQty)

{

this.mHPreviewWnd = hHalconWnd; // 设置显示窗口

this.mWndWidth = wndWidth;

this.mWndHeight = wndHeight;

// ... 省略部分代码 ...

// 获取相机设备信息

HOperatorSet.InfoFramegrabber("GigEVision2", "device", out hvDeviceInfo, out hvDeviceInfoValues);

if (hvDeviceInfoValues.Length == 0) // 检查设备是否存在

{

this.mLastErrorMsg = "获取不到相机设备";

return false;

}

// 打开第一台相机

HOperatorSet.OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive",

-1, "default", -1, "false", "default", hvDeviceInfoValues.TupleSelect(0), 0, -1, out mHvAcqHandle);

// 设置相机超时参数

HOperatorSet.SetFramegrabberParam(mHvAcqHandle, "grab_timeout", 2000);

HOperatorSet.GrabImageStart(mHvAcqHandle, -1); // 开始异步图像采集

// ... 双相机处理逻辑 ...

return true;

}

```

  • **功能**:打开相机并初始化参数,支持单/双相机配置。

  • **关键函数**:

  • `OpenFramegrabber`:通过GigE Vision协议连接相机。

  • `GrabImageStart`:开始异步图像采集(`-1`表示连续采集)。

  • **参数说明**:

  • `"GigEVision2"`:指定相机协议类型。

  • `hvDeviceInfoValues.TupleSelect(0)`:选择第一个检测到的相机设备。


**4. 相机关闭与资源释放**

```csharp

public void CloseImageAcq()

{

if (this.mHoImage != null)

{

this.mHoImage.Dispose(); // 释放图像对象

this.mHoImage = null;

}

if (this.mHvAcqHandle != null)

{

HOperatorSet.CloseFramegrabber(this.mHvAcqHandle); // 关闭相机

this.mHvAcqHandle.Dispose();

this.mHvAcqHandle = null;

}

// ... 处理第二个相机和窗口 ...

}

```

  • **功能**:释放相机句柄、图像对象和窗口资源。

  • **关键函数**:

  • `CloseFramegrabber`:关闭相机连接。

  • `Dispose`:释放Halcon对象资源。


**5. 条码扫描核心逻辑(单相机)**

```csharp

public void StartScanBarcode(int qty)

{

List<string> barcodeList = new List<string>();

HObject hoImage; // 当前捕获的图像

HTuple hvBarCodeHandle; // 条码检测模型句柄

// ... 省略部分代码 ...

// 创建条码检测模型并配置参数

HOperatorSet.CreateBarCodeModel(new HTuple("quiet_zone"), new HTuple("true"), out hvBarCodeHandle);

HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "majority_voting", "true"); // 启用多数表决

HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "element_size_variable", "true"); // 允许模块尺寸变化

// 捕获图像并检测条码

HOperatorSet.GrabImageAsync(out hoImage, mHvAcqHandle, -1); // 异步获取图像

HOperatorSet.FindBarCode(hoImage, out hoSymbolRegions, hvBarCodeHandle, "Code 128", out hvDecodedDataStrings);

// 处理检测结果

for (int i = 0; i < hvDecodedDataStrings.Length; i++)

{

barcodeList.Add(hvDecodedDataStrings[i]); // 存储解码后的条码数据

}

// 释放资源并触发回调

if (mCallback != null) mCallback.FoundBarcode(barcodeList); // 通知回调函数

}

```

  • **功能**:捕获图像并检测Code 128条码,通过回调返回结果。

  • **关键步骤**:

  1. **创建检测模型**:`CreateBarCodeModel`配置静区检测。

  2. **设置参数**:启用多数表决(减少误检)、允许模块尺寸变化(适应变形条码)。

  3. **图像采集**:`GrabImageAsync`异步获取单张图像。

  4. **条码检测**:`FindBarCode`返回解码结果。

  • **参数解释**:

  • `quiet_zone`:检测条码周围的空白区域,提升稳定性。

  • `majority_voting`:通过多条扫描线投票选择最终解码结果。


**6. 双相机拼接与检测**

```csharp

public void StartScanBarcodeBy2Device(bool reverse)

{

HObject hoImage, hoImage2, hoImages; // 两台相机的图像

// ... 省略部分代码 ...

// 捕获并拼接图像

HOperatorSet.GrabImageAsync(out hoImage, mHvAcqHandle, -1);

HOperatorSet.GrabImageAsync(out hoImage2, mHvAcqHandle2, -1);

if (reverse) // 根据参数决定拼接顺序

{

HOperatorSet.ConcatObj(hoImages, hoImage2, out hoImages);

HOperatorSet.ConcatObj(hoImages, hoImage, out hoImages);

}

else

{

HOperatorSet.ConcatObj(hoImages, hoImage, out hoImages);

HOperatorSet.ConcatObj(hoImages, hoImage2, out hoImages);

}

HOperatorSet.TileImages(hoImages, out hoObjectsConcat, 2, "horizontal"); // 水平拼接

// 执行检测(后续步骤与单相机相同)

}

```

  • **功能**:同时捕获双相机图像并拼接,再进行条码检测。

  • **关键函数**:

  • `ConcatObj`:合并图像对象。

  • `TileImages`:将图像拼接成单个图像(`horizontal`表示水平方向)。

  • **参数`reverse`**:控制图像拼接的顺序(是否反转双相机的顺序)。


**7. 窗口适配与显示**

```csharp

private HTuple[] GetDisplayRect(int wndWidth, int wndHeight, int imageWidth, int imageHeight)

{

// 计算图像在窗口中的显示区域,保持比例

double widthRatio = (double)imageWidth / wndWidth;

double heightRatio = (double)imageHeight / wndHeight;

HTuple row1, column1, row2, column2;

if (widthRatio >= heightRatio) // 宽度占优,以宽度比例缩放

{

row1 = -(wndHeight * widthRatio - imageHeight) / 2;

column1 = 0;

row2 = row1 + wndHeight * widthRatio;

column2 = column1 + wndWidth * widthRatio;

}

else // 高度占优,以高度比例缩放

{

row1 = 0;

column1 = -(wndWidth * heightRatio - imageWidth) / 2;

row2 = row1 + wndHeight * heightRatio;

column2 = column1 + wndWidth * heightRatio;

}

return new HTuple[] { row1, column1, row2, column2 };

}

```

  • **功能**:根据窗口尺寸计算图像显示区域,保持图像比例。

  • **实现逻辑**:

  • 计算宽高比,选择占优方向进行缩放。

  • 调整显示区域的坐标,确保图像居中显示。


**关键函数与参数说明**

**Halcon函数**

| 函数名 | 功能 | 参数示例 |

|--------|------|----------|

| `OpenFramegrabber` | 打开相机 | `"GigEVision2"`, 设备标识符 |

| `GrabImageAsync` | 异步抓取图像 | `out HObject`, 相机句柄 |

| `CreateBarCodeModel` | 创建条码检测模型 | `'quiet_zone'`, `'true'` |

| `FindBarCode` | 检测图像中的条码 | `'Code 128'` |

| `TileImages` | 拼接图像 | `'horizontal'` |

**参数解释**

| 参数名 | 作用 |

|--------|------|

| `quiet_zone` | 启用条码周围空白区域检测 |

| `majority_voting` | 启用多条扫描线投票机制 |

| `element_size_variable` | 允许条码模块尺寸变化(适应变形条码) |

| `start_stop_tolerance` | 设置起始符/终止符的容错级别 |


**代码优势与注意事项**

**优势**

  1. **资源管理**:通过`IDisposable`接口确保相机和图像对象的正确释放。

  2. **双相机支持**:可扩展至多相机协同检测,提升检测范围。

  3. **条码检测优化**:通过静区和容错参数提升鲁棒性。

**注意事项**

  1. **相机配置**:
  • 需确保相机型号与`GigEVision2`协议兼容。

  • 网络配置(如IP地址)需提前设置。

  1. **资源泄漏风险**:
  • 必须在`using`块或`Dispose()`中调用`CloseImageAcq()`。
  1. **性能优化**:
  • 双相机拼接可能增加处理时间,需根据场景调整。

**典型使用流程**

```csharp

// 1. 初始化类并设置回调

var scanner = new GeneralBarcodeScan();

scanner.setCallback(new MyCcdCallback());

// 2. 打开相机(假设单相机)

scanner.OpenImageAcq(window, 1280, 720, 1);

// 3. 开始检测

scanner.StartScanBarcode(1);

// 4. 关闭资源

scanner.Dispose();

```

通过以上注释和解释,开发者可以清晰理解代码功能、参数含义及实现逻辑。

cs 复制代码
using HalconDotNet;  // 引用 Halcon 图像处理库
using Hggit.Hwodc.Common;  // 引用其他库
using System;  // 引用系统库
using System.Collections.Generic;  // 引用集合类库
using System.Linq;  // 引用 LINQ 查询库
using System.Text;  // 引用字符串处理类库
using System.Threading.Tasks;  // 引用异步任务处理库

namespace Hggit.Hwodc.Halcon
{
    public class GeneralBarcodeScan : IDisposable
    {
        private string mLastErrorMsg;  // 保存最后的错误信息

        private ICcdCallback mCallback;  // 相机回调接口,用于处理扫描到的条形码

        /// <summary>
        /// 相机句柄,用于相机图像捕获
        /// </summary>
        private HTuple mHvAcqHandle;

        /// <summary>
        /// 第二个相机句柄
        /// </summary>
        private HTuple mHvAcqHandle2;

        /// <summary>
        /// 捕获的图像对象
        /// </summary>
        private HObject mHoImage;

        /// <summary>
        /// 第二个捕获的图像对象
        /// </summary>
        private HObject mHoImage2;

        /// <summary>
        /// 窗口的宽度
        /// </summary>
        private int mWndWidth;

        /// <summary>
        /// 窗口的高度
        /// </summary>
        private int mWndHeight;

        /// <summary>
        /// Halcon 图像显示窗口
        /// </summary>
        private HWindow mHPreviewWnd;

        /// <summary>
        /// Halcon 图像显示窗口内部句柄
        /// </summary>
        HTuple mHvWindowHandle;

        /// <summary>
        /// 第二个窗口宽度
        /// </summary>
        private int mWndWidth2;

        /// <summary>
        /// 第二个窗口高度
        /// </summary>
        private int mWndHeight2;

        /// <summary>
        /// 第二个窗口的句柄
        /// </summary>
        HTuple mHvWindowHandle2;

        public void Dispose()
        {
            // 清理资源,关闭相机及图像对象
            CloseImageAcq();
        }

        public GeneralBarcodeScan()
        {
            // 初始化相机句柄和图像对象
            mHvAcqHandle = new HTuple();
            mHvAcqHandle2 = new HTuple();
            HOperatorSet.GenEmptyObj(out mHoImage);
            HOperatorSet.GenEmptyObj(out mHoImage2);

            // 设置系统默认图像大小
            HOperatorSet.SetSystem("width", 512);
            HOperatorSet.SetSystem("height", 512);

            mWndWidth2 = 0;
            mWndHeight2 = 0;
        }

        public void setCallback(ICcdCallback callback)
        {
            // 设置回调接口
            this.mCallback = callback;
        }

        /// <summary>
        /// 打开相机并初始化图像采集
        /// </summary>
        /// <returns>返回是否成功打开相机</returns>
        public bool OpenImageAcq(HWindow hHalconWnd, int wndWidth, int wndHeight, int deviceQty)
        {
            this.mHPreviewWnd = hHalconWnd;
            this.mWndWidth = wndWidth;
            this.mWndHeight = wndHeight;

            // 检查操作系统是否为 Windows,设置线程安全
            if (HalconAPI.isWindows)
                HOperatorSet.SetSystem("use_window_thread", "true");

            HTuple hvDeviceInfo;
            HTuple hvDeviceInfoValues;

            // 获取相机设备信息
            HOperatorSet.InfoFramegrabber("GigEVision2", "device", out hvDeviceInfo, out hvDeviceInfoValues);

            // 如果没有设备信息,返回错误
            if (hvDeviceInfoValues.Length == 0)
            {
                this.mLastErrorMsg = "获取不到相机设备";
                return false;
            }

            // 检查是否有两个相机设备
            if (deviceQty == 2)
            {
                if (hvDeviceInfoValues.Length < 2)
                {
                    this.mLastErrorMsg = "只检测到了一个相机设备!";
                    return false;
                }
            }

            // 打开第一个相机
            HOperatorSet.OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive",
                -1, "default", -1, "false", "default", hvDeviceInfoValues.TupleSelect(0), 0, -1, out mHvAcqHandle);

            // 设置相机参数
            HOperatorSet.SetFramegrabberParam(mHvAcqHandle, "grab_timeout", 2000);
            HOperatorSet.GrabImageStart(mHvAcqHandle, -1);

            // 打开第二个相机(如果有)
            if (deviceQty == 2)
            {
                HOperatorSet.OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive",
                -1, "default", -1, "false", "default", hvDeviceInfoValues.TupleSelect(1), 0, -1, out mHvAcqHandle2);
                HOperatorSet.SetFramegrabberParam(mHvAcqHandle2, "grab_timeout", 2000);
                HOperatorSet.GrabImageStart(mHvAcqHandle2, -1);
            }

            // 关闭之前打开的窗口
            if (HDevWindowStack.IsOpen())
            {
                var hvWnd = HDevWindowStack.Pop();
                while (hvWnd != null)
                {
                    HOperatorSet.CloseWindow(hvWnd);
                }
            }

            // 打开显示窗口并设置窗口句柄
            HOperatorSet.OpenWindow(0, 0, wndWidth, wndHeight, hHalconWnd, "visible", "", out this.mHvWindowHandle);
            HDevWindowStack.Push(mHvWindowHandle);

            return true;
        }

        public void CloseImageAcq()
        {
            // 清理相机和图像资源
            if (HalconAPI.isWindows)
                HOperatorSet.SetSystem("use_window_thread", "true");

            if (this.mHoImage != null)
            {
                this.mHoImage.Dispose();
                this.mHoImage = null;
            }

            if (this.mHvWindowHandle != null)
            {
                this.mHvWindowHandle.Dispose();
                this.mHvWindowHandle = null;
            }

            if (this.mHvAcqHandle != null)
            {
                HOperatorSet.CloseFramegrabber(this.mHvAcqHandle);
                this.mHvAcqHandle.Dispose();
                this.mHvAcqHandle = null;
            }

            if (mHoImage2 != null)
            {
                this.mHoImage2.Dispose();
                this.mHoImage2 = null;
            }

            if (this.mHvWindowHandle2 != null)
            {
                this.mHvWindowHandle2.Dispose();
                this.mHvWindowHandle2 = null;
            }

            if (this.mHvAcqHandle2 != null)
            {
                HOperatorSet.CloseFramegrabber(this.mHvAcqHandle2);
                this.mHvAcqHandle2.Dispose();
                this.mHvAcqHandle2 = null;
            }
        }

        public string GetLastErrorMsg()
        {
            // 获取最后的错误信息
            return this.mLastErrorMsg;
        }

        public void GrabImage()
        {
            // 异步捕获图像并显示在窗口
            HObject hoImage;
            HTuple imageWidth;
            HTuple imageHeight;

            if (HalconAPI.isWindows)
                HOperatorSet.SetSystem("use_window_thread", "true");

            HOperatorSet.GrabImageAsync(out hoImage, mHvAcqHandle, -1);
            HOperatorSet.GetImageSize(hoImage, out imageWidth, out imageHeight);

            if (HDevWindowStack.IsOpen())
            {
                HTuple[] displayRect = GetDisplayRect(mWndWidth, mWndHeight, imageWidth, imageHeight);
                HOperatorSet.SetPart(HDevWindowStack.GetActive(), displayRect[0], displayRect[1], displayRect[2], displayRect[3]);
            }

            if (HDevWindowStack.IsOpen())
            {
                HOperatorSet.DispImage(hoImage, HDevWindowStack.GetActive());
            }

            if (hoImage != null)
            {
                hoImage.Dispose();
            }

            if (imageWidth != null)
            {
                imageWidth.Dispose();
            }

            if (imageHeight != null)
            {
                imageHeight.Dispose();
            }
        }

        public void StartScanBarcode(int qty)
        {
            // 执行条形码扫描
            List<string> barcodeList = new List<string>();

            HObject hoImage;
            HObject hoObjectsConcat;
            HObject hoSymbolRegions;
            HTuple hvBarCodeHandle;

            HTuple imageWidth;
            HTuple imageHeight;


            HTuple hvWindowHandle;

            int wndWidth, wndHeight;
            if (qty == 1)
            {
                // 如果是单台相机,使用第一个相机窗口和尺寸
                wndWidth = this.mWndWidth;
                wndHeight = this.mWndHeight;
                hvWindowHandle = this.mHvWindowHandle;
            }
            else
            {
                // 如果是双台相机,使用第二个相机窗口和尺寸
                wndWidth = this.mWndWidth2;
                wndHeight = this.mWndHeight2;
                hvWindowHandle = this.mHvWindowHandle2;
            }

            HTuple hvDecodedDataStrings;

            // 创建空对象,用于存放图像和检测到的条形码区域
            HOperatorSet.GenEmptyObj(out hoObjectsConcat);
            HOperatorSet.GenEmptyObj(out hoSymbolRegions);

            // 创建条形码检测模型,指定条形码检测的类型
            HOperatorSet.CreateBarCodeModel("quiet_zone", "true", out hvBarCodeHandle);

            // 设置条形码检测的参数
            HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "majority_voting", "true");  // 启用多数投票
            HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "element_size_variable", "true");  // 启用可变元素大小
            HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "start_stop_tolerance", "low");  // 设置开始/停止容差为低

            // 根据相机数量,选择相应的相机进行图像捕获
            if (qty == 1)
            {
                // 如果只有一台相机,捕获图像
                HOperatorSet.GrabImageAsync(out hoImage, mHvAcqHandle, -1);
            }
            else
            {
                // 如果有两台相机,捕获两台相机的图像
                HOperatorSet.GrabImageAsync(out hoImage, mHvAcqHandle, -1);
            }

            // 获取图像的尺寸
            HOperatorSet.GetImageSize(hoImage, out imageWidth, out imageHeight);

            // 根据窗口尺寸和图像尺寸,计算显示区域
            if (HDevWindowStack.IsOpen())
            {
                HTuple[] displayRect = GetDisplayRect(wndWidth, wndHeight, imageWidth, imageHeight);
                HOperatorSet.SetPart(hvWindowHandle, displayRect[0], displayRect[1], displayRect[2], displayRect[3]);
            }

            // 显示捕获的图像
            if (HDevWindowStack.IsOpen())
            {
                HOperatorSet.DispImage(hoImage, hvWindowHandle);
            }

            // 执行条形码查找
            HOperatorSet.FindBarCode(hoImage, out hoSymbolRegions, hvBarCodeHandle,
                                    "Code 128", out hvDecodedDataStrings);

            // 显示条形码区域
            if (HDevWindowStack.IsOpen())
            {
                HOperatorSet.DispRegion(hoSymbolRegions, hvWindowHandle);
            }

            // 将找到的条形码数据加入列表
            for (int i = 0; i < hvDecodedDataStrings.Length; i++)
            {
                string item = hvDecodedDataStrings[i];
                barcodeList.Add(string.Copy(item));
            }

            // 释放资源
            if (hoObjectsConcat != null)
            {
                hoObjectsConcat.Dispose();
            }

            if (hoSymbolRegions != null)
            {
                hoSymbolRegions.Dispose();
            }

            if (hvBarCodeHandle != null)
            {
                hvBarCodeHandle.Dispose();
            }

            if (hoImage != null)
            {
                hoImage.Dispose();
            }

            if (imageWidth != null)
            {
                imageWidth.Dispose();
            }

            if (imageHeight != null)
            {
                imageHeight.Dispose();
            }

            if (hvDecodedDataStrings != null)
            {
                hvDecodedDataStrings.Dispose();
            }

            // 调用回调函数,将条形码数据返回
            if (mCallback != null)
            {
                mCallback.FoundBarcode(barcodeList);
            }
        }

        public void StartScanBarcodeBy2Device(bool revease)
        {
            // 使用两台相机进行条形码扫描
            List<string> barcodeList = new List<string>();

            HObject hoImage;
            HObject hoImage2;
            HObject hoImages;
            HObject hoObjectsConcat;
            HObject hoSymbolRegions;
            HTuple hvBarCodeHandle;

            HTuple imageWidth;
            HTuple imageHeight;
            HTuple hvWindowHandle;

            int wndWidth, wndHeight;

            // 设置第一个相机的窗口尺寸
            wndWidth = this.mWndWidth;
            wndHeight = this.mWndHeight;
            hvWindowHandle = this.mHvWindowHandle;

            HTuple hvDecodedDataStrings;

            // 创建空对象,用于存放图像和检测到的条形码区域
            HOperatorSet.GenEmptyObj(out hoImage);
            HOperatorSet.GenEmptyObj(out hoImage2);
            HOperatorSet.GenEmptyObj(out hoImages);
            HOperatorSet.GenEmptyObj(out hoObjectsConcat);
            HOperatorSet.GenEmptyObj(out hoSymbolRegions);

            // 创建条形码检测模型
            HOperatorSet.CreateBarCodeModel("quiet_zone", "true", out hvBarCodeHandle);

            // 设置条形码检测的参数
            HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "majority_voting", "true");
            HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "element_size_variable", "true");
            HOperatorSet.SetBarCodeParam(hvBarCodeHandle, "start_stop_tolerance", "low");

            // 捕获第一台相机的图像
            HOperatorSet.GrabImageAsync(out hoImage, mHvAcqHandle, -1);

            // 捕获第二台相机的图像
            HOperatorSet.GrabImageAsync(out hoImage2, mHvAcqHandle2, -1);

            // 根据 `revease` 参数的值,决定图像的拼接顺序
            if (revease)
            {
                HOperatorSet.ConcatObj(hoImages, hoImage2, out hoImages);
                HOperatorSet.ConcatObj(hoImages, hoImage, out hoImages);
            }
            else
            {
                HOperatorSet.ConcatObj(hoImages, hoImage, out hoImages);
                HOperatorSet.ConcatObj(hoImages, hoImage2, out hoImages);
            }

            // 将两台相机的图像拼接在一起
            HOperatorSet.TileImages(hoImages, out hoObjectsConcat, 2, "horizontal");

            // 获取拼接后的图像尺寸
            HOperatorSet.GetImageSize(hoObjectsConcat, out imageWidth, out imageHeight);

            // 根据窗口尺寸和图像尺寸,计算显示区域
            if (HDevWindowStack.IsOpen())
            {
                HTuple[] displayRect = GetDisplayRect(wndWidth, wndHeight, imageWidth, imageHeight);
                HOperatorSet.SetPart(hvWindowHandle, displayRect[0], displayRect[1], displayRect[2], displayRect[3]);
            }

            // 显示拼接后的图像
            if (HDevWindowStack.IsOpen())
            {
                HOperatorSet.DispImage(hoObjectsConcat, hvWindowHandle);
            }

            // 执行条形码查找
            HOperatorSet.FindBarCode(hoObjectsConcat, out hoSymbolRegions, hvBarCodeHandle,
                                    "Code 128", out hvDecodedDataStrings);

            // 显示条形码区域
            if (HDevWindowStack.IsOpen())
            {
                HOperatorSet.DispRegion(hoSymbolRegions, hvWindowHandle);
            }

            // 将找到的条形码数据加入列表
            for (int i = 0; i < hvDecodedDataStrings.Length; i++)
            {
                string item = hvDecodedDataStrings[i];
                barcodeList.Add(string.Copy(item));
            }

            // 释放资源
            if (hoObjectsConcat != null)
            {
                hoObjectsConcat.Dispose();
            }

            if (hoImages != null)
            {
                hoImages.Dispose();
            }

            if (hoSymbolRegions != null)
            {
                hoSymbolRegions.Dispose();
            }

            if (hvBarCodeHandle != null)
            {
                hvBarCodeHandle.Dispose();
            }

            if (hoImage != null)
            {
                hoImage.Dispose();
            }

            if (hoImage2 != null)
            {
                hoImage2.Dispose();
            }

            if (imageWidth != null)
            {
                imageWidth.Dispose();
            }

            if (imageHeight != null)
            {
                imageHeight.Dispose();
            }

            if (hvDecodedDataStrings != null)
            {
                hvDecodedDataStrings.Dispose();
            }

            // 调用回调函数,将条形码数据返回
            if (mCallback != null)
            {
                mCallback.FoundBarcode(barcodeList);
            }
        }

        /// <summary>
        /// 计算显示区域的矩形,以适应图像与窗口的大小比例
        /// </summary>
        /// <param name="wndWidth">窗口宽度</param>
        /// <param name="wndHeight">窗口高度</param>
        /// <param name="imageWidth">图像宽度</param>
        /// <param
        /// <summary>
        /// 计算显示区域的矩形,以适应图像与窗口的大小比例
        /// </summary>
        /// <param name="wndWidth">窗口宽度</param>
        /// <param name="wndHeight">窗口高度</param>
        /// <param name="imageWidth">图像宽度</param>
        /// <param name="imageHeight">图像高度</param>
        /// <returns>显示区域矩形</returns>
        private HTuple[] GetDisplayRect(int wndWidth, int wndHeight, int imageWidth, int imageHeight)
        {
            // 计算图像与窗口的宽高比例
            double widthRatio = (1.0) * imageWidth / wndWidth;
            double heightRatio = (1.0) * imageHeight / wndHeight;

            HTuple row1, colume1, row2, colume2;

            // 如果图像的宽度比高度占优,则以宽度比例为主
            if (widthRatio >= heightRatio)
            {
                row1 = -(1.0) * (wndHeight * widthRatio - imageHeight) / 2;
                colume1 = 0;
                row2 = row1 + wndHeight * widthRatio;
                colume2 = colume1 + wndWidth * widthRatio;
            }
            else
            {
                // 否则,以高度比例为主
                row1 = 0;
                colume1 = -(1.0) * (wndWidth * heightRatio - imageWidth) / 2;
                row2 = row1 + wndHeight * heightRatio;
                colume2 = colume1 + wndWidth * heightRatio;
            }

            // 返回显示区域的四个坐标点(行列)
            return new HTuple[] { row1, colume1, row2, colume2 };
        }
    }
}
相关推荐
中达瑞和-高光谱·多光谱1 天前
多光谱相机:海洋管道漏油(溢油)监测
数码相机
程序员Android1 天前
相机预览闪花屏条纹问题处理方法
数码相机
Spiffo_Sir1 天前
【Spiffo】光速项目:LVGL v9框架下的MIPI简易相机_Part2
linux·c语言·数码相机
东方芷兰1 天前
JavaWeb 课堂笔记 —— 08 请求响应
xml·java·笔记·spring·tomcat·html·idea
爱吃零食的白糖1 天前
索尼相机视频文件格式规格
数码相机
吹35度风1 天前
Spark-Core编程
spark·scala·idea
Microvision维视智造2 天前
突破焊丝虚影干扰,端子焊点缺陷检测如何实现自动化?
数码相机·自动化·视觉检测
中达瑞和-高光谱·多光谱2 天前
多光谱相机:水环境监测(水体富营养化、黑臭水体、藻类水华)
数码相机
石头dhf2 天前
IDEA快速入门
idea
吴梓穆2 天前
UE5 运行时动态将玩家手部模型设置为相机的子物体
数码相机·ue5