机器视觉 /从bottle.hdev示例程序开启HalconHDevelop征程

文章目录

  • 概述
  • 示例程序bottle.hdev源码
    • Step 0: Preparations
    • Step 1: Segmentation - 读取并显示图片
    • Step 1: Segmentation - 创建并设置OCR模型
    • Step 1: Segmentation - 文本分割与识别
    • 计算结果显示
    • 内存释放
  • 导出为C++代码
    • 导出为C++代码
    • 配置 VS + Halcon 环境
    • VS程序执行结果
    • HTuple hv_Classes 转 std::string
  • OCR训练/字体文件(bottlet.hdev)
    • Step2 - 生成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 := 'bottle'

Preparations是准备工作。其中指定了用于读取瓶子上日期的字体的名称。如果您运行了目录中的 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,后续章节可能添加)
OCR(Optical 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 array)Classes 存储了每个字符的识别类别。进行文本识别时,每个字符都被分类到不同的类别中。这些类别可以表示不同的字符、数字、符号等。例如,类别可以是字母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.lib

VS程序执行结果

注意在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_cast<char*>(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 // 0

OCR训练/字体文件(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 函数的功能比较好理解,它使用(I+1)索引值,从对象数组SortedRegions中获取单个对象。如I==2时,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分割成二值图像,将像素值在095之间的像素设为前景,其他像素设为背景
threshold (Bottle, RawSegmentation, 0, 95)
* 对RawSegmentation中连通域面积15个像素之间的区域进行填充,用于去除图像中的噪点或细小的非感兴趣区域
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)
* 根据面积的大小选择形状,保留面积在3009999(个像素)之间的对象,并将结果保存在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)

在这里插入图片描述

总结

?真的要在这条路上走下去吗

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

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

相关文章

LeetCode刷题---填充每个节点的下一个右侧节点指针

官方题解:LeetCode官方题解 解题思想: 因为是一棵满二叉树&#xff0c;所以除了叶子节点外的其他节点都有两个子节点。 可以根据每一层来依次遍历 从根节点开始&#xff0c;根节点的左子节点的next节点就指向根节点的右子节点 因为根节点的next节点为NULL&#xff0c;开始从根…

DR模式下LVS负载均衡聚集部署实验

目录 1、实验准备 2、配置负载调度器&#xff08;ens33&#xff1a;192.168.80.9 VIP:192.168.80.188&#xff09; 2.1 配置虚拟ip地址&#xff08;VIP&#xff1a;192.168.80.188&#xff09; 2.2 调整proc响应参数 2.3 设置负载分配策略 3、部署共享存储&#xff08;NF…

static详解

前言 大家好我是jiantaoyab&#xff0c;这篇文章来谈一谈c中的static&#xff0c;根据对static的使用&#xff0c;我分为类内和类外2种情况 static简介 static是c常用的修饰符&#xff0c;它用来控制变量的存储方式和可见性&#xff0c;在变量前面加上一个static&#xff0c…

ECMAScript 语法

ECMAScript 语法 一、ECMAScript1.ECMAScript简介2.ECMAScript历史 二、ECMAScript 语法区分大小写变量是弱类型的每行结尾的分号可有可无注释与 Java、C 和 PHP 语言的注释相同括号表示代码块 一、ECMAScript ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协…

大唐杯学习笔记:Day6

1.1小区选择 一、概述 1.UE在RRC_IDLE和RRC——INACTIVATE状态下进行的过程&#xff1b; 2.UE首先需要完成PLMN的选择,在已选择的PLMN上寻找合适的小区,获取合适的服务,监听控制信道,这个过程即小区选择过程&#xff1b; 3.根据小区重选准则,UE寻找其他更适合的小区进行小区…

论文《Exploring CLIP for Assessing the Look and Feel of Images》阅读

论文《Exploring CLIP for Assessing the Look and Feel of Images》阅读 论文概述Preliminary方法论Experiments结论 论文概述 今天带来的是论文《Exploring CLIP for Assessing the Look and Feel of Images》&#xff0c;论文主要通过 CLIP 模型来完成图像的质量&#xff0…

js五星评价的制作方法

方法有两种&#xff0c;1、jquer插件&#xff1b;2、图片循环&#xff1b; 第一种、效果图 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

一文了解 ArrayList 的扩容机制

了解 ArrayList 在 Java 中常用集合类之间的关系如下图所示&#xff1a; 从图中可以看出 ArrayList 是实现了 List 接口&#xff0c;并是一个可扩容数组&#xff08;动态数组&#xff09;&#xff0c;它的内部是基于数组实现的。它的源码定义如下&#xff1a; public class A…

通过hyperbeam创建梁单元截面属性

1、为模型中标准的圆柱形创建梁单元和赋予属性&#xff1b; 2、为模型中不标准的对称性实体创建梁单元和赋予属性&#xff1b; 3、为模型中壳体部分创建梁单元和赋予属性&#xff1b;

Linux系统之rename命令的基本使用

Linux系统之rename命令的基本使用 一、rename命令介绍二、raname工具版本2.1 C语言版本2.2 Perl版本 三、centos下的rename使用3.1 基本语法3.2 命令选项3.3 rename的基本使用 四、ubuntu下的rename使用4.1 基本语法4.2 命令选项4.3 rename命令的基本操作 五、rename注意事项 一…

“色狼”用英语怎么说?柯桥日常英语,成人英语口语学习

最近有粉丝问我"色狼"英文翻译是啥 首先声明不是"colour wolf"哈 关于“色狼”的英文表达有很多 快和C姐一起来看看吧&#xff01; 1.pervert 这个单词的意思是变态、色狼 是对性变态者最直观的描述 He is such a pervert&#xff01; I saw him lo…

学习基于 JavaScript 语言 的计算机界三大神书”之一 ——SICP

如何阅读“计算机界三大神书”之一 ——SICP 《计算机程序的构造和解释》&#xff08;Structure and Interpretation of Computer Programs&#xff0c;简记为SICP&#xff09;是MIT的基础课教材&#xff0c;出版后引起计算机教育界的广泛关注&#xff0c;对推动全世界大学计算…

微前端之什么是微前端

什么是微前端 微前端分类 基于路由的微前端&#xff1a;组件化微前端&#xff1a;iframe嵌入式微前端&#xff1a; 优点缺点 动态加载/懒加载微前端&#xff1a;微应用容器化方案&#xff1a; 微前端解决方案 single-spa阿里巴巴 Cloud Alfaiframe 方案Web ComponentsModule Fe…

关于Mybatis-Plus报错 Not Found TableInfoCache 解决办法

0. 接口结构&#xff1a;1. 方法报错&#xff1a;2. 解决方法&#xff1a;3. 原因分析&#xff1a; 0. 接口结构&#xff1a; 【接口】&#xff1a; public interface PurchaseOrderService extends IService<PurchaseOrder> {}【接口实现类】&#xff1a; public cla…

【Python】新手入门(5):# -*- coding: UTF-8 -*- 的作用详解

【Python】新手入门&#xff08;5&#xff09;&#xff1a;# -*- coding: UTF-8 -*- 的作用详解 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础…

ssm基于javaEE+springboot校园闲置二手物品拍卖交易平台_ngad7

为提升浏览用户观感及使用体验&#xff0c;本系统要具有易用性和美观性。通过页面的简单提示就可完成操作&#xff0c;校园闲置物品交易平台展示界面应该清楚简洁&#xff0c;使用户通过美观的前台页面能快速定位想要浏览的校园闲置物品交易平台信息。后台界面也应简约&#xf…

java岗位面试题总结,关于网络优化你必须要知道的重点

前言 最近有很多朋友向我求教经验&#xff0c;因为我自己工作相对于稳定&#xff0c;在这里给大家分享一个粉丝朋友的经历&#xff0c;他作为一个曾经的菜鸡面试者&#xff0c;在不断的失败中成长&#xff0c;最终斩获了多份offer&#xff0c;因此特别想在此分享一下他的面试成…

【C++精简版回顾】20.模板的使用

1.模板起源 1.模板的定义 1.针对函数属性模板 //针对函数属性 template <class VOID > VOID print1(int a) {cout << a << endl; } 2.针对数据属性模板 //针对数据属性 template <typename INT,typename FLOAT> void print2(INT a,FLOAT b) {cout <…

从零学习Linux操作系统 第三十部分 部署Anisble

一、ansible实验环境的部署 主控机 更改服务器主机名 hostnamectl set-hostname westos_ansible.westos.org 主服务器需要能够实现上网 修改网卡使之能够上网 能ping通 代表可以连接外网 搭载本地软件仓库 并且挂载镜像 装载 dnf install httpd -y 让其开机启动并且…

【C#面向对象设计模式】02. Singleton单件(创建型模式)

【C#面向对象设计模式】02. Singleton单件&#xff08;创建型模式&#xff09; 0. 模式分类 从目的来看&#xff1a; 创建型模式&#xff1a;负责对象创建。结构型模式&#xff1a;处理类与对象间的组合。行为型模式&#xff1a;类与对象交互中的职责分配。 从范围来看&#…