PP-OCRv5
目录
PP-OCRv5官方效果如下
C++封装、C#调用效果
项目
代码
下载
PP-OCRv5官方效果如下
C++封装、C#调用效果
项目
代码
using Newtonsoft.Json;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace OCRV5Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
const string DllName = "lw.PPOCRSharp.dll";
//初始化
[DllImport(DllName, EntryPoint = "init", CallingConvention = CallingConvention.StdCall)]
public extern static int init(ref IntPtr engine
, int cpu_threads
, bool enable_mkldnn
, string det_model_dir
, int limit_side_len
, double det_db_thresh
, double det_db_box_thresh
, double det_db_unclip_ratio
, bool use_dilation
, bool cls
, bool use_angle_cls
, string cls_model_dir
, double cls_thresh
, double cls_batch_num
, string rec_model_dir
, string rec_char_dict_path
, int rec_batch_num
, int rec_img_h
, int rec_img_w
, StringBuilder msg);
//识别
[DllImport(DllName, EntryPoint = "ocr", CallingConvention = CallingConvention.StdCall)]
public extern static int ocr(IntPtr engine, IntPtr image, StringBuilder msg, out IntPtr ocr_result, out int ocr_result_len);
//释放
[DllImport(DllName, EntryPoint = "destroy", CallingConvention = CallingConvention.StdCall)]
public extern static int destroy(IntPtr engine, StringBuilder msg);
static IntPtr OCREngine;
private Bitmap bmp;
private String imgPath = null;
private List<OCRResult> ltOCRResult;
private string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tif;*.png";
private StringBuilder OCRResultInfo = new StringBuilder();
private StringBuilder OCRResultAllInfo = new StringBuilder();
Pen pen = new Pen(Brushes.Red, 2f);
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = fileFilter;
if (ofd.ShowDialog() == DialogResult.OK)
{
imgPath = ofd.FileName;
bmp = new Bitmap(imgPath);
pictureBox1.Image = bmp;
richTextBox1.Clear();
button2_Click(null, null);
}
}
private void button2_Click(object sender, EventArgs e)
{
if (imgPath == null)
{
return;
}
button1.Enabled = false;
button2.Enabled = false;
richTextBox1.Clear();
OCRResultInfo.Clear();
OCRResultAllInfo.Clear();
Application.DoEvents();
Mat img = new Mat(imgPath);
StringBuilder msgTemp = new StringBuilder(128);
StringBuilder ocrResultStr = new StringBuilder(1024 * 100);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
IntPtr strPtr;
int ocr_result_len = 0;
int res = ocr(OCREngine, img.CvPtr, msgTemp, out strPtr, out ocr_result_len);
byte[] buffer = new byte[ocr_result_len];
Marshal.Copy(strPtr, buffer, 0, ocr_result_len);
string ocr_result = Encoding.UTF8.GetString(buffer);
Marshal.FreeCoTaskMem(strPtr);
//Console.WriteLine(ocr_result);
stopwatch.Stop();
double totalTime = stopwatch.Elapsed.TotalSeconds;
OCRResultAllInfo.AppendLine($"耗时: {totalTime:F2}s");
OCRResultAllInfo.AppendLine("---------------------------");
OCRResultInfo.AppendLine($"耗时: {totalTime:F2}s");
OCRResultInfo.AppendLine("---------------------------");
if (res == 0)
{
ltOCRResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<OCRResult>>(ocr_result);
OCRResultAllInfo.Append(JsonConvert.SerializeObject(ltOCRResult, Newtonsoft.Json.Formatting.Indented));
Graphics graphics = Graphics.FromImage(bmp);
foreach (OCRResult item in ltOCRResult)
{
OCRResultInfo.AppendLine(item.text);
System.Drawing.Point[] pt = new System.Drawing.Point[] {
new System.Drawing.Point(item.x1, item.y1)
, new System.Drawing.Point(item.x2, item.y2)
, new System.Drawing.Point(item.x3, item.y3)
, new System.Drawing.Point(item.x4, item.y4)
};
graphics.DrawPolygon(pen, pt);
}
graphics.Dispose();
if (checkBox1.Checked)
{
richTextBox1.Text = OCRResultAllInfo.ToString();
}
else
{
richTextBox1.Text = OCRResultInfo.ToString();
}
pictureBox1.Image = null;
pictureBox1.Image = bmp;
}
else
{
MessageBox.Show("识别失败," + msgTemp.ToString());
}
img.Release();
button1.Enabled = true;
button2.Enabled = true;
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
radioButton1.Checked = true;
}
void LoadModel()
{
StringBuilder msgTemp = new StringBuilder(128);
string root_dir = Application.StartupPath + @"\inference";
int cpu_threads = Environment.ProcessorCount + 2;
bool enable_mkldnn = true;
string det_model_dir = "";
int limit_side_len = 960;
double det_db_thresh = 0.3;
double det_db_box_thresh = 0.6;
double det_db_unclip_ratio = 1.2;
bool use_dilation = false;
bool cls = false;
bool use_angle_cls = true;
string cls_model_dir = root_dir + @"\ch_ppocr_mobile_v2.0_cls_infer\";
double cls_thresh = 0.9;
int cls_batch_num = 1;
string rec_model_dir = "";
string rec_char_dict_path = root_dir + @"\ppocrv5_dict.txt";
int rec_batch_num = Environment.ProcessorCount;
int rec_img_h = 48;
int rec_img_w = 320;
if (radioButton1.Checked)
{
det_model_dir = root_dir + @"\PP-OCRv5_mobile_det_infer\";
rec_model_dir = root_dir + @"\PP-OCRv5_mobile_rec_infer\";
}
else
{
det_model_dir = root_dir + @"\PP-OCRv5_server_det_infer\";
rec_model_dir = root_dir + @"\PP-OCRv5_server_rec_infer\";
}
int res = init(ref OCREngine
, cpu_threads
, enable_mkldnn
, det_model_dir
, limit_side_len
, det_db_thresh
, det_db_box_thresh
, det_db_unclip_ratio
, use_dilation
, cls
, use_angle_cls
, cls_model_dir
, cls_thresh
, cls_batch_num
, rec_model_dir
, rec_char_dict_path
, rec_batch_num
, rec_img_h
, rec_img_w
, msgTemp);
if (res == 0)
{
MessageBox.Show("模型加载成功!");
}
else
{
string msg = msgTemp.ToString();
MessageBox.Show("模型加载失败," + msg);
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
richTextBox1.Clear();
if (checkBox1.Checked)
{
richTextBox1.Text = OCRResultAllInfo.ToString();
}
else
{
richTextBox1.Text = OCRResultInfo.ToString();
}
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
RadioButton rb = sender as RadioButton;
if (rb != null && rb.Checked)
{
//MessageBox.Show("选中的是:" + rb.Text);
LoadModel();
}
}
}
}
using Newtonsoft.Json;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;namespace OCRV5Test
{public partial class Form1 : Form{public Form1(){InitializeComponent();}const string DllName = "lw.PPOCRSharp.dll";//初始化[DllImport(DllName, EntryPoint = "init", CallingConvention = CallingConvention.StdCall)]public extern static int init(ref IntPtr engine, int cpu_threads, bool enable_mkldnn, string det_model_dir, int limit_side_len, double det_db_thresh, double det_db_box_thresh, double det_db_unclip_ratio, bool use_dilation, bool cls, bool use_angle_cls, string cls_model_dir, double cls_thresh, double cls_batch_num, string rec_model_dir, string rec_char_dict_path, int rec_batch_num, int rec_img_h, int rec_img_w, StringBuilder msg);//识别[DllImport(DllName, EntryPoint = "ocr", CallingConvention = CallingConvention.StdCall)]public extern static int ocr(IntPtr engine, IntPtr image, StringBuilder msg, out IntPtr ocr_result, out int ocr_result_len);//释放[DllImport(DllName, EntryPoint = "destroy", CallingConvention = CallingConvention.StdCall)]public extern static int destroy(IntPtr engine, StringBuilder msg);static IntPtr OCREngine;private Bitmap bmp;private String imgPath = null;private List<OCRResult> ltOCRResult;private string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tif;*.png";private StringBuilder OCRResultInfo = new StringBuilder();private StringBuilder OCRResultAllInfo = new StringBuilder();Pen pen = new Pen(Brushes.Red, 2f);private void button1_Click(object sender, EventArgs e){OpenFileDialog ofd = new OpenFileDialog();ofd.Filter = fileFilter;if (ofd.ShowDialog() == DialogResult.OK){imgPath = ofd.FileName;bmp = new Bitmap(imgPath);pictureBox1.Image = bmp;richTextBox1.Clear();button2_Click(null, null);}}private void button2_Click(object sender, EventArgs e){if (imgPath == null){return;}button1.Enabled = false;button2.Enabled = false;richTextBox1.Clear();OCRResultInfo.Clear();OCRResultAllInfo.Clear();Application.DoEvents();Mat img = new Mat(imgPath);StringBuilder msgTemp = new StringBuilder(128);StringBuilder ocrResultStr = new StringBuilder(1024 * 100);Stopwatch stopwatch = new Stopwatch();stopwatch.Start();IntPtr strPtr;int ocr_result_len = 0;int res = ocr(OCREngine, img.CvPtr, msgTemp, out strPtr, out ocr_result_len);byte[] buffer = new byte[ocr_result_len];Marshal.Copy(strPtr, buffer, 0, ocr_result_len);string ocr_result = Encoding.UTF8.GetString(buffer);Marshal.FreeCoTaskMem(strPtr);//Console.WriteLine(ocr_result);stopwatch.Stop();double totalTime = stopwatch.Elapsed.TotalSeconds;OCRResultAllInfo.AppendLine($"耗时: {totalTime:F2}s");OCRResultAllInfo.AppendLine("---------------------------");OCRResultInfo.AppendLine($"耗时: {totalTime:F2}s");OCRResultInfo.AppendLine("---------------------------");if (res == 0){ltOCRResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<OCRResult>>(ocr_result);OCRResultAllInfo.Append(JsonConvert.SerializeObject(ltOCRResult, Newtonsoft.Json.Formatting.Indented));Graphics graphics = Graphics.FromImage(bmp);foreach (OCRResult item in ltOCRResult){OCRResultInfo.AppendLine(item.text);System.Drawing.Point[] pt = new System.Drawing.Point[] {new System.Drawing.Point(item.x1, item.y1), new System.Drawing.Point(item.x2, item.y2), new System.Drawing.Point(item.x3, item.y3), new System.Drawing.Point(item.x4, item.y4)};graphics.DrawPolygon(pen, pt);}graphics.Dispose();if (checkBox1.Checked){richTextBox1.Text = OCRResultAllInfo.ToString();}else{richTextBox1.Text = OCRResultInfo.ToString();}pictureBox1.Image = null;pictureBox1.Image = bmp;}else{MessageBox.Show("识别失败," + msgTemp.ToString());}img.Release();button1.Enabled = true;button2.Enabled = true;}/// <summary>/// 初始化/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void Form1_Load(object sender, EventArgs e){radioButton1.Checked = true;}void LoadModel(){StringBuilder msgTemp = new StringBuilder(128);string root_dir = Application.StartupPath + @"\inference";int cpu_threads = Environment.ProcessorCount + 2;bool enable_mkldnn = true;string det_model_dir = "";int limit_side_len = 960;double det_db_thresh = 0.3;double det_db_box_thresh = 0.6;double det_db_unclip_ratio = 1.2;bool use_dilation = false;bool cls = false;bool use_angle_cls = true;string cls_model_dir = root_dir + @"\ch_ppocr_mobile_v2.0_cls_infer\";double cls_thresh = 0.9;int cls_batch_num = 1;string rec_model_dir = "";string rec_char_dict_path = root_dir + @"\ppocrv5_dict.txt";int rec_batch_num = Environment.ProcessorCount;int rec_img_h = 48;int rec_img_w = 320;if (radioButton1.Checked){det_model_dir = root_dir + @"\PP-OCRv5_mobile_det_infer\";rec_model_dir = root_dir + @"\PP-OCRv5_mobile_rec_infer\";}else{det_model_dir = root_dir + @"\PP-OCRv5_server_det_infer\";rec_model_dir = root_dir + @"\PP-OCRv5_server_rec_infer\";}int res = init(ref OCREngine, cpu_threads, enable_mkldnn, det_model_dir, limit_side_len, det_db_thresh, det_db_box_thresh, det_db_unclip_ratio, use_dilation, cls, use_angle_cls, cls_model_dir, cls_thresh, cls_batch_num, rec_model_dir, rec_char_dict_path, rec_batch_num, rec_img_h, rec_img_w, msgTemp);if (res == 0){MessageBox.Show("模型加载成功!");}else{string msg = msgTemp.ToString();MessageBox.Show("模型加载失败," + msg);}}private void checkBox1_CheckedChanged(object sender, EventArgs e){richTextBox1.Clear();if (checkBox1.Checked){richTextBox1.Text = OCRResultAllInfo.ToString();}else{richTextBox1.Text = OCRResultInfo.ToString();}}private void radioButton1_CheckedChanged(object sender, EventArgs e){RadioButton rb = sender as RadioButton;if (rb != null && rb.Checked){//MessageBox.Show("选中的是:" + rb.Text);LoadModel();}}}}
下载
源码下载
相关文章:

PP-OCRv5
目录 PP-OCRv5官方效果如下 C封装、C#调用效果 项目 代码 下载 PP-OCRv5官方效果如下 C封装、C#调用效果 项目 代码 using Newtonsoft.Json; using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; usi…...

nginx的一些配置的意思
1.用这个端口可以访问到nginx 2.工作进程,设置成和cpu核心数一样即可 3.每个工作进程的最大网络连接数。 4.主机名称 设置反向代理时,把server_name设置成ip。 5.反向代理进行转发,localhost指的是nginx所在的机器。 关键字proxy_pass。 …...

Agent模型微调
这篇文章讲解: 把 Agent 和 Fine-Tuning 的知识串起来,在更高的技术视角看大模型应用;加深对 Agent 工作原理的理解;加深对 Fine-Tuning 训练数据处理的理解。 1. 认识大模型 Agent 1.1 大模型 Agent 的应用场景 揭秘Agent核心…...
Android-OkHttp与Retrofit学习总结
OkHttp核心机制与工作流程 面试官:能简单介绍一下OkHttp的工作流程吗? 候选人: 好的,OkHttp的工作流程大致可以分为几个步骤。首先,我们需要创建一个OkHttpClient实例,通常会用建造者模式来配置…...
移远三款主流5G模块RM500U,RM520N,RG200U比较
文章目录 概要一、技术架构差异1. 3GPP协议版本2. 芯片平台与性能3. 频段覆盖与区域适配4. 协议增强与特殊功能 二、功能与应用定位1. 网络兼容性2. 封装与接口扩展 三、典型应用场景总结 概要 本文介绍下移远两款主流5G模块RM500U RM520N RG200U。 一…...
C++引用以及和指针的区别
C++ 引用 引用(reference)是 C++ 中的一种变量类型,是另一个变量的别名。一旦引用被初始化,就不能再改变它所指向的对象。 引用的特点 必须初始化:声明引用时必须立即对其进行初始化。不可更改绑定:一旦引用绑定到某个变量,就不能再指向其他变量。语法简洁:使用引用不…...
firfox 国外版和国内版本账号不互通问题处理
https://blog.csdn.net/sinat_37891718/article/details/147445621 现在国际服的火狐浏览器修改使用国内的账号服务器,需要先在搜索框输入about:config 中改变三项配置,然后重启浏览器,才能正常使用国内的火狐账号服务器 identity.fxaccount…...

Linux基本指令篇 —— whoami指令
whoami 是 Linux 和 Unix 系统中一个简单但实用的命令,全称 Who Am I(我是谁)。它的功能是显示当前登录用户的用户名。以下是关于 whoami 的详细解析: 目录 1. 基本用法 2. 命令特点 3. 实际应用场景 场景 1:脚本中…...
用go从零构建写一个RPC(3)--异步调用+多路复用实现
在前两个版本中,我们实现了基础的客户端-服务端通信、连接池、序列化等关键模块。为了进一步提升吞吐量和并发性能,本版本新增了 异步发送机制 和 多路复用支持,旨在减少资源消耗、提升连接利用率。 代码地址:https://github.com/…...

力扣395做题笔记
题目链接 力扣395 第一次尝试 class Solution {public int longestSubstring(String str, int k) {char[] s str.toCharArray();int n s.length;int[] cnts new int[256];int ans 0;for (int r 0, l 0; r < n; r ) { cnts[s[r]];if (cnts[s[r]] > k) { ans Mat…...
Python-numpy中常用的统计函数及转换函数
numpy中常用的统计函数 numpy中常用统计函数numpy普通统计函数忽略 NaN 值进行统计百分位数 numpy中形状转换函数重塑数组(reshape)展平数组(flatten/ravel)转置(transpose/T) 数据类型的转换使用astype()转…...
【C语言干货】free细节
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、为啥*phead free掉了之后,为啥下面还 提示:以下是本篇文章正文内容,下面案例可供 可以用? 前言参考 一、为…...
网络安全-等级保护(等保) 2-0 等级保护制度现行技术标准
################################################################################ 第二章:现行等保标准要求,通过表格方式详细拆分了等保的相关要求。 GB 17859-1999 计算机信息系统 安全保护等级划分准则【现行】 GB/T22240-2020 《信息安全技术 网…...

WebSocket(看这一篇就够了)
文章目录 WebSocket 基本概念什么是WebSocket?为什么需要 WebSocket?与 HTTP 协议的区别WebSocket协议的原理WebSocket工作流程WebSocket 数据帧结构和控制帧结构。JavaScript 中 WebSocket 对象的属性和方法,以及如何创建和连接 WebSocket。webSocket简…...

旧物回收小程序:让闲置焕发光彩,为生活增添价值
你是否常常为家中堆积如山的闲置物品而烦恼?那些曾经心爱的物品,如今却成了占据空间的“鸡肋”,丢弃可惜,留着又无处安放。别担心,一款旧物二手回收小程序将为你解决这一难题,让闲置物品重新焕发光彩&#…...
精益数据分析(73/126):黏性阶段的功能优先级法则——七问决策模型与风险控制
精益数据分析(73/126):黏性阶段的功能优先级法则——七问决策模型与风险控制 在创业的黏性阶段,如何从海量的功能创意中筛选出真正能提升用户留存的关键改动?今天,我们结合《精益数据分析》中的“开发功能…...
React声明式编程(手动控制,大型项目,深度定制)与Vue响应式系统(自动优化,中小型项目,快速开发)区别
文章目录 React声明式与Vue响应式区别详解一、响应式机制原理对比1.1 Vue的响应式系统Vue响应式流程图Vue响应式代码示例 1.2 React的声明式更新React声明式流程图React声明式代码示例 二、更新触发逻辑差异2.1 Vue的自动更新Vue依赖收集机制 2.2 React的手动更新React Diff算法…...

数学建模MathAI智能体-2025电工杯A题实战
题目: 光伏电站发电功率日前预测问题 光伏发电是通过半导体材料的光电效应,将太阳能直接转化为电能的技术。光伏电站是由众多光伏发电单元组成的规模化发电设施。 光伏电站的发电功率主要由光伏板表面接收到的太阳辐射总量决定,不同季节太阳…...
跨平台游戏引擎 Axmol-2.6.0 发布
Axmol 2.6.0 版本是一个以错误修复和功能改进为主的次要LTS长期支持版本 🙏感谢所有贡献者及财务赞助者:scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox、DelinWorks 相对于2.5.0版本的重要变更: 通…...

C# Windows Forms应用程序-002
目录 项目结构 主类和命名空间 构造函数和析构函数 初始化组件 (InitializeComponent) 按钮点击事件处理程序 主程序入口点 项目截图: 完整代码: 项目结构 这个项目是一个简单的C# Windows Forms应用程序,获取指定文件的根信息…...

理解计算机系统_线程(八):并行
前言 以<深入理解计算机系统>(以下称“本书”)内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定 引入 接续理解计算机系统_并发编程(10)_线程(七):基于预线程化的…...

【MySQL】09.索引
索引是用来提高数据库的性能的,但查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值在于提高一个海量数据的检索速度。 1. 认识磁盘 MySQL 给用户提供存储服务,而存储的都是数据&…...

【备忘】 windows 11安装 AdGuardHome,实现开机自启,使用 DoH
windows 11安装 AdGuardHome,实现开机自启,使用 DoH 下载 AdGuardHome解压 AdGuardHome启动 AdGuard Home设置 AdGuardHome设置开机自启安装 NSSM设置开机自启重启电脑后我们可以访问 **http://127.0.0.1/** 设置使用 AdGuardHome DNS 效果图 下载 AdGua…...

[Windows] 游戏常用运行库- Game Runtime Libraries Package(6.2.25.0409)
游戏常用运行库 合集 整合了许多游戏会用到的运行库,支持 Windows XP – Windows 11 系统,并且支持自动检测系统勾选推荐的运行库,方便快捷。 本版特点: By:mefcl 整合常见最新游戏所需运行库 根据系统自动勾选推荐…...
MYSQL order 、group 与row_number详解
一、order by order by A ASC, B DESC,C ASC … 上述语句会先按照A排序,当A相同的时候再按照B排序,当B相同的再按照C排序,并会不按照ABC组合一起排序 二、group by group by A,B,C… select 中的字段必须是group by中的字段,…...
QT之巧用对象充当信号接收者
备注:以下仅为演示不代表合理性,适合简单任务,逻辑简单、临时使用,可保持代码简洁,对于复杂的任务应创建一个专门的类来管理信号和线程池任务. FileScanner类继承QObject和QRunnable,扫描指定目录下的文件获…...
《红警2000》游戏信息
游戏背景:与《红色警戒》系列的其他版本类似,基于红警 95 的背景设定,讲述了第二次世界大战期间,世界各国为了争夺全球霸权而展开战争。游戏画面与音效:在画面上相比早期的红警版本有一定提升,解析度更高&a…...
Vue3 + ThinkPHP8 + PHP8.x 生态与 Swoole 增强方案对比分析
一、基础方案:Vue3 ThinkPHP8 PHP8.x 传统架构 优点 成熟稳定 组合经过长期验证,文档和社区资源丰富ThinkPHP8 对PHP8.x有良好支持,性能比PHP7提升20-30% 开发效率高 TP8的ORM和路由系统大幅减少样板代码Vue3组合式API Vite开发…...

(九)PMSM驱动控制学习---高阶滑膜观测器
在之前的文章中,我们介绍了永磁同步电机无感控制中的滑模观测器,但是同时我们也认识到了他的缺点:因符号函数带来的高频切换分量,使用低通滤波器引发相位延迟;在本篇文章,我们将会介绍高阶滑模观测器的无感…...

25年上半年五月之软考之设计模式
目录 一、单例模式 二、工厂模式 三、 抽象工厂模式 四、适配器模式 五、策略模式 六、装饰器模式 编辑 考点:会挖空super(coffeOpertion); 七、代理模式 为什么必须要使用代理对象? 和装饰器模式的区别 八、备忘录模式 一、单例模式 这个…...