ZYNQ图像处理(6)——均值滤波和中值滤波

一、均值和中值滤波基本原理

首先要做的是最简单的均值滤波算法。均值滤波是典型的线性滤波算法,它是指在图像上对目标像素给一个模板,该模板包括了其周围的临近像素(以目标象素为中心的周围 8 个像素,构成一个滤波模板,即去掉目标像素本身),再用模板中的全体像素的平均值来代替原来像素值。中值滤波算法可以形象的用上述表格来描述,即对于每个 33 的阵列而言,中间像素的值,等于边缘 8 个像素的平均值。
在这里插入图片描述
无论是直接获取的灰度图像,还是由彩色图像转换得到的灰度图像,里面都有噪声的存在,噪声对图像质量有很大的影响。进行中值滤波不仅可以去除孤点噪声,而且可以保持图像的边缘特性,不会使图像产生显著的模糊,比较适合于实验中的人脸图像。中值滤波算法与均值滤波非常的相似,但滤波的效果却有很大的差别,区别如下:(1) 均值滤波相当于低通滤波,有将图像模糊化的趋势,对椒盐噪声基本无能力。(2) 中值滤波的有点事可以很好的过滤椒盐噪声,缺点是容易造成图像的不连续。中值滤波的算法非常简单,只要求得 3
3 像素阵列的中间值即可,这样就有效的移植了最大值与最小值,图像会变得均匀,对椒盐噪声有很好的滤除效果!
《基于FPGA的实时中值滤波器的硬件实现》这篇文章中介绍了一种适合FPGA实现的快速寻找中值的算法,算法过程是这样的:首先计算每一行的最大值、中值以及最小值;之后计算第一列最大值中的最小值,第二列中间值中的中间值,第三列最小值中的最大值;最后再次计算这三个值的中间值就可以得到中值了。
在这里插入图片描述

二、3×3图像矩阵的提取

可以发现,无论是均值滤波还是中值滤波,一个必要的步骤就是得到3×3的图像矩阵。这个图像矩阵在matlab中很容易得到,因为matlab读取的是整一张的图片。但是在FPGA中对图像的实时处理,则需要先缓存两到三行的数据,然后从这3行数据中获得3×3图像矩阵。
查阅了一些资料,发现在VIVADO中实现3×3矩阵有以下几种方法。(1)第一种是利用vivado的IP核:ram based shift register,这个ip核在缓存数据量小的时候可以用。但是数据量很大,为几百个以上的时候,缓存数量就不准了,不知道是不是bug,因此不推荐使用。(2)第二种是利用RAM(3)第三种是利用fifo实现,利用fifo实现野火教程很详细。我这边是利用了两个ram实现数据的缓存和3×3矩阵的提取,接下来介绍实现过程。

2.1、例化ram ip核

首先自然是先需要例化一个ram的IP核,这个ram的配置信息如下如所示,是一个双端口的ram,宽度8位,深度1024位,优先写操作。详细的可以看配置的图。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2、基于ram的移位寄存器

基于ram 的移位寄存器代码如下图所示。输入端口有时钟信号、时钟使能信号、行同步信号、数据输入信号。输出端口有前一行数据、前前一行的数据。具体实现是这样的,当行同步和时钟使能到来时,地址开始累加了,当行同步结束,地址清零。之后对时钟信号、地址信号、数据信号都延迟三拍。最后两个ram分别存储了前一行和前前一行的图像。
比较关键的一点是,ram是先开始读,延迟几个时钟后才开始写,因为刚开始是读不到数据的,只有下一个行同步信号来的时候,前一行数据才会被读出来,这里比较绕。

module line_shift_RAM_8bit1(
	input clock,

	input          clken,
	input          per_frame_href,
	
	input   [7:0]  shiftin,  //当前行的数据
	output  [7:0]  taps0x,   //前一行的数据
	output  [7:0]  taps1x    //前前一行的数据
);

//reg define
reg  [2:0]  clken_dly;
reg  [9:0]  ram_rd_addr;
reg  [9:0]  ram_rd_addr_d0;
reg  [9:0]  ram_rd_addr_d1;
reg  [9:0]  ram_rd_addr_d2;
reg  [7:0]  shiftin_d0;
reg  [7:0]  shiftin_d1;
reg  [7:0]  shiftin_d2;
reg  [7:0]  taps0x_d0;

//在数据到来时,RAM的读地址累加
always@(posedge clock)begin
	if(per_frame_href)
		if(clken)
			ram_rd_addr <= ram_rd_addr + 1 ;
		else
			ram_rd_addr <= ram_rd_addr ;
	else
		ram_rd_addr <= 0 ;
end

//对时钟延迟3拍
always@(posedge clock) begin
	clken_dly <= { clken_dly[1:0] , clken };
end

//将RAM地址延迟3拍
always@(posedge clock ) begin
	ram_rd_addr_d0 <= ram_rd_addr;
	ram_rd_addr_d1 <= ram_rd_addr_d0;
	ram_rd_addr_d2 <= ram_rd_addr_d1;
end

//输入数据延迟3拍送入RAM
always@(posedge clock)begin
	shiftin_d0 <= shiftin;
	shiftin_d1 <= shiftin_d0;
	shiftin_d2 <= shiftin_d1;
end

//用于存储前一行图像的RAM
blk_mem_gen_0  u_ram_1024x8_0(
  .clka   (clock),
  .wea    (clken_dly[2]),
  .addra  (ram_rd_addr_d2),     //在延迟的第三个时钟周期,当前行的数据写入RAM0
  .dina   (shiftin_d2),
  
  .clkb   (clock),
  .addrb  (ram_rd_addr),    
  .doutb  (taps0x)              //延迟一个时钟周期,输出RAM0中前一行图像的数据
);

//寄存前一行图像的数据
always@(posedge clock)begin
	taps0x_d0  <= taps0x;
end

//用于存储前前一行图像的RAM
blk_mem_gen_0  u_ram_1024x8_1(
	.clka   (clock),
	.wea    (clken_dly[1]),
	.addra  (ram_rd_addr_d1),
	.dina   (taps0x_d0),       //在延迟的第二个时钟周期,将前一行图像的数据写入RAM1

	.clkb   (clock),
	.addrb  (ram_rd_addr),
	.doutb  (taps1x)           //延迟一个时钟周期,输出RAM1中前前一行图像的数据
);

endmodule

我这边结合仿真说一下,当clken[2]信号为高时,开始往里边写信号,可以看到第一次是把十六进制51写到了地址0中,但是可以发现读地址是已经到了3了,所以读速度是比写速度快了三个时钟周期,因为下一个行同步来时才可以得到上一行数据。
在这里插入图片描述
可以看到,下面这张图就是下一个行同步时间内,下一行数据从taps0x中读了出来。taps1x也是同样的道理,这边不在叙述。
在这里插入图片描述

2.3、3×3图像矩阵提取

3×3图像矩阵提取的代码如下图所示,代码本身比较简单,就是将输入信号缓存,然后提取出矩阵。这里需要注意信号的同步,得到3×3矩阵信号要比进来的信号慢了两个时钟周期。所以需要延迟两拍。

module  VIP_matrix_generate
(
	input				clk,  		
	input				rst_n,				

	input				per_frame_vsync,
	input				per_frame_href,
	input				per_frame_clken,
	input        [7:0]  per_img_Y,

	output				matrix_frame_vsync,
	output				matrix_frame_href,
	output				matrix_frame_clken,
	output	reg  [7:0]  matrix_p11, 
	output	reg  [7:0]  matrix_p12,
	output	reg  [7:0]  matrix_p13,
	output	reg	 [7:0]  matrix_p21, 
	output	reg  [7:0]  matrix_p22, 
	output	reg  [7:0]  matrix_p23,
	output	reg	 [7:0]  matrix_p31, 
	output	reg  [7:0]  matrix_p32, 
	output	reg  [7:0]  matrix_p33
);

//wire define
wire  [7:0]  row1_data;
wire  [7:0]  row2_data;
wire	     read_frame_href ;
wire	     read_frame_clken;

//reg define
reg  [7:0]  row3_data;
reg  [1:0]  per_frame_vsync_r;
reg  [1:0]  per_frame_href_r;
reg  [1:0]  per_frame_clken_r;

assign	read_frame_href    = per_frame_href_r[0] ;
assign	read_frame_clken   = per_frame_clken_r[0];
assign	matrix_frame_vsync = per_frame_vsync_r[1];
assign	matrix_frame_href  = per_frame_href_r[1] ;
assign	matrix_frame_clken = per_frame_clken_r[1];

//present signal
always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		row3_data <= 0;
	else begin		
		if(per_frame_clken)
			row3_data <= per_img_Y ;
		else
			row3_data <= row3_data ;
	end
end


line_shift_RAM_8bit1 u_line_shift_RAM_8bit1(
    .clock          ( clk               ),
    .clken          ( per_frame_clken   ),
    .per_frame_href ( per_frame_href    ),
    .shiftin        ( per_img_Y         ),
    .taps0x         ( row2_data         ),
    .taps1x         ( row1_data         )
);


//delay 2 tclk
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		per_frame_vsync_r <= 0;
		per_frame_href_r  <= 0;
		per_frame_clken_r <= 0;
	end
	else begin		
		per_frame_vsync_r <= { per_frame_vsync_r[0], per_frame_vsync };
		per_frame_href_r  <= { per_frame_href_r[0],  per_frame_href  };
		per_frame_clken_r <= { per_frame_clken_r[0], per_frame_clken };
	end
end

//generate the 3X3 matrix 
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
		{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
		{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
	end
	else if(read_frame_href) begin
		if(read_frame_clken) begin			
			{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};
			{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};
			{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};
		end
		else begin			
			{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};
			{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};
			{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};
		end	
	end
	else begin		
		{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
		{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
		{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
	end
end

endmodule

仿真的结果如下面三张图所示,当pei_img_Y数据来到的时候,延迟两个时钟周期进入到row3_data中,之后在延迟一个周期数据送到p31、p32以及p33中。row2_data和row1_data的得到也是同样的原理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、均值滤波的实现

前面已经清楚描述了均值滤波的实现过程,主要就是将图像像素点周围8个像素的值加起来然后除以8就可以了,下面式均值滤波的实现代码。

module mean_filter(
    input clk,
    input rst_n,

    input [23:0] pre_data,
    input per_vsync,
    input per_href,
    input per_clken,
    
    output [23:0] post_data,
    output post_vsync,
    output post_href,
    output post_clken
);

//-----------------------------
//generate 3×3 picture matrix
//-----------------------------
wire matrix_frame_clken;
wire matrix_frame_href;
wire matrix_frame_vsync;
wire [7:0] matrix_p11;
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;

VIP_matrix_generate u_VIP_matrix_generate(
    .clk                ( clk                ),
    .rst_n              ( rst_n              ),
    .per_frame_vsync    ( per_vsync   	 	 ),
    .per_frame_href     ( per_href           ),
    .per_frame_clken    ( per_clken      	 ),
    .per_img_Y          ( pre_data[7:0]      ),
    .matrix_frame_vsync ( matrix_frame_vsync ),
    .matrix_frame_href  ( matrix_frame_href  ),
    .matrix_frame_clken ( matrix_frame_clken ),
    .matrix_p11         ( matrix_p11         ),
    .matrix_p12         ( matrix_p12         ),
    .matrix_p13         ( matrix_p13         ),
    .matrix_p21         ( matrix_p21         ),
    .matrix_p22         ( matrix_p22         ),
    .matrix_p23         ( matrix_p23         ),
    .matrix_p31         ( matrix_p31         ),
    .matrix_p32         ( matrix_p32         ),
    .matrix_p33         ( matrix_p33         )
);

//-----------------------------
//mean filter function
//-----------------------------
reg [11:0] add_p1;
reg [11:0] add_p2;
reg [11:0] add_p3;
reg [11:0] add_all;
//step1:add every href
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        add_p1<=12'd0;
        add_p2<=12'd0;
        add_p3<=12'd0;
    end
    else begin
      add_p1<=matrix_p11+matrix_p12+matrix_p13;
      add_p2<=matrix_p21+matrix_p23;
      add_p3<=matrix_p31+matrix_p32+matrix_p33;
    end
end
//step2:add all the data
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        add_all<=12'd0;
    end
    else begin
        add_all<=add_p1+add_p2+add_p3;
    end
end
//step3:shift to get mean filter data
assign post_data={3{add_all[10:3]}};

//-----------------------------
//clk signal synchronization
//-----------------------------
reg [1:0] post_clken_dy;
reg [1:0] post_href_dy;
reg [1:0] post_vsync_dy;
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        post_clken_dy<=2'd0;
        post_href_dy<=2'd0;
        post_vsync_dy<=2'd0;
    end
    else begin
        post_clken_dy<={post_clken_dy[0],matrix_frame_clken};
        post_href_dy<={post_href_dy[0],matrix_frame_href};
        post_vsync_dy<={post_vsync_dy[0],matrix_frame_vsync};
    end
end
assign post_clken=post_clken_dy[1];
assign post_href=post_href_dy[1];
assign post_vsync=post_vsync_dy[1];

endmodule 

然后下面两张图分别是我用vivado仿真的加了椒盐噪声之后的图片以及均值滤波之后的图片。可以发现,均值滤波片让图片变得模糊了,而且椒盐噪声也没有有效滤除。这边的vivado仿真图像处理是用verilog去模拟了摄像头的产生时序,实现过程是参考了b站up主大磊FPGA。
在这里插入图片描述
在这里插入图片描述

四、中值滤波的实现

中值滤波的实现和均值滤波相似,也是需要先提取3×3的图像矩阵,然后提取9个值中的中值,提取的方法在上面也讲到了。
首先下面是对三个输出信号进行排序的代码,分别排序并且输出最大值,中间值以及最小值。

module sort_three(
    input clk,
    input rst_n,
    input [7:0] data1,
    input [7:0] data2,
    input [7:0] data3,
    
    output reg [7:0] max_data,
    output reg [7:0] mid_data,
    output reg [7:0] min_data

);

//find max,mid and min data 
//one clk to finish
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      max_data<=8'd0;
      mid_data<=8'd0;
      min_data<=8'd0;
    end
    else if (data1>=data2 && data2>=data3)begin
        max_data<=data1;
        mid_data<=data2;
        min_data<=data3;
    end
    else if (data1>=data3 && data3>=data2)begin
        max_data<=data1;
        mid_data<=data3;
        min_data<=data2;
    end
    else if (data2>=data1 && data1>=data3)begin
        max_data<=data2;
        mid_data<=data1;
        min_data<=data3;
    end
    else if (data2>=data3 && data3>=data1)begin
        max_data<=data2;
        mid_data<=data3;
        min_data<=data1;
    end
    else if (data3>=data1 && data1>=data2)begin
        max_data<=data3;
        mid_data<=data1;
        min_data<=data2;
    end
    else if (data3>=data2 && data2>=data1)begin
        max_data<=data3;
        mid_data<=data2;
        min_data<=data1;
    end
end

endmodule 

之后是中值滤波的代码,这部分实现的原理就是上文说到的提取中值的过程。

module median_filter(
    input clk,
    input rst_n,

    input [23:0] per_data,
    input per_vsync,
    input per_href,
    input per_clken,
    
    output [23:0] post_data,
    output post_vsync,
    output post_href,
    output post_clken
);

//-----------------------------
//generate 3×3 picture matrix
//-----------------------------
wire matrix_frame_clken;
wire matrix_frame_href;
wire matrix_frame_vsync;
reg [2:0] post_clken_dy;
reg [2:0] post_href_dy;
reg [2:0] post_vsync_dy;
wire [7:0] matrix_p11;
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;

VIP_matrix_generate u_VIP_matrix_generate(
    .clk                ( clk                ),
    .rst_n              ( rst_n              ),
    .per_frame_vsync    ( per_vsync   	 	 ),
    .per_frame_href     ( per_href           ),
    .per_frame_clken    ( per_clken      	 ),
    .per_img_Y          ( per_data[7:0]      ),
    .matrix_frame_vsync ( matrix_frame_vsync ),
    .matrix_frame_href  ( matrix_frame_href  ),
    .matrix_frame_clken ( matrix_frame_clken ),
    .matrix_p11         ( matrix_p11         ),
    .matrix_p12         ( matrix_p12         ),
    .matrix_p13         ( matrix_p13         ),
    .matrix_p21         ( matrix_p21         ),
    .matrix_p22         ( matrix_p22         ),
    .matrix_p23         ( matrix_p23         ),
    .matrix_p31         ( matrix_p31         ),
    .matrix_p32         ( matrix_p32         ),
    .matrix_p33         ( matrix_p33         )
);

//---------------------------------------------------
//              midian filter function
//---------------------------------------------------
//[a11 a12 a13]             [max1 med1 min1]
//[a21 a22 a23]      to     [max2 med2 min2]
//[a31 a32 a33]             [max3 med3 min3]  
//                                 to
//                 [min_of_max med_of_med max_of_min]
//                                to
//                               [med]    
wire [7:0] max_data_1;
wire [7:0] mid_data_1;
wire [7:0] min_data_1;
wire [7:0] max_data_2;
wire [7:0] mid_data_2;
wire [7:0] min_data_2;
wire [7:0] max_data_3;
wire [7:0] mid_data_3;
wire [7:0] min_data_3;
wire [7:0] max_data_4;
wire [7:0] mid_data_4;
wire [7:0] min_data_4;
wire [7:0] max_data_5;
wire [7:0] mid_data_5;
wire [7:0] min_data_5;
wire [7:0] max_data_6;
wire [7:0] mid_data_6;
wire [7:0] min_data_6;
wire [7:0] max_data_7;
wire [7:0] mid_data_7;
wire [7:0] min_data_7;

//step1:every line find max,mid and min data
sort_three u_sort_three_1(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( matrix_p11       ),
    .data2    ( matrix_p12       ),
    .data3    ( matrix_p13       ),
    .max_data ( max_data_1       ),
    .mid_data ( mid_data_1       ),
    .min_data ( min_data_1       )
);
sort_three u_sort_three_2(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( matrix_p21       ),
    .data2    ( matrix_p22       ),
    .data3    ( matrix_p23       ),
    .max_data ( max_data_2       ),
    .mid_data ( mid_data_2       ),
    .min_data ( min_data_2       )
);
sort_three u_sort_three_3(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( matrix_p31       ),
    .data2    ( matrix_p32       ),
    .data3    ( matrix_p33       ),
    .max_data ( max_data_3       ),
    .mid_data ( mid_data_3       ),
    .min_data ( min_data_3       )
);
//step2:every line find min_of_max,med_of_med,max_of_min
sort_three u_sort_three_4(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( max_data_1       ),
    .data2    ( max_data_2       ),
    .data3    ( max_data_3       ),
    .max_data ( max_data_4       ),
    .mid_data ( mid_data_4       ),
    .min_data ( min_data_4       )
);
sort_three u_sort_three_5(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( mid_data_1       ),
    .data2    ( mid_data_2       ),
    .data3    ( mid_data_3       ),
    .max_data ( max_data_5       ),
    .mid_data ( mid_data_5       ),
    .min_data ( min_data_5       )
);
sort_three u_sort_three_6(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( min_data_1       ),
    .data2    ( min_data_2       ),
    .data3    ( min_data_3       ),
    .max_data ( max_data_6       ),
    .mid_data ( mid_data_6       ),
    .min_data ( min_data_6       )
);
//step3:find median value
sort_three u_sort_three_7(
    .clk      ( clk              ),
    .rst_n    ( rst_n            ),
    .data1    ( min_data_4       ),
    .data2    ( mid_data_5       ),
    .data3    ( max_data_6       ),
    .max_data ( max_data_7       ),
    .mid_data ( mid_data_7       ),
    .min_data ( min_data_7       )
);
assign post_data={3{mid_data_7}};

//-----------------------------
//   signal synchronization
//-----------------------------
assign post_clken=post_clken_dy[2];
assign post_href=post_href_dy[2];
assign post_vsync=post_vsync_dy[2];
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        post_clken_dy<=3'd0;
        post_href_dy<=3'd0;
        post_vsync_dy<=3'd0;
    end
    else begin
        post_clken_dy<={post_clken_dy[1:0],matrix_frame_clken};
        post_href_dy<={post_href_dy[1:0],matrix_frame_href};
        post_vsync_dy<={post_vsync_dy[1:0],matrix_frame_vsync};
    end
end

endmodule

接下来是中值滤波的仿真结果,我打个比方,比如下面69这个数值是输出的中值。获得中值需要消耗三个时钟周期,因此矩阵数据比中值要快三个时钟周期,所以和这个中值对应的矩阵数据是:36、36、36、d7、bc、36、ff、f5、69。对应中值就是69,这边显示696969是把8位数据拼接成了24位的。
在这里插入图片描述
最后给出的这三张图片一次是加了椒盐噪声的RGB图片,均值滤波后的图片以及中值滤波后的图片。可以明显看到,中值滤波对椒盐噪声的滤除作用明显。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、总结

均值滤波和中值滤波是图像处理的基本操作,主要核心都是提取3×3的图像矩阵。因为现在的摄像头含噪声很少,所有没有放到FPGA开发板中进行测试,只是对其进行了matlab和vivado的仿真。后面做完这部分基础的图像处理后我整理下代码并供大家参考。

  • 9
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

树叶~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值