从零开始学习SFR--4.0

从零开始学习SFR--4.0


前言:因为课题涉及镜头质量检测,而现在镜头检测最普遍的方法便是MTF曲线作为检测镜头质量的标准。网上相关的学习资料并不多,也有一些大佬做了相关算法的研究,不过零零散散,难以成系统。为了学习并实现相关算法,参考各大佬的文章,对整个学习思路进行整理,特开此贴作为学习笔记。


从这篇继上一篇对sfr_iso.c文件注解之后,继续对find_area.c和mitre_sfr.c文件中的代码进行注解,当中参考了很多大佬的文章。

代码注解参考:
链接: Mitre_sfr代码注解(一)main函数
链接: Mitre_sfr代码注解(二) sfrproc函数
链接: Mitre_sfr代码注解(三) 将ROI超采样为ESF(locate_centroids函数/fit函数和bin_to_regular_xgrid函数)
链接: Mitre_sfr代码注解(四) LSF / 汉明窗 / SFR(DFT)计算


find_area.c

这里从拍摄的图像(一般为RGB)中截取ROI区域,并将其转换为Gray。

有个问题就是:用于SFR算法的图像大小多大比较合适?ROI的区域大小信息并没有严格定义,但会有影响,具体各个方面的要求可参考如下:
对于定义ROI,有以下几个要求:(针对垂直的斜边)
1) 对于垂直的斜边,ROI的宽度要小于高度。
2)在ROI中,只允许出现一个斜边,并且大致居中。
3)在底部或者顶部,比例小的那一部分不能低于5个像素
4)在中心位置,边界到中间斜边的距离需要在20至60个像素之间
5)高度需要在80到300之间。
6)ROI中避免污点,保证图像边界基本连续。

ROI要求

/*****************************************************************************/
/* External functions */
unsigned short fit(unsigned long, double *, double *, double *, double *, 
		   double *, double *, double *);
unsigned short locate_centroids(double *, double *, double *,
				unsigned short, unsigned short, double *);
unsigned short check_slope(double, unsigned short *, int *, double, int);
/* Internal function */
void find_10percent(double *, int, double, int *, int *);

/*****************************************************************************/
/* Data passed to this function is assumed to be radiometrically corrected,  */
/* and oriented vertically, with black on left, white on right. The black to */
/* white orientation doesn't appear to be terribly important in this code.   */
//传递给该函数的数据被认为是经过辐射校正的,并垂直定向,黑色在左边,白色在右边。
//在这段代码中,黑白方向似乎并不十分重要。
/*     Parameters:
          Input:  farea  = radiometric image data in original ROI
                  size_x = number of columns in original ROI
		  size_y = number of rows in original ROI
		  verbose  Whether ROI search messages are desired
		           0 = No printout
			   1 = Print info as better ROIs are found

	  Output: ncols  = number of columns in recommended ROI
	          nrows  = number of rows in recommended ROI
		  center_x = column center of recommended ROI
		  center_y = row center of recommended ROI

*/
/*
参数:
输入:farea=原始ROI中的辐射图像数据
      size_x=原始ROI中的列数
      size_y=原始ROI中的行数
      是否需要ROI搜索消息
      0=无打印输出
      1=找到更好的ROI时打印信息

输出:ncols=建议ROI中的列数
      nrows=建议ROI中的行数
      center_x=建议ROI的列中心
      center_y=建议ROI的行中心
*/
/*****************************************************************************/
short find_area (double *farea, unsigned short size_x, unsigned short size_y, 
		 int *ncols, int *nrows, int *center_x, int *center_y, 
		 unsigned char verbose)
{
  int numcycles;
  unsigned short len, i, dummy, err = 0;
  double *temp=NULL, *shifts=NULL;
  double R2, avar, bvar, offset1, offset2;
  double slope, min, expect_edge;
  int minlen, mincenter, center, signflag, cnt;
  int center_row, check_row, half_width;
  int min_x, max_x, first1, first2, last1, last2;
  double *tptr, *sptr;

  *nrows = size_y;
  *ncols = size_x;

  /* Allocate memory */
  shifts = (double *)malloc(size_y*sizeof(double));
  temp = (double *)malloc(size_y*sizeof(double));

  center_row = *nrows/2;

  /* These values should change during correct operation */
  mincenter = -1;
  minlen = -1;

  err = locate_centroids(farea, temp, shifts, size_x, size_y, &offset1); 

  min = 0.0;
  slope = -1./10.;
  signflag = 1;
  for (i = 0; i<center_row - 1.0/fabs(slope)/2;) {
    center = center_row + signflag*i;
    len = (center< size_y-center)? center: size_y-center;
    sptr = shifts + center - len;
    tptr = temp + center - len;
    len = 2*len+1;
    numcycles = 5;

    cnt = 0;
    for (; numcycles>4; len--, sptr++, tptr++) {
      /* Calculate the best fit line to the centroids */\
      //计算与质心的最佳拟合线
      if (len <= size_y) {
	err = fit(len, tptr, sptr, &slope, &offset2, &R2, &avar, &bvar);
    
	/* Check slope is OK, and set size_y to be full multiple of cycles */
    //检查斜率是否正常,并将size_y设置为周期的全倍数
	dummy = len;
	numcycles = 0;
	err = check_slope(slope, &dummy, &numcycles,5.0, 0);
	
	if (numcycles > 4 && R2 > min) {
	  if(verbose)
	    MTFPRINT7("Center row: %d Rows: %d  Angle %.3f  R2 = %f #Cycles=%d SE =  %.3f\n",
		      center, len, atan(fabs(slope))*180.0/M_PI, 
		      R2, numcycles, atan(bvar)*180/M_PI)
	  min = R2;
	  minlen = len;
	  mincenter = center;
	}
	if (numcycles > 4) cnt++;
      }
      len--;

      /* Calculate the best fit line to the centroids */
      //计算与质心的最佳拟合线
      err = fit(len, tptr, sptr, &slope, &offset2, &R2, &avar, &bvar);
    
      /* Check slope is OK, and set size_y to be full multiple of cycles */
      //检查斜率是否正常,并将size_y设置为周期的全倍数
      dummy = len;
      numcycles = 0;
      err = check_slope(slope, &dummy, &numcycles, 5.0, 0);
    

      if (numcycles > 4 && R2 > min) {
	if(verbose)
	  MTFPRINT7("Center row: %d Rows: %d  Angle %.3f  R2 = %f #Cycles=%d SE =  %.3f\n",
		    center, len, atan(fabs(slope))*180.0/M_PI, 
		    R2, numcycles, atan(bvar)*180/M_PI)
	min = R2;
	minlen = len;
	mincenter = center;
      }
	if (numcycles > 4) cnt++;
    }
    signflag *= -1;
    if (signflag == -1) i++;
    if (signflag == -1 && cnt == 0) break;
  }

  sptr = shifts + mincenter - minlen/2;
  tptr = temp + mincenter - minlen/2;
  err = fit(minlen, tptr, sptr, &slope, &offset2, &R2, &avar, &bvar);
                                                   
  /* Check slope is OK, and set size_y to be full multiple of cycles */
  dummy = minlen;
  numcycles = 0;
  err = check_slope(slope, &dummy, &numcycles,5.0, 1);
	
  if(verbose)
   MTFPRINT7("Best Center: %d Hgt: %d  Angle %.3f  R2 = %f #Cycles=%d SE =  %.3f\n",
	      mincenter, minlen, atan(fabs(slope))*180.0/M_PI, 
	     R2, numcycles, atan(bvar)*180/M_PI)

  *center_x = *ncols/2;
  *center_y = mincenter;
  *nrows = minlen;

  /* Figure out where edge is on first/last line, and then find
     where derivative goes consistently (5pixels) below 10% that value */
  //计算出边在第一行/最后一行的位置,然后找出导数一致(5像素)低于该值10%的位置
  check_row = mincenter - dummy/2;
  expect_edge = (double)(check_row-center_row)*slope + offset2 + offset1;
  find_10percent(farea+check_row*size_x,size_x,expect_edge,&first1, &last1);
  check_row = mincenter + dummy/2;
  expect_edge = (double)(check_row-center_row)*slope + offset2 + offset1;
  find_10percent(farea+check_row*size_x,size_x,expect_edge,&first2, &last2);

  max_x = (last1 < last2)? last2: last1;
  min_x = (first1 < first2)? first1: first2;

  if(verbose)
  MTFPRINT4("Needs from: %d to %d = %d cols \n",min_x, max_x, max_x-min_x)

  if (max_x-min_x+31 < *ncols) { /* Try adding 15 pixels beyond ends */
    *ncols = max_x-min_x+30;
    *center_x = (max_x+min_x)/2;
    half_width = (*ncols+1)/2;
    if (*center_x < half_width) *center_x = half_width;
    if (size_x - *center_x < half_width) *center_x = size_x - half_width;
    if ((*ncols)%2 != 0) (*ncols)++;
  }
  else if (max_x-min_x > 32) {
    *ncols = max_x-min_x;
    *center_x = (max_x+min_x)/2;
    if ((*ncols)%2 != 0) (*ncols)++;
  }
  else { /* Can't add 15 at each end, but not as big as 32 yet */
    /* Need to add enough to get up to 32 */
    *center_x = (max_x + min_x)/2;
    if (*center_x < 16) *center_x = 16; 
    else if (*center_x > size_x-16) *center_x = size_x - 16;
    if (size_x>= 32) *ncols = 32;
  }

  free(temp);
  free(shifts);

  return(0);
}

mitre_sfr.c

在获取图像以后还要对图像进行线性化处理。

在Sensor获得图像之后,呈现出来的图像由于要符合人眼的感觉,会对图像像素进行伽马变换,使得其变成非线性的像素数据,从而使图像的显示更加符合人眼的感受。所以,当我们要进行sensor的成像解析力分析时,要先将图像处理成没有经过伽马变换前的。

主要是对获取到的斜边数据进行伽马变换,获取到线性的图像数据。

为什么要获取线性的图像数据?
在SFR的标准算法中,建议的算法是用OECF将图像数据转化为线性数据。因此需要讨论下我们获取到的数据是否线性数据。线性跟非线性应该怎样去理解。

通常,sensor获取到的数据是线性的,sensor上每个pixel都是根据物体所呈现出来的颜色强度转化为对应的数值。但是,由于人眼对颜色的感知不是线性的,因此,通常在相机内部处理中,都有对转化后的图像进行伽马变换,变换后的图像就不是线性的,使得图像数据能够适应人眼。而通常取值为2.2的伽马变换。

为了获取到线性的图像数据,需要抵消这个伽马变换,在算法上,我们可以采取1/2.2的伽马变换来抵消。

gamma

int main(int argc, char **argv)
{
  char problem_string[82];
  unsigned char rotation;
  int i;
  double *farea;
  double slope, scale, b;
  int size_x, size_y;
  int len, err, bin_len;
  int rgt_side, left_side;
  int center;
  int numcycles=0;
  double *Freq=NULL;
  double *disp=NULL;
  double *ref_lut;
  double off, R2;
  int center_x, center_y, new_x, new_y;
  int lowest_val, highest_val, grey_level, first, last;
  int piv_err;

  TIFF* tif = NULL;

#if !defined(MSDOS)
  char *fnamep;
  fnamep = dirname(argv[0]);
  if (argc==1 && fnamep != NULL && fnamep[0] == '/') {
    printf ("Changed to directory of executable:  %s\n\n", fnamep);
    chdir((const char *)fnamep);
  }
#else
  atexit(wait_to_exit);
#endif

  /* Once-only initializations */
  g_scan_image_file_id = 0;
  g_problem_count = 0;                /* # of problems in our problem report */
  g_IQS_problem_count = 0;

  get_switches(argc, argv, image_filename, data_filename);              

  /* always append output to this file */
  if ((g_mtfout = fopen(MTFOUTNAME,"a")) == NULL) {
    fprintf(stderr,"Can't open %s\n",MTFOUTNAME);
    exit(1);
  }

  /* command line args */
  get_args(image_filename,data_filename,&tif); // 获取源文件名并读取文件头, 获取ROI位置, 如果需要读取计算角度的两个点位
 
  /* print the user entered data (corner coords, etc.) in the output */
  /* 时间, 版本号, 以及get_args函数里面获取到的信息*/
  print_header(image_filename,data_filename);

  g_target_res = 500;
  if (abs((int)g_ppi - g_target_res) > 10) {
    sprintf(problem_string,
	    "Resolution outside the %d-%d ppi PIV spec range\n",
	    g_target_res-10,g_target_res+10);
    put_problem(problem_string, IQS);
    put_problem("\n", IQS);
  }

  // 从ROI 4个角落各读取4个像素点, 用于判断刀口方向, 并确认ROI区域的合法性
  input_area(tif, &rotation, 
	     g_test_pattern_yul, g_test_pattern_ylr, 
	     g_test_pattern_xul, g_test_pattern_xlr);

  /* read the image in - we store it internally in a standard way */
  // 读取ROI图像, 如果并将水平刀口转换为垂直刀口
  read_in_image(tif,rotation,image_filename,&size_x,&size_y);

  /* get reflectance */
  // 创建反射查找表, 将图像灰阶映射在0~1范围内
  ref_lut = read_in_ref_lut(data_filename, &lowest_val, &highest_val);

  // 将原图ROI映射到反射ROI
  len = size_x*size_y;
  farea = (double *)malloc(len*sizeof(double));
  for(i=0; i<len; i++) {    
    grey_level = g_image_array[i];
    if (grey_level < lowest_val || grey_level > highest_val) {
      MTFPRINT("ERROR: OECF range does not cover the entire image\n")
      MTFPRINT2("       Greylevel %d found in image, but not OECF\n", grey_level)
      exit(-1);
    }
    farea[i] = (double)ref_lut[grey_level];
  }

  if(g_autorefine) {   
    find_area(farea, (unsigned short)size_x, (unsigned short)size_y, &new_x, &new_y, &center_x, &center_y, g_extended);
    if (new_x != size_x || new_y != size_y ||
	center_x != size_x/2  || center_y != size_y/2) {
      center_x += (g_test_pattern_xlr + g_test_pattern_xul)/2 - size_x/2;
      center_y += (g_test_pattern_ylr + g_test_pattern_yul)/2 - size_y/2;
      g_test_pattern_xul = center_x - new_x/2;
      g_test_pattern_yul = center_y - new_y/2;
      g_test_pattern_xlr = g_test_pattern_xul + new_x;
      g_test_pattern_ylr = g_test_pattern_yul + new_y;
      read_in_image(tif,rotation,image_filename,&size_x,&size_y);
      len = size_x*size_y;
      for(i=0; i<len; i++)
	farea[i] = (double)ref_lut[g_image_array[i]];
    }
    MTFPRINT("Refined coordinates of slanted edge area:\n")
      MTFPRINT3("Upper Left Col =\t\t%d\tUpper Left Row =\t%d\n",
            g_test_pattern_xul,g_test_pattern_yul)
      MTFPRINT3("Center Col =\t\t\t%d\tCenter Row =\t\t%d\n",
            (g_test_pattern_xlr+g_test_pattern_xul)/2,
	    (g_test_pattern_ylr+g_test_pattern_yul)/2)
      MTFPRINT3("Width =\t\t\t\t%d\tHeight =\t\t%d\n",
            (g_test_pattern_xlr-g_test_pattern_xul),
	    (g_test_pattern_ylr-g_test_pattern_yul))
    MTFPRINT("\n\n")
  }

  if (g_userangle) {
    double fslope;

    slope = (g_pt1x-g_pt2x)/(double)(g_pt1y-g_pt2y);
    b = g_pt1x - slope*g_pt1y;
    if (rotation == RIGHT)
      off = (g_test_pattern_yul + size_y/2)*slope + b - g_test_pattern_xul;
    else {
      off = (g_test_pattern_xlr - 1 - size_y/2 - b ) / slope - g_test_pattern_yul;
      slope = -1.0/slope;
    }
    fslope = fabs(slope)*size_y/2;
    if(  off-fslope < 2  || off+fslope >= size_x-2 ) {
      MTFPRINT("** ERROR: User specified edge does not work with ROI **\n");
      if (off-fslope < 2)
	MTFPRINT3("Edge Angle = %.3f  degrees    located %d pixels before ROI\n",
              (atan(slope)*(double)(180.0/M_PI)), (int)rint(off))  
      else
	MTFPRINT3("Edge Angle = %.3f  degrees    located %d pixels after ROI\n",
              (atan(slope)*(double)(180.0/M_PI)), (int)rint(off-size_x))  
      if (g_debug) {
	MTFPRINT("See location of edge points in diagnostic image\n")
	write_debug_image(g_debug_array,g_debug_fullwidth, g_debug_fullheight);
      }
      exit(-1);
    }
  }  

  // 计算ROI中超出取值范围[0, 255]外的像素比例
  // 前面像素映射的时候已经逐点确认过是否所有像素在oecf范围内, 感觉这个函数没什么实际作用
  clipping (0, 255, 0.02, g_image_array, len);

  /* calculate the sfr on this area */
  err = sfrProc(&Freq, &disp, &bin_len, farea, (unsigned short)size_x, &size_y, &slope, &numcycles,&center,&off, &R2, g_version, 0, g_userangle);

  /* Add messages to problem report */
  slope_bounds( slope, size_y, numcycles, 5.0, problem_string);

  if(g_debug) {
    draw_lines(g_debug_array, slope, rotation, center, size_y, off, size_x);
    write_debug_image(g_debug_array,g_debug_fullwidth, g_debug_fullheight);
  }

  if (!g_userangle)
    MTFPRINT2("R2 of linear edge fit = %.3f\n", R2)
  else 
    MTFPRINT("User input edge location: No R2 available\n")
  MTFPRINT4("Edge Angle = %.3f  degrees  \nCycle Length = %.3f  \t#Cycles = %d\n",
              (atan(slope)*(double)(180.0/M_PI)), fabs(1.0/slope), numcycles )  

  left_side = size_x/2+off;  rgt_side = size_x/2-off;
  if (left_side > bin_len/4) left_side = bin_len/4;
  if (rgt_side > bin_len/4) rgt_side = bin_len/4;

  MTFPRINT2("SFR computed using ~%d pixels on left or top side of the edge,\n", left_side)
  MTFPRINT2("               and ~%d pixels on right or bottom side of the edge,\n", rgt_side)
  MTFPRINT("              all within the ROI\n")
  if (rotation == RIGHT)
    MTFPRINT3("    over %d rows, centered at row %d\n", size_y, (g_test_pattern_yul + g_test_pattern_ylr)/2 )
  else
    MTFPRINT3("    over %d cols, centered at col %d\n", size_y, (g_test_pattern_xul + g_test_pattern_xlr)/2 )

  if (center/4 < 16) {
    MTFPRINT("\nERROR: Too few pixels across the edge for valid SFR computation\n\n")
    exit(-1);
  }
  if ( left_side < 20 || rgt_side < 20) 
    MTFPRINT("Warning: Low width across the edge. SFR values may be suspect.\n")

  /* log any problems we encountered into output */
  if (g_reversepolarity)
    MTFPRINT("\nNOTE: Original image polarity was reversed by user request.\n")

  if (err) {
    MTFPRINT ("** ERROR in computation. SFR values unknown **\n\n")
      exit(-1);
  }

  scale = g_ppi/MM_PER_INCH;
  MTFPRINT("\n\ncy/mm         \t SFR  ")
  if(rotation == RIGHT)
    MTFPRINT("\t  Vert ")
  else
    MTFPRINT("\t  Horz ")
  if(farea[4*size_x-1] - farea[0] > 0) 
    MTFPRINT("black-to-white** edge\n(obj.plane)\n")
  else
    MTFPRINT("white-to-black** edge\n(obj.plane)\n")

  piv_err=0;
  for( i=0; i<bin_len/2; i++) {
    double f, sfr;
    double freq, fd_scale;

	// 对加海明窗导致的功率损失进行补偿?
    freq = M_PI*Freq[i];
    /* [-1 0 0 0 1]  freq /= 1.0; */
    if (g_version & 4) /* [-1 0 1] */
      freq /= 2.0;
    else freq /= 4.0;  /* [-1 1] */
    if (freq == 0.0) fd_scale = 1.0;
    else             fd_scale = freq / sin(freq); 

    f = Freq[i]*scale;
    sfr = disp[i]*fd_scale;

    MTFPRINT3("%9.6f   \t%f ",f,  sfr) 

    if(Freq[i] > 0.5)
      MTFPRINT("\t#\n")    /* Mark frequencies above Nyquist */
    else {
      double lower = 1.02829 - 1.67473e-1*f + 1.06255e-2*f*f - 2.80874e-4*f*f*f;
      double upper = 1.12;
      if ((f<=10 && f >= 0.998) && (sfr < lower || sfr > upper)) {
	     if (g_nocompare)
	       MTFPRINT("\n")
	     else {
		   piv_err = 1;
	       MTFPRINT("\t*\n")    /* Mark out of PIV spec values */
	    }
        sprintf(problem_string,
                "Computed SFR at %.3f cy/mm is outside PIV spec range of %.3f - %.2f\n",
		  f,lower,upper);
        put_problem(problem_string, IQS);
      }
      else MTFPRINT("\n")
    }
  }

  MTFPRINT("\n#  Frequencies above Nyquist\n") 
  if (piv_err)
    MTFPRINT("\n*  SFR is outside PIV spec.  See problem report for details.\n")
  first = reverse_lut(ref_lut, farea[0], lowest_val, highest_val); 
  last = reverse_lut(ref_lut, farea[4*size_x-1], lowest_val, highest_val); 
  if (rotation == RIGHT) {
    MTFPRINT2("** Edge type based on leftside gray level (%d)\n", first)
    MTFPRINT2("    and rightside gray level (%d)\n", last)
  }
  else {
    MTFPRINT2("** Edge type based on top gray level (%d)\n", first)
    MTFPRINT2("    and bottom gray level (%d)\n", last)
  }
  MTFPRINT("    If type isn't correct, use 'f' option to adjust polarity\n\n")

  print_problems();

  if (g_extended){
    int offset, begin, end;
    int j, cnt;
    double threshold,max_edge,current;

    /* Derivatives below this value is considered insignificant for printing */
    max_edge = fabs(farea[4*size_x+bin_len]);
    threshold = max_edge / 25.0;

    /* Find 5 contiguous insignificant values first occur before center */
    offset = center - bin_len;
    j = bin_len;    cnt = 0;
    while (cnt < 5 && j>=0) {
      current = fabs(farea[4*size_x+j]);
      if ( current < threshold ) cnt ++;
      else cnt = 0;
      if (current > max_edge) max_edge = current;
      j--;
    }
    if (j<0) j=0;
    begin = j + offset;

    /* Find 5 contiguous insignificant values first occur after center */
    j = bin_len;    cnt = 0;
    while (cnt < 5 && j<bin_len*2) {
      current = fabs(farea[4*size_x+j]);
      if ( current < threshold ) cnt ++;
      else cnt = 0;
      if (current > max_edge) max_edge = current;
      j++;
    }
    if (j==2*bin_len) j=2*bin_len-1;
    end = j + offset;

    if ( farea[4*size_x+bin_len] < 0 ) max_edge *= -1;


    MTFPRINT("\n\nPixel+ \t\t LSF_w \t\t AvgESF")
    if (g_version&4)
	MTFPRINT(" \t \n")
    else
	MTFPRINT("(with -0.125 offset)\n")

    for( i=begin, j=begin-offset; i<0; j++, i++)
	MTFPRINT3("% .2f \t\t% f\n", (i-center)/4.0,  farea[4*size_x + j]/max_edge)

    for( ; i<end && i<size_x*4; j++, i++) {
        MTFPRINT4("% .2f \t\t% f \t% f\n", (i-center)/4.0,  farea[4*size_x + j]/max_edge,  farea[i])
    }
    for(; i<end; j++, i++)
        MTFPRINT3("% .2f \t\t% f\n", (i-center)/4.0,  farea[4*size_x + j]/max_edge)

    MTFPRINT("\n")

    MTFPRINT("+ Pixel values are centered on the computed line fit\n")
    MTFPRINT("  LSF_w values are scaled to a maximum of 1\n")
    if (!(g_version&4))
       MTFPRINT("  Actual pixel values for the AvgESF are shifted up +0.125 from values here\n")
    MTFPRINT("\n")
  }

  return(EXIT_SUCCESS);

}                               /* end of main */
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值