OpenCV-Part5——综合运用

[TOC]

注:本篇仅为记录本人使用过的比较实战高阶的OpenCV算法流程。

K-Means聚类

  • 从分布的角度重新构造图像色彩度,减少图像中颜色数量。
  • cv2.kmeans(data, K, bestLabels, criteria, attempts, flags)
    • data:np.float32数据类型,每个功能应该放在一个列中
    • K:集群数(nclusters)
    • bestLabels:预设的分类标签,没有则设为None
    • criteria:迭代终止标准。满足此条件时,算法迭代停止。它由3个参数的元组组成:(type,max_iter,epsilon)
      • type:
        • cv2.TERM_CRITERIA_EPS:如果达到指定的精度epsilon,则停止算法迭代。
        • cv2.TERM_CRITERIA_MAX_ITER:在指定的迭代次数max_iter之后停止算法。
        • cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER:当满足上述任何条件时停止迭代。
      • max_iter:指定最大迭代次数的整数
      • epsilon:要求的准确性
    • attempts:重复试验kmeans算法次数,将会返回最好的一次结果。
    • flags:该标志用于指定初始中心的采用方式。通常会使用两个标志:cv2.KMEANS_PP_CENTERScv2.KMEANS_RANDOM_CENTERS
    • 返回三个数据:
      • retval:从每个点到它们相应中心的平方距离之和。
      • bestLabels:标签数组。是不固定的。
      • centers:一组聚类中心。即标签对应的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np
import cv2

img = cv2.imread('test.jpg')
Z = img.reshape((-1, 3)) # 把所有像素拉成一条直线

# 1. convert to np.float32
Z = np.float32(Z) # uint8 -> float32

# 2. define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 3
ret, label, center = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)

# 3. Now convert back into uint8, and make original image
center = np.uint8(center) # float32 -> uint8
res = center[label.flatten()] # 将标签数组赋予真正的bgr值
res2 = res.reshape(img.shape) # 重构图像

cv2.imshow('res2', res2)
cv2.waitKey(0)
cv2.destroyAllWindows()

极坐标与直角坐标变换

极坐标转直角坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import numpy as np
import math
import cv2

cir_img = cv2.imread('4-1.jpg')
cv2.imshow('panoramagram', cir_img)

# 得到圆形区域的中心坐标
x0, y0 = cir_img.shape[0] // 2, cir_img.shape[1] // 2
# 通过圆形区域半径构造展开后的图像
radius = cir_img.shape[0] // 2
rect_height = radius
rect_width = int(2 * math.pi * radius)
rect_img = np.zeros((rect_height, rect_width, 3), dtype="u1")

start = 0 # 从正下方开始切,start设定偏移角度,单位为度
except_count = 0
for j in range(rect_width):
theta = 2 * math.pi * (j / rect_width) + 2 * math.pi * (start / 360)
for i in range(rect_height):
# 适应椭圆的极坐标展开
x = (x0 - i) * math.cos(theta) + x0 # "sin" is clockwise but "cos" is anticlockwise
y = (y0 - i) * math.sin(theta) + y0
x, y = int(x), int(y)
try:
rect_img[i, j, :] = cir_img[x, y, :]
except Exception as e:
except_count = except_count + 1

print(except_count)
cv2.imwrite("rect_img.jpg", rect_img)
cv2.imshow("rect_img", rect_img)
cv2.waitKey(0)

直角坐标转极坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import numpy as np
import math
import cv2

img_name = '8.jpeg'
img = cv2.imread(img_name)
cv2.imshow('panoramagram', img)
img = wrapped_img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
# 准备工作,计算原图像尺寸和变换后的图片大小
x0 = img.shape[0]
y0 = img.shape[1]
print(x0, y0)
# 最大半径计算
radius = int(y0 / (2 * math.pi))
w = 2 * radius
h = 2 * radius
wrapped_img = 255 * np.ones((w, h, 3), dtype="u1")

except_count = 0
for j in range(y0):
# 1. 求极坐标系中对应的角度theta
theta = 2 * math.pi * (j / y0)
# print(theta)
for i in range(x0):
# 2. 计算半径缩放系数
wrapped_radius = (i - x0) * radius / x0
# 3. 利用对应关系进行换算
y = wrapped_radius * math.cos(theta) + radius
x = wrapped_radius * math.sin(theta) + radius
x, y = int(x), int(y)
try:
wrapped_img[x, y, :] = img[i, j, :]
# 注意点,在数学坐标系中的坐标与数字图像中的坐标表示存在差异需要注意
except Exception as e:
except_count = except_count + 1

print(except_count)
# 提取ROI区域进行平滑处理,效果一般
roi = wrapped_img[0:radius, radius - 5:radius + 5, :]
roi_blur = cv2.blur(roi, (3, 3))
wrapped_img[0:radius, radius - 5:radius + 5, :] = roi_blur
# wrapped_img = cv2.resize(wrapped_img,None,fx=1,fy=1,interpolation=cv2.INTER_CUBIC)
name = 'p_' + img_name
cv2.imwrite(name, wrapped_img)
cv2.imshow("Unwrapped", wrapped_img)
cv2.waitKey(0)

傅里叶变换实现的高低通滤波

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('source/work3/lena.jpg', 0)
# 返回结果与Numpy相同,但有两个通道。第一个通道为有结果的实部,第二个通道为有结果的虚部。
img_dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
# 移频 将FFT输出中的直流分量移动到频谱的中央
img_shift = np.fft.fftshift(img_dft)

center_row, center_col = int(img.shape[0] / 2), int(img.shape[1] / 2)
# 低通滤波
cov = np.zeros((img.shape[0], img.shape[1], 2), np.uint8)
cov[center_row - 30:center_row + 30, center_col - 30:center_col + 30, :] = 1
f1_shift = img_shift * cov
f1_ishift = np.fft.ifftshift(f1_shift)
f1_back = cv2.idft(f1_ishift)
f1_back = cv2.magnitude(f1_back[:, :, 0], f1_back[:, :, 1])

# 高通滤波
cov = np.ones((img.shape[0], img.shape[1], 2), np.uint8)
cov[center_row - 30:center_row + 30, center_col - 30:center_col + 30] = 0
f2_shift = img_shift * cov
f2_ishift = np.fft.ifftshift(f2_shift)
f2_back = cv2.idft(f2_ishift)
f2_back = cv2.magnitude(f2_back[:, :, 0], f2_back[:, :, 1])

plt.subplot(221), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(222), plt.imshow(f1_back, cmap='gray')
plt.title('LPF'), plt.xticks([]), plt.yticks([])
plt.subplot(223), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(224), plt.imshow(f2_back, cmap='gray')
plt.title('HPF'), plt.xticks([]), plt.yticks([])
plt.show()

直方图传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import cv2
import numpy as np
from matplotlib import pyplot as plt


# 计算直方图累计概率
def histCalculate(src):
m, n = np.shape(src)
# 直方图
hist = np.zeros(256, dtype=np.float32)
# 累积直方图
cumhist = np.zeros(256, dtype=np.float32)

for i in range(m):
for j in range(n):
hist[src[i, j]] += 1
cumhist[0] = hist[0]
for i in range(1, 256):
cumhist[i] = cumhist[i - 1] + hist[i]

return cumhist / (m * n) # 累积直方图概率

# 直方图规定化
def histNormalize(img1, img2):
# 代匹配直方图
img1Pro = histCalculate(img1)
# 参考直方图
img2Pro = histCalculate(img2)

correspondValue = np.zeros(256, dtype=np.uint8)
# 直方图传递
for i in range(256):
diff = np.abs(img1Pro[i] - img2Pro[i])
matchValue = i
for j in range(256):
if np.abs(img1Pro[i] - img2Pro[j]) < diff:
diff = np.abs(img1Pro[i] - img2Pro[j])
matchValue = j
correspondValue[i] = matchValue
return cv2.LUT(img1, correspondValue)

img1 = cv2.imread('source/detect2.jpg', 0)
img1 = cv2.resize(img1, (640, 640 * img1.shape[0] // img1.shape[1]))
img2 = cv2.imread('source/detect1.jpg', 0)
img2 = cv2.resize(img2, (640, 640 * img1.shape[0] // img1.shape[1]))
# print(img1.shape, img2.shape)

img_out = histNormalize(img1, img2)

# 计算直方图
hist1 = cv2.calcHist([img1], channels=[0], mask=None, histSize=[256], ranges=[0, 256])
hist2 = cv2.calcHist([img2], channels=[0], mask=None, histSize=[256], ranges=[0, 256])
hist3 = cv2.calcHist([img_out], channels=[0], mask=None, histSize=[256], ranges=[0, 256])

plt.subplot(2, 3, 1), plt.imshow(img1, cmap='gray')
plt.title('img1'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 3, 2), plt.imshow(img2, cmap='gray')
plt.title('img2'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 3, 3), plt.imshow(img_out, cmap='gray')
plt.title('img_out'), plt.xticks([]), plt.yticks([])

plt.subplot(2, 3, 4), plt.plot(hist1, 'r')
plt.title('img1 hist ratio')
plt.subplot(2, 3, 5), plt.plot(hist2, 'b')
plt.title('img2 hist ratio')
plt.subplot(2, 3, 6), plt.plot(hist3, 'g')
plt.title('img_out hist ratio')
plt.show()

np旋转缩放的手动实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import numpy as np


def scale(img, ratio_h, ratio_w):
try: # 适应3通道图
target_img = np.zeros((int(img.shape[1] * ratio_h), int(img.shape[0] * ratio_w), img.shape[2]), dtype=img.dtype)
except:
target_img = np.zeros((int(img.shape[1] * ratio_h), int(img.shape[0] * ratio_w)), dtype=img.dtype)

convert_matrix = [[ratio_h, 0, 0],
[0, ratio_w, 0],
[0, 0, 1]] # 变换矩阵
convert_matrix_inv = np.linalg.inv(convert_matrix) # 逆变换

for x in range(int(img.shape[1] * ratio_h)):
for y in range(int(img.shape[0] * ratio_w)):
target_pos = [x, y, 1]
origin_pos = np.dot(convert_matrix_inv, target_pos) # 原像素点的位置

if (img.shape[0] >= origin_pos[0] + 1 and img.shape[1] >= origin_pos[1] + 1) and (origin_pos >= 0).all():
x_low = np.floor(origin_pos[0])
x_up = np.ceil(origin_pos[0])
y_low = np.floor(origin_pos[1])
y_up = np.ceil(origin_pos[1]) # 最靠近原像素点的左上点和右下点

s = origin_pos[0] - x_low
t = origin_pos[1] - y_low # 原像素在像素方格中的位置

p1 = img[int(x_low), int(y_low)]
p2 = img[int(x_up), int(y_low)]
p3 = img[int(x_low), int(y_up)]
p4 = img[int(x_up), int(y_up)] # 包裹原像素点的四个像素点的值

# 插值
value = (1 - s) * (1 - t) * p1 + (1 - s) * t * p3 + (1 - t) * s * p2 + s * t * p4
target_img[x, y] = value
return target_img


def rotate(img, angle):
beta = angle / 180 * np.pi
new_width = int(abs(img.shape[1] * np.cos(beta)) + abs(img.shape[0] * np.sin(beta)))
new_height = int(abs(img.shape[1] * np.sin(beta)) + abs(img.shape[0] * np.cos(beta)))

try: # 适应3通道图
target_img = np.zeros((new_height, new_width, img.shape[2]), dtype=img.dtype)
except:
target_img = np.zeros((new_height, new_width), dtype=img.dtype)

rotate_matrix = [[np.cos(beta), np.sin(beta), 0],
[-np.sin(beta), np.cos(beta), 0],
[0, 0, 1]] # 旋转
translation_matrix = [[1, 0, -img.shape[1] / 2],
[0, 1, -img.shape[0] / 2],
[0, 0, 1]] # 平移
convert_matrix = np.dot(rotate_matrix, translation_matrix) # 目标变换矩阵
convert_matrix_inv = np.linalg.inv(convert_matrix) # 逆矩阵

for x in range(new_width):
for y in range(new_height):
# 由于之前是先转换后再加上宽高的一半得到的坐标,所以此处做逆运算时就要先减去宽高的一半
target_pos = [int(x - new_width / 2), int(y - new_height / 2), 1]
origin_pos = np.dot(convert_matrix_inv, target_pos)

if (img.shape[0] > origin_pos[0] + 1 and img.shape[1] > origin_pos[1] + 1) and (origin_pos >= 0).all():
x_low = np.floor(origin_pos[0])
x_up = np.ceil(origin_pos[0])
y_low = np.floor(origin_pos[1])
y_up = np.ceil(origin_pos[1]) # 最靠近原像素点的左上点和右下点

s = origin_pos[0] - x_low
t = origin_pos[1] - y_low # 原像素在像素方格中的位置

p1 = img[int(x_low), int(y_low)]
p2 = img[int(x_up), int(y_low)]
p3 = img[int(x_low), int(y_up)]
p4 = img[int(x_up), int(y_up)] # 包裹原像素点的四个像素点的值

# 插值
value = (1 - s) * (1 - t) * p1 + (1 - s) * t * p3 + (1 - t) * s * p2 + s * t * p4
target_img[x, y] = value
return target_img

def part3_1():
img0 = cv2.imread('source/work2/lena.jpg')
img = scale(img0, ratio_h=1.6, ratio_w=2.3) # 宽放大2.3, 高放大1.6
print(img0.shape, img.shape) # (256, 256, 3) -> (512, 512, 3)
cv2.imshow("img", img)
cv2.waitKey()


def part1():
img = np.array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])
print(scale(img, 2.3, 1.6))


def part2():
img = np.array([[59, 60, 58, 57],
[61, 59, 59, 57],
[62, 59, 60, 58],
[59, 61, 60, 56]])
print(rotate(img, 45))


def part3_1():
img0 = cv2.imread('source/work2/lena.jpg')
img = scale(img0, ratio_h=1.6, ratio_w=2.3) # 宽放大2.3, 高放大1.6
print(img0.shape, img.shape) # (256, 256, 3) -> (512, 512, 3)
cv2.imshow("img", img)
cv2.waitKey()


def part3_2():
img0 = cv2.imread('source/work2/lena.jpg')
img = rotate(img0, -45) # 逆时针旋转45
print(img0.shape, img.shape) # (256, 256, 3) -> (362, 362, 3)
cv2.imshow("img", img)
cv2.waitKey()


if __name__ == '__main__':
part1()
part2()
part3_1()
part3_2()