机器视觉 /从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 variables
  HObject  ho_Bottle, ho_Characters;

  // Local control variables
  HTuple  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 hdevelop
  ReadImage(&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 parameters
  CreateTextModelReader("auto", hv_FontName, &hv_TextModel);
  //The printed date has a significantly higher stroke width
  SetTextModelParam(hv_TextModel, "min_stroke_width", 6);
  //The "best before" date has a particular and known structure
  SetTextModelParam(hv_TextModel, "text_line_structure", "2 2 2");
  //
  //Read the "best before" date
  FindText(ho_Bottle, hv_TextModel, &hv_TextResultID);
  //
  //Display the segmentation results
  GetTextObject(&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 results
  GetTextResult(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 memory
  ClearTextResult(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 0
  HString 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"]
#else
  std::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 1
    select_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)

在这里插入图片描述

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录day13(1)栈与队列:用栈实现队列(leetcode232)

题目要求&#xff1a;使用栈实现push、pop、empty、peek&#xff08;返回队列首部元素&#xff09;。 思路&#xff1a;本题思路比较容易&#xff0c;即用两个栈模拟即可&#xff0c;pop时只需要先判断stackout栈是否为空&#xff0c;如果不空直接弹出&#xff0c;如果空就将s…

数据库-ER图教程

一.什么是E-R图 E-R图全称&#xff1a;“Entity-Relationship Approach”&#xff0c;是一种“实体-联系”方法。 E-R图的优点&#xff1a; 1.自然地描述现实世界。 2.图形结构简单。 3.设计者和用户易理解。 4.是数据库设计的中间步骤&#xff0c;易于向数据模型转换。 …

SSM框架,SSM框架的整合

SSM整合的介绍 微观&#xff1a;将Spring SpringMVC Mybatis框架应用到项目中 SpringMVC框架负责控制层Spring 框架负责整体和业务层的声明式事务管理MyBatis框架负责数据库访问层 宏观&#xff1a;Spring接管一切&#xff08;将框架核心组件交给Spring进行IoC管理&#xff09…

贝叶斯:最大似然估计与最大后验估计

最大似然估计 最大后验估计 《神经网络与深度学习》 Neural Networks and Deep Learning 邱锡鹏 P36-37

网康科技 NS-ASG 应用安全网关 SQL注入漏洞复现(CVE-2024-2022)

0x01 产品简介 网康科技的NS-ASG应用安全网关是一款软硬件一体化的产品,集成了SSL和IPSec,旨在保障业务访问的安全性,适配所有移动终端,提供多种链路均衡和选择技术,支持多种认证方式灵活组合,以及内置短信认证、LDAP令牌、USB KEY等多达13种认证方式。 0x02 漏洞概述 …

python+django+vue电影票订购系统dyvv4

电影院订票信息管理系统综合网络空间开发设计要求。目的是将电影院订票通过网络平台将传统管理方式转换为在网上操作&#xff0c;方便快捷、安全性高、交易规范做了保障&#xff0c;目标明确。电影院订票信息管理系统可以将功能划分为用户和管理员功能[10]。 语言&#xff1a;…

云原生基础知识:容器技术的历史

容器化的定义&#xff1a; 容器化是一种轻量级的虚拟化技术&#xff0c;将应用程序及其所有依赖项&#xff08;包括运行时、系统工具、系统库等&#xff09;打包到一个称为容器的单独单元中。容器提供了一种隔离的执行环境&#xff0c;使得应用程序可以在不同的环境中运行&…

list链表的创建,排序,插入, test ok

1. 链表的建立&#xff0c;打印 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <stack> #include <iostream> #include <string.h> #include <string>using namespace std;struct node {int data;s…

一体化对账和结算平台的设计与实现

随着企业规模的扩大和业务的复杂化&#xff0c;对账和结算工作变得愈发繁琐和复杂。为了提高工作效率和降低错误率&#xff0c;许多企业开始寻求一体化对账和结算平台的解决方案。本文将探讨一体化对账和结算平台的设计原则和实施步骤&#xff0c;以及其在企业管理中的重要性。…

jdk安装,配置path系统变量

直接点击安装 不要包含空格&#xff0c;中文字符 3.找到刚刚的路径&#xff0c;看一下&#xff0c;有东西就说明安装对了 配置path winr输入sysdm.cpl点击确定 全部依次点击 确定 即可。 验证jdk是否安装成功 看java、javac是否可用看java、javac版本号是否无问题 win…

about batch[0].new(storage)的问题(VOT)

最近在处理VOT数据集时&#xff0c;遇到了一个奇怪的问题&#xff0c;特此记录。 源代码如下&#xff1a; def ltr_collate_stack1(batch):"""Puts each data field into a tensor. The tensors are stacked at dim1 to form the batch"""error…

Java多线程——synchronized、volatile 保障可见性

目录 引出synchronized、volatile 保障可见性Redis冲冲冲——缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Java多线程——synchronized、volatile 保障可见性 synchronized、volatile 保障可见性 原子性&#xff1a;在一次或者多次操作时…

[动态规划]---part2

前言 作者&#xff1a;小蜗牛向前冲 专栏&#xff1a;小蜗牛算法之路 专栏介绍&#xff1a;"蜗牛之道&#xff0c;攀登大厂高峰&#xff0c;让我们携手学习算法。在这个专栏中&#xff0c;将涵盖动态规划、贪心算法、回溯等高阶技巧&#xff0c;不定期为你奉上基础数据结构…

python基础第二天

世界杯小组赛成绩 注意&#xff1a; 1.循环 1.1while 1.2for 1.3 range 1.4 while else while 循环正常执行完才能执行else语句

算法竞赛基础:树状数组

算法竞赛基础&#xff1a;树状数组 是什么&#xff1f; 树状数组虽然语义上是树状&#xff0c;但是实际上还是一个数组。 树状数组的功能就是单点和区间的修改和查询。 例如&#xff0c;如果想增加一个点的值&#xff0c;那么你需要让其上方所有能对齐的树状数组c全部增加相同…

流畅的Python(十七)-使用future处理并发

一、核心要义 主要以三个模拟网络下载的代码-分别是依序下载、使用concurrent.futures模块中的Executor.map方法、以及使用该模块的executor.submit和futures.as_completed方法&#xff0c;来展示Python实现并发编程的其中一种方式。 二、代码示例 1、依序下载的脚本 #!/us…

JS数组,if等结构语序

目录 浏览器的断点调试&#xff1a; 流程控制&#xff1a; 顺序流程控制&#xff1a;流程代码会逐行向下进行。 分支流程控制&#xff1a; IF语句&#xff1a; Switch语句&#xff1a; Switch和if的区别&#xff1a; 三元表达式&#xff1a; 循环&#xff1a; for循环…

XSS漏洞--概念、类型、实战--分析与详解[结合靶场pikachu]

目录 一、XSS概念简述 1、XSS简介&#xff1a; 2、XSS基本原理&#xff1a; 3、XSS攻击流程&#xff1a; 4、XSS漏洞危害&#xff1a; 二、XSS类型&#xff1a; 1、反射型XSS&#xff1a; 2、存储型XSS&#xff1a; 3、DOM型XSS&#xff1a; 三、靶场漏洞复现(pikach…

数据结构之顺序表及其实现!

目录 ​编辑 1. 顺序表的概念及结构 2. 接口的实现 2.1 顺序表的初始化 2.2 检查顺序表容量是否已满 2.3 顺序表的尾插 ​编辑 2.4 顺序表的尾删 2.5 顺序表的头插 2.6 顺序表的头删 2.7 顺序表在pos位置插入 2.8 顺序表在pos位置删除 2.9 顺序表的查找 2.10 顺…

喜报|3DCAT成为国内首批适配Vision Pro内容开发者

近日&#xff0c;苹果在上海总部举办了国内首场 Apple Vision Pro 开发者实验室活动&#xff0c;3DCAT作为国内领先的实时渲染云平台参与了此次活动&#xff0c;成为国内首批适配 Vision Pro 的内容开发者之一。 Vision Pro是苹果于2023年6月发布的首个空间计算设备&#xff0…