一、LBP提取图像特征
1.LBP特征的描述
原始的 LBP 算子定义为在 3*3 的窗口内,以窗口中心像素为阈值,将相邻的 8 个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为 1,否则为 0。这样,3*3 邻域内的 8 个点经比较可产生 8 位二进制数(通常转换为十进制数即 LBP 码,共 256 种),即得到该 窗口中心像素点的 LBP 值,并用这个值来反映该区域的纹理信息。如下图所示:
2.旋转不变LBP算子
基础 LBP 特征具有灰度不变性,但不具备旋转不变性,进行扩展,我对得到的 LBP 字符串进行 切片存储在数组中,获取旋转圆形邻域内的 LBP 特征,找到最小的 LBP 值,再将 LBP 值转换为整数型。
旋转不变LBP代码
def lbp(img,i,j):#计算8邻域内特征值
sum = ''#字符串来存取lbp值
if img[i-1,j] > img[i,j]:#左边点
sum += str(1)
else:
sum += str(0)
if img[i-1,j+1] > img[i,j]:#左上方
sum += str(1)
else:
sum += str(0)
if img[i-1,j-1] > img[i,j]:#左下方
sum += str(1)
else:
sum += str(0)
if img[i,j-1] > img[i,j]:#下方点
sum += str(1)
else:
sum += str(0)
if img[i+1,j-1] > img[i,j]:#右下方
sum += str(1)
else:
sum += str(0)
if img[i+1,j] > img[i,j]:#右边点
sum += str(1)
else:
sum += str(0)
if img[i+1,j+1] > img[i,j]:#右上方
sum += str(1)
else:
sum += str(0)
if img[i,j+1] > img[i,j]:#上方点
sum += str(1)
else:
sum += str(0)
#找出最小lbp值,构造旋转不变性
tem = []#创建列表存储8个lbp值,找最小的
for i in range(len(sum)):
sum = sum[1:] + sum[0]#通过切片改变数据位置来改变数值
tem.append(sum)
sum = min(tem)#最小的值
return sum
3.等价模式LBP
在实际图像中,绝大多数 LBP 模式最多只包含两次从 1 到 0 或从 0 到 1 的跳变。当某个 LBP 所 对应的循环二进制数从 0 到 1 或从 1 到 0 最多有两次跳变时,该 LBP 所对应的二进制就称为一个等 价模式类。如 00000000(0 次跳变),00000111(只含一次从 0 到 1 的跳变),10001111(先由 1 跳到 0, 再由 0 跳到 1,共两次跳变)都是等价模式类。除等价模式类以外的模式都归为另一类,称为混合模 式类,例如 10010111(共四次跳变)。通过这样的改进,二进制模式的种类大大减少,而不会丢失任 何信息。我计算得出采样点数为 8 时,共有 58 种特征值,统计二进制 LBP 跳变次数,当跳变次数>2 时 LBP 值为 58,否则为其映射到的位置号。
通过等价模式可以使特征向量的维数减少,使得数据量减少的情况下能最好的代表图片的信息,并且减少高频噪声带来的影响。
3.1等价模式LBP代码
#统计二进制lbp跳变次数
def equ_lbp(num):
tem = 0
for i in range(len(num)-1):
if num[i] == num[i+1]:
tem = tem
else:
tem += 1
return tem
#计算等价lbp,当跳变<=2时lbp值映射到它的位置号,当跳变>2时lbp值为58
"""
#计算lbp值映射的等价值
x = np.arange(0,256,1)
#生成0-256对应的二进制数
bin_x = []
for i in range(256):
num = bin(x[i])#变为二进制
num = num[2:]
if len(num) < 8:#bin函数转换得到的二进制有些长度不是8位,需要在前加0增长
num = (8-len(num)) * str(0) + num
else:
num = num
bin_x.append(num)
print(bin_x)
#统计跳变次数
def count_jump_change(num):
count = 0
for m in range(7):
if num[m] == num[m+1]:
count = count
else:
count +=1
return count
#求每个二进制数对应的等价值
dec_num = []
for j in range(256):
num = bin_x[j]
res = 0
count = count_jump_change(num)#得跳变数
if count <= 2:
for n in range(8):
res += int(num[n]) * np.power(2,len(num)-n-1) # 对应位置乘以对应的2的阶乘
else:
res = 58
dec_num.append(res)#存取等价值
y = np.array(dec_num)
y = np.unique(y)#去除重复数据
y = np.delete(y,np.where(y==58))#删除58这个数值
"""
#y为上面三引号所注释程序计算得出的等价模式的58种特征值,从小到大排序
y = np.array([0, 1, 2, 3, 4, 6, 7, 8, 12, 14, 15, 16, 24, 28, 30, 31, 32, 48, 56, 60, 62, 63, 64, 96,
112, 120, 124, 126, 127, 128, 129, 131, 135, 143, 159, 191, 192, 193, 195, 199, 207, 223,
224, 225, 227, 231, 239, 240, 241, 243, 247, 248, 249, 251, 252, 253, 254, 255])
#将lbp二进制值转换成十进制值
def bin_to_dec(bin):
#当跳变<=2时lbp值映射到它的位置号
if equ_lbp(bin) <= 2:
res = 0
for i in range(8):
res += int(bin[i]) * np.power(2 , len(bin)-i-1)#对应位置乘以对应的2的阶乘
loc = np.where(y==res)
res = loc[0][0]
#当跳变>2时lbp值为58
else:
res = 58
return res
def uniform_LBP(img):
img_array = np.zeros(img.shape,np.uint8)#创建等图片大小的矩阵
for i in range(img_array.shape[0] - 1):
for j in range(img_array.shape[1] - 1):
img_array[i,j] = bin_to_dec(lbp(img,i,j))#对应位置换成lbp值
return img_array
3.2旋转不变等价LBP效果及其直方图展示
二、PCA降低维度
1.PCA简介
特征提取与处理里面,涉及高维特征向量的问题往往容易陷入维度灾难。随着数据集维度的增 加,算法学习需要的样本数量呈指数级增加。有些应用中,遇到这样的大数据是非常不利的,而且 从大数据集中学习需要更多的内存和处理能力。 主成分分析也称为卡尔胡宁-勒夫变换,是一种用于探索高维数据结构的技术。PCA 通常用于高 维数据集的探索与可视化。还可以用于数据压缩,数据预处理等。PCA 可以把可能具有相关性的高 维变量合成线性无关的低维变量,称为主成分。新的低维数据会尽可能地保留原始数据的变量。 PCA 将数据投射到一个低维子空间实现降维。例如,二维数据集降维就是把点投射成一条线, 数据集的每个样本都可以用一个值表示,不需要两个值。三维数据集可以降成二维,就是把变量映 射成一个平面。一般情况下,n 维数据集可以通过映射降成 k 维子空间,其中 k 是选取的主成分数目。
2.PCA步骤
1)将原始数据按列组成 n 行 m 列矩阵 X
2)将 X 的每一行(代表一个属性字段)进行零均值化,即减去这一行的均值
3)求出协方差矩阵
4)求出协方差矩阵的特征值及对应的特征向量 r
5)将特征向量按对应特征值大小从上到下按行排列成矩阵,取前 k 行组成矩阵 P
6)即为降维到 k 维后的数据
3.PCA代码
def PCA(array,mean,k):
#1.去中心化,即每一位特征减去平均值
decen_array = array - mean
#2.计算协方差矩阵
covar_matrix = np.cov(decen_array,rowvar=False)
#3.求解协方差矩阵的特征值和特征向量
featValue, featVec = np.linalg.eig(covar_matrix)
#4.对特征值从大到小排序,选择其中最大的 k 个;并与其对应的 k 个特征向量组成特征向量矩阵
index = np.argsort(-featValue)#按照特征值从小到大排序
selectVec = np.matrix(featVec.T[index[:k]])#将列向量转为横向量后选择 k 个
#5.将数据投影到选取的特征向量上
data = np.dot(decen_array,selectVec.T)
return data
三、MLP多层感知机对图片分类
1.MLP简介
多层感知机的一个重要特点就是多层,层与层之间是全连接的,我们将第一层称之为输入层, 最后一层称之有输出层,中间的层称之为隐层。MLP 并没有规定隐层的数量,因此可以根据各自的 需求选择合适的隐层层数。且对于输出层神经元的个数也没有限制。 使用激活函数,能够给神经元引入非线性因素,使得神经网络可以任意逼近任何非线性函数, 这样神经网络就可以利用到更多的非线性模型中。
2.MLP代码
MLP参数:
**MLPClassifier( hidden_layer_sizes=(100,),第 i 个元素表示第 i 个隐藏层的神经元个数 activation=’relu’, 激活函数,整流后的线性单位函数 **
**solver=’adam’, 权重优化的求解器 **
alpha=0.0001, 惩罚参数
**batch_size=’auto’, 随即优化的 **
**minibatch 大小 **
**learning_rate=’constant’, 恒定学习率 **
**learning_rate_init=0.001,初始学习率,控制更新权重的步长) **
#构建模型
mlp = MLPClassifier(solver='adam',activation='relu',alpha=1e-4,hidden_layer_sizes=(50,50),random_state=1,max_iter=100,verbose=True)
#训练模型
mlp.fit(x_train,y_train)
test_pre = mlp.predict(x_test)#预测测试集
print("solver=adam,activation=relu:测试集准确率:%.4f"%metrics.accuracy_score(y_test,test_pre))
print("solver=adam,activation=relu:测试集f1分数:%.4f"%metrics.f1_score(y_test,test_pre,average='micro'))
print(classification_report(y_test,test_pre,target_names=['car','dog','face','snake','0']))
print("混淆矩阵")
print(metrics.confusion_matrix(y_test,test_pre))
2.1MLP训练结果
2.2模型评估
**准确率分数:metrics.accuracy_score(y_true, y_pred, normalize=True, sample_weight=None) **
**F1分数: metrics.f1_score(y_true, y_pred, labels=None, pos_label=1, average=’binary’, sample_weight=None) **
混淆矩阵:metrics.confusion_matrix(y_true,y_pred, labels=None, sample_weight=None)
四、完整代码
包括图片读取和计算后的数据保存步骤
import copy
import matplotlib.pyplot as plt
import cv2
import numpy as np
import os
import glob
import warnings
from sklearn import svm
from sklearn import metrics
from sklearn.metrics import classification_report
import pandas as pd
from sklearn.neural_network import MLPClassifier
warnings.filterwarnings('ignore')#忽视警告
path = "E:\\Python\\end_work\\picture\\data\\"
#读取四个图片集中图片名字
car_data = os.listdir(path + "car")
dog_data = os.listdir(path + "dog")
face_data = os.listdir(path + "face")
snake_data = os.listdir(path + "snake")
# 获取四类图片集中图片数量,好取训练集和测试集
# print(len(car_data))
# print(len(dog_data))
# print(len(face_data))
# print(len(snake_data))
#读取图片并转换为灰度图,resize为256*256
def trans(img):#此处name为字符串类型
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # 转换为灰度图
img = cv2.resize(img, (256, 256)) # 改变图片大小为256*256
return img
def lbp(img,i,j):#计算8邻域内特征值
sum = ''#字符串来存取lbp值
if img[i-1,j] > img[i,j]:#左边点
sum += str(1)
else:
sum += str(0)
if img[i-1,j+1] > img[i,j]:#左上方
sum += str(1)
else:
sum += str(0)
if img[i-1,j-1] > img[i,j]:#左下方
sum += str(1)
else:
sum += str(0)
if img[i,j-1] > img[i,j]:#下方点
sum += str(1)
else:
sum += str(0)
if img[i+1,j-1] > img[i,j]:#右下方
sum += str(1)
else:
sum += str(0)
if img[i+1,j] > img[i,j]:#右边点
sum += str(1)
else:
sum += str(0)
if img[i+1,j+1] > img[i,j]:#右上方
sum += str(1)
else:
sum += str(0)
if img[i,j+1] > img[i,j]:#上方点
sum += str(1)
else:
sum += str(0)
#找出最小lbp值,构造旋转不变性
tem = []#创建列表存储8个lbp值,找最小的
for i in range(len(sum)):
sum = sum[1:] + sum[0]#通过切片改变数据位置来改变数值
tem.append(sum)
sum = min(tem)#最小的值
return sum
#统计二进制lbp跳变次数
def equ_lbp(num):
tem = 0
for i in range(len(num)-1):
if num[i] == num[i+1]:
tem = tem
else:
tem += 1
return tem
#计算等价lbp,当跳变<=2时lbp值映射到它的位置号,当跳变>2时lbp值为58
"""
#计算lbp值映射的等价值
x = np.arange(0,256,1)
#生成0-256对应的二进制数
bin_x = []
for i in range(256):
num = bin(x[i])#变为二进制
num = num[2:]
if len(num) < 8:#bin函数转换得到的二进制有些长度不是8位,需要在前加0增长
num = (8-len(num)) * str(0) + num
else:
num = num
bin_x.append(num)
print(bin_x)
#统计跳变次数
def count_jump_change(num):
count = 0
for m in range(7):
if num[m] == num[m+1]:
count = count
else:
count +=1
return count
#求每个二进制数对应的等价值
dec_num = []
for j in range(256):
num = bin_x[j]
res = 0
count = count_jump_change(num)#得跳变数
if count <= 2:
for n in range(8):
res += int(num[n]) * np.power(2,len(num)-n-1) # 对应位置乘以对应的2的阶乘
else:
res = 58
dec_num.append(res)#存取等价值
y = np.array(dec_num)
y = np.unique(y)#去除重复数据
y = np.delete(y,np.where(y==58))#删除58这个数值
"""
#y为上面三引号所注释程序计算得出的等价模式的58种特征值,从小到大排序
y = np.array([0, 1, 2, 3, 4, 6, 7, 8, 12, 14, 15, 16, 24, 28, 30, 31, 32, 48, 56, 60, 62, 63, 64, 96,
112, 120, 124, 126, 127, 128, 129, 131, 135, 143, 159, 191, 192, 193, 195, 199, 207, 223,
224, 225, 227, 231, 239, 240, 241, 243, 247, 248, 249, 251, 252, 253, 254, 255])
#将lbp二进制值转换成十进制值
def bin_to_dec(bin):
#当跳变<=2时lbp值映射到它的位置号
if equ_lbp(bin) <= 2:
res = 0
for i in range(8):
res += int(bin[i]) * np.power(2 , len(bin)-i-1)#对应位置乘以对应的2的阶乘
loc = np.where(y==res)
res = loc[0][0]
#当跳变>2时lbp值为58
else:
res = 58
return res
def uniform_LBP(img):
img_array = np.zeros(img.shape,np.uint8)#创建等图片大小的矩阵
for i in range(img_array.shape[0] - 1):
for j in range(img_array.shape[1] - 1):
img_array[i,j] = bin_to_dec(lbp(img,i,j))#对应位置换成lbp值
return img_array
# 画lbp直方图
def show_hist(array):
hist = cv2.calcHist([array],[0],None,[256],[0,256])#计算直方图函数
hist = cv2.normalize(hist,hist)#归一化数组
plt.figure(figsize=(8,4))#定义画布长宽
plt.plot(hist,color='b')
plt.xlim([0,60]),plt.title("LBP_Hist")
plt.show()
show_hist(uniform_LBP(img))
#将旋转不变等价lbp值转换为一维数组(对应0-58的次数)
def change_lbp_to_odim(lbp):
odim = []
for i in range(59):
loc = np.where(lbp==i)#获取lbp=i的位置
lenth = len(lbp[loc])
odim.append(lenth)#存取进列表
odim = np.array(odim)
return odim
#读取每个类别下图片的lbp并合成一个x*59的数组,x为图片张数
def read_img(category,data_name,num):#传入两个参数,category是图片种类(字符型),name是图片名字
lenth = len(data_name)
t_len = int(num * lenth)#每个图片集中取num倍长作为训练集
t_array = np.random.randint(0,lenth,t_len)#生成长度为训练集长度的随机数组
data = []#创建列表用于存取每张图片的一维数组
for i in range(t_len):
loc = t_array[i]#随机位置序列
img = cv2.imread(path + category + "\\" + data_name[loc],cv2.IMREAD_GRAYSCALE)#读取图片
# img = cv2.resize(img, (256, 256))
img = trans(img)#将图片变为大小为256*256的灰度图
lbp = change_lbp_to_odim(uniform_LBP(img))
data.append(lbp)
data = np.array(data)
return data
#计算每个类别的lbp均值,后面作为测试集的均值
def class_lbp(array):
row, col = array.shape
mean = np.array([np.mean(array[:,i]) for i in range(col)])
return mean
#计算每类训练集的均值
car_mean = class_lbp(read_img("car",car_data,0.35))#3000多张车图片数据均值
dog_mean = class_lbp(read_img("dog",dog_data,0.43))#3000多张狗图片数据均值
face_mean = class_lbp(read_img("face",face_data,0.49))#3000多张脸图片数据均值
snake_mean = class_lbp(read_img("snake",snake_data,0.35))#3000多张蛇图片数据均值
#将提取的LBP特征进行降维处理,k为想要降到的维度
#基于特征值分解协方差矩阵实现PCA
def PCA(array,mean,k):
#1.去中心化,即每一位特征减去平均值
decen_array = array - mean
#2.计算协方差矩阵
covar_matrix = np.cov(decen_array,rowvar=False)
#3.求解协方差矩阵的特征值和特征向量
featValue, featVec = np.linalg.eig(covar_matrix)
#4.对特征值从大到小排序,选择其中最大的k个;并与其对应的k个特征向量组成特征向量矩阵
index = np.argsort(-featValue)#按照特征值从小到大排序
selectVec = np.matrix(featVec.T[index[:k]])#将列向量转为横向量后选择k个
#5.将数据投影到选取的特征向量上
data = np.dot(decen_array,selectVec.T)
return data
# ["car":1 ; "dog":2; "face":3; "snake":4]
# 将每类图片贴上标签,存在csv文件中,方便后面创建训练集和测试集
def save_data_to_csv(data,num,name):#num为图片类别对应的数字,name为图片集名字(字符型)
col = data.shape[0]
arr_mark = np.ones((col,1)) * num
data = np.concatenate((data,arr_mark),axis=1)#将标签和数组合并在一起
# data = np.transpose(data)#将数组转置,由(k+1)*x变为x*(k+1)
file_path = path + name
data = pd.DataFrame(data).to_csv(file_path)
return data,file_path
#获取训练集数据和训练集路径
car_train,car_train_path = save_data_to_csv(PCA(read_img("car",car_data,0.35),car_mean,9),1,"car_train.csv")
dog_train,dog_train_path = save_data_to_csv(PCA(read_img("dog",dog_data,0.43),dog_mean,9),2,"dog_train.csv")
face_train,face_train_path = save_data_to_csv(PCA(read_img("face",face_data,0.49),face_mean,9),3,"face_train.csv")
snake_train,snake_train_path = save_data_to_csv(PCA(read_img("snake",snake_data,0.35),snake_mean,9),4,"snake_train.csv")
#获取测试集数据和测试集路径
car_test,car_test_path = save_data_to_csv(PCA(read_img("car",car_data,0.058),car_mean,9),1,"car_test.csv")
dog_test,dog_test_path = save_data_to_csv(PCA(read_img("dog",dog_data,0.071),dog_mean,9),2,"dog_test.csv")
face_test,face_test_path = save_data_to_csv(PCA(read_img("face",face_data,0.081),face_mean,9),3,"face_test.csv")
snake_test,snake_test_path = save_data_to_csv(PCA(read_img("snake",snake_data,0.058),snake_mean,9),4,"snake_test.csv")
#合并训练集和测试集
#要拼接的文件夹及其完整路径
trp ="E:\\Python\\end_work\\picture\\train_data"#训练集
tep = "E:\\Python\\end_work\\picture\\test_data"#测试集
#合并后要保存的文件名
train = "train.csv"
test = "test.csv"
def link_data(file_path,save_name):#传入参数为文件路径和合并后文件名
os.chdir(file_path)#修改当前工作目录
csv_list = glob.glob('*.csv')#查看同文件夹下csv文件数
#遍历列表中各个csv文件,并追加到合并后的文件
for i in csv_list:
fr = open(i,'rb').read()
with open(file_path+'\\'+ save_name,'ab') as f:#保存结果
f.write(fr)
return file_path+'\\'+ save_name
train_file = link_data(trp,train)#训练集合并,获取训练集地址
test_file = link_data(tep,test)#测试集合并,获取测试集地址
train_data = pd.read_csv(train_file,header=None,index_col=0)
test_data = pd.read_csv(test_file,header=None,index_col=0)
train_data = np.array(train_data)
test_data = np.array(test_data)
#获取训练集和测试集的样本和特征
"训练集"
x_train = train_data[1:,1:11]
y_train = train_data[1:,-1]
"测试集"
x_test = test_data[1:,1:11]
y_test = test_data[1:,-1]
# print("多层感知机")
#构建模型
mlp = MLPClassifier(solver='adam',activation='relu',alpha=1e-4,hidden_layer_sizes=(50,50),random_state=1,max_iter=100,verbose=True)
#训练模型
mlp.fit(x_train,y_train)
test_pre = mlp.predict(x_test)#预测测试集
print("solver=adam,activation=relu:测试集准确率:%.4f"%metrics.accuracy_score(y_test,test_pre))
print("solver=adam,activation=relu:测试集f1分数:%.4f"%metrics.f1_score(y_test,test_pre,average='micro'))
print(classification_report(y_test,test_pre,target_names=['car','dog','face','snake','0']))
print("混淆矩阵")
print(metrics.confusion_matrix(y_test,test_pre))
参考感谢:
[1]:LBP原理
版权归原作者 晴一千天 所有, 如有侵权,请联系我们删除。