【庖丁解牛】机器视觉核心算法之圆拟合

切比雪夫(最小区域法)圆拟合算法 切比雪夫,最小区域法拟合圆 阅读详情


作为全球某机器视觉头部大厂的资深工程师,深感学校和互联网上主要资料局限于计算机视觉领域,能落地工业领域的算法寥寥无几,且相关算法原理语焉不详、乏善可陈,或示例代码缺少关键实现、谬误百出, 故作【庖丁解牛】系列课程,掰开揉碎了讲解机器视觉领域核心算法,并提供完整的高质量代码,力争为从业者提供严格贴近工业应用场景的、高质量的国际一线算法剖析。
本系列课程围绕工业视觉算法原理及实现,具体场景落地应考虑的工程实践化设计要点及经验,笔者将在其他系列课程中讲解。
庖丁解牛本期公开课的主题是 圆拟合。

什么是圆拟合

顾名思义,圆拟合是从给定的点集数据中拟合出一个圆。

定义

通过一堆离散的点,然后根据这些离散点的特点,拟合出一个圆。

特征

  1. 一堆离散的点,点数大于等于2;
  2. 离散点的特征,满足几何基元约束;
  3. 拟合出一个圆,结果一定是个圆。

主流算法

机器视觉(或是计算机视觉)领域,主流的圆拟合算法有:

  1. 最小二乘法
  2. 迭代重加权最小二乘法
  3. 非线性优化法
  4. 旋转角度法
  5. 旋转角度最小二乘法

后面两种算法仅用于小角度圆拟合场景,严格意义上讲不属于典型圆拟合范畴,因此本文不做介绍。

最小二乘法(LS)

原理

圆的标准方程为:
( r − a ) 2 + ( c − b ) 2 = R 2 (r-a)^2+(c-b)^2=R^2 (r−a)2+(c−b)2=R2
展开可得:
2 r a + 2 c b − a 2 − b 2 + R 2 = r 2 + c 2 2ra+2cb-a^2-b^2+R^2=r^2+c^2 2ra+2cb−a2−b2+R2=r2+c2
令 q = a 2 + b 2 − R 2 q=a^2+b^2-R^2 q=a2+b2−R2,则有:
[ r 1 ^ 2 + c 1 ^ 2 r 2 ^ 2 + c 2 ^ 2 r n ^ 2 + c n ^ 2 ] = [ 2 r 1 ^ 2 c 1 ^ 1 2 r 2 ^ 2 c 2 ^ 1 2 r n ^ 2 c n ^ 1 ] [ a b q ] \begin{equation} \left[ \begin{array}{c} \hat{r_{1}}^2+\hat{c_{1}}^2 \\ \hat{r_{2}}^2+\hat{c_{2}}^2 \\ \hat{r_{n}}^2+\hat{c_{n}}^2 \end{array} \right]= \left[ \begin{array}{ccc} \hat{2r_{1}} & \hat{2c_{1}} & 1 \\ \hat{2r_{2}} & \hat{2c_{2}} & 1 \\ \hat{2r_{n}} & \hat{2c_{n}} & 1 \end{array} \right] \left[ \begin{array}{c} a \\ b \\ q \end{array} \right] \end{equation} ​r1​^​2+c1​^​2r2​^​2+c2​^​2rn​^​2+cn​^​2​ ​= ​2r1​^​2r2​^​2rn​^​​2c1​^​2c2​^​2cn​^​​111​ ​ ​abq​ ​​​
式中, ( a , b ) (a,b) (a,b)是圆心, a 2 + b 2 − q \sqrt{a^2+b^2-q} a2+b2−q ​是圆半径。
误差函数:
e 2 = ∑ i = 1 n [ ( r i ^ − a ) 2 + ( c i ^ − b ) 2 − R 2 ] 2 e^2=\sum_{i=1}^n[(\hat{r_{i}}-a)^2+(\hat{c_{i}}-b)^2-R^2]^2 e2=i=1∑n​[(ri​^​−a)2+(ci​^​−b)2−R2]2
定义矩阵A:
[ 2 r 1 ^ 2 c 1 ^ 1 2 r 2 ^ 2 c 2 ^ 1 2 r n ^ 2 c n ^ 1 ] \begin{equation} \left[ \begin{array}{ccc} \hat{2r_{1}} & \hat{2c_{1}} & 1 \\ \hat{2r_{2}} & \hat{2c_{2}} & 1 \\ \hat{2r_{n}} & \hat{2c_{n}} & 1 \end{array} \right] \end{equation} ​2r1​^​2r2​^​2rn​^​​2c1​^​2c2​^​2cn​^​​111​ ​​​
定义矩阵B:
[ r 1 ^ 2 + c 1 ^ 2 r 2 ^ 2 + c 2 ^ 2 r n ^ 2 + c n ^ 2 ] \begin{equation} \left[ \begin{array}{c} \hat{r_{1}}^2+\hat{c_{1}}^2 \\ \hat{r_{2}}^2+\hat{c_{2}}^2 \\ \hat{r_{n}}^2+\hat{c_{n}}^2 \end{array} \right] \end{equation} ​r1​^​2+c1​^​2r2​^​2+c2​^​2rn​^​2+cn​^​2​ ​​​
则根据最小二乘法有:
[ a b q ] = ( A T A ) − 1 A T B \begin{equation} \left[ \begin{array}{c} a \\ b \\ q \end{array} \right]=(A^TA)^{-1}A^TB \end{equation} ​abq​ ​=(ATA)−1ATB​​

核心代码

double A_matrix[MAX_POINT_NUM] = { 0.0 };
double B_matrix[MAX_POINT_NUM] = { 0.0 };
double x_matrix[3] = { 0.0 };

for (int i = 0; i < nPointArrayCount; i++)
{
	A_matrix[i * 3 + 0] = 2 * fFitPointX[i];
	A_matrix[i * 3 + 1] = 2 * fFitPointY[i];
	A_matrix[i * 3 + 2] = 1;
	B_matrix[i] = pow(fFitPointX[i], 2) + pow(fFitPointY[i], 2);
}
int nRet = LeastSquare(A_matrix, B_matrix, x_matrix, nPointArrayCount);

fCenterX = x_matrix[0];
fCenterY = x_matrix[1];
fRadius = sqrt(pow(x_matrix[0], 2) + pow(x_matrix[1], 2) + x_matrix[2]);

迭代重加权最小二乘法(IRLS)

原理

最小平方圆拟合对于离群值不够鲁棒,因此需要引入权重并使用它来减小离群值的影响。先采用正常的最小二乘法拟合一个圆,然后用各拟合点到此圆的距离来计算在后续的迭代中将使用的各点所对应的权重。
误差函数:
e 2 = ∑ i = 1 n w i [ ( r i ^ − a ) 2 + ( c i ^ − b ) 2 − R 2 ] 2 e^2=\sum_{i=1}^nw_{i}[(\hat{r_{i}}-a)^2+(\hat{c_{i}}-b)^2-R^2]^2 e2=i=1∑n​wi​[(ri​^​−a)2+(ci​^​−b)2−R2]2
式中, ( a , b ) (a,b) (a,b)是圆心, R R R是圆半径, w i w_{i} wi​是权重函数。
第一种权重函数为Huber权重函数:
w ( δ ) = { 1 ∣ δ ∣ ≤ τ τ / ∣ δ ∣ ∣ δ ∣ > τ \begin{equation} w(\delta)=\left\{ \begin{aligned} 1 && |\delta| \leq \tau \\ \tau/|\delta| && |\delta| > \tau \\ \end{aligned} \right. \end{equation} w(δ)={1τ/∣δ∣​​∣δ∣≤τ∣δ∣>τ​​​
式中, δ \delta δ为拟合点到圆周的距离, τ \tau τ为削波因数。所有距离 ≤ τ \leq\tau ≤τ的点对应的权重都是1,距离 > τ >\tau >τ的点将分配更小的权重,从而减小离群点对结果的干扰。
第二种权重函数为Tukey权重函数:
w ( δ ) = { [ 1 − ( δ / τ ) 2 ] 2 ∣ δ ∣ ≤ τ 0 ∣ δ ∣ > τ \begin{equation} w(\delta)=\left\{ \begin{aligned} [1-(\delta/\tau)^2]^2 && |\delta| \leq \tau \\ 0 && |\delta| > \tau \\ \end{aligned} \right. \end{equation} w(δ)={[1−(δ/τ)2]20​​∣δ∣≤τ∣δ∣>τ​​​
式中, δ \delta δ为拟合点到圆周的距离, τ \tau τ为削波因数。所有距离 ≤ τ \leq\tau ≤τ的点对应的权重在1~0之间平滑变化,距离 > τ >\tau >τ的点将被完全忽略,不参与拟合,从而减小离群点对结果的干扰。
削波因数 τ \tau τ的计算公式为:
σ δ = m e d i a n ∣ δ i ∣ 0.6745 \sigma_{\delta}=\frac{median|\delta_{i}|}{0.6745} σδ​=0.6745median∣δi​∣​
τ = 2 σ δ \tau=2\sigma_{\delta} τ=2σδ​
对于符合正态分布的离群值,鲁棒标准偏差 σ δ \sigma_{\delta} σδ​的分子为所有拟合点到圆心距离的中值,分母为基于正态分布计算的标准偏差。

核心代码

// TODO Code 此处代码不做展示,留作读者自行实现

非线性优化法

原理

将所有拟合点到待拟合圆的平方距离进行连加求和,然后使求得的总和最小化。
误差函数:
e 2 = ∑ i = 1 n [ ( r i ^ − a ) 2 + ( c i ^ − b ) 2 − R ] 2 e^2=\sum_{i=1}^n[\sqrt{(\hat{r_{i}}-a)^2+(\hat{c_{i}}-b)^2}-R]^2 e2=i=1∑n​[(ri​^​−a)2+(ci​^​−b)2 ​−R]2
式中, ( a , b ) (a,b) (a,b)是圆心, R R R是圆半径。与之前的方法不同,这是一个非线性优化的问题,只能采用非线性优化技术来求解。
常见的非线性优化算法有:

  1. 梯度下降法
  2. 牛顿法
  3. 拟牛顿法
  4. 高斯牛顿法
  5. 列文伯格-马夸尔特法

其中,高斯牛顿法(Gauss–Newton algorithm)在牛顿法的基础上做了改进。牛顿法中的H矩阵需要计算目标函数的二阶偏导,计算量巨大,高斯牛顿法采用G矩阵替代H矩阵,大大减小了计算量。
因此,选择高斯牛顿法实现圆拟合的非线性优化。

核心代码

// 开始Gauss-Newton迭代
int iterations = 20;            // 迭代次数
double cost = 0, lastCost = 0;  // 本次迭代的cost和上一次迭代的cost

// CostFunction = Σ(AXi - Bi)²  A=(a,b,c) Xi=(xi,yi,1)^T Bi = -xi² - yi²
for (int iter = 0; iter < iterations; iter++) {
	Matrix3d H = Matrix3d::Zero();             // Hessian = J^T W^{-1} J in Gauss-Newton
	Vector3d b = Vector3d::Zero();             // bias
	cost = 0;

	for (int i = 0; i < N; i++) {
		double xi = x_data[i], yi = y_data[i];  // 第i个数据点
		double error = xi * xi + yi * yi + (ae * xi + be * yi + ce);
		Vector3d J; // 雅可比矩阵
		J[0] = xi;  // de/da
		J[1] = yi;  // de/db
		J[2] = 1;   // de/dc

		H += J * J.transpose();
		b += -error * J;

		cost += error * error;
	}

	// 求解线性方程 Hx=b
	Vector3d dx = H.ldlt().solve(b);
	if (isnan(dx[0])) {
		cout << "result is nan!" << endl;
		break;
	}

	if (iter > 0 && cost >= lastCost) {
		cout << "cost: " << cost << ">= last cost: " << lastCost << ", break." << endl;
		break;
	}
	
	ae += dx[0];
	be += dx[1];
	ce += dx[2];

	lastCost = cost;

	cout << "total cost: " << cost << ", \t\tupdate: " << dx.transpose() <<
		"\t\testimated params: " << ae << "," << be << "," << ce << endl;
}

double centerX = ae / -2;
double centerY = be / -2;
double radius = sqrt(ae * ae / 4 + be * be / 4 - ce);

运行结果对比

采集多组独立的数据,分别采用笔者提出的算法、VisionMaster(国内最领先的算法平台,后面统称为VM)进行圆拟合,从圆拟合结果(含拟合误差)、算法运行耗时两个方面评估算法的准确性和效率性。
VM圆拟合结果:
图1 VM圆拟合结果
本文圆拟合结果:
图2 本文圆拟合结果
算法运行效率对比:
图3 算法运行效率对比
可以看出,算法准确性方面,笔者提出的算法足以媲美VM;算法效率性方面,在拟合点数为200个点时,笔者提出的算法运行耗时稳定在0.23ms内,亦可媲美VM。

本期课程就到此结束了,下期课程我们不见不散。

如觉得本文内容不错,请记得给小编点赞、收藏、分享一键三连,您的支持是笔者更新的不懈动力。
想要算法源码工程的读者朋友可在评论区留言或私信小编。

别再乱选算法了!Halcon圆拟合算子fit_circle_contour_xld的6种算法深度评测与避坑指南 本文深入评测Halcon圆拟合算子fit_circle_contour_xld的6种核心算法,包括algebraic、geometric、Huber系列和Tukey系列,详细分析各算法在抗噪性、计算效率和轮廓适应性等方面的表现。通过实测数据和选型策略,帮助工业视觉检测开发者根据实际场景选择最优圆拟合算法,提升检测精度和效率。 阅读详情

相关推荐

最小二乘2D圆拟合(高斯牛顿法)

高斯牛顿拟合圆

闪电彬彬的博客 2113

圆拟合算法

圆拟合算法 文章目录圆拟合算法最小二乘法拟合(least square fitting)代数拟合 (algebraic fitting)Kåsa FitPratt Fit算法分析Taubin Fit迭代重加权最小二乘法(IRLS, iteratively reweighted least-squares)迭代加权最小二乘法在图形拟合中的应用削波因数的计算Huber 函数M 估计M 估计的发展M 估...

lingtianyulong的专栏 2万+

圆形参数拟合计算(解析法)

基于圆形参数原理,求解拟合圆参数

551

圆拟合算法(距离之和最小)

上一篇博客介绍了最小二乘法拟合圆的方法。这种方法对误差符合正态分布的数据点很有效。但是在机器视觉应用中经常会碰到一些干扰点。这些干扰点多数时候是偏向某一个方向的。这时要是用最小二乘法拟合,拟合出的圆会偏很多。因此,有必要研究更有效的拟合算法。这里介绍一个我常用的拟合算法,根据数据点到圆的距离绝对值的和来确定圆的参数,也就是下面这个式子:f=∑∣∣(xi−xc)2+(yi−yc)2−−−−−−−−−−

Ivan 的专栏 4万+

轮廓拟合之圆拟合与椭圆拟合

轮廓拟合 记录最近的研究内容: 针对如下的刨面图,分别对内圆和外圆进行拟合,然后得出其面积比,这是最终需求 涉及算法:阈值分割、轮廓提取、K均值聚类、圆+椭圆拟合、凸包检测 1.外圆拟合 外圆即根据最外侧的大轮廓拟合出的圆心,算法实现为:灰度化->大津阈值分割->轮廓提取->最小外接圆(最小面积)->得到圆参数 代码如下: struct Circle_ {//若为圆,赋值半径 Point2f center; float radius; float area; };

yohnyang的博客 1412

OpenCV 最小二乘+距离最小拟合圆

OpenCV 最小二乘+距离最小拟合圆, 不使用 GSL 算法库

Mr-MegRob 1万+

圆拟合算法学习【1】最小二乘法数据拟合

圆拟合算法学习【1】最小二乘法数据拟合 学过统计学的朋友们应该知道最小二乘法至关重要的地位,目前我的研究方面要对数据进行拟合,具体任务是在平面坐标系中拟合出圆心位置和半径来,首当其冲的想到了这一种方法,因此便把所学内容整理出来和大家分享。 1. 什么是最小二乘? 1805年最小二乘法便被提出,让我们来看看这两百多年前的成果吧。其实该方法用语言表达或者公式表达都很容易理解: yi是我们得到的样本组,对于后者可以说是我们拟合的试验品,那么E便是样本和拟合的误差,机器学习中将此描述为损失函数,最小二乘法的思想便

minglan123的博客 8188

拟合圆的几种方法

拟合圆的几种方法 1.平均值法 2.加权平均值法 3.最小二乘法 4.误差分析与讨论

圆拟合方法综述及代码解释

圆拟合代码

m0_44975814的博客 3012

拟合圆算法

包括一个matlab代码,用来拟合程序图像中的圆,方便快捷。

从零解析:富斯i6遥控器与STM32的IBUS协议通信实战

本文详细解析了富斯i6遥控器与STM32通过IBUS协议通信的实战指南,涵盖硬件准备、协议解析、驱动实现及常见问题解决。特别适合机器人及智能小车开发者,提供低成本、高可靠性的无线控制方案,助力快速实现多通道控制功能。

weixin_29193359的博客 440

最小二乘法拟合圆

普通最小二乘法:通过最小化点到圆的距离平方和,求解圆心ab(a, b)ab和半径rrr。带有约束的最小二乘法:在普通最小二乘法的基础上,引入约束条件(如圆心或半径固定),通过拉格朗日乘数法求解最优解。

T20151470的博客 2816

rk3568 rockit编译测试

编译rockit不能使用SDK默认编译kernel的交叉编译,需要采用编译buildroot的,而编译buildroot的编译器SDK并没有存放,需要编译过buildroot之后才可以用。命令参数相关解析可查阅代码,这里提醒一下-l参数,这是程序运行计数,默认是100,时间很短,基本上显示画面一闪而过,一度以为是程序哪里出问题了,改用-l 1000,画面显示时间加长。此处跳过系统,单领出来编译。rockit组件包在4.10版本需要手动编译,奈何我的版本怎么都编译不了,后来改用5.10版本才编译通过。

zfenggo的博客 4197

球面拟合算法

球面拟合算法 我在以前的博客中写过一篇介绍过最小二乘法圆拟合算法。前段时间有人给我留言,问我他的数据点是分布在一个球的球面上的,该如何拟合这个球的参数。球面拟合与圆的拟合其实是很类似的。只要按照我上篇文章的方法重新推导一下就能出来。正好这两天不太忙,在出差的路上推导了一下,把结果放到这里供大家参考。顺便多说一句,在高铁上用降噪耳机效果真的很好,很安静,值得拥有。 简单描述一下问题:我们有一组数...

Ivan 的专栏 1万+

从零到一:基于STM8的125KHz RFID读卡器实现与曼彻斯特码解析实战

本文详细介绍了基于STM8单片机的125KHz RFID读卡器实现过程,重点解析了曼彻斯特码的编码原理与解码算法。通过硬件电路设计、软件实现及性能优化,帮助开发者低成本构建稳定读取EM4100卡片的系统,适用于门禁、考勤等应用场景。

weixin_42525211的博客 236

微信小程序订阅消息实战:如何绕过长期订阅限制实现多次发送(附PHP代码)

本文针对微信小程序开发者无法获取长期订阅消息权限的痛点,提供了一套完整的实战解决方案。通过深入剖析一次性订阅与长期订阅的规则差异,结合巧妙的交互设计、稳健的PHP服务端发送引擎(含高并发处理与失败重试机制)以及授权池管理等高级策略,帮助开发者在合规前提下,构建高可用的消息推送系统,有效提升通知触达率与用户体验。

weixin_29008805的博客 621

圆拟合算法-平均值法

1、平均值法:问题:已知平均分布在圆上的n个点(xi,yi),拟合出圆(即求解出圆心坐标及半径)方法:理论上圆心坐标(a,b):,圆半径:。

pan的博客 6191

VisualStudio2022配置CGAL

vs2022和vs2017安装配置boost没什么区别,只需要注意boost的msvc得是14.3的,这玩意跟visualstudio版本挂钩的。压缩包解压,得到下面这两文件,然后把auxiliary这个文件挪到CGAL-5.6里面就行了。我用cgal读取一个stl模型,并输出相关信息(想可视化的话需要安装Qt,不想装Qt也可以装VTK)然后下载下面这两个压缩包。照着下面的截图填就完事了。

weixin_43850909的博客 1171

空间圆上N点拟合圆的算法

在空间圆上测量出N个点(如下图)的三维坐标(x,y,z),即平面x,y坐标加上高程z,根据这N组点的坐标求出圆心的坐标和半径。即: x^2 + y^2 + z ^2 + a^2 +b^2 +c^2 - 2ax - 2by -2cz = R ^2。设N组点的坐标数据为(x1,y1,z1)、(x2,y2,z2)、...(xn,yn,zn)得: x^2 + y^2 + z ^2 - Ax -By - Cz + D =0。球心坐标为(a,b,c)

测绘人lf的博客 1283

手把手教你用UC3842搭建12V/5A反激电源(附PSIM仿真文件)

本文详细介绍了基于UC3842的12V/5A反激电源设计与PSIM仿真实践。从UC3842芯片选型、高频变压器计算到PSIM仿真模型搭建,提供完整的开关电源设计流程。特别包含PCB布局要点和实测优化建议,帮助工程师快速掌握反激电源开发技巧。

u6v7w8x的博客 1203

Vue3 - Vite运行打包时Uncaught ReferenceError: process is not defined(解决vue3+vite报错process is not defined)

vue3,vite,vue3+vite报错,process,UncaughtReferenceError:processisnotdefined,浏览器控制台报错processisnotdefined解决方法,vue3如何解决报错,vite报错怎么办,前端打包UncaughtReferenceError:processisnotdefined,npmrunbuild打包报错,vite打包时出现错误,vue3打包运行找不到process,热更新会报错UncaughtRefer

🏆 前端领域优质创作者 7737

MATLAB圆拟合

查过很多材料,圆拟合的代码主要有两种,但是其实原理相同只是从两种方式表达而已。开始时只查到代码,并不知其原理也没查到,偶然一个机会下载一个资源得知其原理。 以下主要从三个方面总结MATLAB圆拟合 代码(含圆拟合、画圆) 原理 注意(这个最重要,见证了我沙雕的智商~) 1.代码 第一种:利用行列式,短小精悍 function [xc,yc,R,a] = circfit(x,y) %...

是江姑娘呀 3万+

《QGIS快速入门与应用基础》239:指北针样式选择(预设/自定义)

系统解析指北针设计的核心规范与实操方法。文章首先强调行业规范红线,包括强制要求N字标注、箭头辨识度等关键要素,通过正误示例对比强化合规意识。其次详细讲解QGIS内置预设样式的分类应用,提供极简箭头、标准三角箭头等4类样式的选型对照表,并配图说明适用场景。最后介绍从基础微调到SVG导入的进阶自定义方法,帮助用户实现从合规到专业的样式设计。全文贯穿"合规优先、可读为本、美观为辅"原则,配有详细操作截图与规范对照表,适合从GIS新手到专业制图人员参考。

LuckyHanMo的博客 530

圆孔毛刺检测系统:RANSAC圆拟合算法深度解析

本文详细讲解了RANSAC圆拟合算法在圆孔定位中的应用。主要内容包括:1)RANSAC原理及其对噪声数据的优势;2)三点定圆的数学推导与实现;3)优化的RANSAC实现,包含动态阈值设定和提前终止策略;4)最小二乘精拟合提升精度;5)预处理坐标还原;6)性能优化技巧;7)常见问题解决方案。该算法通过随机采样、内点验证和迭代优化,能够有效排除噪声干扰,精确拟合圆孔轮廓。文章还预告了下篇将探讨圆孔毛刺检测算法设计。

caixuwen的专栏 336

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

精准圆拟合:最小二乘法圆拟合算法C实现

精准圆拟合:最小二乘法圆拟合算法C#实现 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在数据分析和计算机视觉领域,圆拟合是一个常见且重要的任务。为了满足这一需求,我们推出了一个基于C#实现的最小二乘法圆拟合算法。该算法不仅能够生成随机点用于测试,还支持读取特定格式的点数据文件,从而对实际数据进行处理。通过最小二乘法,算法能够对任意给定的点集进行精准的圆拟合,输出拟合...

gitblog_09802的博客 974

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: 【庖丁解牛】机器视觉核心算法之N点标定
下一篇: 【庖丁解牛】机器视觉核心算法之圆环展开
AI Researcher
博客等级 码龄7年 310粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值