Halcon编程总结

1. 使用算子处理图像基本流程

* 关闭当前窗体
dev_close_window ()
* 读取图像
read_image (Image, 'test.jpg')
* 图像尺寸
get_image_size (Image, Width, Height)
* 打开窗体
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
* 绘制轮廓不填充
dev_set_draw ('margin')
* 线宽
dev_set_line_width (1)
* 字体
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
* 使用图像增强算子
emphasize (Image, ImageEmphasize, 5, 5, 2)
* 显示图像
dev_display(ImageEmphasize)
* 显示文本
dev_disp_text ('NG', 'image', 12, 12, 'red', [], [])

2. 图像预处理

mean_image、gauss_filter、binomial_filter:降噪 
median_image:抑制小斑点、细线 
smooth_image、anisotropic_diffusion:图像平滑 
腐蚀:对边界向内部收缩,消除边界点,去除小元素 
膨胀:对边界向外部扩充,填充空洞; 
opening_circle:先腐蚀后膨胀,去除孤立点、边缘毛刺,消除小对象、平滑边界,面积不变 
closing_circle:先膨胀后腐蚀,填充内部小孔,连接临近对象、平滑边界,面积不变

3. 图像分割

* 阈值分割,用到灰度直方图
threshold (Image, Region, MinGray, MaxGray)
* 联通区域
connection (Region, ConnectedRegions)
* 特征选择,用到特征直方图
select_shape (Regions, SelectedRegions, Features, Operation, Min, Max)
* 目标计数
count_obj (SelectedRegions, Number)
* 面积和中心坐标
area_center(SelectedRegions, Area, Row, Column)
* 填充
fill_up (SelectedRegions, RegionFillUp)

* 局部阈值 对边缘敏感
read_image (Image, ImageFiles[Index])
mean_image (Image, ImageMean, 5, 5)
dyn_threshold (Image, ImageMean, RegionDynThresh, 5, 'dark')

4. 轮廓处理

* 提取指定图像的外轮廓
gen_contour_region_xld (Regions, Contours, Mode)
* 将轮廓线分为多个部分
segment_contours_xld (Contours, ContoursSplit, Mode, SmoothCont, MaxLineDist1, MaxLineDist2)
* 通过不同的特征,提取出分割后轮廓中满足要求的轮廓线段
select_contours_xld (Contours, SelectedContours, Feature, Min1, Max1, Max2, Max2)
* 从轮廓线段集合中选择指定线段
select_obj (Objects, ObjectSelected, Index)
* 获取指定轮廓上点的像素坐标
get_contour_xld (Contour, Row, Col)
* 将位于轮廓线 Region 内的图像提取出来
reduce_domain (Image, Region, ImageReduced)
* 计算两个轮廓线之间最小的距离
distance_cc_min (Contour1, Contour2, Mode, DistanceMin)

5. 基于形状的模板匹配步骤

read_image (mage, 'test.jpg')
* 选择 ROI 区域
gen_rectangle1 (Rectangle, Row1, Column1, Row2, Column2)
* 抠图
reduce_domain (Image, Region, ImageReduced)
* 确定金字塔层级(默认'auto')
inspect_shape_model (ImageReduced, ModelImages, ModelRegions, 1, 40)
area_center (ModelRegions, AreaModelRegions, RowModelRegions, ColumnModelRegions)
count_obj (ShapeModelRegions, HeightPyramid)
for i := 1 to HeightPyramid by 1
    if (AreaModelRegions[i - 1] >= 15)
        NumLevels := i
    endif
endfor
* 创建模板
* create_shape_model (ImageReduced, 'auto', -0.39, 0.79, 'auto', 'auto', 'use_polarity', 'auto', 'auto', ModelID)
* 获取模板轮廓 位置在图像左上角
get_shape_model_contours (ModelContours, ModelID, Level)
* 在另一幅图像中寻找模板对应的区域
find_shape_model (Image, ModelID, AngleStart, AngleExtent, MinScore, NumMatches, MaxOverlap, SubPixel, NumLevels, Greediness, Row, Column, Angle, Score)
* 确定匹配位置的轮廓
for I := 0 to |Score| - 1 by 1
    * 刚性变换 
    vector_angle_to_rigid (0, 0, 0, Row[I], Column[I], Angle[I], HomMat2DRotate)
    * 与下面的等价
    * hom_mat2d_identity (HomMat2DRotate)
    * hom_mat2d_rotate (HomMat2DRotate, Angle[I], 0, 0, HomMat2DRotate)
    * hom_mat2d_translate (HomMat2DRotate, Row[I], Column[I], HomMat2DRotate)
    * 应用变换
    affine_trans_contour_xld (ModelContours, ModelTrans, HomMat2DRotate)
    * 显示找到的轮廓实际位置
    dev_display (ModelTrans)
endfor

* 尺度形状模型
reduce_domain (Image, RegionDilation, ImageReduced)
create_scaled_shape_model (ImageReduced, 5, rad(-45), rad(90), 0, 0.8, 1.0, 0, ['none','no_pregeneration'], 'ignore_global_polarity', 40, 10, ModelID)
* 保存模型
write_shape_model (ModelID, 'module.shm')
* 读取模型
read_shape_model ('module.shm', ModelID)
find_scaled_shape_model (ImageSearch, ModelID, rad(-45), rad(90), 0.8, 1.0, 0.5, 0, 0.5, 'least_squares', 5, 0.8, Row, Column, Angle, Scale, Score)
get_shape_model_contours (ModelContours, ModelID, 1)
for I := 0 to |Score| - 1 by 1
    vector_angle_to_rigid (0, 0, 0, Row[I], Column[I], Angle[I], HomMat2DRotate)
    hom_mat2d_scale (HomMat2DRotate, Scale[I], Scale[I], Row[I], Column[I], HomMat2DScale)
    affine_trans_contour_xld (ModelContours, ModelTrans, HomMat2DScale)
    dev_display (ModelTrans)
endfor

6. 批量导入图片

list_image_files ('scratch', 'png', [], ImageFiles)

7. 频域滤波

decompose3 (Image, R, G, B)
rft_generic (B, ImageFFT, 'to_freq', 'none', 'complex', Width)
gen_gauss_filter (ImageGauss, 100, 100, 0, 'n', 'rft', Width, Height)
convol_fft (ImageFFT, ImageGauss, ImageConvol)
rft_generic (ImageConvol, ImageFFT1, 'from_freq', 'none', 'byte', Width)
sub_image (B, ImageFFT1, ImageSub, 2, 100)

8. 训练差异模型比较图像

* 创建形状模型
create_shape_model (ImageReduced, 5, rad(-10), rad(20), 'auto', 'none', 'use_polarity', 20, 10, ShapeModelID)
* 创建差异模型
create_variation_model (Width, Height, 'byte', 'standard', VariationModelID)
* 寻找模板
find_shape_model (Image, ShapeModelID, rad(-10), rad(20), 0.5, 1, 0.5, 'least_squares', 0, 0.9, Row, Column, Angle, Score)
* 刚性变换 当前图像 → 参考图像
vector_angle_to_rigid (Row, Column, Angle, RowRef, ColumnRef, 0, HomMat2D)
* 图像校正
affine_trans_image (Image, ImageTrans, HomMat2D, 'constant', 'false')
* 用好的图像训练差异模型
train_variation_model (ImageTrans, VariationModelID)
* 获取差异模型图像
get_variation_model (MeanImage, VarImage, VariationModelID)
* 准备用于比较图像的模板
prepare_variation_model (VariationModelID, 20, 3)

* 应用差异模型进行图像比较 待检测图像也必须和模板图像对齐
compare_variation_model (ImageReduced, RegionDiff, VariationModelID)

9. 选择指定区域合并

* 生成空区域
gen_empty_region (Region)
* gen_empty_obj (EmptyObject)
for I := 1 to Number by 1
    select_obj (Regions, ObjectSelected, I)
    * 合并
    union2 (Region, ObjectSelected, Region)
    * 或者 concat_obj (EmptyObject, ObjectSelected, EmptyObject)
endfor

10 . 根据骨架合并直线

threshold (Image, Region, 0, 150)
connection (Region, ConnectedRegions)
* 区域骨架
skeleton (ConnectedRegions, Skeleton)
* 骨架 → XLD 轮廓
gen_contours_skeleton_xld (Skeleton, Contours, 1, 'filter')
* 合并共线轮廓
union_collinear_contours_xld (Contours, UnionContours, 30, 2, 10, 0.7, 'attr_keep')

11. 获取骨架、XLD 端点

* gen_contour_region_xld (Region, Contours, 'center')
gen_region_contour_xld (Contours, Region, 'filled')
skeleton (Region, Skeleton)
* 骨架的交接点和终点
junctions_skeleton (Skeleton, EndPoints, JuncPoints)
* 区域像素坐标
get_region_points (EndPoints, Rows, Columns)

12. Halcon 控件坐标 ↔ 图像坐标

// 控件坐标和图像坐标转换,有些许误差
private Point GetImageHalconPoint(double x, double y, bool flag = true)
{
	// Halcon 控件宽高
	double cHeight = Halcon.ActualHeight;
	double cWidth = Halcon.ActualWidth;
	// Halcon 图像区域
	double x0 = Halcon.HImagePart.X;
	double y0 = Halcon.HImagePart.Y;
	double imHeight = Halcon.HImagePart.Height;
	double imWidth = Halcon.HImagePart.Width;
	double ratio_y = imHeight / cHeight;
	double ratio_x = imWidth / cWidth;
	// 当前点坐标:相对控件或者相对图像
	double x1;
	double y1;
	// Halcon → Image
	if (flag)
	{

		x1 = (ratio_x * x) + x0;
		y1 = (ratio_y * y) + y0;
	}
	else
	// Image → Halcon
	{
		x1 = (x - x0) / ratio_x;
		y1 = (y - y0) / ratio_y;
	}
	return new Point(x1, y1);
}

// Halcon 事件
private void Halcon_HMouseMove(object sender, HSmartWindowControlWPF.HMouseEventArgsWPF e)
{
	// 图像坐标
	int row = (int)e.Row;
	int column = (int)e.Column;
}

13. 矩形、多边形、圆、椭圆模板

* contour 类型,光滑线型,不填充
* 普通矩形,填充,光滑
gen_rectangle1 (Rectangle1, 30, 20, 100, 200)
* 旋转矩形,不填充,光滑
gen_rectangle2_contour_xld (Rectangle, 300, 200, 0.78540, 100.5, 20.5)
* 旋转矩形,填充,锯齿
gen_rectangle2 (Rectangle2, 300, 200, 0.78540, 100, 20)

* 多边形,不填充,光滑
gen_contour_polygon_xld (Contour, rows, cols)
* 多边形,不填充,光滑
gen_polygons_xld (Contour, Polygons, 'ramer', 2)
* 多边形,不填充,锯齿
gen_region_polygon (Region2, rows, cols)
* 多边形(填充,有锯齿)
gen_region_polygon_filled (Region1, rows, cols)

* 普通椭圆,填充,锯齿
gen_ellipse (Ellipse, 200, 200, 0, 100, 60)
* 扇形椭圆,填充,锯齿
gen_ellipse_sector (EllipseSector, 200, 200, 0.78540, 100, 50, 0, 6.28318)
* 旋转椭圆,不填充,光滑
gen_ellipse_contour_xld (ContEllipse, 200, 200, 0.78540, 100, 50, 0, 6.28318, 'positive', 1.5)

* 普通圆,填充,锯齿
gen_circle (Circle, 200, 200, 100.5)
* 扇形圆,填充,锯齿
gen_circle_sector (CircleSector, 200, 200, 100.5, 0, 1.57080)
* 圆形,不填充,光滑
gen_circle_contour_xld (ContCircle, 200, 200, 100, 0, 6.28318, 'positive', 1)

* 通用,将 XLD contour 转成 region
gen_region_contour_xld (ContEllipse, Region, 'filled')

* 抠图
reduce_domain (Image, Region, ImageReduced)

14. 单相机标定:标定板+标定助手

Taosy.W:【Halcon 案例】单相机标定:标定板+标定助手11 赞同 · 6 评论文章正在上传…重新上传取消

15. 九点标定

Taosy.W:【Halcon 案例】九点标定17 赞同 · 0 评论文章正在上传…重新上传取消

16. 拟合圆

* 拟合圆所需点
gen_contour_polygon_xld (Contour, Rows, Cols)
fit_circle_contour_xld (Contour, 'geotukey', -1, 0, 0, 3, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)

17. 判断目标为空

* 和空目标对比
gen_empty_obj (EmptyObject)
test_equal_obj (EmptyObject, Region, IsEqual)
* 面积为 0
area_center (Region, Area, Row, Column)

// C#
if (ho_Region.IsInitialized())
if (Hv_ShapeModelID != null && Hv_ShapeModelID.ToString().Length > 5)
if (Hv_AcqHandle.ToString().Length > 5)
if (Hv_ShapeModelID.TupleIsValidHandle())

18. 文本显示格式设置

dev_disp_text ('平均距离:' + Distance$'.2f', 'image', 100, 100, 'black', [], [])

19. BadImageFormatException: 试图加载格式不正确的程序。(异常来自 HRESULT:0x8007000B)

项目右键【属性】→【目标平台】→【x64】,根据 halcondotnet.dll 来确定64位还是32位;

20. HSmartWindowControlWPF 自动自适应

WPF 软件尺寸大小改变时,HSmartWindowControlWPF 图像会自动自适应

21. 获取区域的点坐标

// 此命令出错 
// "HALCON error #1501: Wrong number of values of object parameter 1 in operator get_region_points"
HOperatorSet.GetRegionPoints(ho_Region, out hv_rows, out hv_cols);
// 解决:把 region 合并成一个整体
HOperatorSet.Union1(ho_Region, out HObject region);
HOperatorSet.GetRegionPoints(region, out hv_rows, out hv_cols);

* 区域边界
boundary (Region, RegionBorder, 'inner')
* 区域 → 点
get_region_contour (RegionBorder, Rows, Columns)
* 点 → 区域
gen_region_points (Region1, Rows, Columns)

22. HOperatorSet.GetGrayval 获取不到像素信息

Taosy.W:【WPF & Halcon】HOperatorSet.GetGrayval 和HOperatorSet.ReduceDomain 两个问题1 赞同 · 0 评论文章正在上传…重新上传取消

// 实时显示时获取不到像素值;
// 两次显示图像的时间间隔大一些
Thread.Sleep(100);

try
{
	HOperatorSet.GetGrayval(Ho_Image, point.Y, point.X, out HTuple ho_Grayval);
	StrCurGrayValue = ho_Grayval.ToString();
	ho_Grayval.Dispose();
}
catch (Exception)
{
	StrCurGrayValue = "null";
}

23. Region 模板 ReduceDomain 内容为空

Taosy.W:【WPF & Halcon】HOperatorSet.GetGrayval 和HOperatorSet.ReduceDomain 两个问题1 赞同 · 0 评论文章正在上传…重新上传取消

// 线程里加载本地图像,线程外访问此图像变量进行模板 ReduceDomain 内容始终为空
HOperatorSet.GenEmptyObj(out Ho_Image);
Ho_Task = new Task(TriggerGrabImage);
Ho_Task.Start();

// 创建模板前先保存到本地,然后从本地重新读取;
DirectoryInfo folder = new DirectoryInfo(@"module");
if (!folder.Exists)
{
    folder.Create();
}
HOperatorSet.WriteImage(Ho_Image, "bmp", 0, @"module\module.bmp");
Ho_Image.Dispose();
HOperatorSet.ReadImage(out Ho_Image, @"module\module.bmp");
Ho_Window.DispObj(Ho_Image);

HOperatorSet.GenRectangle2(out ho_Region, row, col, angle, len2, len1);
HOperatorSet.ReduceDomain(Ho_Image, ho_Region, out HObject ho_ImageReduced);

24. “HalconDotNet.HHandleBase”的类型初始值设定项引发异常

项目右键【属性】→【目标平台】→【x64】,根据 halcondotnet.dll 来确定64位还是32位;

25. Halcon 触发式拍图延迟

用 MVS 的 api 接口实现触发式拍图,转 Halcon 图像格式; 
// 这种方式比较慢 
HOperatorSet.GrabImageStart(Hv_AcqHandle, -1); 
HOperatorSet.GrabImageAsync(out Ho_Image, Hv_AcqHandle, -1);

26. Halcon 获取相机 ID

get_framegrabber_param (Hv_AcqHandle, 'DeviceID', Value)

27. 试图加载格式不正确的程序。(Exception from HRESULT: 0x8007000B)

// 在构造函数里初始化了变量导致这个错误
HOperatorSet.GenEmptyObj(out Ho_Image);

28. Halcon 文本显示去除边框

dev_disp_text ('hello', 'window', 20, 5, 'green', 'box', 'false')
HOperatorSet.DispText(Ho_Window, StrInfoResult, "window", "top", "left", "orange red", "box", "false");

29. 保存图片

private void SaveImages()
{
    // 建议选择 png 格式
    string filename = @"image\" + ComMethod.DateTimeNowToString() + ".png";
    HImage image = Ho_Window.DumpWindowImage();
    image.WriteImage("png", 0, filename);
    image.Dispose();
}

30. Halcon 图像窗口自适应

Halcon.SetFullImagePart();

31. 初始化控制变量

r1 := gen_tuple_const(9, 100)
r2 := [80:10:100]

32. WPF 显示 Halcon 窗体文字背景设置

黑色背景,文字没有边框,没有阴影

/// <summary>
/// 显示文本
/// </summary>
/// <param name="ho_Windwo"></param>
/// <param name="info"></param>
/// <param name="row"></param>
/// <param name="col"></param>
/// <param name="color"></param>
/// <param name="box_color"></param>
public static void DispText(this HWindow ho_Windwo, string info, double row, double col, string color = "red", string box_color = "black")
{
    // 背景
    HTuple atts = new HTuple();
    HTuple values = new HTuple();
    atts[0] = "box";
    values[0] = "true";
    atts[1] = "box_color";
    values[1] = box_color;
    atts[2] = "shadow";
    values[2] = "false";
    HOperatorSet.DispText(ho_Windwo, info, "window", row, col, color, atts, values);
}

// 实际应用可以先创建一个黑色背景,然后再上面覆盖重写文字
Ho_Windows[idx].DispText("最大宽度:00.00\n最大宽度:00.00\n最大宽度:00.00\n", 0, 0, background, background);
Ho_Windows[idx].DispText("缺陷检测:NG", 130, 0, color, background);

33. 海康相机触发式抓图时间优化

单次抓图时间约为 250 ms,两个相机约 500 ms,采用两个 Task 同时采集,时间减少一半;

T0_Time = DateTime.Now;
RefreshLogging("检测开始时间:");
Ho_ImageDown.Dispose();
Ho_ImageUp.Dispose();

// 发送拍照指令
MyCameraDown.MV_CC_SetCommandValue_NET("TriggerSoftware");
MvsMethod.TriggerReceiveImageMVS(MyCameraDown, out Ho_ImageDown);

MyCameraDown.MV_CC_SetCommandValue_NET("TriggerSoftware");
MvsMethod.TriggerReceiveImageMVS(MyCameraDown, out Ho_ImageUp);

Ho_Windows[0].DispObj(Ho_ImageDown);
Ho_Windows[1].DispObj(Ho_ImageUp);
RefreshLogging("拍照完成时间:");

// 2个任务取图抓图
T0_Time = DateTime.Now;
RefreshLogging("检测开始时间:");
AutoResetEvent[] watchers = new AutoResetEvent[2];
watchers[0] = new AutoResetEvent(false);
watchers[1] = new AutoResetEvent(false);
Task[] tasks = new Task[2];
// 线程执行完的时候通知
tasks[0] = new Task(() => { ReceiveImageTaskTest1(); _ = watchers[0].Set(); });
tasks[1] = new Task(() => { ReceiveImageTaskTest2(); _ = watchers[1].Set(); });
tasks[0].Start();
tasks[1].Start();

bool result = true;
for (int i = 0; i < watchers.Length; i++)
{
    result &= watchers[i].WaitOne();
}
RefreshLogging("拍照完成时间:");

private void ReceiveImageTaskTest1()
{
    MyCameraDown.MV_CC_SetCommandValue_NET("TriggerSoftware");
    MvsMethod.TriggerReceiveImageMVS(MyCameraDown, out HObject ho_Image);
    Ho_Windows[0].DispObj(ho_Image);
    ho_Image.Dispose();
}
private void ReceiveImageTaskTest2()
{
    Thread.Sleep(100);
}

34. 图像旋转、镜像耗时处理

图像旋转、镜像操作是比较耗时的,如果实际工作中如果因为相机安装方向、需要提取 ROI 等工作,可以先提取 ROI 之后再做旋转、镜像,这样可以省下很多时间,我这里测试省下了 150 ms;

T0_Time = DateTime.Now;
RefreshLogging("PLC 信号时间:");  // 0 ms
// 下相机:旋转 + 镜像
HOperatorSet.RotateImage(Ho_ImageDown, out HObject ho_ImageRotate, 90, "constant");
Ho_ImageDown.Dispose();
RefreshLogging("图像旋转时间:");  // 67 ms
HOperatorSet.MirrorImage(ho_ImageRotate, out Ho_ImageDown, "column");
ho_ImageRotate.Dispose();
RefreshLogging("图像镜像时间:");  // 100 ms
// 上相机:旋转
HOperatorSet.RotateImage(Ho_ImageUp, out ho_ImageRotate, 270, "constant");
Ho_ImageUp.Dispose();
RefreshLogging("图像旋转时间:");  // 159 ms
// 提取 4 个子图
for (int i = 0; i < Ho_Images.Length; i++)
{
    double row1 = Hv_Params[i].Row1;
    double col1 = Hv_Params[i].Col1;
    double row2 = Hv_Params[i].Row2;
    double col2 = Hv_Params[i].Col2;
    Ho_Images[i].Dispose();
    if (i < 2)
    {
        HOperatorSet.CropPart(Ho_ImageDown, out Ho_Images[i], row1, col1, col2 - col1, row2 - row1);
    }
    else
    {
        HOperatorSet.CropPart(ho_ImageRotate, out Ho_Images[i], row1, col1, col2 - col1, row2 - row1);
    }
}
Ho_ImageDown.Dispose();
ho_ImageRotate.Dispose();
RefreshLogging("图像处理时间:");  // 181 ms

/// 先提取子图后旋转、镜像
// 读取图片
HOperatorSet.ReadImage(out Ho_ImageDown, filename);
HOperatorSet.ReadImage(out Ho_ImageUp, filename);
T0_Time = DateTime.Now;
RefreshLogging("PLC 信号时间:");  // 0 ms
for (int i = 0; i < Ho_Images.Length; i++)
{
    double row1 = Hv_Params[i].Row1;
    double col1 = Hv_Params[i].Col1;
    double row2 = Hv_Params[i].Row2;
    double col2 = Hv_Params[i].Col2;
    Ho_Images[i].Dispose();
    if (i < 2)
    {
        HOperatorSet.CropPart(Ho_ImageDown, out Ho_Images[i], row1, col1, col2 - col1, row2 - row1);
    }
    else
    {
        HOperatorSet.CropPart(Ho_ImageUp, out Ho_Images[i], row1, col1, col2 - col1, row2 - row1);
    }
}
Ho_ImageDown.Dispose();
Ho_ImageUp.Dispose();
for (int i = 0; i < Ho_Images.Length; i++)
{
    if (i < 2)
    {
        HOperatorSet.RotateImage(Ho_Images[i], out ho_ImageRotate, 90, "constant");
        Ho_Images[i].Dispose();
        HOperatorSet.MirrorImage(ho_ImageRotate, out Ho_Images[i], "column");
        ho_ImageRotate.Dispose();
    }
    else
    {
        HOperatorSet.RotateImage(Ho_Images[i], out ho_ImageRotate, 270, "constant");
        Ho_Images[i].Dispose();
        HOperatorSet.CopyImage(ho_ImageRotate, out Ho_Images[i]);
        ho_ImageRotate.Dispose();
    }
}
RefreshLogging("图像处理时间:");  // 24 ms

35. DLL“halcon”: 找不到指定的模块

环境变量 → 系统变量 → Path → 新建,粘贴 Halcon 所在目录
C:\Program Files\MVTec\HALCON-18.11-Progress\bin\x64-win64

36. 区域的最小外接矩形

shape_trans (SortedChars, CharsArea, 'rectangle1')              

37. HSmartWindowControlWPF 不刷新图像

更新显卡驱动

 

  • 7
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值