常见的特征提取算法主要分为以下3类:
- 基于颜色特征:如颜色直方图、颜色集、颜色矩、颜色聚合向量等;
- 基于纹理特征:如Tamura纹理特征、自回归纹理模型、Gabor变换、小波变换、MPEG7边缘直方图等;
- 基于形状特征:如傅立叶形状描述符、不变矩、小波轮廓描述符等;
目录
1、LBP纹理特征提取算法
LBP(Local Binary Patterns,局部二值模式),它具有旋转不变性和灰度不变性的优点。基本的是3*3窗格的(种模式),经过改进之后出现了圆形LBP算子,表示半径为R的圆形邻域内有P个像素点的LBP算子(种模式)。
LBP算子还有等价模式,定义为:当某个LBP所对应的循环二进制数从0到1或者是从1到0最多有两次跳变时,该LBP所对应的二进制就称为一个等价模式类(P(P-1)+2种模式),除了等价模式类之外的模式都归为混合模式类。这样使得特征向量的维数更少,并且可以减少高频噪声带来的影响。
对LBP算子进行扩展,提出了具有旋转不变性的LBP算子(LBP旋转不变模式),即不断旋转圆形邻域得到一系列初始定义的LBP值,取其最小值作为该邻域的LBP值,表示为。这使得LBP模式的种类进一步减少,是的纹理的识别更加容易,但是丢失了方向信息。
LBP算子还可以与等价模式结合起来,将等价模式类进行旋转得到旋转不变的的等价模式类,舍得类别减小到 P+1 类,所有的非等价模式被归为第 P+1 类。
LBP用于检测的步骤:
- 首先将检测窗口划分为16×16的小区域(cell);
- 对于每个cell中的一个像素,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3*3邻域内的8个点经比较可产生8位二进制数,即得到该窗口中心像素点的LBP值;
- 然后计算每个cell的直方图,即每个数字(假定是十进制数LBP值)出现的频率;然后对该直方图进行归一化处理。
- 最后将得到的每个cell的统计直方图进行连接成为一个特征向量,也就是整幅图的LBP纹理特征向量; 然后利用SVM或者其他机器学习算法进行分类。
代码实现:
import numpy as np
import cv2
import matplotlib.pyplot as plt
import math
# 对图像进行预处理:将每个图像转换为灰度图像
# 先定义计算旋转后灰度值的函数,以保证旋转不变的结果
def value_rotation(num):
value_list = np.zeros((8), np.uint8)
temp = int(num)
value_list[0] = temp
for i in range(7):
temp = ((temp << 1) | (temp // 128)) % 256
value_list[i+1] = temp
return np.min(value_list)
# 等价模式LBP,就是限制一个二进制序列从 0到 1或从 1到 0的跳变次数不超过2次
# 计算跳变次数
def getHopcnt(num):
'''
: param num :8位的整形数,0-255
: return :
'''
if num > 255:
num = 255
elif num < 0:
num = 0
num_b = bin(num)
num_b = str(num_b)[2:]
#补0
if len(num_b) < 8:
temp = []
for i in range(8-len(num_b) ):temp.append( '0')
temp.extend(num_b)
num_b = temp
cnt = 0
for i in range(8):
if i == 0:
former = num_b[-1]
else:
former = num_b[i-1]
if former == num_b[i]:
pass
else:
cnt += 1
return cnt
# 归一化函数,将像素值归一化,并重新投影到新的灰度空间
# 默认最大值为255,最小值为0
def img_max_min_normalization(src, min=0, max=255):
height = src.shape[0]
width = src.shape[1]
if len(src.shape) > 2:
channel = src.shape[2]
else:
channel = 1
src_min = np.min(src)
src_max = np.max(src)
if channel == 1:
dst = np.zeros([height,width], dtype=np.float32)
for h in range(height):
for w in range(width):
dst[h, w] = float(src[h, w] - src_min) / float(src_max - src_min) * (max - min) + min
else:
dst = np.zeros([height,width,channel], dtype=np.float32)
for c in range(channel):
for h in range(height) :
for w in range(width):
dst[h, w, c] = float(src[h, w, c] - src_min) / float(src_max - src_min)* (max - min) + min
return dst
# 旋转不变模式+等价模式LBP
def rotation_invariant_uniform_LBP(src):
table = np.zeros((256), dtype=np. uint8)
temp = 1
for i in range(256):
if getHopcnt(i) <= 2 :
table[i] = temp
temp += 1
height = src.shape[0]
width = src.shape[1]
dst = np.zeros([height, width], dtype=np.uint8)
dst = src.copy()
lbp_value = np.zeros((1, 8), dtype=np.uint8)
neighbours = np.zeros((1, 8), dtype=np.uint8)
for x in range(1, width - 1):
for y in range(1,height - 1):
neighbours[0, 0] = src[y - 1, x - 1]
neighbours[0, 1] = src[y - 1, x]
neighbours[0, 2] = src[y - 1, x + 1]
neighbours[0, 3] = src[y, x - 1]
neighbours[0, 4] = src[y, x + 1]
neighbours[0, 5] = src[y + 1, x - 1]
neighbours[0, 6] = src[y + 1, x]
neighbours[0, 7] = src[y + 1, x + 1]
center = src[y, x]
for i in range(8):
if neighbours[0, i] > center:
lbp_value[0, i] = 1
else:
lbp_value[0, i] = 0
lbp = lbp_value[0, 0] * 1 + lbp_value[0, 1] * 2 + lbp_value[0, 2]* 4 + lbp_value[0, 3] * 8 \
+ lbp_value[0, 4]* 16 + lbp_value[0, 5] * 32 + lbp_value[0, 6]* 64 + lbp_value[0, 0] * 128
dst[y, x] = table[lbp]
dst = img_max_min_normalization(dst)
for x in range(width):
for y in range(height) :
dst[y, x] = value_rotation(dst[y, x])
return dst
# 结果可视化
def disp_test_result(img, gray, dst, mode=0):
'''
: param mode : 0 , opencv 显示图片; 1 , matplotlib 显示图片。
: return:
'''
if mode == 0:
cv2.imshow('src', img)
cv2.imshow('gray', gray)
cv2.imshow('LBP', dst)
cv2.waitKey()
cv2.destroyAllWindows()
else:
plt.figure()
# plt.figure(figsize=(1, 1))
plt.subplot(131)
plt.imshow(img)
plt.title('src')
plt.subplot(132)
plt.imshow(gray, cmap='gray')
plt.title('gray')
plt.subplot(133)
plt.imshow(dst,cmap='gray')
plt.title('LBP')
plt.show()
if __name__ == '__main__' :
img = cv2.imread('test_2.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 将图片变为灰度图像
dst3 = rotation_invariant_uniform_LBP(gray)
disp_test_result(img, gray, dst3, mode=0)
结果展示:
2、Harris角点检测算法
Harris角点,一种显著点,在任何方向上移动小观察窗,导致大的像素变动。
数学模型:偏移(u,v)后窗内图像变化,取E(u,v)大的patch:
代码实现:
import cv2
import numpy as np
# 读取图像
img=cv2.imread('test_2.jpg')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 计算导数
Ix=cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=3)
Iy=cv2.Sobel(gray,cv2.CV_64F,0,1,ksize=3)
# 计算Harris矩阵的三个分量
Ix2=Ix*Ix
Iy2=Iy*Iy
Ixy=Ix*Iy
# 计算窗口内的和
ksize=3
kernel=np.ones((ksize,ksize),np.float32)
Sx2=cv2.filter2D(Ix2,-1,kernel)
Sy2=cv2.filter2D(Iy2,-1,kernel)
Sxy=cv2.filter2D(Ixy,-1,kernel)
# 计算Harris响应函数R
k=0.04
R=(Sx2*Sy2-Sxy*Sxy)-k*((Sx2+Sy2)**2)
# 设定阈值并进行非极大值抑制
thresh=0.01*np.max(R)
radius=5
corners=[]
for y in range(radius,R.shape[0]-radius):
for x in range(radius,R.shape[1]-radius):
if R[y,x]>thresh and R[y,x]== np.max(R[y-radius:y+radius+1,x-radius:x+radius+1]):
corners.append((y,x))
# 在图像中标记角点
for corner in corners:
cv2.circle(img,corner,radius,(0,0,255),-1)
if __name__ == '__main__':
# 显示图像
cv2.imshow('Harris Corners',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果展示:
3、Fast角点检测算法
FAST角点:若某像素点与其周围领域内足够多的像素点处于不同的区域,则该像素点可能为角点。也就是某些属性与众不同,考虑灰度图像,即若该点的灰度值比其周围领域内足够多的像素点的灰度值大或者小,则该点可能为角点。
算法步骤:
- 从图片中选取一个像素P,首先把它的亮度值设为Ip;
- 设定一个合适的阈值t;
- 考虑以该像素点为中心的一个半径等于3像素的离散化的Bresenham圆,这个圆的边界上有16个像素;
- 如果在这个大小为16个像素的圆上有n个连续的像素点,他们的像素值要么都比Ip+t大,要么都比Ip−t小,那么他就是一个角点。n的值可以设置为12或者9。
代码实现:
import cv2
import numpy as np
def non_max_suppression(img,nms_window_size):
# 创建一个全零的同大小矩阵
nms_img = np.zeros_like(img)# 获取图像尺寸
rows, cols = img.shape[:2]
# 定义非极大值抑制窗口的大小
half_size= nms_window_size // 2
# 遍历图像中所有的像素点
for r in range(half_size, rows - half_size):
for c in range(half_size,cols - half_size):
# 获取当前像素点的灰度值
current_pixel = img[r,c]
# 定义当前窗口内灰度值最大的像素点
max_pixel = current_pixel
# 遍历当前窗口内所有的像素点
for i in range(-half_size,half_size + 1):
for j in range(-half_size,half_size + 1):
#如果当前像素点的灰度值大于当前窗口内的最大灰度值
if img[r + i,c + j]> max_pixel:
# 更新最大灰度值
max_pixeL= img[r + i,c + j]
# 如果当前像素点的灰度值等于当前窗口内的最大灰度值
if current_pixel == max_pixel:
# 将当前像素点标记为非极大值抑制
nms_img[r,c]=255
return nms_img
if __name__ == '__main__':
# 读取图像
img = cv2.imread("test_2.jpg")
# Fast角点检测
fast = cv2.FastFeatureDetector_create(threshold=40)
kp = fast.detect(img, None)
# 提取角点坐标
pts = cv2.KeyPoint_convert(kp)
# 绘制角点
img_kp = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0))
# 非极大值抑制
nms_window_size = 5
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
nms_img = non_max_suppression(gray, nms_window_size)
# 将非极大值抑制后的图像转换为彩色图像
nms_img_color = cv2.cvtColor(nms_img, cv2.COLOR_GRAY2BGR)
# 将角点绘制在非极大值抑制后的图像上
for pt in pts:
x, y = pt
cv2.circle(nms_img_color, (int(x), int(y)), 5, (0, 0, 255), 2)
# 显示图像
cv2.imshow("Original Image with Keypoints",img_kp)
cv2.imshow("Non-maximum Suppressed Image", nms_img_color)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果展示: