雙相機結合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.pswp.cn/diannao/77426.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77426.shtml
英文地址,請注明出處:http://en.pswp.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…