使用FCN做图像语义分割(实践篇)

http://blog.csdn.net/gavin__zhou/article/details/52142696

FCN原理

原理我已经在上篇博客说过,大家可以参考FCN原理篇

代码

FCN有官方的代码,具体地址是FCN官方代码
不过我用的不是这个代码,我用的是别人修改官方的版本的代码,使用Chainer框架实现的,Chainer的源码链接:
Chainer框架源码,如果大家使用过Keras的话,应该对它不会感到特别的陌生,Chainer: a neural network framework

好了,我使用的代码是FCN的Chainer implementation, 具体地址是FCN Chainer implementation

安装

安装很简单,直接pip或者源码安装都可以,但是我在我的机器上装过几次,发现使用pip的方式最后fcn.data_dir这个变量的值会指向到你系统的Python下的dist-packages这个目录,但是这个目录需要root权限,所以不推荐使用pip直接安装的方式; 关于此问题的说明见:
fcn.data_dir的问题

所以我最后使用的是源码安装的方式,这里推荐使用virtualenv工具建立虚拟环境,实践中发现这是最不会出错的方式,推荐使用!

clone代码

Git clone https://github.com/wkentaro/fcn.git –recursive

使用virtualenv安装

sudo pip install virtualenv #安装virtualenv
创建虚拟目录
virtualenv test-fcn
cd test-fcn
激活虚拟环境
source ./bin/activate
克隆fcn代码
git clone https://github.com/wkentaro/fcn.git –recursive
cd fcn
安装fcn
python setup.py develop

demo

下载VOC2012数据集,放入fcn-data-pascal-VOC2012路径下
1. 转换caffe model为Chainer model
./scripts/caffe_to_chainermodel.py
2. load model,进行分割
./scripts/fcn_forward.py –img-files data/pascal/VOC2012/JPEGImages/2007_000129.jpg

训练自己的数据

这个前后搞了快一个月,才把最终的训练搞定,其中艰辛很多,在这里写出来供大家参考

准备自己的数据集

数据集做成VOC2012segementClass的样子,下图是示例,上面一张是原图,下面一张是分割图

ori
seg

但是每一种label指定的物体都有对应的具体的颜色,这个我们犯了很多错,最后跟踪代码找出来的,具体的每一类的RGB值如下:

IndexRGB值
0(0,0,0)
1(0,128,0)
2(128,128,0)
3(0,0,128)
4(128,0,128)
5(0,128,128)
6(128,128,128)
7(64,0,0)
8(192,0,0)
9(62,128,0)
10(192,128,0

这里只列出10类的值,更多类的可以看下面这段代码:

<code class="language-python hljs  has-numbering"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bitget</span><span class="hljs-params">(byteval, idx)</span>:</span>
    <span class="hljs-keyword">return</span> ((byteval & (<span class="hljs-number">1</span> << idx)) != <span class="hljs-number">0</span>)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">labelcolormap</span><span class="hljs-params">(N=<span class="hljs-number">256</span>)</span>:</span>
    cmap = np.zeros((N, <span class="hljs-number">3</span>))  <span class="hljs-comment">#N是类别数目</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> xrange(<span class="hljs-number">0</span>, N):
        id = i
        r, g, b = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
        <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> xrange(<span class="hljs-number">0</span>, <span class="hljs-number">8</span>):
            r = np.bitwise_or(r, (bitget(id, <span class="hljs-number">0</span>) << <span class="hljs-number">7</span>-j))
            g = np.bitwise_or(g, (bitget(id, <span class="hljs-number">1</span>) << <span class="hljs-number">7</span>-j))
            b = np.bitwise_or(b, (bitget(id, <span class="hljs-number">2</span>) << <span class="hljs-number">7</span>-j))
            id = (id >> <span class="hljs-number">3</span>)
        cmap[i, <span class="hljs-number">0</span>] = r
        cmap[i, <span class="hljs-number">1</span>] = g
        cmap[i, <span class="hljs-number">2</span>] = b
    cmap = cmap.astype(np.float32) / <span class="hljs-number">255</span> <span class="hljs-comment">#获得Cmap的RGB值</span>
    <span class="hljs-keyword">return</span> cmap

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_label_rgb_to_32sc1</span><span class="hljs-params">(self, label_rgb)</span>:</span>
        <span class="hljs-keyword">assert</span> label_rgb.dtype == np.uint8
        label = np.zeros(label_rgb.shape[:<span class="hljs-number">2</span>], dtype=np.int32)
        label.fill(-<span class="hljs-number">1</span>)
        cmap = fcn.util.labelcolormap(len(self.target_names)) 
        cmap = (cmap * <span class="hljs-number">255</span>).astype(np.uint8)  <span class="hljs-comment">#转换为整数值</span>
        <span class="hljs-keyword">for</span> l, rgb <span class="hljs-keyword">in</span> enumerate(cmap):
            mask = np.all(label_rgb == rgb, axis=-<span class="hljs-number">1</span>)
            label[mask] = l
        <span class="hljs-keyword">return</span> label</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul>

按照此颜色表做图就没有问题,代码可以正确的读取分割的ground-truth结果
原始的图像放在fcn/data/pascal/VOC2012/JPEGImages
分割的图像放在fcn/data/pascal/VOC2012/SegmentationClass

之后在fcn/data/pascal/VOC2012/ImageSets/Segmentationtrain.txt,trainval.txt,val.txt,写入需要进行相应任务的图片的编号

修改代码

  1. fcn/scripts/fcn_train.py
<code class="language-python hljs  has-numbering"><span class="hljs-comment"># setup optimizer</span>
    optimizer = O.MomentumSGD(lr=<span class="hljs-number">1e-10</span>, momentum=<span class="hljs-number">0.99</span>) <span class="hljs-comment">#这里的lr一定要小,大的话程序会报错,我使用的是1e-9</span>
    optimizer.setup(model)

    <span class="hljs-comment"># train</span>
    trainer = fcn.Trainer(
        dataset=dataset,
        model=model,
        optimizer=optimizer,
        weight_decay=<span class="hljs-number">0.0005</span>,
        test_interval=<span class="hljs-number">1000</span>,
        max_iter=<span class="hljs-number">100000</span>,
        snapshot=<span class="hljs-number">4000</span>,
        gpu=gpu,
    )</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>
  1. fcn/fcn/pascal.py
<code class="language-python hljs  has-numbering">target_names = np.array([
        <span class="hljs-string">'background'</span>,
        <span class="hljs-string">'aeroplane'</span>,
        <span class="hljs-string">'bicycle'</span>,
        <span class="hljs-string">'bird'</span>,
        <span class="hljs-string">'boat'</span>,
        <span class="hljs-string">'bottle'</span>,
        <span class="hljs-string">'bus'</span>,
        <span class="hljs-string">'car'</span>,
        <span class="hljs-string">'cat'</span>,
        <span class="hljs-string">'chair'</span>,
        <span class="hljs-string">'cow'</span>,
        <span class="hljs-string">'diningtable'</span>,
        <span class="hljs-string">'dog'</span>,
        <span class="hljs-string">'horse'</span>,
        <span class="hljs-string">'motorbike'</span>,
        <span class="hljs-string">'person'</span>,
        <span class="hljs-string">'potted plant'</span>,
        <span class="hljs-string">'sheep'</span>,
        <span class="hljs-string">'sofa'</span>,
        <span class="hljs-string">'train'</span>,
        <span class="hljs-string">'tv/monitor'</span>,
    ]) <span class="hljs-comment">#修改成自己的,记得按照颜色表写</span>
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li></ul>
  1. fcn/fcn/util.py
<code class="language-python hljs  has-numbering"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">resize_img_with_max_size</span><span class="hljs-params">(img, max_size=<span class="hljs-number">500</span>*<span class="hljs-number">500</span>)</span>:</span>  <span class="hljs-comment">#修改max_size,按照实际写</span>
    <span class="hljs-string">"""Resize image with max size (height x width)"""</span>
    <span class="hljs-keyword">from</span> skimage.transform <span class="hljs-keyword">import</span> rescale
    height, width = img.shape[:<span class="hljs-number">2</span>]
    scale = max_size / (height * width)
    resizing_scale = <span class="hljs-number">1</span>
    <span class="hljs-keyword">if</span> scale < <span class="hljs-number">1</span>:
        resizing_scale = np.sqrt(scale)
        img = rescale(img, resizing_scale, preserve_range=<span class="hljs-keyword">True</span>)
        img = img.astype(np.uint8)
    <span class="hljs-keyword">return</span> img, resizing_scale</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>
  1. fcn/fcn/models/fcn32s.py
<code class="language-python hljs  has-numbering"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self, n_class=<span class="hljs-number">21</span>)</span>:</span>  <span class="hljs-comment">#修改类别n_class</span>
        self.n_class = n_class
        super(self.__class__, self).__init__(
            conv1_1=L.Convolution2D(<span class="hljs-number">3</span>, <span class="hljs-number">64</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">100</span>),
            conv1_2=L.Convolution2D(<span class="hljs-number">64</span>, <span class="hljs-number">64</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),

            conv2_1=L.Convolution2D(<span class="hljs-number">64</span>, <span class="hljs-number">128</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv2_2=L.Convolution2D(<span class="hljs-number">128</span>, <span class="hljs-number">128</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),

            conv3_1=L.Convolution2D(<span class="hljs-number">128</span>, <span class="hljs-number">256</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv3_2=L.Convolution2D(<span class="hljs-number">256</span>, <span class="hljs-number">256</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv3_3=L.Convolution2D(<span class="hljs-number">256</span>, <span class="hljs-number">256</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),

            conv4_1=L.Convolution2D(<span class="hljs-number">256</span>, <span class="hljs-number">512</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv4_2=L.Convolution2D(<span class="hljs-number">512</span>, <span class="hljs-number">512</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv4_3=L.Convolution2D(<span class="hljs-number">512</span>, <span class="hljs-number">512</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),

            conv5_1=L.Convolution2D(<span class="hljs-number">512</span>, <span class="hljs-number">512</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv5_2=L.Convolution2D(<span class="hljs-number">512</span>, <span class="hljs-number">512</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),
            conv5_3=L.Convolution2D(<span class="hljs-number">512</span>, <span class="hljs-number">512</span>, <span class="hljs-number">3</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">1</span>),

            fc6=L.Convolution2D(<span class="hljs-number">512</span>, <span class="hljs-number">4096</span>, <span class="hljs-number">7</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">0</span>),
            fc7=L.Convolution2D(<span class="hljs-number">4096</span>, <span class="hljs-number">4096</span>, <span class="hljs-number">1</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">0</span>),

            score_fr=L.Convolution2D(<span class="hljs-number">4096</span>, self.n_class, <span class="hljs-number">1</span>, stride=<span class="hljs-number">1</span>, pad=<span class="hljs-number">0</span>),

            upscore=L.Deconvolution2D(self.n_class, self.n_class, <span class="hljs-number">64</span>,
                                      stride=<span class="hljs-number">32</span>, pad=<span class="hljs-number">0</span>),
        )
        self.train = <span class="hljs-keyword">False</span></code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li></ul>

训练

./scripts/fcn_train.py

  1. 其会在fcn/data/ 下创建一个目录叫做SegmentationClassDataset_db,里面存放训练的图片的pickle数据,如果需要修改原始的训练图片则需要将此目录删除,否则默认读取此目录内的pickle数据作为图像的原始数据

  2. 会在fcn下创建snapshot这个目录,里面有训练保存的model,日志文件等,重新训练的话,建议删除此目录

使用自己训练的model

./scripts/fcn_forward.py -c path/to/your/model -i path/to/your/image
结果存放在fcn/data/forward_out


本课程适合具有一定深度学习基础,希望发展为深度学习之计算机视觉方向的算法工程师和研发人员的同学们。基于深度学习的计算机视觉是目前人工智能最活跃的领域,应用非常广泛,如人脸识别和无人驾驶中的机器视觉等。该领域的发展日新月异,网络模型和算法层出不穷。如何快速入门并达到可以从事研发的高度对新手和中级水平的学生而言面临不少的挑战。精心准备的本课程希望帮助大家尽快掌握基于深度学习的计算机视觉的基本原理、核心算法和当前的领先技术,从而有望成为深度学习之计算机视觉方向的算法工程师和研发人员。本课程系统全面地讲述基于深度学习的计算机视觉技术的原理并进行项目实践。课程涵盖计算机视觉的七大任务,包括图像分类、目标检测、图像分割(语义分割、实例分割、全景分割)、人脸识别、图像描述、图像检索、图像生成(利用生成对抗网络)。本课程注重原理和实践相结合,逐深入解读经典和前沿论文70余,图文并茂破译算法难点, 使用思维导图梳理技术要点。项目实践使用Keras框架(后端为Tensorflow),学员可快速上手。通过本课程的学习,学员可把握基于深度学习的计算机视觉的技术发展脉络,掌握相关技术原理和算法,有助于开展该领域的研究与开发实战工作。另外,深度学习之计算机视觉方向的知识结构及学习建议请参见本人CSDN博客。本课程提供课程资料的课件PPT(pdf格式)和项目实践代码,方便学员学习和复习。本课程分为上下两部分,其中上部包含课程的前五章(课程介绍、深度学习基础、图像分类、目标检测、图像分割),下部包含课程的后四章(人脸识别、图像描述、图像检索、图像生成)。
### 回答1: 遥感图像语义分割是指将遥感图像中的每个像素点进行分类,确定其对应的地物类别,如建筑、道路、植被等。PyTorch是一种用于构建和训练深度学习模型的开源框架,可以高效地实现遥感图像语义分割。 以下是使用PyTorch实现遥感图像语义分割的简要教程: 1. 数据准备:首先,需要准备用于训练的遥感图像数据集。该数据集应包含遥感图像及对应的标签图像,其中每个像素点都标注了地物类别。可以使用现有的公开数据集,或者通过遥感图像数据集的制作工具对自己的数据进行标注。 2. 数据加载:使用PyTorch中的数据加载器来加载训练数据。可以自定义一个数据加载类,继承PyTorch的Dataset类,实现__getitem__和__len__方法,将遥感图像和对应的标签图像读取并返回。 3. 模型设计:选择适合任务的深度学习模型,如U-Net、DeepLab等。可以使用PyTorch提供的预训练模型作为基础网络,然后根据具体任务进行修改。在模型中添加适当的卷积、池化和上采样层,并加入跳跃连接等技巧以提高模型性能。 4. 损失函数定义:在语义分割中,常使用交叉熵损失函数来度量模型输出与标签之间的差异。可以使用PyTorch提供的交叉熵损失函数或自定义损失函数。 5. 模型训练:使用定义好的数据加载器、模型和损失函数进行训练。通过定义优化器和学习率,使用PyTorch自带的训练函数进行模型的训练。可以设置合适的批量大小、学习率衰减等超参数,根据训练集和验证集的损失和准确率进行调整。 6. 模型评估:训练完成后,使用测试集对模型进行评估,计算准确率、召回率、F1值等指标,评估模型在遥感图像语义分割任务上的性能。 以上是一个简要的遥感图像语义分割在PyTorch中的实现教程,希望对你有帮助。当然,实际应用中还可能涉及到更多细节和技巧,需要根据具体情况进行调整和改进。 ### 回答2: 遥感图像语义分割是指使用遥感图像数据进行像素级别的分类和分割,即将图像中的每个像素按照其所属的类别进行标注。PyTorch是一种流行的深度学习框架,可以用于实现遥感图像语义分割。 以下是一个简单的遥感图像语义分割的PyTorch实现教程: 1. 数据准备:首先,准备好遥感图像数据集,包括训练集和测试集。每张图像都需要有相应的标注,标注应为像素级别的类别信息。 2. 数据预处理:对于遥感图像数据进行预处理,包括图像增强、尺寸调整和标准化等操作。这可以使用Python的PIL库等工具来实现。 3. 搭建模型:选择适合遥感图像语义分割的模型,比如U-Net、DeepLab等。使用PyTorch搭建网络模型,定义网络结构、损失函数和优化器等。 4. 数据加载和训练:使用PyTorch的数据加载器加载训练数据集,并使用定义的优化器和损失函数进行训练。可以设置适当的批次大小和训练轮数。 5. 模型评估:在训练过程中,可以使用测试集对模型进行评估,计算准确率、召回率、F1分数等指标,以了解模型的性能。 6. 模型优化:根据评估结果,可以尝试调整模型的参数、损失函数或优化器等,以提高模型的准确性和鲁棒性。 7. 模型应用:训练好的模型可以应用于新的遥感图像数据,进行像素级别的语义分割任务。 总结:遥感图像语义分割的PyTorch实现可以按照上述步骤进行,其中数据准备、搭建模型、数据加载和训练等是关键步骤。通过不断优化和调整,可以得到高准确性的语义分割模型,从而应用于遥感图像的各种应用场景。 ### 回答3: 遥感图像语义分割是指利用遥感图像对地表进行分类和分割的技术。PyTorch是一个流行的深度学习框架,提供了强大的功能和易于使用的API,因此在遥感图像语义分割任务中也经常被使用。 以下是一个简要的遥感图像语义分割PyTorch实现教程: 1. 数据准备:首先,你需要准备用于训练的遥感图像数据集。这些数据集应包含遥感图像和相应的标签图像,其中标签图像用于指示每个像素的类别。可以使用遥感图像处理软件,如ENVI或GDAL,来预处理和准备数据。 2. 数据加载:使用PyTorch中的数据加载器,如torch.utils.data.DataLoader,加载准备好的数据集。你可以自定义一个子类,继承自torch.utils.data.Dataset,来处理数据加载和转换。 3. 构建模型:在PyTorch中,可以使用torch.nn模块来构建语义分割模型。常用的模型包括U-Net、FCN和DeepLab等。你可以根据任务的具体需求选择适当的模型结构,并根据需要进行修改和调整。 4. 定义损失函数:在语义分割任务中,常用的损失函数是交叉熵损失函数。在PyTorch中,可以使用torch.nn.CrossEntropyLoss来定义损失函数。 5. 训练模型:使用PyTorch的训练循环,将图像输入模型,计算损失函数,更新模型参数,并循环迭代该过程。你需要选择合适的优化器,如SGD或Adam,并选择适当的超参数。 6. 评估和预测:训练完成后,可以使用模型对新的遥感图像进行预测。通过将图像输入模型,可以得到每个像素的类别预测结果。你可以使用各种评估指标,如交并比和准确率,来评估模型的性能。 以上是一个简单的遥感图像语义分割PyTorch实现教程。通过理解和实践这些步骤,你可以开始进行遥感图像语义分割任务,并逐渐提升你的模型和技术水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值