这段代码是一个使用 Halcon 图像处理库进行条形码扫描的类实现,名为 `GeneralBarcodeScan`。它包含了多个方法来初始化相机、捕获图像、检测条形码以及在窗口中显示图像。以下是对代码的详细分析:
### 1. **类结构与主要功能**
- **类名**: `GeneralBarcodeScan`
- **功能**: 该类主要用于通过 Halcon 库进行条形码扫描,支持单相机和双相机模式。主要功能包括:
- 初始化相机并打开图像采集。
- 捕获图像并显示在窗口中。
- 检测图像中的条形码。
- 通过回调接口返回扫描到的条形码数据。
- 支持双相机模式,能够同时处理两台相机的图像。
### 2. **主要成员变量**
- `mLastErrorMsg`: 保存最后的错误信息。
- `mCallback`: 相机回调接口,用于处理扫描到的条形码。
- `mHvAcqHandle` 和 `mHvAcqHandle2`: 相机句柄,用于控制相机的图像捕获。
- `mHoImage` 和 `mHoImage2`: 捕获的图像对象。
- `mWndWidth` 和 `mWndHeight`: 窗口的宽度和高度。
- `mHPreviewWnd`: Halcon 图像显示窗口。
- `mHvWindowHandle` 和 `mHvWindowHandle2`: 窗口句柄,用于控制图像显示。
### 3. **主要方法**
- **`OpenImageAcq`**: 打开相机并初始化图像采集。支持单相机和双相机模式。
- 通过 `InfoFramegrabber` 获取相机设备信息。
- 使用 `OpenFramegrabber` 打开相机并设置采集参数。
- 打开显示窗口并设置窗口句柄。
- **`CloseImageAcq`**: 关闭相机并释放资源,确保相机和图像对象被正确清理。
- **`GrabImage`**: 异步捕获图像并显示在窗口中。
- 使用 `GrabImageAsync` 捕获图像。
- 通过 `GetImageSize` 获取图像尺寸。
- 使用 `DispImage` 显示图像。
- **`StartScanBarcode`**: 执行条形码扫描。
- 创建条形码检测模型并设置参数。
- 使用 `FindBarCode` 查找图像中的条形码。
- 将扫描到的条形码数据通过回调接口返回。
- **`StartScanBarcodeBy2Device`**: 使用两台相机进行条形码扫描。
- 捕获两台相机的图像并拼接。
- 使用 `FindBarCode` 查找拼接图像中的条形码。
- 将扫描到的条形码数据通过回调接口返回。
- **`GetDisplayRect`**: 计算显示区域的矩形,确保图像按比例适应窗口尺寸。
### 4. **回调机制**
- 通过 `ICcdCallback` 接口,当扫描到条形码时,调用 `FoundBarcode` 方法将扫描到的条形码数据返回给外部使用者。
### 5. **资源管理**
- 实现了 `IDisposable` 接口,确保在对象销毁时释放相机和图像资源,防止内存泄漏。
- 在 `CloseImageAcq` 方法中,显式释放了相机句柄、图像对象和窗口句柄。
### 6. **双相机支持**
- 代码支持双相机模式,能够同时捕获两台相机的图像并拼接显示。
- 在 `StartScanBarcodeBy2Device` 方法中,通过 `ConcatObj` 和 `TileImages` 合并两张图像,并根据 `revease` 参数决定图像的拼接顺序。
### 7. **图像显示与比例调整**
- 通过 `GetDisplayRect` 方法计算图像显示区域,确保图像按比例适应窗口尺寸,避免图像被拉伸或压缩。
### 8. **可能的改进建议**
- **错误处理**: 当前的错误处理是通过字符串 `mLastErrorMsg` 来记录错误,建议增加更多详细的错误日志和异常抛出机制,以便于调试和定位问题。
- **资源释放**: 虽然代码中已经处理了大部分资源的释放,但在某些方法中(例如 `StartScanBarcode`),可以进一步确保所有 `HObject` 类型的变量在使用完之后都被明确释放,以避免内存泄漏。
- **并行处理**: 如果对条形码扫描有高效需求,可以考虑使用多线程或异步方法对多相机模式进行优化,以提高系统的性能和响应速度。
### 9. **总结**
- 这段代码是一个功能较为完整的条形码扫描工具,结合了 Halcon 库的强大图像处理能力,支持单相机和双相机模式。
- 代码结构清晰,功能模块化,便于扩展和维护。
- 通过回调机制,能够将扫描到的条形码数据返回给外部使用者,适用于需要实时处理条形码的应用场景。
如果你有具体的需求或问题,可以进一步讨论如何优化或扩展这段代码。
/*
这段代码是一个通过 Halcon 图像处理库进行条形码扫描的类实现。类名为
GeneralBarcodeScan,包含多个方法来初始化相机、捕获图像、检测条形码以
及在窗口中显示图像。接下来,我将详细解释代码的功能并在每一部分添加注释。
*/
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;
/// <summary>
/// 释放资源,关闭相机及图像对象
/// </summary>
public void Dispose()
{
CloseImageAcq();
}
/// <summary>
/// 构造函数,初始化相机句柄和图像对象
/// </summary>
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;
}
/// <summary>
/// 设置回调接口
/// </summary>
/// <param name="callback">回调接口实例</param>
public void setCallback(ICcdCallback callback)
{
this.mCallback = callback;
}
/// <summary>
/// 打开相机并初始化图像采集
/// </summary>
/// <param name="hHalconWnd">Halcon 窗口</param>
/// <param name="wndWidth">窗口宽度</param>
/// <param name="wndHeight">窗口高度</param>
/// <param name="deviceQty">相机数量</param>
/// <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;
}
/// <summary>
/// 关闭相机并释放资源
/// </summary>
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;
}
}
/// <summary>
/// 获取最后的错误信息
/// </summary>
/// <returns>最后的错误信息</returns>
public string GetLastErrorMsg()
{
return this.mLastErrorMsg;
}
/// <summary>
/// 异步捕获图像并显示在窗口
/// </summary>
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();
}
}
/// <summary>
/// 执行条形码扫描
/// </summary>
/// <param name="qty">相机数量</param>
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);
}
}
/// <summary>
/// 使用两台相机进行条形码扫描
/// </summary>
/// <param name="revease">是否反转图像顺序</param>
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 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 };
}
}
}
代码注释总结:
-
类与方法注释: 每个方法和类都有详细的注释,解释了其功能、参数和返回值。
-
关键代码块注释: 在关键代码块(如相机初始化、图像捕获、条形码检测等)中添加了注释,帮助理解代码的逻辑。
-
资源管理注释: 在资源释放的代码块中添加了注释,确保资源的正确释放,避免内存泄漏。
通过这些注释,代码的可读性和可维护性得到了显著提升。
1490

被折叠的 条评论
为什么被折叠?



