Skip to content

Commit 74d2264

Browse files
committed
KNN&DC&Grabcut
1 parent ca14eaa commit 74d2264

File tree

13 files changed

+417
-2
lines changed

13 files changed

+417
-2
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,8 @@ code_111 | [KMeans Image Segmentation](python/code_111) | ✔️
153153
code_112 | [KMeans Background Change](python/code_112) | ✔️
154154
code_113 | [KMeans Extract Image Color Card](python/code_113) | ✔️
155155
code_114 | [KNN Classification](python/code_114) | ✔️
156-
code_115 | [KNN-Train Data Save and Load](python/code_115) | ✔️
156+
code_115 | [KNN-Train Data Save and Load](python/code_115) | ✔️
157+
code_116 | [Decision Tree Algorithm](python/code_116) | ✔️
158+
code_117 | [Image Mean-shift Segmentation](python/code_117) | ✔️
159+
code_118 | [Grabcut-Image Segmentation](python/code_118) | ✔️
160+
code_119 | [Grabcut-Background Change](python/code_119) | ✏️

README_CN.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,8 @@ code_111 | [KMeans 图像分割](python/code_111) | ✔️
152152
code_112 | [KMeans 图像替换](python/code_112) | ✔️
153153
code_113 | [KMeans 图像色卡提取](python/code_113) | ✔️
154154
code_114 | [KNN 分类模型](python/code_114) | ✔️
155-
code_115 | [KNN 数据保存](python/code_115) | ✔️
155+
code_115 | [KNN 数据保存](python/code_115) | ✔️
156+
code_116 | [决策树算法](python/code_116) | ✔️
157+
code_117 | [图像均值漂移分割](python/code_117) | ✔️
158+
code_118 | [Grabcut-图像分割](python/code_118) | ✔️
159+
code_119 | [Grabcut-背景替换](python/code_119) | ✏️

python/code_116/README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Opencv 决策树算法
2+
3+
✔️ OpenCV中机器学习模块的决策树算法分为两个类别:
4+
5+
- 一个是随机森林(Random Trees)
6+
- 强化分类(Boosting Classification)
7+
8+
Opencv的函数使用方法和前面的KNN一样,都是通过`cv2.ml` 创建。
9+
10+
eg. 随机森林
11+
12+
```python
13+
import numpy as np
14+
import cv2 as cv
15+
from sklearn import metrics
16+
17+
# 读取数据
18+
img = cv.imread('../code_114/digits.png')
19+
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
20+
cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]
21+
x = np.array(cells)
22+
print('data loading...')
23+
24+
# 创建训练与测试数据
25+
train = x[:,:50].reshape(-1,400).astype(np.float32)
26+
test = x[:,50:100].reshape(-1,400).astype(np.float32)
27+
k = np.arange(10)
28+
train_labels = np.repeat(k,250)[:,np.newaxis]
29+
test_labels = train_labels.copy()
30+
31+
# 训练随机树
32+
dt = cv.ml.RTrees_create()
33+
dt.train(train, cv.ml.ROW_SAMPLE, train_labels)
34+
retval, results = dt.predict(test)
35+
36+
# 计算准确率
37+
matches = results==test_labels
38+
correct = np.count_nonzero(matches)
39+
accuracy = correct*100.0/results.size
40+
print('acc is : ', accuracy)
41+
```
42+
输出:
43+
```
44+
data loading...
45+
acc is : 83.72
46+
```

python/code_116/opencv_116.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import numpy as np
2+
import cv2 as cv
3+
from sklearn import metrics
4+
5+
# 读取数据
6+
img = cv.imread('../code_114/digits.png')
7+
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
8+
cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]
9+
x = np.array(cells)
10+
print('data loading...')
11+
12+
# 创建训练与测试数据
13+
train = x[:,:50].reshape(-1,400).astype(np.float32)
14+
test = x[:,50:100].reshape(-1,400).astype(np.float32)
15+
k = np.arange(10)
16+
train_labels = np.repeat(k,250)[:,np.newaxis]
17+
test_labels = train_labels.copy()
18+
19+
# 训练随机树
20+
dt = cv.ml.RTrees_create()
21+
dt.train(train, cv.ml.ROW_SAMPLE, train_labels)
22+
retval, results = dt.predict(test)
23+
24+
# 计算准确率
25+
matches = results==test_labels
26+
correct = np.count_nonzero(matches)
27+
accuracy = correct*100.0/results.size
28+
print('acc is : ', accuracy)

python/code_117/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# 图像均值漂移
2+
3+
## 概述
4+
5+
✔️ MeanShfit 均值漂移算法是一种通用的聚类算法,通常可以实现彩色图像分割。
6+
7+
**基本原理**
8+
9+
✔️ 对于给定的一定数量样本,任选其中一个样本,以该样本为中心点划定一个圆形区域,求取该圆形区域内样本的质心,即密度最大处的点,再以该点为中心继续执行上述迭代过程,直至最终收敛。
10+
11+
**彩色图像分割**
12+
13+
✔️ 均值迁移可以不断分割找到空间颜色分布的峰值,然后根据峰值进行相似度合并,解决过度分割问题,得到最终的分割图像,对于图像多维度数据颜色值(RGB)与空间位置(x,y),所以需要两个窗口半径,一个是空间半径、另外一个是颜色半径,经过均值漂移窗口的所有的像素点会具有相同的像素值。
14+
15+
> 严格来说并不是图像的分割,而是图像在色彩层面的平滑滤波,它可以中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域。
16+
17+
## 函数
18+
19+
```
20+
dst = cv.pyrMeanShiftFiltering(src, sp, sr, maxLevel, termcrit)
21+
```
22+
其中:
23+
- src --> 输入图像;
24+
- dst --> 输出结果;
25+
- sp --> 表示空间窗口大小;
26+
- sr --> 表示表示颜色空间;
27+
- maxLevel --> 表示金字塔层数,总层数为maxlevel+1;
28+
- termcrit --> 表示停止条件;
29+
30+
## 代码示例
31+
32+
```python
33+
import cv2 as cv
34+
import numpy as np
35+
36+
src = cv.imread("master.jpg")
37+
dst = cv.pyrMeanShiftFiltering(src, 25, 40, None, 2)
38+
cv.imshow("result", np.hstack((src,dst)))
39+
```
40+
<img src=https://i.loli.net/2019/09/20/s9MjNbZPvnDhHeg.jpg width=350>

python/code_117/master.jpg

57.4 KB
Loading

python/code_117/opencv_117.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import cv2 as cv
2+
import numpy as np
3+
4+
src = cv.imread("master.jpg")
5+
dst = cv.pyrMeanShiftFiltering(src, 25, 40, None, 2)
6+
cv.imshow("result", np.hstack((src,dst)))
7+
cv.imwrite("result.jpg", np.hstack((src,dst)))
8+
cv.waitKey(0)
9+
cv.destroyAllWindows()

python/code_118/README.md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Grabcut图像分割
2+
3+
## 概述
4+
5+
✔️ Grabcut是基于图割(graph cut)实现的图像分割算法,它需要用户输入一个bounding box作为分割目标位置,实现对目标与背景的分离/分割。
6+
7+
✔️ Grabcut分割速度快,效果好,支持交互操作,因此在很多APP图像分割/背景虚化的软件中经常使用。
8+
9+
**算法流程**
10+
11+
- 在图片中定义含有(一个或多个)物体的矩形;
12+
- 矩形外的区域被自动认为是背景;
13+
- 对于用户定义的矩形区域,可用背景中数据来区分是前景还是背景;
14+
- 用高斯混合模型(GMM)来对被禁和前景见面,并将未定义的像素标记为可能的前景或背景;
15+
- 图像中的每一个像素都被看作通过通过虚拟变与周围像素连接,而每条边都有一个属于前景或背景的概率这基于它和周围像素颜色上的相似性;
16+
- 每一个像素(即算法中的节点)会与前一各前景或背景节点连接;
17+
- 在节点连接完成后,用图论中最大流最小割的方法来分割。
18+
19+
## 函数
20+
21+
```python
22+
cv2.grabCut(img, rect, mask,
23+
bgdModel, fgdModel,
24+
iterCount, mode = GC_EVAL)
25+
```
26+
其中
27+
- img --> 输入的三通道图像;
28+
- mask --> 输入的单通道图像,初始化方式为GC_INIT_WITH_RECT表示ROI区域可以被初始化为:
29+
- GC_BGD --> 定义为明显的背景像素 0
30+
- GC_FGD --> 定义为明显的前景像素 1
31+
- GC_PR_BGD --> 定义为可能的背景像素 2
32+
- GC_PR_FGD --> 定义为可能的前景像素 3
33+
- rect --> 表示roi区域;
34+
- bgdModel --> 表示临时背景模型数组;
35+
- fgdModel --> 表示临时前景模型数组;
36+
- iterCount --> 表示图割算法迭代次数, 次数越多,效果越好;
37+
- mode --> 当使用用户提供的roi时候使用GC_INIT_WITH_RECT。
38+
39+
## 示例代码
40+
41+
```
42+
import cv2 as cv
43+
import numpy as np
44+
45+
src = cv.imread("m1.jpg")
46+
src = cv.resize(src, (0,0), fx=0.5, fy=0.5)
47+
r = cv.selectROI('input', src, False) # 返回 (x_min, y_min, w, h)
48+
49+
# roi区域
50+
roi = src[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
51+
52+
# 原图mask
53+
mask = np.zeros(src.shape[:2], dtype=np.uint8)
54+
55+
# 矩形roi
56+
rect = (int(r[0]), int(r[1]), int(r[2]), int(r[3])) # 包括前景的矩形,格式为(x,y,w,h)
57+
58+
bgdmodel = np.zeros((1,65),np.float64) # bg模型的临时数组
59+
fgdmodel = np.zeros((1,65),np.float64) # fg模型的临时数组
60+
61+
cv.grabCut(src,mask,rect,bgdmodel,fgdmodel, 11, mode=cv.GC_INIT_WITH_RECT)
62+
63+
# 提取前景和可能的前景区域
64+
mask2 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
65+
66+
print(mask2.shape)
67+
68+
result = cv.bitwise_and(src,src,mask=mask2)
69+
cv.imwrite('result.jpg', result)
70+
cv.imwrite('roi.jpg', roi)
71+
72+
cv.imshow('roi', roi)
73+
cv.imshow("result", result)
74+
cv.waitKey(0)
75+
cv.destroyAllWindows()
76+
```
77+
78+
输入:
79+
80+
采用 selectROI, 可以在图中自己选定ROI区域:
81+
- 选定后,按enter 或则 Space 进行grabcut;
82+
- 重新选ROI,只需用鼠标重新选择即可;
83+
- 按 c 结束程序。
84+
85+
1)显示原图:
86+
87+
<img src=https://i.loli.net/2019/09/20/UyJQqzXd7bwfg1u.jpg>
88+
89+
90+
2)选择ROI:
91+
92+
<img src=https://i.loli.net/2019/09/20/muQ1S62YtLfUjkT.jpg>
93+
94+
3)输出结果:
95+
96+
<img src= https://i.loli.net/2019/09/20/5F1w7pY6mPDWock.jpg>

python/code_118/m1.jpg

166 KB
Loading

python/code_118/opencv_118.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import cv2 as cv
2+
import numpy as np
3+
4+
src = cv.imread("m1.jpg")
5+
src = cv.resize(src, (0,0), fx=0.5, fy=0.5)
6+
cv.imwrite('mm1.jpg', src)
7+
r = cv.selectROI('input', src, False) # 返回 (x_min, y_min, w, h)
8+
9+
# roi区域
10+
roi = src[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
11+
img = src.copy()
12+
cv.rectangle(img, (int(r[0]), int(r[1])),(int(r[0])+int(r[2]), int(r[1])+ int(r[3])), (255, 0, 0), 2)
13+
cv.imwrite('img.jpg', img)
14+
15+
# 原图mask
16+
mask = np.zeros(src.shape[:2], dtype=np.uint8)
17+
18+
# 矩形roi
19+
rect = (int(r[0]), int(r[1]), int(r[2]), int(r[3])) # 包括前景的矩形,格式为(x,y,w,h)
20+
21+
bgdmodel = np.zeros((1,65),np.float64) # bg模型的临时数组 13 * iterCount
22+
fgdmodel = np.zeros((1,65),np.float64) # fg模型的临时数组 13 * iterCount
23+
24+
cv.grabCut(src,mask,rect,bgdmodel,fgdmodel, 11, mode=cv.GC_INIT_WITH_RECT)
25+
26+
# 提取前景和可能的前景区域
27+
mask2 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
28+
29+
print(mask2.shape)
30+
31+
result = cv.bitwise_and(src,src,mask=mask2)
32+
#cv.imwrite('result.jpg', result)
33+
#cv.imwrite('roi.jpg', roi)
34+
35+
cv.imshow('roi', roi)
36+
cv.imshow("result", result)
37+
cv.waitKey(0)
38+
cv.destroyAllWindows()

python/code_119/README.md

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Grabcut应用背景替换
2+
3+
## 概述
4+
5+
✔️ 使用Grabcut实现图像对象提取,通过背景图像替换,实现图像合成,通过对背景图像高斯模糊实现背景虚化效果,完整的步骤如下:
6+
7+
1. ROI区域选择;
8+
2. Grabcut对象分割;
9+
3. Mask生成,并转化为alpha值;
10+
4. 使用 `com = alpha*fg + (1-alpha)*bg` 公式融合图片。
11+
12+
## 示例代码
13+
14+
```python
15+
import cv2 as cv
16+
import numpy as np
17+
18+
src = cv.imread("../code_118/m1.jpg")
19+
src = cv.resize(src, (0,0), fx=0.5, fy=0.5)
20+
r = cv.selectROI('input', src, False) # 返回 (x_min, y_min, w, h)
21+
22+
# roi区域
23+
roi = src[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
24+
img = src.copy()
25+
cv.rectangle(img, (int(r[0]), int(r[1])),(int(r[0])+int(r[2]), int(r[1])+ int(r[3])), (255, 0, 0), 2)
26+
27+
# 原图mask
28+
mask = np.zeros(src.shape[:2], dtype=np.uint8)
29+
30+
# 矩形roi
31+
rect = (int(r[0]), int(r[1]), int(r[2]), int(r[3])) # 包括前景的矩形,格式为(x,y,w,h)
32+
33+
bgdmodel = np.zeros((1,65),np.float64) # bg模型的临时数组 13 * iterCount
34+
fgdmodel = np.zeros((1,65),np.float64) # fg模型的临时数组 13 * iterCount
35+
36+
cv.grabCut(src,mask,rect,bgdmodel,fgdmodel, 11, mode=cv.GC_INIT_WITH_RECT)
37+
38+
# 提取前景和可能的前景区域
39+
mask2 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
40+
background = cv.imread("flower.png")
41+
42+
h, w, ch = src.shape
43+
background = cv.resize(background, (w, h))
44+
cv.imwrite("background.jpg", background)
45+
46+
mask = np.zeros(src.shape[:2], dtype=np.uint8)
47+
bgdmodel = np.zeros((1,65),np.float64)
48+
fgdmodel = np.zeros((1,65),np.float64)
49+
50+
cv.grabCut(src,mask,rect,bgdmodel,fgdmodel,5,mode=cv.GC_INIT_WITH_RECT)
51+
mask2 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
52+
53+
# 高斯模糊
54+
se = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
55+
cv.dilate(mask2, se, mask2)
56+
mask2 = cv.GaussianBlur(mask2, (5, 5), 0)
57+
cv.imshow('background-mask',mask2)
58+
cv.imwrite('background-mask.jpg',mask2)
59+
60+
61+
# 虚化背景
62+
background = cv.GaussianBlur(background, (0, 0), 15)
63+
mask2 = mask2/255.0
64+
a = mask2[..., None]
65+
66+
# 融合方法 com = a*fg + (1-a)*bg
67+
result = a* (src.astype(np.float32)) +(1 - a) * (background.astype(np.float32))
68+
69+
70+
cv.imshow("result", result.astype(np.uint8))
71+
cv.imwrite("result.jpg", result.astype(np.uint8))
72+
73+
cv.waitKey(0)
74+
cv.destroyAllWindows()
75+
```
76+
1)选择ROI:
77+
78+
<img src=https://i.loli.net/2019/09/20/muQ1S62YtLfUjkT.jpg>
79+
80+
2)生成mask并高斯化:
81+
82+
<img src=https://i.loli.net/2019/10/08/GsS7M9LNo4wtZCV.jpg>
83+
84+
3)读取背景图:
85+
86+
<img src=https://i.loli.net/2019/10/08/aIMcoHjm3KNbvtd.jpg>
87+
88+
4)融合前景背景:
89+
90+
<img src=https://i.loli.net/2019/10/08/PiucCeAOflsXJIj.jpg>

python/code_119/flower.png

264 KB
Loading

0 commit comments

Comments
 (0)