opencv学习笔记(python版)

最近为了毕设的车牌识别准备学习一下opencv这里记录一下学习的过程和笔记

opencv

图像操作

imread 读取图像

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

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

cv.IMREAD_COLOR: 加载彩色图像。任何图像的透明度都会被忽视。它是默认标志。
cv.IMREAD_GRAYSCALE:以灰度模式加载图像
cv.IMREAD_UNCHANGED:加载图像,包括alpha通道
注意 除了这三个标志,你可以分别简单地传递整数1、0或-1。

cv2.imread()读取图片后以多维数组的形式保存图片信息,前两维表示图片的像素坐标,最后一维表示图片的通道索引,具体图像的通道数由图片的格式来决定

if __name__ == '__main__':
    car=cv2.imread('./car4.jpg')
    print(car.shape) #(300, 560, 3)数组形状 高度:300像素 宽度:560像素 3维
    print(type(car)) #numpy数组
    print(car) #三维数组 (彩色图片:高度,宽度,像素 红绿蓝)
    cv2.imshow('car',car[::-1,:,:]) # 弹出窗口
    #第一维表示高度 ::-1表示翻转,颠倒,上下颠倒
    #第二维代表宽度
    #第三维代表颜色 蓝0绿1红2
    cv2.waitKey() #等待键盘输入,任意输出,发出这个代码窗口消失  0表示无限等待 ;1000毫秒
    cv2.destroyAllWindows() #销毁内存
import  cv2
import numpy as np

if __name__ == '__main__':
    img = cv2.imread('car4.jpg', cv2.IMREAD_UNCHANGED)  # 包含alpha通道
    cv2.imshow('car',img)

    img2 = cv2.imread('car4.jpg', cv2.IMREAD_COLOR)  # 彩色图像
    cv2.imshow('car2',img2)

    img3 = cv2.imread('car4.jpg', cv2.IMREAD_GRAYSCALE)  # 灰度图像
    cv2.imshow('car3', img3)
    cv2.waitKey()  # 等待键盘输入,任意输出,发出这个代码窗口消失  0表示无限等待 ;1000毫秒
    cv2.destroyAllWindows()  # 销毁内存

opencv学习笔记(python版)
从详细信息可以看到,car4.jpg含有3个通道(R G B)即位深为24,每一个通道占8位,所以不包含alpha通道,所以前两个读出来是一样的效果

imshow显示图像

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

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

img3 = cv2.imread('car4.jpg', cv2.IMREAD_GRAYSCALE)  # 灰度图像
    cv2.imshow('car3', img3)
    cv2.waitKey()  # 等待键盘输入,任意输出,发出这个代码窗口消失  0表示无限等待 ;1000毫秒
    cv2.destroyAllWindows()  # 销毁内存

opencv学习笔记(python版)

waitKey

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

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

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

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

cv.namedWindow('image',cv.WINDOW_NORMAL)
cv.imshow('image',img)
cv.waitKey(0)
cv.destroyAllWindows()

resize改变图像大小

函数功能: 缩小或者放大函数至某一个大小

resize(InputArray src, OutputArray dst, Size dsize, 
        double fx=0, double fy=0, int interpolation=INTER_LINEAR )

参数解释:
InputArray src :输入,原图像,即待改变大小的图像;
OutputArray dst: 输出,改变后的图像。这个图像和原图像具有相同的内容,只是大小和原图像不一样而已;
dsize:输出图像的大小。
如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:
dsize = Size(round(fxsrc.cols), round(fysrc.rows))

其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。
fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width/src.cols来计算;
fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height/src.rows来计算;

使用注意事项:

dsize和fx/fy不能同时为0,
要么你就指定好dsize的值,让fx和fy空置直接使用默认值,就像resize(img, imgDst, Size(30,30));
要么你就让dsize为0,指定好fx和fy的值,比如fx=fy=0.5,那么就相当于把原图两个方向缩小一倍!

img.shape #(300, 560, 3)数组形状 高度:300像素 宽度:560像素 3维 先高度再宽度
resize 是先宽度在高度

	car=cv2.imread('./car4.jpg')
    car2=cv2.resize(car,(150,280))
    cv2.imshow('car',car) # 弹出窗口
    cv2.waitKey() #等待键盘输入,任意输出,发出这个代码窗口消失  0表示无限等待 ;1000毫秒
    cv2.destroyAllWindows() #销毁内存

cvtColor改变颜色

cvtcolor()函数是一个颜色空间转换函数,可以实现RGB颜色向HSV,HSI等颜色空间转换。也可以转换为灰度图

   car=cv2.imread('./car4.jpg')
    
    car2=cv2.cvtColor(car,code=cv2.COLOR_BGR2GRAY)
    cv2.imshow('car',car2) # 弹出窗口
    cv2.waitKey() #等待键盘输入,任意输出,发出这个代码窗口消失  0表示无限等待 ;1000毫秒
    cv2.destroyAllWindows() #销毁内存

opencv学习笔记(python版)

 car=cv2.imread('./car4.jpg')
    
    car2=cv2.cvtColor(car,code=cv2.COLOR_BGR2HSV)
    cv2.imshow('car',car2) # 弹出窗口
    cv2.waitKey() #等待键盘输入,任意输出,发出这个代码窗口消失  0表示无限等待 ;1000毫秒
    cv2.destroyAllWindows() #销毁内存

opencv学习笔记(python版)

imwrite 写入图像

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

第一个参数是文件名,第二个参数是要保存的图像。 cv.imwrite(‘messigray.png’,img)

这会将图像以PNG格式保存在工作目录中。

img3 = cv2.imread('car4.jpg', cv2.IMREAD_GRAYSCALE)  # 灰度图像
cv2.imwrite('./car3.jpg',img3)

hsv

HSV(Hue, Saturation, Value)是根据颜色的直观特性的一种颜色空间
opencv学习笔记(python版)
这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)

import cv2
import numpy as np
if __name__ == '__main__':
    img1 = cv2.imread('./test.png') # 颜色空间BGR
    img2 = cv2.cvtColor(img1,code = cv2.COLOR_BGR2HSV) # 颜色空间的转变
    # 定义在HSV颜色空间中蓝色的范围
    lower_blue = np.array([110,50,50]) # 浅蓝色
    upper_blue = np.array([130,255,255]) # 深蓝色
    # 根据蓝色的范围,标记图片中哪些位置是蓝色
    # inRange 是否在这个范围内 lower_bule ~ upper_blue:蓝色
    # 如果在那么就是255,不然就是0
    mask = cv2.inRange(img2,lower_blue,upper_blue)
    res = cv2.bitwise_and(img1, img1,mask = mask) # 位运算:与运算!
    cv2.imshow('rgb',img1)
    cv2.imshow('hsv',img2)
    cv2.imshow('mask',mask)
    cv2.imshow('res',res)

    cv2.waitKey(0)
    cv2.destroyAllWindows() # 键盘输入,窗口,销毁,释放内存

opencv学习笔记(python版)

如何找到要追踪的HSV值?

这是在*.com上发现的一个常见问题。它非常简单,你可以使用相同的函数cv.cvtColor()。你只需传递你想要的BGR值,而不是传递图像。例如,要查找绿色的HSV值,

green = np.uint8([[[0,255,0 ]]])
hsv_green = cv.cvtColor(green,cv.COLOR_BGR2HSV)
print( hsv_green )

[[[ 60 255 255]]]

马赛克

后面都将以这个图片为例,宽350,高232
opencv学习笔记(python版)

马赛克方式一

就是把图片先缩小,在放大,其实就是让图片变模糊

import cv2
import numpy as np

if __name__ == '__main__':
    img = cv2.imread('./bao.jpeg')
    print(img.shape) # 高度232,宽度350
    # 马赛克方式一
    img2 = cv2.resize(img,(35,23))
    img3 = cv2.resize(img2,(350,232))
    cv2.imshow('bao',img3)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

马赛克方式二

import cv2
import numpy as np

if __name__ == '__main__':
    img = cv2.imread('./bao.jpeg')
    print(img.shape)  # 高度232,宽度350
    img2 = cv2.resize(img,(35,23))  #先缩小10倍
    img3 = np.repeat(img2,10,axis=0) # 重复行
    img4 = np.repeat(img3,10,axis=1) # 重复列
    cv2.imshow('bao',img4)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

马赛克方式三

import cv2
import numpy as np

if __name__ == '__main__':
    img = cv2.imread('./bao.jpeg')
    print(img.shape)  # 高度232,宽度350
    # 马赛克方式3
    img2 = img[::10, ::10]  # 每10个中取出一个像素,细节
    cv2.namedWindow('bao', flags=cv2.WINDOW_NORMAL)
    cv2.resizeWindow('bao', 350, 232)
    cv2.imshow('bao', img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

人脸马赛克

import numpy as np
import cv2
if __name__ == '__main__':
    img = cv2.imread('./bao.jpeg')
    # 1、人为人脸定位
    # 人脸左上角坐标(143,42);右下角(239,164)(x,y)(宽度、高度)
    # 2、切片获取人脸
    face = img[42:164,138:244]
    # 3、间隔切片,重复,切片,赋值
    face = face[::7,::7] # 每7个中取出一个像素,马赛克
    face = np.repeat(face,7,axis = 0) # 行方向重复7次
    face = np.repeat(face, 7, axis=1) # 列方向上重复7次
    img[42:164,138:244] = face[:122,:106] # 填充,尺寸一致
    # 4、显示
    cv2.imshow('bao',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

人脸检测

首先到github 搜opencv,
opencv学习笔记(python版)
我这里用的第一个特征的xml

import numpy as np
import cv2

if __name__ == '__main__':
    img = cv2.imread('./bao.jpeg')

    # 人脸特征详细说明,1万多行,计算机根据这些特征,进行人脸检测
    # 符合其中一部分,算做人脸
    # 级联分类器,检测器,
    face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
    faces=face_detector.detectMultiScale(img) # 前两个是人脸坐标 x,y 后两个是宽和高

    print(faces) # [[125  38 133 133]] x,y,w,h
    for x,y,w,h in faces:
        cv2.rectangle(img,pt1=(x,y),pt2=(x+w,y+h),color=[0,0,255],thickness=2)
        #矩形 pt1是左上角 ,pt2是右下角, color 矩形的颜色这里是红色 ,thickness粗细 值越大越粗

    cv2.imshow('face',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

多张人脸检测

import numpy as np
import cv2

if __name__ == '__main__':
    img = cv2.imread('./sew2.jpeg')
    gray = cv2.cvtColor(img,code=cv2.COLOR_BGR2GRAY) # 使用灰白可以使数据变少
    # 人脸特征详细说明,1万多行,计算机根据这些特征,进行人脸检测
    # 符合其中一部分,算做人脸
    # 级联分类器,检测器,
    face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
    faces = face_detector.detectMultiScale(gray,
                                           scaleFactor=1.05,# 缩放
                                           minNeighbors=10,minSize=(60,60)) # 坐标x,y,w,h
    # 可以调整参数使算法检测到人脸,
    for x,y,w,h in faces: # for循环可以进行数组遍历!
        # cv2.rectangle(img,
        #               pt1=(x,y),
        #               pt2=(x + w,y+h),
        #               color = [0,0,255],
        #               thickness=2) # 矩形
        cv2.circle(img,center=(x + w//2,y+h//2),# 圆心
                   radius=w//2, # 半径
                   color=[0,0,255],thickness=2)

    cv2.imshow('face',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
  1. image表示的是要检测的输入图像
  2. objects表示被检测物体的矩形框向量组;
  3. scaleFactor表示每次图像尺寸减小的比例
  4. minNeighbors表示构成检测目标的相邻矩形的最小个数(默认为3个)。如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框
  5. minSize为目标的最小尺寸
  6. maxSize为目标的最大尺寸

opencv学习笔记(python版)
这里一定要加上minSize=(60,60)因为有个人的领带会被识别成人脸,通过打印可以看到这个图只有(53,53)比正常的人脸要小一些,所以就可以通过最小大小过滤掉

多张人脸检测+马赛克

import numpy as np
import cv2

if __name__ == '__main__':
    img = cv2.imread('./sew2.jpeg')
    gray = cv2.cvtColor(img,code=cv2.COLOR_BGR2GRAY) # 数据变少
    # 人脸特征详细说明,1万多行,计算机根据这些特征,进行人脸检测
    # 符合其中一部分,算做人脸
    # 级联分类器,检测器,
    face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
    faces = face_detector.detectMultiScale(gray,
                                           scaleFactor=1.05,# 缩放
                                           minNeighbors=10,minSize=(60,60)) # 坐标x,y,w,h
    print(faces)
    for x,y,w,h in faces: # for循环可以进行数组遍历!
       
        face = img[y:y+h,x:x+w]
        face = face[::10,::10]
        face = np.repeat(face, 10, axis=0)
        face = np.repeat(face, 10, axis=1)
        img[y:y+h,x:x+w] = face[:h,:w]
    cv2.imshow('face',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

人脸贴纸画

opencv学习笔记(python版)

import cv2
import numpy as np

if __name__ == '__main__':
    img=cv2.imread('./han.jpeg')
    gray=cv2.cvtColor(img,code=cv2.COLOR_BGR2GRAY)
    star=cv2.imread('./star.jpg')
    # 级联分类器,检测器,
    face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
    faces = face_detector.detectMultiScale(gray)  # 前两个是人脸坐标 x,y 后两个是宽和高

    for x,y,w,h in faces:
        cv2.rectangle(img, pt1=(x, y), pt2=(x + w, y + h), color=[0, 0, 255], thickness=2)
        # 矩形 pt1是左上角 ,pt2是右下角, color 矩形的颜色这里是红色 ,thickness粗细 值越大越粗
        img[y:y+h//4,x+3*w//8:x+w//4+3*w//8]=cv2.resize(star,(w//4,h//4))#将坐标移到正*
    cv2.imshow('face', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


opencv学习笔记(python版)

import cv2
if __name__ == '__main__':
    img = cv2.imread('./han.jpeg')
    gray = cv2.cvtColor(img, code=cv2.COLOR_BGR2GRAY)
    face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
    faces = face_detector.detectMultiScale(gray)
    star = cv2.imread('./star.jpg')
    for x,y,w,h in faces:
        star_s = cv2.resize(star, (w // 2, h // 2))
        w1 = w//2
        h1 = h//2
        for i in range(h1):
            for j in range(w1):#遍历 图片数据
                if not (star_s[i,j] > 180).all():# 红色
                    img[i+y,j+x+w//4] = star_s[i,j]
    cv2.imshow('face',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

手工绘制轮廓

import cv2
import numpy as np
if __name__ == '__main__':
    img = cv2.imread('./flower.jpg') # 蓝绿红,适合显示图片
    hsv = cv2.cvtColor(img, code=cv2.COLOR_BGR2HSV) # 颜色空间,适合计算
    # 轮廓查找,使用的是,颜色值,进行的
    lower_red = (156,50,50) # 浅红色
    upper_red = (180,255,255) # 深红色
    mask = cv2.inRange(hsv, lower_red, upper_red)

    # # 手工绘制轮廓
    h,w,c = img.shape
    mask = np.zeros((h, w), dtype=np.uint8)
    x_data = np.array([124, 169, 208, 285, 307, 260, 175])+110 # 横坐标
    y_data = np.array([205, 124, 135, 173, 216, 311, 309])+110# 纵坐标
    pts = np.c_[x_data,y_data] # 横纵坐标合并,点(x,y)
    print(pts)
    cv2.fillPoly(mask, [pts], (255)) # 绘制多边形!

    res = cv2.bitwise_and(img, img, mask=mask)
    cv2.imshow('flower',res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)
opencv学习笔记(python版)

查找轮廓边界

import cv2
import numpy as np
if __name__ == '__main__':
    dog = cv2.imread('./head.jpg')
    gray = cv2.cvtColor(dog, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.GaussianBlur(gray,(5,5),0) # 高斯平滑,高斯模糊
    canny = cv2.Canny(gray2,150,200)
    cv2.imshow('dog',gray)
    cv2.imshow('dog2',gray2)
    cv2.imshow('canny',canny)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)
使用cv2做高斯模糊,只要一行代码调用GaussianBlur函数,给出高斯矩阵的尺寸和标准差就可以:

gray2 = cv2.GaussianBlur(gray,(5,5),0) # 高斯平滑,高斯模糊

这里(5, 5)表示高斯矩阵的长与宽都是5,标准差取0时OpenCV会根据高斯矩阵的尺寸自己计算。概括地讲,高斯矩阵的尺寸越大,标准差越大,处理过的图像模糊程度越大。也可以自己构造高斯核,相关函数:cv2.GaussianKernel().

可以来退出由照相机或其他环境产生的噪声,减少在边缘提取时的其余边缘的数目

cv2.Canny(src, thresh1, thresh2) 进行canny边缘检测

  • src表示输入的图片,
  • thresh1表示最小阈值,
  • thresh2表示最大阈值,用于进一步删选边缘信息

人脸轮廓替换

import numpy as np
import cv2
if __name__ == '__main__':
    han = cv2.imread('./han.jpeg')
    head = cv2.imread('./head.jpg')
    face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
    han_gray = cv2.cvtColor(han, code=cv2.COLOR_BGR2GRAY)
    head_gray = cv2.cvtColor(head, code=cv2.COLOR_BGR2GRAY)
    # 将狗头像二值化处理,THRESH_OTSU会自动设置阈值
    threshold,head_binary = cv2.threshold(head_gray, 0, 255, cv2.THRESH_OTSU)
    contours, hierarchy = cv2.findContours(head_binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    areas = []
    for contour in contours:
        areas.append(cv2.contourArea(contour)) #计算面积
    areas = np.asarray(areas)
    index = areas.argsort() # 从小到大,倒数第二个,第二大轮廓
    mask = np.zeros_like(head,dtype=np.uint8) # mask面具
    mask = cv2.drawContours(mask,contours,index[-2],(255,255,255),
                            thickness=-1)
    faces = face_detector.detectMultiScale(han_gray)
    for x,y,w,h in faces:
        mask2 = cv2.resize(mask,(w,h))
        head2 = cv2.resize(head, (w, h)) # 彩色图片
        for i in range(h):
            for j in range(w):
                if (mask2[i,j] == 255).all():
                    han[i + y,j + x]=head2[i,j]
    cv2.imshow('mask',mask)
    cv2.imshow('face',han)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

opencv学习笔记(python版)

cv2.threshold(img, threshold, maxval,type)

其中:

  • threshold是设定的阈值
  • maxval是当灰度值大于(或小于)阈值时将该灰度值赋成的值
  • type规定的是当前二值化的方式

返回值 是阈值和目标

cv2.THRESH_BINARY 大于阈值的部分被置为255,小于部分被置为0
cv2.THRESH_BINARY_INV 大于阈值部分被置为0,小于部分被置为255
cv2.THRESH_TRUNC 大于阈值部分被置为threshold,小于部分保持原样
cv2.THRESH_TOZERO 小于阈值部分被置为0,大于部分保持不变
cv2.THRESH_TOZERO_INV 大于阈值部分被置为0,小于部分保持不变
其实还有很重要的cv2.THRESH_OTSU 作为图像自适应二值化的一个很优的算法Otsu大津算法的参数:
使用为cv2.threshold(img, 0, 255, cv2.THRESH_OTSU )

函数cv2.findContours()有三个参数。
第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。

RETR_LIST 从解释的角度来看,这中应是最简单的。它只是提取所有的轮廓,而不去创建任何父子关系。
RETR_EXTERNAL 如果你选择这种模式的话,只会返回最外边的的轮廓,所有的子轮廓都会被忽略掉。
RETR_CCOMP 在这种模式下会返回所有的轮廓并将轮廓分为两级组织结构。
RETR_TREE 这种模式下会返回所有轮廓,并且创建一个完整的组织结构列表。它甚至会告诉你谁是爷爷,爸爸,儿子,孙子等

返回值有两个,第一个是轮廓,第二个是(轮廓的)层析结构

cv2.drawContours()

cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)

InputOutputArray image,//要绘制轮廓的图像
InputArrayOfArrays contours,//所有输入的轮廓,每个轮廓被保存成一个point向量
int contourIdx,//指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
const Scalar& color,//绘制轮廓所用的颜色
int thickness = 1, //绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充
int lineType = 8, /绘制轮廓的线的连通性
InputArray hierarchy = noArray(),//关于层级的可选参数,只有绘制部分轮廓时才会用到
int maxLevel = INT_MAX,//绘制轮廓的*别,这个参数只有hierarchy有效的时候才有效
//maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
//maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
//maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点
Point offset = Point()

上一篇:openmesh - impl - Remove Duplicated Vertices


下一篇:python常用工具类