灰度共生矩阵的纹理特征、颜色特征和形状特征利用KNN进行分类
一、获取合适的纹理特征数据
灰度共生矩阵图像预处理
先降分辨率到552*1080,提升程序运行速度,进行灰度共生矩阵的各项特征进行处理,然后进行数据比对,选出对于图片区分率最高的几组数据,并且设定阈值
# 转换为灰度图像并且降低图片分辨率
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray = cv2.resize(img_gray, (552, 1080))
参考文章:
求灰度共生矩阵:
def glcm(arr, d_x, d_y, gray_level=16):
# 计算并返回归一化后的灰度共生矩阵
max_gray = arr.max()
height, width = arr.shape
arr = arr.astype(np.float64) # 将uint8类型转换为float64,避免数据失真
# 若灰度级数大于gray_level,则将图像的灰度级缩小至gray_level,减小灰度共生矩阵的大小。量化后灰度值范围:0 ~ gray_level - 1
arr = arr * (gray_level - 1) // max_gray
ret = np.zeros([gray_level, gray_level])
for j in range(height - abs(d_y)):
for i in range(width - abs(d_x)):
rows = arr[j][i].astype(int)
cols = arr[j + d_y][i + d_x].astype(int)
ret[rows][cols] += 1
if d_x >= d_y:
# 归一化, 水平方向或垂直方向
ret = ret / float(height * (width - 1))
else:
# 归一化, 45度或135度方向
ret = ret / float((height - 1) * (width - 1))
return ret
# 四个方向的共生矩阵
glcm_0 = glcm(img_gray, 1, 0) # 水平方向
glcm_45 = glcm(img_gray, 1, 1) # 45度方向
glcm_90 = glcm(img_gray, 0, 1) # 垂直方向
glcm_135 = glcm(img_gray, -1, 1) # 135度方向
1.灰度共生矩阵的能量
计算各个角度的平均能量,公式为:
∑
i
∑
j
P
2
(
i
,
j
)
\sum_{i}\sum_{j}P^2(i,j)
i∑j∑P2(i,j)
代码为:
# 计算能量
def power(array):
rows, cols = array.shape
pow = 0
for i in range(rows):
for j in range(cols):
pow += np.square(array[i][j])
return pow
2.对比度
计算各个方向对比度差异,公式为:
∑
i
∑
j
∣
i
−
j
∣
2
P
(
i
,
j
)
\sum_{i}\sum_{j}\lvert{i-j}\rvert{^2}P(i,j)
i∑j∑∣i−j∣2P(i,j)
代码为:
# 计算对比度
def duibidu(array):
rows, cols = array.shape
duibidu = 0
for i in range(rows):
for j in range(cols):
# duibidu += {np.square{abs(i-j)}}*array[i, j]
duibidu += np.square(abs(i - j))*array[i, j]
return duibidu
3.最大概率
计算四个方向里的最大概率,公式为:
M
a
x
i
j
P
(
i
,
j
)
Max_{ij}P(i,j)
MaxijP(i,j)
代码块:
# 计算最大概率值
def maxp(array):
a = array[0, 0]
rows, cols = array.shape
for i in range(rows):
for j in range(cols):
if a <= array[i, j]:
a = array[i, j]
else:
a = a
return a
4.均匀度
计算四个方向分布的均匀度,公式为:
∑
i
∑
j
P
(
i
,
j
)
1
+
∣
i
−
j
∣
\sum_i\sum_j\frac{P(i,j)}{1+\lvert{i-j}\rvert}
i∑j∑1+∣i−j∣P(i,j)
代码块:
# 计算均匀度
def junyundu(array):
rows, cols = array.shape
junyun = 0
for i in range(rows):
for j in range(cols):
junyun += array[i, j]/(1+abs(i-j))
return junyun
5.逆差分矩
四个方向的逆差分矩,公式为:
∑
i
∑
j
P
(
i
,
j
)
∣
i
−
j
∣
2
\sum_i\sum_j\frac{P(i,j)}{\lvert{i-j}\rvert^2}
i∑j∑∣i−j∣2P(i,j)
代码块:
# 计算逆差分矩
def nicha(array):
rows, cols = array.shape
x = 0
for i in range(rows):
for j in range(cols):
if i != j:
x += array[i, j]/(np.square(abs(i-j)))
else:
continue
return x
6.差异方差
计算四个方向的差异方差,公式为:
∑
i
∑
j
∣
i
−
j
∣
P
(
i
,
j
)
\sum_i\sum_j\vert{i-j}\rvert{P(i,j)}
i∑j∑∣i−j∣P(i,j)
代码块:
# 计算差异分差
def chayi(array):
rows, cols = array.shape
a = 0
for i in range(rows):
for j in range(cols):
a += abs(i-j)*array[i, j]
return a
7.熵
计算四个方向的熵值,公式为:
−
∑
i
∑
j
P
(
i
,
j
)
log
2
P
(
i
,
j
)
-\sum_i\sum_jP(i,j)\log_2P(i,j)
−i∑j∑P(i,j)log2P(i,j)
def shang(array):
rows, cols = array.shape
a = 0
for i in range(rows):
for j in range(cols):
if array[i, j] != 0:
a += array[i, j]*(math.log(array[i, j], 2))
else:
continue
return -a
图像数据选取
时间原因选择能量值和熵值作为分类的指标
二、颜色特征
图片转换到HSV空间进行饱和度和明度的获取H和S
# 获取图像的色调和饱和度
def color(image):
rows, cols, channels = image.shape
# 对图像进行预处理后再转换空间,减小误差,把烟叶以外区域设置为纯黑色
for row in range(rows):
for col in range(cols):
if image[row, col, 0] < 30:
image[row][col][0] = 0
image[row][col][1] = 0
image[row][col][2] = 0
# cv2.imshow('black', image)
# cv2.imwrite('black.jpg', image)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 转换为HSV空间图像,uint8
# cv2.imshow('hsv', hsv)
cv2.imwrite('hsv.jpg', hsv)
a = 0
b = 0
c = 0
color = []
# 遍历图像求烟叶区域的平均色调和平均饱和度
for row in range(rows):
for col in range(cols):
if hsv[row][col][1] > 0:
a += 1
b += hsv[row][col][1]
c += hsv[row][col][2]
color.append(round(b/a, 2))
color.append(round(c/a, 2))
return color
三、形状特征
形状特征采用分散度:
f
=
L
2
S
f = \frac{L^2}{S}
f=SL2
轮廓周长为轮廓像素数量,面积为轮廓内像素数量:
# 计算图像的最大轮廓对应的分散度
def fensandu(array):
# 对图像进行二值化,阈值设置为30,阈值为观察灰度值所得
ret, binaryImg = cv2.threshold(array, 30, 255, cv2.THRESH_BINARY)
# 获取图像的所有轮廓
contours, hierarchy = cv2.findContours(binaryImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 设定一个面积初值
a = 0
area = cv2.contourArea(contours[0])
# 遍历所有轮廓,获取最外围轮廓
for c in range(len(contours)):
# 获取最大轮廓的周长面积
if cv2.contourArea(contours[c]) >= area:
a = c
area = cv2.contourArea(contours[c])
arclen = cv2.arcLength(contours[c], True)
# 计算分散度L*L/S,作为函数的返回值
imgnew = cv2.drawContours(img, contours[a], -1, (0, 255, 0), 3)
# cv2.imshow('lunkuo', imgnew)
cv2.imwrite('lunkuo.jpg', imgnew)
fensandu = round(arclen*arclen/area, 2)
return fensandu
四、利用KNN分类器进行数据分类
1.数据预处理
文件格式如下图所示,5种特征,最后一排为数据类别:
对TXT文件进行处理,把TXT文件分割成特征数组和类别数组,便于后续处理:
# open_file.py
import numpy as np
def str_float(value):
# 列表中字符串转化浮点数(读取txt文本可能存在空行,自定义避免报错)
try:
return float(value)
except:
return None
def open_file(filename):
# 读取txt文件 返回分类特征集和类别集
fr = open(filename, encoding='utf-8')
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
returnMat = np.zeros((numberOfLines, 5)) # 创建一个行*5的0矩阵
classLabelVector = []
index = 0
# print(arrayOLines)
for line in arrayOLines:
line = line.strip() # 去除每一行的空格或换行符
listFromLine = line.split("\t") # 按照回车分割每一行为每一个数组
returnMat[index, :] = list(map(str_float, listFromLine[0:5])) # 把每一行数据添加到之前创建的0矩阵之中
classLabelVector.append((float(listFromLine[-1])))
index += 1
return returnMat, classLabelVector
2.进行数据的监督学习
KNN进行数据分类
from numpy import np
# KNN分类算法函数定义
def kNNClassify(newInput, dataSet, labels, k):
numSamples = dataSet.shape[0] # shape[0]表示行数
# # step 1: 计算距离
diff = tile(newInput, (numSamples, 1)) - dataSet # 按元素求差值
# print(diff)
squaredDiff = diff ** 2 # 将差值平方
squaredDist = sum(squaredDiff, axis=1) # 按行累加
distance = squaredDist ** 0.5 # 将差值平方和求开方,即得距离
# print(distance)
# # step 2: 对距离排序
# argsort() 返回排序后的索引值
sortedDistIndices = argsort(distance)
# print(distance)
# print(sortedDistIndices)
classCount = {} # define a dictionary (can be append element)
for i in range(k):
# # step 3: 选择k个最近邻
voteLabel = labels[sortedDistIndices[i]]
# print(sortedDistIndices[i])
# # step 4: 计算k个最近邻中各类别出现的次数
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
# print(classCount)
# # step 5: 返回出现次数最多的类别标签
maxCount = 0
for key, value in classCount.items():
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex