Create Gaussian Filter on Frequency Domain



I've done anything that i got, I just want to create a Gaussian Filter from DFT code that I've done. Here is the code :

//#include <stdafx.h>

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <stdio.h>
#include <iostream>

using namespace cv;
using namespace std;

double pixelDistance(double u, double v)
{
    return cv::sqrt(u*u + v*v);
}

double gaussianCoeff(double u, double v, double d0)
{
    double d = pixelDistance(u, v);
    return cv::exp((-d*d) / (2 * d0*d0));
}

cv::Mat createGaussianHighPassFilter(cv::Size size, double cutoffInPixels)
{
    Mat ghpf(size, CV_64F);

    cv::Point center(size.width / 2, size.height / 2);

    for (int u = 0; u < ghpf.rows; u++)
    {
        for (int v = 0; v < ghpf.cols; v++)
        {
            ghpf.at<double>(u, v) = gaussianCoeff(u - center.y, v - center.x, cutoffInPixels);
        }
    }

    return ghpf;
}


void translateImg(Mat& imgIn, Mat& imgOut)
{
    int i, j;

    for (i = 0; i < imgIn.rows; i++)
        for (j = 0; j < imgIn.cols; j++)
            imgOut.at<double>(i, j) = imgIn.at<double>(i, j) * pow(-1.0, i + j);
}
void scaleImg(Mat& imgIn, Mat& imgOut, float scaleFactor)
{
    int i, j;

    for (i = 0; i < imgIn.rows; i++)
        for (j = 0; j < imgIn.cols; j++)
            imgOut.at<double>(i, j) = (double)scaleFactor * log(1.0 + imgIn.at<double>(i, j));
}

void consoleOut(cv::Mat outMat, int rows = 5, int cols = 5)
{
    rows = ((rows == -1 || rows >= outMat.rows) ? outMat.rows : rows);
    cols = ((cols == -1 || cols >= outMat.cols) ? outMat.cols : cols);

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            cout << outMat.at<double>(i, j);
            cout << " ";
        }
        cout << endl;
    }
}

double calcMSE(Mat& imgOrig, Mat& imgReconst)
{
    int valOrig = 0, valReconst = 0;
    double MSE = 0.0;

    for (int i = 0; i < imgOrig.rows; i++)
    {
        for (int j = 0; j < imgOrig.cols; j++)
        {
            valOrig = imgOrig.at<unsigned char>(i, j);
            valReconst = imgReconst.at<unsigned char>(i, j);

            MSE += pow((double)(valOrig - valReconst), 2.0);
        }
    }
    return (MSE / (imgOrig.rows * imgOrig.cols));
}

string convertInt(int number) // converts integer to string
{
    stringstream ss;
    ss << number;
    return ss.str();
}

int main(unsigned int argc, char* const argv[])
{
    int dftH, dftW;
    cv::Mat imgIn;

    imgIn = cv::imread("fri.pgm", 0); //grayscale
    imshow("Original Image", imgIn);
    waitKey();

    dftH = cv::getOptimalDFTSize(imgIn.rows);
    dftW = cv::getOptimalDFTSize(imgIn.cols);

    Mat imgMod;
    Mat imgPrecFFT(dftH, dftW, CV_64FC1, Scalar::all(0));
    imgIn.convertTo(imgMod, CV_64FC1);
    imgPrecFFT = imgMod(cv::Range::all(), cv::Range::all()).clone();

    // translate image
    std::vector<Mat> imgsTrans;
    imgsTrans.push_back(Mat_<double>(imgIn.size(), CV_64FC1));
    imgsTrans.push_back(Mat_<double>(imgIn.size(), CV_64FC1));
    imgsTrans[1].setTo(Scalar::all(0), Mat());
    translateImg(imgPrecFFT, imgsTrans[0]);

    Mat imgPrecTransFFT(imgIn.size(), CV_64FC2, Scalar::all(0));
    cv::merge(imgsTrans, imgPrecTransFFT);

    // dft
    cv::Mat imgFFT;
    dft(imgPrecTransFFT, imgFFT, DFT_COMPLEX_OUTPUT);
    cv::Mat imgDispFFT;

    // gaussian filter
    Mat ghpf = createGaussianHighPassFilter(Size(128, 128), 16.0);
    imshow("Gaussian Filter", ghpf);
    mulSpectrums(imgFFT, ghpf, imgFFT, 0, 0);
    waitKey();

    // calculate magnitude
    Mat imgMagnitude(imgIn.size(), CV_64FC1);
    std::vector<Mat> chans;
    cv::split(imgFFT, chans);
    cv::magnitude(chans[0], chans[1], imgMagnitude);

    // scale magnitude image
    Mat imgMagnitudeScaled(imgIn.size(), CV_64FC1);
    scaleImg(imgMagnitude, imgMagnitudeScaled, 10.0);

    // display magnitude image
    cv::Mat imgDisp;
    cv::convertScaleAbs(imgMagnitudeScaled, imgDisp);
    imshow("Magnitude Output", imgDisp);
    waitKey();

    // inverse dft
    cv::split(imgFFT, chans);
    chans[1].zeros(imgIn.size(), CV_64FC1);
    cv::merge(chans, imgFFT);
    cv::Mat invFFT;
    cv::idft(imgFFT, invFFT, DFT_REAL_OUTPUT + DFT_SCALE);

    // translate image back to original location
    cv::split(invFFT, imgsTrans);
    Mat imgAfterTrans(imgIn.size(), CV_64FC1);
    translateImg(imgsTrans[0], imgAfterTrans);
    imgAfterTrans.convertTo(imgDisp, CV_8UC1);

    imshow("After Inverse Output", imgDisp);
    waitKey();

    // calculate and output mean-squared error between input/output images
    double MSE = calcMSE(imgIn, imgDisp);
    cout << endl << "MSE: " << MSE << endl;
    waitKey();

    return 0;
}

I've try mulspectrum but it said it's error, then how can I merge the Gaussian Filter with the DFT one?

import torch as pt import torch.nn as nn from torch import optim from torch.autograd import Variable import torch.nn.functional as FUN import numpy as np import math import imageio from scipy.io import loadmat ''' This code is a direct pytorch implementation of the original FSIM code provided by Lin ZHANG, Lei Zhang, Xuanqin Mou and David Zhang in Matlab. For the original version please see: https://www4.comp.polyu.edu.hk/~cslzhang/IQA/FSIM/FSIM.htm ''' class FSIM_base(nn.Module): def __init__(self): nn.Module.__init__(self) self.cuda_computation = False self.nscale = 4 # Number of wavelet scales self.norient = 4 # Number of filter orientations self.k = 2.0 # No of standard deviations of the noise # energy beyond the mean at which we set the # noise threshold point. # below which phase congruency values get # penalized. self.epsilon = .0001 # Used to prevent division by zero self.pi = math.pi minWaveLength = 6 # Wavelength of smallest scale filter mult = 2 # Scaling factor between successive filters sigmaOnf = 0.55 # Ratio of the standard deviation of the # Gaussian describing the log Gabor filter's # transfer function in the frequency domain # to the filter center frequency. dThetaOnSigma = 1.2 # Ratio of angular interval between filter orientations # and the standard deviation of the angular Gaussian # function used to construct filters in the # freq. plane. self.thetaSigma = self.pi/self.norient/dThetaOnSigma # Calculate the standard deviation of the # angular Gaussian function used to # construct filters in the freq. plane. self.fo = (1.0/(minWaveLength*pt.pow(mult,(pt.arange(0,self.nscale,dtype=pt.float64))))).unsqueeze(0) # Centre frequency of filter self.den = 2*(math.log(sigmaOnf))**2 self.dx = -pt.tensor([[[[3, 0, -3], [10, 0,-10], [3,0,-3]]]])/16.0 self.dy = -pt.tensor([[[[3, 10, 3], [0, 0, 0], [-3 ,-10, -3]]]])/16.0 self.T1 = 0.85 self.T2 = 160 self.T3 = 200; self.T4 = 200; self.lambdac = 0.03 def set_arrays_to_cuda(self): self.cuda_computation = True self.fo = self.fo.cuda() self.dx = self.dx.cuda() self.dy = self.dy.cuda() def forward_gradloss(self,imgr,imgd): I1,Q1,Y1 = self.process_image_channels(imgr) I2,Q2,Y2 = self.process_image_channels(imgd) #PCSimMatrix,PCm = self.calculate_phase_score(PC1,PC2) gradientMap1 = self.calculate_gradient_map(Y1) gradientMap2 = self.calculate_gradient_map(Y2) gradientSimMatrix = self.calculate_gradient_sim(gradientMap1,gradientMap2) #gradientSimMatrix= gradientSimMatrix.view(PCSimMatrix.size()) gradloss = pt.sum(pt.sum(pt.sum(gradientSimMatrix,1),1)) return gradloss def calculate_fsim(self,gradientSimMatrix,PCSimMatrix,PCm): SimMatrix = gradientSimMatrix * PCSimMatrix * PCm FSIM = pt.sum(pt.sum(SimMatrix,1),1) / pt.sum(pt.sum(PCm,1),1) return FSIM def calculate_fsimc(self, I1,Q1,I2,Q2,gradientSimMatrix,PCSimMatrix,PCm): ISimMatrix = (2*I1*I2 + self.T3) / (pt.pow(I1,2) + pt.pow(I2,2) + self.T3) QSimMatrix = (2*Q1*Q2 + self.T4) / (pt.pow(Q1,2) + pt.pow(Q2,2) + self.T4) SimMatrixC = gradientSimMatrix*PCSimMatrix*(pt.pow(pt.abs(ISimMatrix*QSimMatrix),self.lambdac))*PCm FSIMc = pt.sum(pt.sum(SimMatrixC,1),1)/pt.sum(pt.sum(PCm,1),1) return FSIMc def lowpassfilter(self, rows, cols): cutoff = .45 n = 15 x, y = self.create_meshgrid(cols,rows) radius = pt.sqrt(pt.pow(x,2) + pt.pow(y,2)).unsqueeze(0) f = self.ifftshift2d( 1 / (1.0 + pt.pow(pt.div(radius,cutoff),2*n)) ) return f def calculate_gradient_sim(self,gradientMap1,gradientMap2): gradientSimMatrix = (2*gradientMap1*gradientMap2 + self.T2) /(pt.pow(gradientMap1,2) + pt.pow(gradientMap2,2) + self.T2) return gradientSimMatrix def calculate_gradient_map(self,Y): IxY = FUN.conv2d(Y,self.dx, padding=1) IyY = FUN.conv2d(Y,self.dy, padding=1) gradientMap1 = pt.sqrt(pt.pow(IxY,2) + pt.pow(IyY,2)) return gradientMap1 def calculate_phase_score(self,PC1,PC2): PCSimMatrix = (2 * PC1 * PC2 + self.T1) / (pt.pow(PC1,2) + pt.pow(PC2,2) + self.T1) PCm = pt.where(PC1>PC2, PC1,PC2) return PCSimMatrix,PCm def roll_1(self,x, n): return pt.cat((x[:,-n:,:,:,:], x[:,:-n,:,:,:]), dim=1) def ifftshift(self,tens,var_axis): len11 = int(tens.size()[var_axis]/2) len12 = tens.size()[var_axis]-len11 return pt.cat((tens.narrow(var_axis,len11,len12),tens.narrow(var_axis,0,len11)),axis=var_axis) def ifftshift2d(self,tens): return self.ifftshift(self.ifftshift(tens,1),2) def create_meshgrid(self,cols,rows): ''' Set up X and Y matrices with ranges normalised to +/- 0.5 The following code adjusts things appropriately for odd and even values of rows and columns. ''' if cols%2: xrange = pt.arange(start = -(cols-1)/2, end = (cols-1)/2+1, step = 1, requires_grad=False)/(cols-1) else: xrange = pt.arange(-(cols)/2, (cols)/2, step = 1, requires_grad=False)/(cols) if rows%2: yrange = pt.arange(-(rows-1)/2, (rows-1)/2+1, step = 1, requires_grad=False)/(rows-1) else: yrange = pt.arange(-(rows)/2, (rows)/2, step = 1, requires_grad=False)/(rows) x, y = pt.meshgrid([xrange, yrange]) if self.cuda_computation: x, y = x.cuda(), y.cuda() return x.T, y.T def process_image_channels(self,img): batch, rows, cols = img.shape[0],img.shape[2],img.shape[3] minDimension = min(rows,cols) Ycoef = pt.tensor([[0.299,0.587,0.114]]) Icoef = pt.tensor([[0.596,-0.274,-0.322]]) Qcoef = pt.tensor([[0.211,-0.523,0.312]]) if self.cuda_computation: Ycoef, Icoef, Qcoef = Ycoef.cuda(), Icoef.cuda(), Qcoef.cuda() Yfilt=pt.cat(batch*[pt.cat(rows*cols*[Ycoef.unsqueeze(2)],dim=2).view(1,3,rows,cols)],0) Ifilt=pt.cat(batch*[pt.cat(rows*cols*[Icoef.unsqueeze(2)],dim=2).view(1,3,rows,cols)],0) Qfilt=pt.cat(batch*[pt.cat(rows*cols*[Qcoef.unsqueeze(2)],dim=2).view(1,3,rows,cols)],0) # If images have three chanels if img.size()[1]==3: Y = pt.sum(Yfilt*img,1).unsqueeze(1) I = pt.sum(Ifilt*img,1).unsqueeze(1) Q = pt.sum(Qfilt*img,1).unsqueeze(1) else: Y = pt.mean(img,1).unsqueeze(1) I = pt.ones(Y.size(),dtype=pt.float64) Q = pt.ones(Y.size(),dtype=pt.float64) F = max(1,round(minDimension / 256)) aveKernel = nn.AvgPool2d(kernel_size = F, stride = F, padding =0)# max(0, math.floor(F/2))) if self.cuda_computation: aveKernel = aveKernel.cuda() # Make sure that the dimension of the returned image is the same as the input I = aveKernel(I) Q = aveKernel(Q) Y = aveKernel(Y) return I,Q,Y def phasecong2(self,img): ''' % Filters are constructed in terms of two components. % 1) The radial component, which controls the frequency band that the filter % responds to % 2) The angular component, which controls the orientation that the filter % responds to. % The two components are multiplied together to construct the overall filter. % Construct the radial filter components... % First construct a low-pass filter that is as large as possible, yet falls % away to zero at the boundaries. All log Gabor filters are multiplied by % this to ensure no extra frequencies at the 'corners' of the FFT are % incorporated as this seems to upset the normalisation process when % calculating phase congrunecy. ''' batch, rows, cols = img.shape[0],img.shape[2],img.shape[3] imagefft = pt.rfft(img,signal_ndim=2,onesided=False) x, y = self.create_meshgrid(cols,rows) radius = pt.cat(batch*[pt.sqrt(pt.pow(x,2) + pt.pow(y,2)).unsqueeze(0)],0) theta = pt.cat(batch*[pt.atan2(-y,x).unsqueeze(0)],0) radius = self.ifftshift2d(radius) # Matrix values contain *normalised* radius from centre theta = self.ifftshift2d(theta) # Matrix values contain polar angle. # (note -ve y is used to give +ve # anti-clockwise angles) radius[:,0,0] = 1 sintheta = pt.sin(theta) costheta = pt.cos(theta) lp = self.lowpassfilter(rows,cols) # Radius .45, 'sharpness' 15 lp = pt.cat(batch*[lp.unsqueeze(0)],0) term1 = pt.cat(rows*cols*[self.fo.unsqueeze(2)],dim=2).view(-1,self.nscale,rows,cols) term1 = pt.cat(batch*[term1.unsqueeze(0)],0).view(-1,self.nscale,rows,cols) term2 = pt.log(pt.cat(self.nscale*[radius.unsqueeze(1)],1)/term1) # Apply low-pass filter logGabor = pt.exp(-pt.pow(term2,2)/self.den) logGabor = logGabor*lp logGabor[:,:,0,0] = 0 # Set the value at the 0 frequency point of the filter # back to zero (undo the radius fudge). # Then construct the angular filter components... # For each point in the filter matrix calculate the angular distance from # the specified filter orientation. To overcome the angular wrap-around # problem sine difference and cosine difference values are first computed # and then the atan2 function is used to determine angular distance. angl = pt.arange(0,self.norient,dtype=pt.float64)/self.norient*self.pi if self.cuda_computation: angl = angl.cuda() ds_t1 = pt.cat(self.norient*[sintheta.unsqueeze(1)],1)*pt.cos(angl).view(-1,self.norient,1,1) ds_t2 = pt.cat(self.norient*[costheta.unsqueeze(1)],1)*pt.sin(angl).view(-1,self.norient,1,1) dc_t1 = pt.cat(self.norient*[costheta.unsqueeze(1)],1)*pt.cos(angl).view(-1,self.norient,1,1) dc_t2 = pt.cat(self.norient*[sintheta.unsqueeze(1)],1)*pt.sin(angl).view(-1,self.norient,1,1) ds = ds_t1-ds_t2 # Difference in sine. dc = dc_t1+dc_t2 # Difference in cosine. dtheta = pt.abs(pt.atan2(ds,dc)) # Absolute angular distance. spread = pt.exp(-pt.pow(dtheta,2)/(2*self.thetaSigma**2)) # Calculate the # angular filter component. logGabor_rep = pt.repeat_interleave(logGabor,self.norient,1).view(-1,self.nscale,self.norient,rows,cols) # Batch size, scale, orientation, pixels, pixels spread_rep = pt.cat(self.nscale*[spread]).view(-1,self.nscale,self.norient,rows,cols) filter_log_spread = logGabor_rep*spread_rep array_of_zeros = pt.zeros(filter_log_spread.unsqueeze(5).size(),dtype=pt.float64) if self.cuda_computation: array_of_zeros = array_of_zeros.cuda() filter_log_spread_zero = pt.cat((filter_log_spread.unsqueeze(5),array_of_zeros), dim=5) ifftFilterArray = pt.ifft(filter_log_spread_zero,signal_ndim =2).select(5,0)*math.sqrt(rows*cols) imagefft_repeat = pt.cat(self.nscale*self.norient*[imagefft],dim=1).view(-1,self.nscale,self.norient,rows,cols,2) filter_log_spread_repeat = pt.cat(2*[filter_log_spread.unsqueeze(5)],dim=5) # Convolve image with even and odd filters returning the result in EO EO = pt.ifft(filter_log_spread_repeat*imagefft_repeat,signal_ndim=2) E = EO.select(5, 0) O = EO.select(5, 1) An = pt.sqrt(pt.pow(E,2)+pt.pow(O,2)) sumAn_ThisOrient = pt.sum(An,1) sumE_ThisOrient = pt.sum(E,1) # Sum of even filter convolution results sumO_ThisOrient = pt.sum(O,1) # Sum of odd filter convolution results. # Get weighted mean filter response vector, this gives the weighted mean # phase angle. XEnergy = pt.sqrt(pt.pow(sumE_ThisOrient,2) + pt.pow(sumO_ThisOrient,2)) + self.epsilon MeanE = sumE_ThisOrient / XEnergy MeanO = sumO_ThisOrient / XEnergy MeanO = pt.cat(self.nscale*[MeanO.unsqueeze(1)],1) MeanE = pt.cat(self.nscale*[MeanE.unsqueeze(1)],1) # Now calculate An(cos(phase_deviation) - | sin(phase_deviation)) | by # using dot and cross products between the weighted mean filter response # vector and the individual filter response vectors at each scale. This # quantity is phase congruency multiplied by An, which we call energy. Energy = pt.sum( E*MeanE+O*MeanO - pt.abs(E*MeanO-O*MeanE),1) abs_EO = pt.sqrt(pt.pow(E,2) + pt.pow(O,2)) # % Compensate for noise # We estimate the noise power from the energy squared response at the # smallest scale. If the noise is Gaussian the energy squared will have a # Chi-squared 2DOF pdf. We calculate the median energy squared response # as this is a robust statistic. From this we estimate the mean. # The estimate of noise power is obtained by dividing the mean squared # energy value by the mean squared filter value medianE2n = pt.pow(abs_EO.select(1,0),2).view(-1,self.norient,rows*cols).median(2).values EM_n = pt.sum(pt.sum(pt.pow(filter_log_spread.select(1,0),2),3),2) noisePower = -(medianE2n/math.log(0.5))/EM_n # Now estimate the total energy^2 due to noise # Estimate for sum(An^2) + sum(Ai.*Aj.*(cphi.*cphj + sphi.*sphj)) EstSumAn2 = pt.sum(pt.pow(ifftFilterArray,2),1) sumEstSumAn2 = pt.sum(pt.sum(EstSumAn2,2),2) roll_t1 = ifftFilterArray*self.roll_1(ifftFilterArray,1) roll_t2 = ifftFilterArray*self.roll_1(ifftFilterArray,2) roll_t3 = ifftFilterArray*self.roll_1(ifftFilterArray,3) rolling_mult = roll_t1+roll_t2+roll_t3 EstSumAiAj = pt.sum(rolling_mult,1)/2 sumEstSumAiAj = pt.sum(pt.sum(EstSumAiAj,2),2) EstNoiseEnergy2 = 2*noisePower*sumEstSumAn2+4*noisePower*sumEstSumAiAj tau = pt.sqrt(EstNoiseEnergy2/2) EstNoiseEnergy = tau*math.sqrt(self.pi/2) EstNoiseEnergySigma = pt.sqrt( (2-self.pi/2)*pt.pow(tau,2)) # The estimated noise effect calculated above is only valid for the PC_1 measure. # The PC_2 measure does not lend itself readily to the same analysis. However # empirically it seems that the noise effect is overestimated roughly by a factor # of 1.7 for the filter parameters used here. T = (EstNoiseEnergy + self.k*EstNoiseEnergySigma)/1.7 # Noise threshold T_exp = pt.cat(rows*cols*[T.unsqueeze(2)],dim=2).view(-1,self.norient,rows,cols) AnAll = pt.sum(sumAn_ThisOrient,1) array_of_zeros_energy = pt.zeros(Energy.size(),dtype=pt.float64) if self.cuda_computation: array_of_zeros_energy =array_of_zeros_energy.cuda() EnergyAll = pt.sum(pt.where((Energy - T_exp)<0.0, array_of_zeros_energy,Energy - T_exp ),1) ResultPC = EnergyAll/AnAll return ResultPC class FSIM(FSIM_base): ''' Note, the input is expected to be from 0 to 255 ''' def __init__(self): super().__init__() def forward(self,imgr,imgd): if imgr.is_cuda: self.set_arrays_to_cuda() I1,Q1,Y1 = self.process_image_channels(imgr) I2,Q2,Y2 = self.process_image_channels(imgd) PC1 = self.phasecong2(Y1) PC2 = self.phasecong2(Y2) PCSimMatrix,PCm = self.calculate_phase_score(PC1,PC2) gradientMap1 = self.calculate_gradient_map(Y1) gradientMap2 = self.calculate_gradient_map(Y2) gradientSimMatrix = self.calculate_gradient_sim(gradientMap1,gradientMap2) gradientSimMatrix= gradientSimMatrix.view(PCSimMatrix.size()) FSIM = self.calculate_fsim(gradientSimMatrix,PCSimMatrix,PCm) return FSIM.mean() class FSIMc(FSIM_base, nn.Module): ''' Note, the input is expected to be from 0 to 255 ''' def __init__(self): super().__init__() def forward(self,imgr,imgd): if imgr.is_cuda: self.set_arrays_to_cuda() I1,Q1,Y1 = self.process_image_channels(imgr) I2,Q2,Y2 = self.process_image_channels(imgd) PC1 = self.phasecong2(Y1) PC2 = self.phasecong2(Y2) PCSimMatrix,PCm = self.calculate_phase_score(PC1,PC2) gradientMap1 = self.calculate_gradient_map(Y1) gradientMap2 = self.calculate_gradient_map(Y2) gradientSimMatrix = self.calculate_gradient_sim(gradientMap1,gradientMap2) gradientSimMatrix= gradientSimMatrix.view(PCSimMatrix.size()) FSIMc = self.calculate_fsimc(I1.squeeze(),Q1.squeeze(),I2.squeeze(),Q2.squeeze(),gradientSimMatrix,PCSimMatrix,PCm) return FSIMc.mean()
最新发布
10-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值