建设学校网站的报告,企业vi设计报价,网商网站怎么做,自适用网站的建设文章目录 概述示例程序bottle.hdev源码Step 0: PreparationsStep 1: Segmentation - 读取并显示图片Step 1: Segmentation - 创建并设置OCR模型Step 1: Segmentation - 文本分割与识别计算结果显示内存释放 导出为C代码导出为C代码配置 VS Halcon 环境VS程序执行结果HTuple hv… 文章目录 概述示例程序bottle.hdev源码Step 0: PreparationsStep 1: Segmentation - 读取并显示图片Step 1: Segmentation - 创建并设置OCR模型Step 1: Segmentation - 文本分割与识别计算结果显示内存释放 导出为C代码导出为C代码配置 VS Halcon 环境VS程序执行结果HTuple hv_Classes 转 std::string OCR训练/字体文件bottlet.hdevStep2 - 生成OCR样本数据Step3: 执行OCR训练Step1 - 提取到感兴趣区域 总结 概述
作为Halcon和Hdevlop的菜鸟选手bottle.hdev 算是我踏上这条路的第一次实践。 开始学习Halcon后零零散散地阅读了些资料并无感于是决定从示例程序再下手。基于前阵子读了些Blob内容便在示例程序目录树 [方法 - Blob分析] 分支下希望能找到一个足够简单和实用的例子来研习最终锁定名为 bottle.hdev完成分割和读取啤酒瓶上保质期的程序文件。尽管它可能不是最简单并适合入门的示例程序但我就是一眼相中了它。在阅读该示例程序前我已简单了解Halcon的数据类型、基本语法等知识。
示例程序bottle.hdev源码
* bottle.hdev: Segment and read numbers on a beer bottle本程序的主要功能是分割和读取啤酒瓶上标识的保质期。后续代码注释中的 “best before” date 含义为保质期/最佳饮用日期/最佳食用日期。该程序利用已经训练好的OCR模型识别啤酒瓶上的 “最佳饮用日期”将图片字符转换为日期数字。
Step 0: Preparations
* Step 0: Preparations
* Specify the name of the font to use for reading the date on the bottle.
* It is easiest to use the pre-trained font Universal_0-9_NoRej. If you
* have run the program bottlet.hdev in this directory, you can activate
* the second line to use the font trained with this program.
FontName : Universal_0-9_NoRej
* FontName : bottlePreparations是准备工作。其中指定了用于读取瓶子上日期的字体的名称。如果您运行了目录中的 bottlet.hdev 程序可以取消注释第二行以使用该程序训练的字体。可以看到 bottlet.hdev 程序与 bottle.hdev 程序同在 …/examples\hdevelop\Applications\OCR 目录下前者功能是 Training of the OCR. The font is used in “bottle.hdev”。 根据代码中的注释建议使用预训练的字体Universal_0-9_NoRej该字体可能是经过训练和优化的以便更好地识别瓶子上的数字字符。
补充临时不做深究20240228后续章节可能添加 OCROptical Character Recognition光学字符识别的训练和字体选择是OCR技术中的一部分其中涉及复杂的算法和模型训练过程。在OCR训练中通常需要提供大量的标注数据其中包括图像样本和相应的字符标签。通过将这些样本输入到OCR算法中可以通过训练来调整OCR模型的参数使其能够更好地识别出字符。在OCR中字体Font指的是一种特定的字符设计和排列方式。不同的字体具有不同的字形和样式如字体的大小、粗细、倾斜度等。字体在OCR中起到重要的作用因为不同的字体可能会对字符的外观和特征产生影响从而影响OCR的准确性。
Step 1: Segmentation - 读取并显示图片
*
* Step 1: Segmentation
dev_update_window (off)
read_image (Bottle, bottle2)
get_image_size (Bottle, Width, Height)
dev_close_window ()
dev_open_window (0, 0, 2 * Width, 2 * Height, black, WindowID)
set_display_font (WindowID, 16, mono, true, false)
dev_display (Bottle)
disp_continue_message (WindowID, black, true)
stop ()该段代码实现了在一个窗口中显示读取的图像并等待用户操作。具体的 1、程序先将窗口更新设置为关闭状态在不需要实时更新图像时关闭更新以提升处理速度。 2、从名为’bottle2’的图像文件中读取图像数据并将其存储在名为Bottle的变量中。 3、获取图像Bottle的宽度和高度并将它们存储在Width和Height变量中。 4、关闭了之前可能打开的窗口以便为后面的窗口设置做准备。 5、使用dev_open_window打开一个新的窗口设置窗口的位置和大小。通过将窗口的宽度和高度设置为原始图像的两倍可以提供足够的空间来显示分割和识别结果。新打开的窗口如下图其中图形窗口的名称 200000 为 WindowID 的值。 6、置窗口的显示字体。它将字体大小设置为16字体类型设置为’mono’等宽字体true’表示将字体设置为粗体false’表示不使用斜体。 7、调用 dev_display (Bottle) 在打开的窗口中显示图像Bottle。 8、disp_continue_message 在窗口中显示一条继续消息。它将消息的颜色设置为黑色true’表示以等待用户操作的方式显示消息。在单步调试时图形窗口右下角可见 “Press Run (F5) to continue”。很显然这是一种调试信息。 9、代码行 stop()该函数的作用是提供一个交互式的界面使用户有机会查看并处理窗口中的图像。当程序执行到此行时程序将暂停直到用户采取某种操作或关闭窗口为止。用户可以使用键盘的F5或者是工具栏的运行F5按钮、单步调试按钮等使得程序继续运行。直观的功能是调试过程中当使用一次F5时程序会在一个stop行上停住在执行上述任意用户操作后方能继续。
Step 1: Segmentation - 创建并设置OCR模型
*
* Create Automatic Text Reader and set some parameters
create_text_model_reader (auto, FontName, TextModel)
* The printed date has a significantly higher stroke width
set_text_model_param (TextModel, min_stroke_width, 6)
* The best before date has a particular and known structure
set_text_model_param (TextModel, text_line_structure, 2 2 2)补充 在Halcon算子帮助文档中会首先列出函数签名然后实际的函数声明以使得读者可以辨别出参数数据类型和输出输出类型。Halcon算子函数的签名遵循如下格式 以create_text_model_reader 函数为例其签名如下 这个函数没有输入和输出图像数据只有两个输入控制数据 一个输出控制数据。
create_text_model_reader (‘auto’, FontName, TextModel) 该函数创建一个TextModel文本模型用于描述将要使用find_text进行分割的文本。此行代码是本程序的核心代码它代表了要查找的内容。通过这个文本模型可以定义和描述要查找的文本的特征和结构。例如可以指定要查找的特定字符、字体、大小、颜色等。然后可以使用这个文本模型在图像中查找符合这些特征和结构的文本。
参数’auto’表示使用自动模式来选择合适的OCR模型和字体。参数FontName对应形参OCRClassifier意为分类器是输入型控制数据Halcon提供了很多预定义分类器
set_text_model_param (TextModel, ‘min_stroke_width’, 6) 设置文本模型TextModel的参数用于指定文本中字符的最小笔画宽度以像素为单位。这个设置项仅适用于字符标点符号或分割符的笔画宽度不受其限制。示例中其用于处理印刷日期因为这些字符通常具有较大的笔画宽度。
set_text_model_param (TextModel, ‘text_line_structure’, ‘2 2 2’) 指定文本行的结构(每行字符的数量和结构)。示例中‘2 2 2’ 表示 “best before” 日期的结构为3个字符段每段包含2个字符。
Step 1: Segmentation - 文本分割与识别
*
* Read the best before date
find_text (Bottle, TextModel, TextResultID)在Image图像中函数find_text会查找由TextModel指定的文本并将结果存返回存储到TextResultID中。TextResultID所包含的具体的Specific结果可以使用get_text_result和get_text_object获取。TextResultID中的文本结果会根据使用create_text_model_reader创建TextModel时设置的Mode而有所不同。
计算结果显示
*
* Display the segmentation results
get_text_object (Characters, TextResultID, all_lines)
dev_display (Bottle)
dev_display (Characters)
stop ()
* Display the reading results
get_text_result (TextResultID, class, Classes)
area_center (Characters, Area, Row, Column)
disp_message (WindowID, Classes, image, 80, Column - 3, green, false)get_text_object 函数获取了find_text函数的具体输出数据可以通过ResultName参数指定获取全部字符一行字符、或者指定序号的一个字符等。执行结果如下 有点意外的是代码执行到上述位置图像变量Charactors被赋值后在执行dev_display之前Charactors会在图形窗口中叠加显示当执行到下一行代码时Charactors又被从图形窗口中清除。dev_display (Bottle)、dev_display (Characters) 会再次重新显示图像变量。 不同于get_text_object函数get_text_result输出的是控制数据 ResultValue即变量Classes对应的字符串数组具体执行结果 字符串数组string arrayClasses 存储了每个字符的识别类别。进行文本识别时每个字符都被分类到不同的类别中。这些类别可以表示不同的字符、数字、符号等。例如类别可以是字母A、数字1、标点符号!等等。具体的这里水太深暂不深究。
内存释放
*
* Free memory
clear_text_result (TextResultID)
clear_text_model (TextModel)在Halcon中文本结果和文本模型存储着大量的信息包括字符识别结果、模型参数等。为了有效地管理这些数据并确保在需要时可以释放内存Halcon将这些数据分配在堆上。
导出为C代码
Halcon提供了各种导出选项例如C, C#, Visual Basic等以及与OpenCV等其他常用机器视觉库的集成。导出为其他语言可以让开发人员更方便地将Halcon的功能嵌入到自己的应用程序中并与其他库和工具进行集成。这种灵活性使得Halcon成为一个强大的机器视觉开发平台可以适应不同的应用需求和开发环境
导出为C代码
在文件菜单中选择导出打开如下导出界面暂保持默认配置 本地函数是在当前程序文件中定义的函数它们通常用于封装和组织代码并在程序中多次使用。外部函数是在其他Halcon程序文件中定义的函数它们可能是标准的Halcon函数库函数或自定义的函数。按照上述配置成功导出 bottle.cpp文件其中主要函数实现如下
// Main procedure
void action()
{// Local iconic variablesHObject ho_Bottle, ho_Characters;// Local control variablesHTuple hv_FontName, hv_Width, hv_Height, hv_WindowID;HTuple hv_TextModel, hv_TextResultID, hv_Classes, hv_Area;HTuple hv_Row, hv_Column;////bottle.hdev: Segment and read numbers on a beer bottle////Step 0: Preparations//Specify the name of the font to use for reading the date on the bottle.//It is easiest to use the pre-trained font Universal_0-9_NoRej. If you//have run the program bottlet.hdev in this directory, you can activate//the second line to use the font trained with this program.hv_FontName Universal_0-9_NoRej;//FontName : bottle////Step 1: Segmentation// dev_update_window(...); only in hdevelopReadImage(ho_Bottle, bottle2);GetImageSize(ho_Bottle, hv_Width, hv_Height);if (HDevWindowStack::IsOpen())CloseWindow(HDevWindowStack::Pop());SetWindowAttr(background_color,black);OpenWindow(0,0,2*hv_Width,2*hv_Height,0,visible,,hv_WindowID);HDevWindowStack::Push(hv_WindowID);set_display_font(hv_WindowID, 16, mono, true, false);if (HDevWindowStack::IsOpen())DispObj(ho_Bottle, HDevWindowStack::GetActive());disp_continue_message(hv_WindowID, black, true);// stop(...); only in hdevelop////Create Automatic Text Reader and set some parametersCreateTextModelReader(auto, hv_FontName, hv_TextModel);//The printed date has a significantly higher stroke widthSetTextModelParam(hv_TextModel, min_stroke_width, 6);//The best before date has a particular and known structureSetTextModelParam(hv_TextModel, text_line_structure, 2 2 2);////Read the best before dateFindText(ho_Bottle, hv_TextModel, hv_TextResultID);////Display the segmentation resultsGetTextObject(ho_Characters, hv_TextResultID, all_lines);if (HDevWindowStack::IsOpen())DispObj(ho_Bottle, HDevWindowStack::GetActive());if (HDevWindowStack::IsOpen())DispObj(ho_Characters, HDevWindowStack::GetActive());// stop(...); only in hdevelop//Display the reading resultsGetTextResult(hv_TextResultID, class, hv_Classes);AreaCenter(ho_Characters, hv_Area, hv_Row, hv_Column);disp_message(hv_WindowID, hv_Classes, image, 80, hv_Column-3, green, false);////Free memoryClearTextResult(hv_TextResultID);ClearTextModel(hv_TextModel);
}对应着原来的hdev程序上述C代码很好理解基本上就是换了一个函数名字而已由于初学也不再做太深入的研究。在VS中新建空项目添加HDevelop导出生成的bottle.cpp文件。此时是无法通过编译的因为尚未配置 IDE …
配置 VS Halcon 环境
关于集成开发环境的配置是老生常谈需要配置哪些方面的内容可参见《》此处仅简单描述建议使用相对路径配置本机系统环境变量如下 以Debug x64平台为例进行配置如下
项目属性 - C/C - 常规 - 附加包含目录,
$(HALCONROOT)\include;$(HALCONROOT)\include\halconcpp项目属性 - 连接器 - 常规 - 附加库目录,
$(HALCONROOT)\lib\$(HALCONARCH)项目属性 - 连接器 - 输入 - 附加依赖项,
halconcpp.libVS程序执行结果
注意在main函数中加上 system(“pause”); 语句否则窗口将一闪而过。 HTuple hv_Classes 转 std::string
将结果转换为自己熟悉的C字符串心里踏实了许多。这里成功测试了两种方法但可能不是最简单的方法。
#if 0HString hstring hv_Classes.ToString();std::string std_string hstring.Text();std::cout hv_Classes to std_string, Len: hstring.Length() , Content: std_string std::endl;//print//hv_Classes to std_string, Len:30, Content : [0, 1, 0, 8, 9, 4]
#elsestd::string std_string;for (int i 0; i hv_Classes.Length(); i) {HString str hv_Classes[i].S();char* code const_castchar*(str.Text());std_string code;}std::cout hv_Classes to std_string, Content: std_string std::endl;//print//hv_Classes to std_string, Content: 010894
#endif // 0OCR训练/字体文件bottlet.hdev
学习整理完成 bottle.hdev后直观的感受是在Halcon和HDevelop的加持下实现一个光学字符识别功能真的是挺方便的。前边遗留了OCR训练的过程和概念没搞明白一转眼过去10多天了倒回头来再深入下。下文将试图理解什么是OCR训练如何训练什么是文本模型它的内容是什么 etc.
Step2 - 生成OCR样本数据
* Step2: Training file generation
TrainingNames : [0,1,0,8,9,4]
TrainingFileName : FontName .trf
sort_region (FinalNumbers, SortedRegions, first_point, true, column)
shape_trans (SortedRegions, RegionTrans, rectangle1)
area_center (RegionTrans, Area, Row, Column)
MeanRow : mean(Row)
dev_set_check (~give_error)
delete_file (TrainingFileName)
dev_set_check (give_error)
for I : 0 to |TrainingNames| - 1 by 1select_obj (SortedRegions, CharaterRegions, I 1)append_ocr_trainf (CharaterRegions, Bottle, TrainingNames[I], TrainingFileName)disp_message (WindowID, TrainingNames[I], image, MeanRow - 40, Column[I] - 6, yellow, false)
endfor我们先跳跃着来看看与bottle.hdev最直接相关的部分。如下代码进行了OCR训练文件的存储 具体的 select_obj 函数的功能比较好理解它使用I1索引值从对象数组SortedRegions中获取单个对象。如I2时CharaterRegions对应的Region图如下 有了单字符图像数据后并不是直接简单地与实际字符进行映射关系存储就万事大吉了尽管确实涉及了写文件操作 TrainingFileName ‘bottle.trf’ 即 bottle.hdev 中可以选择使用的字体文件。 如上append_ocr_trainf 函数的功能实际上是在设置OCR训练所直接需要的数据它为后续的 trainf_ocr_class_mlp 训练过程做准备。它将表示字符的regin区域包括区域和像素的灰度值以及相应的类别名称TrainingNames[I]字符值写入文件中。一个图像中可以支持任意arbitrary数量的字符区域。对于signature_Character中的每个字符regin区域必须在signature_Class中指定相应的class name类别名称。灰度值通过Image参数传递。与write_ocr_trainf操作不同的是它使用相同的训练文件格式将字符追加到现有文件中。如果文件不存在将生成一个新文件。这里可得知append_ocr_trainf 确实也是写文件操作的。
Step3: 执行OCR训练
* Step3: Training
* 将训练样本的名称列表进行sort排序并uniq去重得到一个包含所有不同标签的列表
CharNames : uniq(sort(TrainingNames))
* 创建一个基于多层感知器MLP的OCR分类器并设置参数。并将其保存在 OCRHandle 变量中
create_ocr_class_mlp (8, 10, constant, default, CharNames, 10, none, 10, 42, OCRHandle)
* 使用训练文件 TrainingFileName 中的样本数据对 OCRHandle 中的OCR分类器进行训练。训练次数为200次学习率为0.01
trainf_ocr_class_mlp (OCRHandle, TrainingFileName, 200, 1, 0.01, Error, ErrorLog)
* 将训练得到的OCR分类器以 FontName 的名称保存起来以便后续使用即在bottle.hdev中使用的字体名称
write_ocr_class_mlp (OCRHandle, FontName)
* 清除 OCRHandle 中的OCR分类器释放相关的内存资源。
clear_ocr_class_mlp (OCRHandle)不再对上述代码具体展开只重点关注下 trainf_ocr_class_mlp 函数 trainf_ocr_class_mlp trains the OCR classifier OCRHandle with the training characters stored in the OCR training files given by TrainingFile. 函数 trainf_ocr_class_mlp 使用存储在OCR训练文件TrainingFile ‘bottle.trf’中的训练字符来训练OCR分类器。在执行这个训练过程前训练文件必须已经是存在的前边也已经说了append_ocr_trainf 如果检测到文件不存在会自动创建。函数的参数挺多的大体来看一下以便理解
trainf_ocr_class_mlp (OCRHandle, TrainingFileName, 200, 1, 0.01, Error, ErrorLog) 该行代码用于训练基于多层感知器MLP的OCR分类器的操作该操作详细说明如下
OCR分类器的句柄 [OCRHandle]即先前使用create_ocr_class_mlp操作创建的分类器。 训练文件名称 [TrainingFileName]其中包含用于训练的样本数据。该文件应该符合与append_ocr_trainf操作相同的训练文件格式。 训练迭代次数 [200]。在每次迭代中分类器根据样本数据进行参数更新和优化。迭代次数越多训练效果可能会更好但可能导致训练时间较长。 批处理大小 [1]批处理是指一次性将多个样本数据一起输入到分类器进行训练。设置批处理大小可以控制每次迭代中使用的样本数量。在此情况下每次迭代中使用一个批次。 学习率 [0.01]学习率决定了在每次迭代中更新参数时所应用的步长。较高的学习率可以加快训练速度但可能导致不稳定的收敛。较低的学习率可以提高收敛的稳定性但可能需要更多的迭代次数才能达到较好的训练效果。 误差值 [Error]用于存储训练过程中的误差值。通过该参数可以获取每次迭代后分类器的训练误差。 误差日志 [ErrorLog]用于存储训练过程中的误差日志。通过该参数可以获取每次迭代后的详细误差信息。
Step1 - 提取到感兴趣区域
通过前文的学习和分析我们已经大体知道了什么是OCR训练文件、训练的样本数据、基本的训练参数、OCR分类器等基本概念大约明白了 bottle.hdev 使用的 ‘字体’ 到底是怎么搞出来的算是基本将两个halcon程序的功能对接上了。接下来我们将简单地看看最初的样本数据是怎么计算分析出来的…
* Step 1: Segmentation
dev_update_window (off)
read_image (Bottle, bottle2)
get_image_size (Bottle, Width, Height)
dev_close_window ()
dev_open_window (0, 0, 2 * Width, 2 * Height, black, WindowID)
set_display_font (WindowID, 27, mono, true, false)
* 使用阈值化操作将图像Bottle分割成二值图像将像素值在0到95之间的像素设为前景其他像素设为背景
threshold (Bottle, RawSegmentation, 0, 95)
* 对RawSegmentation中连通域面积1到5个像素之间的区域进行填充用于去除图像中的噪点或细小的非感兴趣区域
fill_up_shape (RawSegmentation, RemovedNoise, area, 1, 5)
* 使用半径为2.5的圆形结构元素对RemovedNoise进行圆形开运算通过先腐蚀再膨胀的操作去除小的细节并保留较大的结构
opening_circle (RemovedNoise, ThickStructures, 2.5)
* 对ThickStructures进行形状填充操作填充孔洞形成实心的连通区域并将结果保存在Solid中
fill_up (ThickStructures, Solid)
* 使用宽度为1高度为7的矩形结构元素对Solid进行矩形开运算去除细小的细节并保留较大的结构将结果保存在Cut中
opening_rectangle1 (Solid, Cut, 1, 7)
* 对Cut进行连通性分析将具有相同连通性的像素标记为同一对象并将结果保存在ConnectedPatterns中
connection (Cut, ConnectedPatterns)
* 计算ConnectedPatterns和ThickStructures之间的交集得到候选对象NumberCandidates
intersection (ConnectedPatterns, ThickStructures, NumberCandidates)
* 根据面积的大小选择形状保留面积在300到9999个像素之间的对象并将结果保存在Numbers中
select_shape (NumberCandidates, Numbers, area, and, 300, 9999)
* 根据区域的首点坐标进行排序按列的顺序对Numbers中的区域进行排序并将结果保存在FinalNumbers中
sort_region (Numbers, FinalNumbers, first_point, true, column)
* 显示原始图
dev_display (Bottle)
* 设置图形窗口中绘图颜色为绿色
dev_set_color (green)
* 设置图形窗口中绘图线宽为2像素
dev_set_line_width (2)
* 设置图形窗口中绘图形状为矩形
dev_set_shape (rectangle1)
* 设置图形窗口中绘图绘制方式为边缘margin模式
dev_set_draw (margin)
* 按照上述绘制参数绘制并显示FinalNumbers中的图像区域具体如下图所示
dev_display (FinalNumbers)总结
真的要在这条路上走下去吗 …