OpenCV-Python系列 - OpenCV中的GUI特性 一

系列文章目录

一 OpenCV中的GUI特性
二 核心操作
三 OpenCV中的图像处理
四 特征检测与描述
五 视频分析
六 相机校准和3D重建
七 机器学习
八 计算摄影学
九 目标检测
十 OpenCV-Python Bindings



前言

    本文主要是讲解图像基础知识和OpenCV-Python图像和视频的基础操作。


一、图像基础

图像的种类可以从多个维度进行分类,以下是一些主要的分类方式及对应的图像种类:

1.按颜色和灰度分类

    二值图像:
        图像的二维矩阵仅由0、1两个值构成,“0”代表黑色,“1”代表白色。由于每一像素取值仅有0、1两种可能,所以计算机中二值图像的数据类型通常为1个二进制位。
        应用场景:常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。
    灰度图像:
        灰度图像的矩阵元素取值范围通常为[0,255],数据类型一般为8位无符号整数(int8)。“0”表示纯黑色,“255”表示纯白色,中间的数字表示由黑到白的过渡色。
        应用场景:广泛应用于图像处理、分析和识别等领域。
    彩色图像:
        彩色图像包含红、绿、蓝(RGB)三个颜色通道,每个通道的取值范围通常为[0,255]。通过不同颜色通道的组合,可以表示出丰富的色彩。
        应用场景:广泛应用于摄影、设计、广告等领域。

2.按文件格式分类

    JPEG(JPG):
        最常见的图片格式之一,采用有损压缩方式处理图像数据,适合存储摄影或写实图像。
        特点:文件大小相对较小,但放大和缩小图像会失真。
    PNG:
        另一种常见的图片格式,采用无损压缩方式处理图像数据。
        特点:支持透明背景,文件大小相对JPEG较大,但放大和缩小图像不会失真。
    GIF:
        早期广泛使用的图片格式,支持透明背景和动画效果。
        特点:文件大小较小,但颜色支持较少,不适合存储色彩丰富的图像。
    WebP:
        由谷歌推出的图片格式,支持有损和无损压缩,具有更优的图像数据压缩算法。
        特点:文件大小比PNG和JPEG更小,同时支持透明和动画效果。
    SVG:
        可缩放矢量图形格式,基于XML的矢量图形描述语言。
        特点:文件大小相对较小,放大和缩小图像不会失真,适用于网络传输和动态交互。

二、图像通道

    图片通道我在实际应用中比较常见的就是三通道或者四通道。
    三通道图像指的是图像由红色(R)、绿色(G)、蓝色(B)三个颜色通道组成。(常说的图像通道顺序是RGB,但是在OpenCV中是GBR)这三个通道共同作用,产生了我们所看到的彩色图像。每个颜色通道都是一个二维矩阵(像素矩阵),矩阵中的每个元素(像素)都有一个值,这个值决定了该像素在该颜色通道中的亮度或强度。
    四通道图像是在上述三通道基础上增加了 透明度通道(Alpha通道) 。Alpha通道用于表示图像的透明度信息。在图像中加入Alpha通道后,就可以实现图像的半透明效果。Alpha通道的取值范围通常是0~255,其中0表示完全透明,255表示完全不透明。

机器中的图像:
在这里插入图片描述

三、图像入门

1.读取图像

    使用cv.imread()函数读取图像。图像应该在工作目录或图像的完整路径应给出。

    第二个参数是一个标志,它指定了读取图像的方式。

  • cv.IMREAD_COLOR: 加载彩色图像。任何图像的透明度都会被忽视。它是默认标志。
  • cv.IMREAD_GRAYSCALE:以灰度模式加载图像
  • cv.IMREAD_UNCHANGED:加载图像,包括alpha通道

    注意 除了这三个标志,你可以分别简单地传递整数1、0或-1。

代码如下:

import numpy as np
import cv2 as cv

def main():
    # 程序的主要逻辑
    image = cv.imread(r'C:\Users\zhutianpeng\Desktop\panda.jpg',cv.IMREAD_COLOR)
    
if __name__ == "__main__":
    main()

    警告: 即使图像路径错误,它也不会引发任何错误,但是print img会给出None

2.显示图像

    使用函数**cv.imshow()**在窗口中显示图像。窗口自动适合图像尺寸。

    第一个参数是窗口名称,它是一个字符串。第二个参数是我们的对象。你可以根据需要创建任意多个窗口,但可以使用不同的窗口名称。

import numpy as np
import cv2 as cv

def main():
    # 程序的主要逻辑
    image = cv.imread(r'C:\Users\zhutianpeng\Desktop\panda.jpg',cv.IMREAD_COLOR)
    cv.imshow('image',image)
    cv.waitKey(0)
    cv.destroyAllWindows()
    
if __name__ == "__main__":
    main()

在这里插入图片描述
    cv.waitKey()是一个键盘绑定函数。其参数是以毫秒为单位的时间。该函数等待任何键盘事件指定的毫秒。如果您在这段时间内按下任何键,程序将继续运行。如果0被传递,它将无限期地等待一次敲击键。它也可以设置为检测特定的按键,例如,如果按下键 a 等。

    注意:除了键盘绑定事件外,此功能还处理许多其他GUI事件,因此你必须使用它来实际显示图像。

    cv.destroyAllWindows()只会破坏我们创建的所有窗口。如果要销毁任何特定的窗口,请使用函数 cv.destroyWindow()在其中传递确切的窗口名称作为参数。

    注意:在特殊情况下,你可以创建一个空窗口,然后再将图像加载到该窗口。在这种情况下,你可以指定窗口是否可调整大小。这是通过功能cv.namedWindow()完成的。默认情况下,该标志为cv.WINDOW_AUTOSIZE。但是,如果将标志指定为cv.WINDOW_NORMAL,则可以调整窗口大小。当图像尺寸过大以及向窗口添加跟踪栏时,这将很有帮助。

3.写入图像

    使用函数cv.imwrite()保存图像。

    第一个参数是文件名,第二个参数是要保存的图像。 cv.imwrite(‘messigray.png’,img) 这会将图像以PNG格式保存在工作目录中。
    在下面的程序中,以灰度加载图像,显示图像,按s保存图像并退出,或者按ESC键直接退出而不保存。

import numpy as np
import cv2 as cv

def main():
    # 程序的主要逻辑
    image = cv.imread(r'C:\Users\zhutianpeng\Desktop\panda.jpg',0)
    cv.imshow('image',image)
    k = cv.waitKey(0) & 0xFF
    if k == 27:         # 等待ESC退出
        cv.destroyAllWindows()
    elif k == ord('s'): # 等待关键字,保存和退出
        cv.imwrite('messigray.png',image)
        cv.destroyAllWindows()
    
if __name__ == "__main__":
    main()

在这里插入图片描述
    保存的图像:
在这里插入图片描述

    警告:如果使用的是64位计算机,则必须k = cv.waitKey(0)按如下所示修改行:k = cv.waitKey(0) & 0xFF

使用Matplotlib
    Matplotlib是Python的绘图库,可为你提供多种绘图方法。你将在接下来的文章中看到它们。在这里,你将学习如何使用Matplotlib显示图像。你可以使用Matplotlib缩放图像,保存图像等。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 程序的主要逻辑
    image = cv.imread(r'C:\Users\zhutianpeng\Desktop\panda.jpg',0)
    plt.imshow(image, cmap = 'gray', interpolation = 'bicubic')
    plt.xticks([]), plt.yticks([])  # 隐藏 x 轴和 y 轴上的刻度值
    plt.show()
    
if __name__ == "__main__":
    main()

    显示图像:
在这里插入图片描述

    Matplotlib中提供了许多绘图选项。请参考Matplotlib文档以获取更多详细信息。一些,我们将在路上看到。
    警告OpenCV加载的彩色图像处于BGR模式。但是Matplotlib以RGB模式显示。因此,如果使用OpenCV读取彩色图像,则Matplotlib中将无法正确显示彩色图像。

其他资源:Matplotlib绘图样式和功能:Matplotlib

四、视频入门

1.从相机中读取视频

    通常情况下,我们必须用摄像机捕捉实时画面。提供了一个非常简单的界面。让我们从摄像头捕捉一段视频(我使用的是我笔记本电脑内置的网络摄像头) ,将其转换成灰度视频并显示出来。
    要捕获视频,你需要创建一个 VideoCapture 对象。它的参数可以是设备索引或视频文件的名称。设备索引就是指定哪个摄像头的数字。正常情况下,一个摄像头会被连接(就像我的情况一样)。所以我简单地传0(或-1)。你可以通过传递1来选择第二个相机,以此类推。在此之后,你可以逐帧捕获。但是在最后,不要忘记释放。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    cap = cv.VideoCapture(0)
    if not cap.isOpened():
        print("Cannot open camera")
        exit()
    while True:
        # 逐帧捕获
        ret, frame = cap.read()
        # 如果正确读取帧,ret为True
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break
        # 我们在框架上的操作到这里
        gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
        # 显示结果帧e
        cv.imshow('frame', gray)
        if cv.waitKey(1) == ord('q'):
            break
    # 完成所有操作后,释放捕获器
    cap.release()
    cv.destroyAllWindows()
    
if __name__ == "__main__":
    main()

    效果显示:
在这里插入图片描述
    cap.read()返回布尔值(True/ False)。如果正确读取了帧,它将为True。因此,你可以通过检查此返回值来检查视频的结尾。

    有时,cap可能尚未初始化捕获。在这种情况下,此代码显示错误。你可以通过cap.isOpened()方法检查它是否已初始化。如果是True,那么确定。否则,使用cap.open()打开它。

    你还可以使用cap.get(propId)方法访问该视频的某些功能,其中propId是0到18之间的一个数字。每个数字表示视频的属性(如果适用于该视频),并且可以显示完整的详细信息在这里看到:cv::VideoCapture::get()。其中一些值可以使用cap.set(propId,value)进行修改。value是你想要的新值。

    例如,我可以通过cap.get(cv.CAP_PROP_FRAME_WIDTH)和cap.get(cv.CAP_PROP_FRAME_HEIGHT)检查框架的宽度和高度。默认情况下,它的分辨率为640x480。但我想将其修改为320x240。只需使用和即可。ret = cap.set(cv.CAP_PROP_FRAME_WIDTH,320) and ret = cap.set(cv.CAP_PROP_FRAME_HEIGHT,240).

    注意:如果出现错误,请确保使用任何其他相机应用程序(例如Linux中的Cheese)都可以正常使用相机。

2.从文件播放视频

    从文件与从相机捕获相同,只是用视频文件名更改摄像机索引。另外,在显示框架时,请使用适当的时间cv.waitKey()。如果太小,则视频将非常快,而如果太大,则视频将变得很慢。正常情况下25毫秒就可以了。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    cap = cv.VideoCapture(r'C:\Users\zhutianpeng\Desktop\SD210321010.mp4')
    while cap.isOpened():
        ret, frame = cap.read()
        # 如果正确读取帧,ret为True
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break
        gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
        cv.imshow('frame', gray)
        if cv.waitKey(1) == ord('q'):
            break
    cap.release()
    cv.destroyAllWindows()
    
if __name__ == "__main__":
    main()

    效果:
在这里插入图片描述

3.保存视频

    所以我们捕捉一个视频,一帧一帧地处理,我们想要保存这个视频。对于图像,它非常简单,只需使用 cv.imwrite()。这里还需要做一些工作。

    这次我们创建一个 VideoWriter 对象。我们应该指定输出文件名(例如: output.avi)。然后我们应该指定 FourCC 代码(详见下一段)。然后传递帧率的数量和帧大小。最后一个是颜色标志。如果为 True,编码器期望颜色帧,否则它与灰度帧一起工作。

    FourCC:http://en.wikipedia.org/wiki/FourCC 是用于指定视频编解码器的4字节代码。可用代码列表可在fourcc.org中:http://www.fourcc.org/codecs.php 找到。它取决于平台。遵循编解码器对我来说效果很好。

  • 在Fedora中:DIVX,XVID,MJPG,X264,WMV1,WMV2。(最好使用XVID。MJPG会生成大尺寸的视频。X264会生成非常小的尺寸的视频)
  • 在Windows中:DIVX(尚待测试和添加)
  • 在OSX中:MJPG(.mp4),DIVX(.avi),X264(.mkv)。

    FourCC代码作为MJPG的cv.VideoWriter_fourcc(‘M’,‘J’,‘P’,‘G’)or cv.VideoWriter_fourcc(*‘MJPG’)传递。

    在从摄像机捕获的代码下面,沿垂直方向翻转每一帧并保存。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    cap = cv.VideoCapture(0)
    # 定义编解码器并创建VideoWriter对象
    fourcc = cv.VideoWriter_fourcc(*'XVID')
    out = cv.VideoWriter('output.avi', fourcc, 20.0, (640,  480))
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break
        frame = cv.flip(frame, 0)
        # 写翻转的框架
        out.write(frame)
        cv.imshow('frame', frame)
        if cv.waitKey(1) == ord('q'):
            break
    # 完成工作后释放所有内容
    cap.release()
    out.release()
    cv.destroyAllWindows()
    
if __name__ == "__main__":
    main()

在这里插入图片描述

五、OpenCV中的绘图功能

    以下实验需要使用的接口:cv.line(),cv.circle(),cv.rectangle(),cv.ellipse(),cv.putText()等。

    在上述所有功能中,您将看到一些常见的参数,如下所示:

  • img:您要绘制形状的图像
  • color:形状的颜色。对于BGR,将其作为元组传递,例如:(255,0,0)对于蓝色。对于灰度,只需传递标量值即可。
  • 厚度:线或圆等的粗细。如果对闭合图形(如圆)传递-1 ,它将填充形状。默认厚度= 1
  • lineType:线的类型,是否为8连接线,抗锯齿线等。默认情况下,为8连接线。cv.LINE_AA给出了抗锯齿的线条,看起来非常适合曲线。

1.画线

    要绘制一条线,您需要传递线的开始和结束坐标。我们将创建一个黑色图像,并从左上角到右下角在其上绘制一条蓝线。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 创建黑色的图像
    img = np.zeros((512,512,3), np.uint8)
    # 绘制一条厚度为5的蓝色对角线
    cv.line(img,(0,0),(511,511),(255,0,0),5)
    cv.imshow('img',img)
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

2.画矩形

    要绘制矩形,您需要矩形的左上角和右下角。这次,我们将在图像的右上角绘制一个绿色矩形。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 创建黑色的图像
    img = np.zeros((512,512,3), np.uint8)
    cv.rectangle(img,(384,0),(510,128),(0,255,0),3)
    cv.imshow('img',img)
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

3.画圆圈

    要绘制一个圆,需要其中心坐标和半径。我们将在上面绘制的矩形内绘制一个圆。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 创建黑色的图像
    img = np.zeros((512,512,3), np.uint8)
    cv.circle(img,(447,63), 63, (0,0,255), -1)
    cv.imshow('img',img)
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

4.画椭圆

    要绘制椭圆,我们需要传递几个参数。一个参数是中心位置(x,y)。下一个参数是轴长度(长轴长度,短轴长度)。angle是椭圆沿逆时针方向旋转的角度。startAngle和endAngle表示从主轴沿顺时针方向测量的椭圆弧的开始和结束。即给出0和360给出完整的椭圆。有关更多详细信息,请参阅cv.ellipse的文档。下面的示例在图像的中心绘制一个椭圆形。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 创建黑色的图像
    img = np.zeros((512,512,3), np.uint8)
    cv.ellipse(img,(256,256),(100,50),0,0,180,255,-1)
    cv.imshow('img',img)
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

5.画多边形

    要绘制多边形,首先需要顶点的坐标。将这些点组成形状为ROWSx1x2的数组,其中ROWS是顶点数,并且其类型应为int32。在这里,我们绘制了一个带有四个顶点的黄色小多边形。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 创建黑色的图像
    img = np.zeros((512,512,3), np.uint8)
    pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
    pts = pts.reshape((-1,1,2))
    cv.polylines(img,[pts],True,(0,255,255))
    cv.imshow('img',img)
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

注意:如果第三个参数为False,您将获得一条连接所有点的折线,而不是闭合形状。 cv.polylines()可用于绘制多条线。只需创建要绘制的所有线条的列表,然后将其传递给函数即可。所有线条将单独绘制。与为每条线调用cv.line相比,绘制一组线是一种更好,更快的方法。

6.向图像添加文本

    要将文本放入图像中,需要指定以下内容。 - 您要写入的文字数据 - 您要放置它的位置坐标(即数据开始的左下角)。 - 字体类型(检查cv.putText文档以获取受支持的字体) - 字体比例(指定字体大小) - 常规的内容,例如颜色,厚度,线条类型等。为了获得更好的外观,建议使用lineType = cv.LINE_AA。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    # 创建黑色的图像
    img = np.zeros((512,512,3), np.uint8)
    font = cv.FONT_HERSHEY_SIMPLEX
    cv.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2,cv.LINE_AA)
    cv.imshow('img',img)
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

7.其他资源

椭圆函数中使用的角度不是我们的圆角。有关更多详细信息,请访问此讨论:http://answers.opencv.org/question/14541/angles-in-ellipse-function/。

六、鼠标作为画笔

1.鼠标事件

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def main():
    events = [i for i in dir(cv) if 'EVENT' in i]
    print( events )
    k = cv.waitKey(0) & 0xFF
    
if __name__ == "__main__":
    main()

在这里插入图片描述

2.简单演示

    创建鼠标回调函数具有特定的格式,该格式在所有地方都相同。它仅在功能上有所不同。因此,我们的鼠标回调函数可以做一件事,在我们双击的地方绘制一个圆圈。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

# 鼠标回调函数
def draw_circle(event,x,y,flags,param):
    if event == cv.EVENT_LBUTTONDBLCLK:
        cv.circle(img,(x,y),100,(255,0,0),-1)
# 创建一个黑色的图像,一个窗口,并绑定到窗口的功能
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
    cv.imshow('image',img)
    if cv.waitKey(20) & 0xFF == 27:
        break
cv.destroyAllWindows()

在这里插入图片描述

3.更高级的演示

    我们通过拖动鼠标来绘制矩形或圆形(取决于我们选择的模式) ,就像我们在 Paint 应用程序中所做的那样。所以我们的鼠标回调函数有两部分,一部分用于绘制矩形,另一部分用于绘制圆形。这个具体的例子对于创建和理解一些交互式应用程序非常有帮助,比如目标跟踪,图像分割地图等等。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

drawing = False # 如果按下鼠标,则为真
mode = True # 如果为真,绘制矩形。按 m 键可以切换到曲线
ix,iy = -1,-1
# 鼠标回调函数
def draw_circle(event,x,y,flags,param):
    global ix,iy,drawing,mode
    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
            if mode == True:
                cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
            else:
                cv.circle(img,(x,y),5,(0,0,255),-1)
    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
        if mode == True:
            cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
        else:
            cv.circle(img,(x,y),5,(0,0,255),-1)
# 创建一个黑色的图像,一个窗口,并绑定到窗口的功能
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
    cv.imshow('image',img)
    if cv.waitKey(20) & 0xFF == 27:
        break
cv.destroyAllWindows()

在这里插入图片描述

七、轨迹栏作为调色板

    我们将创建一个简单的应用程序,以显示您指定的颜色。您有一个显示颜色的窗口,以及三个用于指定B、G、R颜色的跟踪栏。滑动轨迹栏,并相应地更改窗口颜色。默认情况下,初始颜色将设置为黑色。

    对于cv.getTrackbarPos()函数,第一个参数是轨迹栏名称,第二个参数是它附加到的窗口名称,第三个参数是默认值,第四个参数是最大值,第五个是执行的回调函数每次跟踪栏值更改。回调函数始终具有默认参数,即轨迹栏位置。在我们的例子中,函数什么都不做,所以我们简单地通过。

    轨迹栏的另一个重要应用是将其用作按钮或开关。默认情况下,OpenCV不具有按钮功能。因此,您可以使用轨迹栏获得此类功能。在我们的应用程序中,我们创建了一个开关,只有在该开关为ON的情况下,该应用程序才能在其中运行,否则屏幕始终为黑色。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def nothing(x):
    pass
# 创建一个黑色的图像,一个窗口
img = np.zeros((300,512,3), np.uint8)
cv.namedWindow('image')
# 创建颜色变化的轨迹栏
cv.createTrackbar('R','image',0,255,nothing)
cv.createTrackbar('G','image',0,255,nothing)
cv.createTrackbar('B','image',0,255,nothing)
# 为 ON/OFF 功能创建开关
switch = '0 : OFF \n1 : ON'
cv.createTrackbar(switch, 'image',0,1,nothing)
while(1):
    cv.imshow('image',img)
    k = cv.waitKey(1) & 0xFF
    if k == 27:
        break
    # 得到四条轨迹的当前位置
    r = cv.getTrackbarPos('R','image')
    g = cv.getTrackbarPos('G','image')
    b = cv.getTrackbarPos('B','image')
    s = cv.getTrackbarPos(switch,'image')
    if s == 0:
        img[:] = 0
    else:
        img[:] = [b,g,r]
cv.destroyAllWindows()

在这里插入图片描述

总结

    以上是一些OpenCV-Python 关于图像的基础用法。

“笑对人生,智慧同行!博客新文出炉,微信订阅号等你笑纳~”
在这里插入图片描述

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

拥有必珍惜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值