Skip to content

Commit 73fd32f

Browse files
author
extronwang
committed
更换新域名
1 parent b10e4d6 commit 73fd32f

File tree

32 files changed

+185
-184
lines changed

32 files changed

+185
-184
lines changed

01. 简介与安装/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程01:简介与安装](http://ex2tron.wang/opencv-python-introduction-and-installation/)
22

3-
![](http://pic.ex2tron.top/cv2_install_opencv-python.jpg)
3+
![](http://blog.codec.wang/cv2_install_opencv-python.jpg)
44

55
相信大部分人知道的OpenCV都是用C++来开发的,那为什么我推荐使用Python呢?<!-- more -->
66

@@ -14,15 +14,15 @@
1414

1515
我举两个简单的例子就一目了然了:一个是读入图片,另一个是调整图片的对比度和亮度:
1616

17-
![](http://pic.ex2tron.top/cv2_python_vs_cplus_speed.jpg)
17+
![](http://blog.codec.wang/cv2_python_vs_cplus_speed.jpg)
1818

1919
**可以看到某些情况下Python的运行速度甚至好于C++,代码行数也直接少一半多!**另外,图像是矩阵数据,OpenCV-Python原生支持[Numpy](https://baike.baidu.com/item/numpy),相当于Python中的Matlab,为矩阵运算、科学计算提供了极大的便利性。
2020

2121
## 人工智能浪潮
2222

2323
近些年,人工智能相关技术的快速发展大家有目共睹,不必多说。在编程语言方面,更多人希望的是具备高效开发效率、跨平台、高度扩展性的语言,尤其是一些AI巨头优先推出支持Python语言的深度学习框架,如Facebook的[PyTorch](https://pytorch.org/)、Google的[Tensorflow](https://tensorflow.google.cn/)等,可以说Python是名副其实的“网红语言”了。
2424

25-
![](http://pic.ex2tron.top/cv2_ai_ml_dl2.jpg)
25+
![](http://blog.codec.wang/cv2_ai_ml_dl2.jpg)
2626

2727
[TIOBE编程语言排行榜](https://www.tiobe.com/tiobe-index/)也可以看到,Python发展迅猛,已经逼近C++的份额。这个排行榜每月更新,我就不截图了,编写时TOP5:Java/C/C++/Python/C#。
2828

@@ -76,7 +76,7 @@ print(cv2.__version__) # '3.4.1'
7676

7777
为了便于学习OpenCV,我写了一个教学款软件[LearnOpenCVEdu](https://github.com/ex2tron/LearnOpenCVEdu),目前只开发了一部分功能,有兴趣的童鞋可以支持一下噢😊
7878

79-
![大家随手点个Star吧(●ˇ∀ˇ●)](http://pic.ex2tron.top/cv2_learn_opencv_edu_soft_screenshot.jpg)
79+
![大家随手点个Star吧(●ˇ∀ˇ●)](http://blog.codec.wang/cv2_learn_opencv_edu_soft_screenshot.jpg)
8080

8181
> 经验之谈:虽然从一开始我就推荐大家使用OpenCV-Python进行图像处理,但*想要深入理解OpenCV*,C++还是必须的,尤其是**OpenCV源码**
8282

02. 基本元素-图片/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程02:基本元素-图片](http://ex2tron.wang/opencv-python-basic-element-image/)
22

3-
![](http://pic.ex2tron.top/cv2_image_coordinate_channels.jpg)
3+
![](http://blog.codec.wang/cv2_image_coordinate_channels.jpg)
44

55
学习如何加载图片,显示并保存图片。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -17,7 +17,7 @@
1717

1818
图像坐标的起始点是在左上角,所以行对应的是y,列对应的是x:
1919

20-
![](http://pic.ex2tron.top/cv2_image_coordinate_channels.jpg)
20+
![](http://blog.codec.wang/cv2_image_coordinate_channels.jpg)
2121

2222
### 加载图片
2323

@@ -54,7 +54,7 @@ cv2.waitKey(0)
5454

5555
参数1是窗口的名字,参数2是要显示的图片。不同窗口之间用窗口名区分,所以窗口名相同就表示是同一个窗口,显示结果如下:
5656

57-
![](http://pic.ex2tron.top/cv2_show_lena_gray.jpg)
57+
![](http://blog.codec.wang/cv2_show_lena_gray.jpg)
5858

5959
`cv2.waitKey()`是让程序暂停的意思,参数是等待时间(毫秒ms)。时间一到,会继续执行接下来的程序,传入0的话表示一直等待。等待期间也可以获取用户的按键输入:`k = cv2.waitKey(0)`[练习1](#练习))。
6060

04. 图像基本操作/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程04:图像基本操作](http://ex2tron.wang/opencv-python-basic-operations/)
22

3-
![](http://pic.ex2tron.top/cv2_lena_face_roi_crop.jpg)
3+
![](http://blog.codec.wang/cv2_lena_face_roi_crop.jpg)
44

55
学习获取和修改像素点的值,ROI感兴趣区域,通道分离合并等基本操作。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -78,7 +78,7 @@ print(img.size) # 263*247*3=194883
7878

7979
[ROI](https://baike.baidu.com/item/ROI/1125333#viewPageContent):Region of Interest,感兴趣区域。什么意思呢?比如我们要检测眼睛,因为眼睛肯定在脸上,所以我们感兴趣的只有脸这部分,其他都不care,所以可以单独把脸截取出来,这样就可以大大节省计算量,提高运行速度。
8080

81-
![只关心脸( ╯□╰ )](http://pic.ex2tron.top/cv2_lena_face_roi_crop.jpg)
81+
![只关心脸( ╯□╰ )](http://blog.codec.wang/cv2_lena_face_roi_crop.jpg)
8282

8383
截取ROI非常简单,指定图片的范围即可(后面我们学了特征后,就可以自动截取辣,(ง •_•)ง):
8484

05. 颜色空间转换/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程05:颜色空间转换](http://ex2tron.wang/opencv-python-changing-colorspaces/)
22

3-
![](http://pic.ex2tron.top/cv2_exercise_tracking_three_colors.jpg)
3+
![](http://blog.codec.wang/cv2_exercise_tracking_three_colors.jpg)
44

55
学习如何进行图片的颜色空间转换,视频中追踪特定颜色的物体。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -50,7 +50,7 @@ print(flags)
5050
3. 提取蓝色范围的物体
5151
4. 只显示蓝色物体
5252

53-
![跟踪视频中的蓝色物体](http://pic.ex2tron.top/cv2_blue_object_tracking.jpg)
53+
![跟踪视频中的蓝色物体](http://blog.codec.wang/cv2_blue_object_tracking.jpg)
5454

5555
```python
5656
import numpy as np
@@ -103,7 +103,7 @@ print(hsv_blue) # [[[120 255 255]]]
103103

104104
1. 尝试在视频中同时提取红色、蓝色、绿色的物体。(效果如下)
105105

106-
![同时追踪3种颜色](http://pic.ex2tron.top/cv2_exercise_tracking_three_colors.jpg)
106+
![同时追踪3种颜色](http://blog.codec.wang/cv2_exercise_tracking_three_colors.jpg)
107107

108108
## 接口文档
109109

06. 阈值分割/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程06:阈值分割](http://ex2tron.wang/opencv-python-image-thresholding/)
22

3-
![](http://pic.ex2tron.top/cv2_threshold_binary_demo.jpg)
3+
![](http://blog.codec.wang/cv2_threshold_binary_demo.jpg)
44

55
学习使用不同的阈值方法"二值化"图像。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -17,7 +17,7 @@
1717

1818
固定阈值分割很直接,一句话说就是像素点值大于阈值变成一类值,小于阈值变成另一类值。
1919

20-
![](http://pic.ex2tron.top/cv2_threshold_binary_demo.jpg)
20+
![](http://blog.codec.wang/cv2_threshold_binary_demo.jpg)
2121

2222
```python
2323
import cv2
@@ -63,11 +63,11 @@ for i in range(6):
6363
plt.show()
6464
```
6565

66-
![5种不同的阈值方式结果](http://pic.ex2tron.top/cv2_different_threshold_demo.jpg)
66+
![5种不同的阈值方式结果](http://blog.codec.wang/cv2_different_threshold_demo.jpg)
6767

6868
> 经验之谈:很多人误以为阈值分割就是[二值化](https://baike.baidu.com/item/%E4%BA%8C%E5%80%BC%E5%8C%96)。从上图中可以发现,两者并不等同,阈值分割结果是两类值,而不是两个值,所以教程开头我把二值化加了引号。
6969
70-
![](http://pic.ex2tron.top/cv2_different_thresholds_theory.jpg)
70+
![](http://blog.codec.wang/cv2_different_thresholds_theory.jpg)
7171

7272
### 自适应阈值
7373

@@ -95,7 +95,7 @@ for i in range(4):
9595
plt.show()
9696
```
9797

98-
![自适应阈值对比固定阈值](http://pic.ex2tron.top/cv2_adaptive_vs_global_thresholding.jpg)
98+
![自适应阈值对比固定阈值](http://blog.codec.wang/cv2_adaptive_vs_global_thresholding.jpg)
9999

100100
- 参数1:要处理的原图
101101
- 参数2:最大阈值,一般为255

07. 图像几何变换/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程07:图像几何变换](http://ex2tron.wang/opencv-python-image-geometric-transformation/)
22

3-
![](http://pic.ex2tron.top/cv2_perspective_transformations_inm.jpg)
3+
![](http://blog.codec.wang/cv2_perspective_transformations_inm.jpg)
44

55
学习如何旋转、平移、缩放和翻转图片。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -45,7 +45,7 @@ dst = cv2.flip(img, 1)
4545

4646
其中,参数2 = 0:垂直翻转(沿x轴),参数2 > 0: 水平翻转(沿y轴),参数2 < 0: 水平垂直翻转。
4747

48-
![](http://pic.ex2tron.top/cv2_flip_image_sample.jpg)
48+
![](http://blog.codec.wang/cv2_flip_image_sample.jpg)
4949

5050
### 平移图片
5151

@@ -78,7 +78,7 @@ cv2.imshow('shift', dst)
7878
cv2.waitKey(0)
7979
```
8080

81-
![](http://pic.ex2tron.top/cv2_translation_100_50.jpg)
81+
![](http://blog.codec.wang/cv2_translation_100_50.jpg)
8282

8383
### 旋转图片
8484

@@ -97,7 +97,7 @@ cv2.imshow('rotation', dst)
9797
cv2.waitKey(0)
9898
```
9999

100-
![逆时针旋转45°并缩放](http://pic.ex2tron.top/cv2_rotation_45_degree.jpg)
100+
![逆时针旋转45°并缩放](http://blog.codec.wang/cv2_rotation_45_degree.jpg)
101101

102102

103103
## 小结

08. 绘图功能/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程08:绘图功能](http://ex2tron.wang/opencv-python-drawing-function/)
22

3-
![](http://pic.ex2tron.top/cv2_drawing_functions.jpg)
3+
![](http://blog.codec.wang/cv2_drawing_functions.jpg)
44

55
学习画线、圆和矩形等多种几何形状,给图片添加文字。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -34,7 +34,7 @@ cv2.imshow('img', img)
3434
cv2.waitKey(0)
3535
```
3636

37-
![绘制各种几何形状](http://pic.ex2tron.top/cv2_drawing_functions.jpg)
37+
![绘制各种几何形状](http://blog.codec.wang/cv2_drawing_functions.jpg)
3838

3939
上图就是本教程绘制的最终效果,下面一步步来看:
4040

@@ -139,7 +139,7 @@ cv2.putText(img, 'ex2tron', (10, 500), font,
139139

140140
1. 你能用已学的绘图功能画出OpenCV的logo吗?(提示:椭圆和圆)
141141

142-
![OpenCV logo](http://pic.ex2tron.top/cv2_draw_opencv_logo.jpg)
142+
![OpenCV logo](http://blog.codec.wang/cv2_draw_opencv_logo.jpg)
143143

144144
## 接口文档
145145

09. 图像混合/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程09:图像混合](http://ex2tron.wang/opencv-python-image-blending/)
22

3-
![](http://pic.ex2tron.top/cv2_image_blending_6_4.jpg)
3+
![](http://blog.codec.wang/cv2_image_blending_6_4.jpg)
44

55
学习图片间的数学运算,图像混合。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -42,19 +42,19 @@ img2 = cv2.imread('opencv-logo-white.png')
4242
res = cv2.addWeighted(img1, 0.6, img2, 0.4, 0)
4343
```
4444

45-
![图像混合](http://pic.ex2tron.top/cv2_image_blending_6_4.jpg)
45+
![图像混合](http://blog.codec.wang/cv2_image_blending_6_4.jpg)
4646

4747
> 经验之谈:α和β都等于1时,就相当于图片相加。
4848
4949
### 按位操作
5050

5151
按位操作包括按位与/或/非/异或操作,有什么用途呢?比如说我们要实现下图的效果:
5252

53-
![](http://pic.ex2tron.top/cv2_bitwise_operations_demo.jpg)
53+
![](http://blog.codec.wang/cv2_bitwise_operations_demo.jpg)
5454

5555
如果将两幅图片直接相加会改变图片的颜色,如果用图像混合,则会改变图片的透明度,所以我们需要用按位操作。首先来了解一下[掩膜](https://baike.baidu.com/item/%E6%8E%A9%E8%86%9C/8544392?fr=aladdin)(mask)的概念:掩膜是用一副二值化图片对另外一幅图片进行局部的遮挡,看下图就一目了然了:
5656

57-
![掩膜概念](http://pic.ex2tron.top/cv2_understand_mask.jpg)
57+
![掩膜概念](http://blog.codec.wang/cv2_understand_mask.jpg)
5858

5959
所以我们的思路就是把原图中要放logo的区域抠出来,再把logo放进去就行了:
6060

10. 平滑图像/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程10:平滑图像](http://ex2tron.wang/opencv-python-smoothing-images/)
22

3-
![](http://pic.ex2tron.top/cv2_bilateral_vs_gaussian.jpg)
3+
![](http://blog.codec.wang/cv2_bilateral_vs_gaussian.jpg)
44

55
学习模糊/平滑图像,消除噪点。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -72,7 +72,7 @@ blur = cv2.boxFilter(img, -1, (3, 3), normalize=True)
7272

7373
前面两种滤波方式,卷积核内的每个值都一样,也就是说图像区域中每个像素的权重也就一样。高斯滤波的卷积核权重并不相同:中间像素点权重最高,越远离中心的像素权重越小,来,数学时间( ╯□╰ ),还记得标准正态分布的曲线吗?
7474

75-
![](http://pic.ex2tron.top/cv2_gaussian_kernel_function_theory.jpg)
75+
![](http://blog.codec.wang/cv2_gaussian_kernel_function_theory.jpg)
7676

7777
显然这种处理元素间权值的方式更加合理一些。图像是2维的,所以我们需要使用[2维的高斯函数](https://en.wikipedia.org/wiki/Gaussian_filter),比如OpenCV中默认的3×3的高斯卷积核(具体原理和卷积核生成方式请参考文末的[番外小篇](#番外小篇:高斯滤波卷积核)):
7878

@@ -96,7 +96,7 @@ gaussian = cv2.GaussianBlur(img, (5, 5), 1) # 高斯滤波
9696

9797
参数3 σx值越大,模糊效果越明显。高斯滤波相比均值滤波效率要慢,但可以有效消除高斯噪声,能保留更多的图像细节,所以经常被称为最有用的滤波器。均值滤波与高斯滤波的对比结果如下(均值滤波丢失的细节更多):
9898

99-
![](http://pic.ex2tron.top/cv2_gaussian_vs_average.jpg)
99+
![](http://blog.codec.wang/cv2_gaussian_vs_average.jpg)
100100

101101
### 中值滤波
102102

@@ -111,7 +111,7 @@ blur = cv2.blur(img, (5, 5)) # 均值滤波
111111
median = cv2.medianBlur(img, 5) # 中值滤波
112112
```
113113

114-
![](http://pic.ex2tron.top/cv2_median_vs_average.jpg)
114+
![](http://blog.codec.wang/cv2_median_vs_average.jpg)
115115

116116
### 双边滤波
117117

@@ -124,7 +124,7 @@ gau = cv2.GaussianBlur(img, (5, 5), 0) # 高斯滤波
124124
blur = cv2.bilateralFilter(img, 9, 75, 75) # 双边滤波
125125
```
126126

127-
![](http://pic.ex2tron.top/cv2_bilateral_vs_gaussian.jpg)
127+
![](http://blog.codec.wang/cv2_bilateral_vs_gaussian.jpg)
128128

129129
可以看到,双边滤波明显保留了更多边缘信息。
130130

11. 边缘检测/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程11:边缘检测](http://ex2tron.wang/opencv-python-edge-detection/)
22

3-
![](http://pic.ex2tron.top/cv2_canny_edge_detection_threshold.jpg)
3+
![](http://blog.codec.wang/cv2_canny_edge_detection_threshold.jpg)
44

55
学习使用Canny获取图像的边缘。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -28,7 +28,7 @@ cv2.imshow('canny', np.hstack((img, edges)))
2828
cv2.waitKey(0)
2929
```
3030

31-
![](http://pic.ex2tron.top/cv2_canny_edge_detection.jpg)
31+
![](http://blog.codec.wang/cv2_canny_edge_detection.jpg)
3232

3333
`cv2.Canny()`进行边缘检测,参数2、3表示最低、高阈值,下面来解释下具体原理。
3434

@@ -64,15 +64,15 @@ $$
6464

6565
梯度其实已经表示了轮廓,但为了进一步筛选,可以在上面的四个角度方向上再取局部极大值:
6666

67-
![](http://pic.ex2tron.top/cv2_understand_canny_direction.jpg)
67+
![](http://blog.codec.wang/cv2_understand_canny_direction.jpg)
6868

6969
比如,A点在45°方向上大于B/C点,那就保留它,把B/C设置为0。
7070

7171
4,滞后阈值:
7272

7373
经过前面三步,就只剩下0和可能的边缘梯度值了,为了最终确定下来,需要设定高低阈值:
7474

75-
![](http://pic.ex2tron.top/cv2_understand_canny_max_min_val.jpg)
75+
![](http://blog.codec.wang/cv2_understand_canny_max_min_val.jpg)
7676

7777
- 像素点的值大于最高阈值,那肯定是边缘(上图A)
7878
- 同理像素值小于最低阈值,那肯定不是边缘
@@ -94,13 +94,13 @@ cv2.waitKey(0)
9494

9595
代码中我用了[番外篇:Otsu阈值法](/opencv-python-extra-otsu-thresholding/)中的自动阈值分割,如果你不太了解,大可以使用传统的方法,不过如果是下面这种图片,推荐用Otsu阈值法。另外Python中某个值不用的话,就写个下划线'_'。
9696

97-
![](http://pic.ex2tron.top/cv2_canny_edge_detection_threshold.jpg)
97+
![](http://blog.codec.wang/cv2_canny_edge_detection_threshold.jpg)
9898

9999
## 练习
100100

101101
1. (选做)如果你不太理解高低阈值的效果,创建两个滑动条来调节它们的值看看:
102102

103-
![](http://pic.ex2tron.top/cv2_trackbar_maxval_minval_canny.gif)
103+
![](http://blog.codec.wang/cv2_trackbar_maxval_minval_canny.gif)
104104

105105
## 小结
106106

12. 腐蚀与膨胀/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [OpenCV-Python教程12:腐蚀与膨胀](http://ex2tron.wang/opencv-python-erode-and-dilate/)
22

3-
![](http://pic.ex2tron.top/cv2_understand_morphological.jpg)
3+
![](http://blog.codec.wang/cv2_understand_morphological.jpg)
44

55
学习常用形态学操作:腐蚀膨胀,开运算和闭运算。<!-- more -->图片等可到[源码处](#引用)下载。
66

@@ -18,15 +18,15 @@
1818

1919
形态学操作其实就是**改变物体的形状**,比如腐蚀就是"变瘦",膨胀就是"变胖",看下图就明白了:
2020

21-
![](http://pic.ex2tron.top/cv2_understand_morphological.jpg)
21+
![](http://blog.codec.wang/cv2_understand_morphological.jpg)
2222

2323
> 经验之谈:形态学操作一般作用于二值化图,来连接相邻的元素或分离成独立的元素。**腐蚀和膨胀是针对图片中的白色部分!**
2424
2525
### 腐蚀
2626

2727
腐蚀的效果是把图片"变瘦",其原理是在原图的小区域内取局部最小值。因为是二值化图,只有0和255,所以小区域内有一个是0该像素点就为0:
2828

29-
![](http://pic.ex2tron.top/cv2_understand_erosion.jpg)
29+
![](http://blog.codec.wang/cv2_understand_erosion.jpg)
3030

3131
这样原图中边缘地方就会变成0,达到了瘦身目的(小胖福利(●ˇ∀ˇ●))
3232

@@ -49,7 +49,7 @@ kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # 椭圆结构
4949
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # 十字形结构
5050
```
5151

52-
![](http://pic.ex2tron.top/cv2_morphological_struct_element.jpg)
52+
![](http://blog.codec.wang/cv2_morphological_struct_element.jpg)
5353

5454
### 膨胀
5555

@@ -77,7 +77,7 @@ img = cv2.imread('j_noise_in.bmp', 0)
7777
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭运算
7878
```
7979

80-
![](http://pic.ex2tron.top/cv2_morphological_opening_closing.jpg)
80+
![](http://blog.codec.wang/cv2_morphological_opening_closing.jpg)
8181

8282
> 经验之谈:很多人对开闭运算的作用不是很清楚(好吧,其实是比较容易混◑﹏◐),但看上图↑,不用怕:如果我们的目标物体外面有很多无关的小区域,就用开运算去除掉;如果物体内部有很多小黑洞,就用闭运算填充掉。
8383
@@ -92,7 +92,7 @@ img = cv2.imread('school.bmp', 0)
9292
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
9393
```
9494

95-
![](http://pic.ex2tron.top/cv2_morphological_gradient.jpg)
95+
![](http://blog.codec.wang/cv2_morphological_gradient.jpg)
9696

9797
- 顶帽:原图减去开运算后的图:`src - opening`
9898

0 commit comments

Comments
 (0)