双相机结合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地址)需提前设置。
2. **资源泄漏风险**:
   - 必须在`using`块或`Dispose()`中调用`CloseImageAcq()`。
3. **性能优化**:
   - 双相机拼接可能增加处理时间,需根据场景调整。

---

### **典型使用流程**
```csharp
// 1. 初始化类并设置回调
var scanner = new GeneralBarcodeScan();
scanner.setCallback(new MyCcdCallback());

// 2. 打开相机(假设单相机)
scanner.OpenImageAcq(window, 1280, 720, 1);

// 3. 开始检测
scanner.StartScanBarcode(1);

// 4. 关闭资源
scanner.Dispose();
```

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

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 };}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/77426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python基础语法12-迭代器与生成器

Python 生成器与迭代器详解 在 Python 中&#xff0c;生成器和迭代器是处理大量数据时的强大工具。它们能够帮助我们节省内存&#xff0c;避免一次性加载过多数据。生成器通过 yield 关键字实现&#xff0c;允许我们逐步产生数据&#xff0c;而迭代器通过实现特定的接口&#…

公司内部建立pypi源

有一篇建立apt源的文章在这里&#xff0c;需要的可以查看&#xff1a;公司内部建立apt源-CSDN博客 server: pip install pypiserver mkdir -d pypi/packages cp test.whl pypi/packages pypi-server run --port 8080 /home/xu/pypi/packages & 网页访问&#xff1a;http:…

VMware Workstation/Player 的详细安装使用指南

以下是 VMware Workstation/Player 的完整下载、安装指南&#xff0c;包含详细步骤、常见问题及解决方法&#xff0c;以及进阶使用技巧&#xff0c;适用于 Windows 和 macOS 用户。 VMware Workstation/Player 的详细安装使用指南—目录 一、下载与安装详细指南1. 系统要求2. 下…

蓝桥杯python组考前准备

1.保留k位小数 round(10/3, 2) # 第二个参数表示保留几位小数 2.输入代替方案&#xff08;加速读取&#xff09; import sys n int(sys.stdin.readline()) # 读取整数&#xff08;不加int就是字符串&#xff09; a, b map(int, sys.stdin.readline().split()) # 一行读取多个…

【JSON2WEB】16 login.html 登录密码加密传输

【JSON2WEB】系列目录 【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSO…

计算机网络起源

互联网的起源和发展是一个充满创新、突破和变革的历程&#xff0c;从20世纪60年代到1989年&#xff0c;这段时期为互联网的诞生和普及奠定了坚实的基础。让我们详细回顾这一段激动人心的历史。 计算机的发展与ARPANET的建立&#xff08;20世纪60年代&#xff09; 互联网的诞生…

洛谷P1824进击的奶牛简单二分

题目如下 代码如下 谢谢观看

如何建立高效的会议机制

建立高效的会议机制需做到&#xff1a;明确会议目标、制定并提前分发议程、控制会议时长、确保有效沟通与反馈、及时跟进执行情况。其中&#xff0c;明确会议目标是核心关键&#xff0c;它直接决定了会议的方向与效率。只有明确目标&#xff0c;会议才不会偏离初衷&#xff0c;…

开源AI大模型AI智能名片S2B2C商城小程序:科技浪潮下的商业新引擎

摘要&#xff1a; 本文聚焦于科技迅猛发展背景下&#xff0c;开源AI大模型、AI智能名片与S2B2C商城小程序的融合应用。通过分析元宇宙、人工智能、区块链、5G等前沿科技带来的商业变革&#xff0c;阐述开源AI大模型AI智能名片S2B2C商城小程序在整合资源、优化服务、提升用户体验…

基于大模型构建金融客服的技术调研

OpenAI-SB api接口 https://openai-sb.com/ ChatGPT与Knowledge Graph (知识图谱)分享交流 https://www.bilibili.com/video/BV1bo4y1w72m/?spm_id_from333.337.search-card.all.click&vd_source569ef4f891360f2119ace98abae09f3f 《要研究的方向和准备》 https://ww…

WSA(Windows Subsystem for Android)安装LSPosed和应用教程

windows安卓子系统WSA的Lsposed和shamiko的安装教程 WSA(Windows Subsystem for Android)安装LSPosed和应用教程 一、环境准备 在开始之前,请确保: 已经安装好WSA(Windows Subsystem for Android)已经安装好ADB工具下载好LSPosed和Shamiko框架安装包 二、连接WSA 首先需要…

辛格迪客户案例 | 河南宏途食品实施电子合约系统(eSign)

01 河南宏途食品有限公司&#xff1a;食品行业的数字化践行者 河南宏途食品有限公司&#xff08;以下简称“宏途食品”&#xff09;作为国内食品行业的创新企业&#xff0c;专注于各类食品的研发、生产和销售。公司秉承“质量为先、创新驱动、服务至上”的核心价值观&#xff…

手机静态ip地址怎么获取?方法与解析‌

而在某些特定情境下&#xff0c;我们可能需要为手机设置一个静态IP地址。本文将详细介绍手机静态IP地址详解及获取方法 一、什么是静态IP地址&#xff1f; 静态IP&#xff1a;由用户手动设置的固定IP地址&#xff0c;不会因网络重启或设备重连而改变。 动态IP&#xff1a;由路…

天下飞飞【老飞飞服务端】+客户端+数据库测试带视频教程

天下飞飞服务器搭建测试视频 天下飞飞【老飞飞服务端】客户端数据库测试带视频教程 完整安装教程。 测试环境 系统server2019 sql2022数据库 sql的安装 odbc搭建 sql加载数据库 此测试端能用于服务器搭建测试。 下载地址为&#xff1a;https://download.csdn.net/d…

Gitea的安装和配置以及应用

Gitea的安装和配置以及应用 一、安装 1、创建数据库和数据库账户&#xff08;pg&#xff09; su – postgres -c "psql" CREATE ROLE gitea WITH LOGIN PASSWORD gitea; CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE …

如何更改OCP与metadb集群的连接方式 —— OceanBase运维管理

背景 许多用户都会借助OCP平台来进行OceanBase集群的运维与监控&#xff0c;且因为考虑单节点的OCP部署&#xff0c;在遇故障时可能会短时间出现无法管控 OceanBase集群&#xff0c;多数用户倾向于采用多节点方式来部署OCP&#xff0c;即 OCP的 metadb集群也是三节点的集群部署…

SpringDoc【使用详解】

SpringDoc使用详解 一、何为SpringDoc二、概念解释三、SpringDoc使用2.1简单集成2.2 配置SpringDoc2.2.1 yml方式配置2.2.2配置文档信息 2.3配置文档分组2.4使用注解2.4.1 Tag2.4.2 Operation2.4.3 Schema2.4.4 NotNull2.4.5 Parameter2.4.6 Parameters2.4.7 ApiResponses 和Ap…

PHP 阿里云oss 使用指南

1.介绍 把图片放到阿里云上的空间上&#xff0c;可以使用cdn加速。 可以在程序里直接调用 要使用阿里云 oss sdk &#xff0c;请先到阿里云下载 或用 copmposer 安装 相关链接&#xff1a; 安装OSS PHP SDK_对象存储(OSS)-阿里云帮助中心 composer require aliyuncs/oss…

【AI提示词】常青笔记生成器

提示说明 生成适用于多个场景和领域的常青笔记&#xff0c;满足“常青笔记”的核心特性和结构。 提示词 # 角色: 常青笔记生成器## 角色简介: - 作者: xxx - 版本号: 1.0 - 更新时间: xxx - 语言: 中文## 定位: - &#x1f3af; 生成适用于多个场景和领域的常青笔记&#x…

在 Lua 中,`for` 和 `while` 是两种核心的循环结构的详细用法

在 Lua 中&#xff0c;for 和 while 是两种核心的循环结构&#xff0c;用于实现重复执行逻辑。以下是它们的详细用法、进阶技巧及注意事项&#xff1a; 在 Lua 中&#xff0c;for 和 while 是两种核心的循环结构的详细用法—目录 一、for 循环1. 数值 for 循环基础语法&#xf…