傅里叶描述子特征点进行提取
提取手部轮廓原理:
由视频中保存手势图像参考:python+opencv提取视频中手部轮廓
图片数据集增强参考:OpenCV-python增强数据集(几何变换原理)
fourierDesciptor.py
注:(此部分代码参考于:基于OpenCV的手势识别完整项目(Python3.7))
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import cv2
import numpy as np
MIN_DESCRIPTOR = 32 # surprisingly enough, 2 descriptors are already enough
# 计算傅里叶描述子
def fourierDesciptor(res):
# Laplacian算子进行八邻域检测
gray = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY)
dst = cv2.Laplacian(gray, cv2.CV_16S, ksize=3)
Laplacian = cv2.convertScaleAbs(dst)
contour = find_contours(Laplacian) # 提取轮廓点坐标
contour_array = contour[0][:, 0, :] # 注意这里只保留区域面积最大的轮廓点坐标
ret_np = np.ones(dst.shape, np.uint8) # 创建黑色幕布
ret = cv2.drawContours(
ret_np, contour[0], -1, (255, 255, 255), 1) # 绘制白色轮廓
contours_complex = np.empty(contour_array.shape[:-1], dtype=complex)
contours_complex.real = contour_array[:, 0] # 横坐标作为实数部分
contours_complex.imag = contour_array[:, 1] # 纵坐标作为虚数部分
fourier_result = np.fft.fft(contours_complex) # 进行傅里叶变换
#fourier_result = np.fft.fftshift(fourier_result)
descirptor_in_use = truncate_descriptor(fourier_result) # 截短傅里叶描述子
#reconstruct(ret, descirptor_in_use)
return ret, descirptor_in_use
def find_contours(Laplacian):
# binaryimg = cv2.Canny(res, 50, 200) #二值化,canny检测
h = cv2.findContours(Laplacian, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_NONE) # 寻找轮廓
contour = h[1]
contour = sorted(contour, key=cv2.contourArea,
reverse=True) # 对一系列轮廓点坐标按它们围成的区域面积进行排序
return contour
# 截短傅里叶描述子
def truncate_descriptor(fourier_result):
descriptors_in_use = np.fft.fftshift(fourier_result)
# 取中间的MIN_DESCRIPTOR项描述子
center_index = int(len(descriptors_in_use) / 2)
low, high = center_index - \
int(MIN_DESCRIPTOR / 2), center_index + int(MIN_DESCRIPTOR / 2)
descriptors_in_use = descriptors_in_use[low:high]
descriptors_in_use = np.fft.ifftshift(descriptors_in_use)
return descriptors_in_use
# 由傅里叶描述子重建轮廓图
def reconstruct(img, descirptor_in_use):
#descirptor_in_use = truncate_descriptor(fourier_result, degree)
#descirptor_in_use = np.fft.ifftshift(fourier_result)
#descirptor_in_use = truncate_descriptor(fourier_result)
# print(descirptor_in_use)
contour_reconstruct = np.fft.ifft(descirptor_in_use)
contour_reconstruct = np.array([contour_reconstruct.real,
contour_reconstruct.imag])
contour_reconstruct = np.transpose(contour_reconstruct)
contour_reconstruct = np.expand_dims(contour_reconstruct, axis=1)
if contour_reconstruct.min() < 0:
contour_reconstruct -= contour_reconstruct.min()
contour_reconstruct *= img.shape[0] / contour_reconstruct.max()
contour_reconstruct = contour_reconstruct.astype(np.int32, copy=False)
black_np = np.ones(img.shape, np.uint8) # 创建黑色幕布
black = cv2.drawContours(
black_np, contour_reconstruct, -1, (255, 255, 255), 3) # 绘制白色轮廓
#cv2.imshow("contour_reconstruct", black)
# cv2.imwrite('recover.png',black)
return black
feature.py 特征提取,内容:
import fourierDescriptor as fd
import cv2
import numpy as np
import os
path = './feature/'
path_img = './image/1_test/'
if not os.path.exists(path):
os.mkdir(path)
list = os.listdir(path_img)
if __name__ == "__main__":
for i in range(1, 2): # 手势1
# 将文件夹中照片的特征值提取出来,写入csv
for j in range(0, len(list)): # 图片147张
out_path = os.path.join(path_img, list[j])
print("Image url is:" + out_path)
roi = cv2.imread(out_path)
# 返回单张图的特征值
# 第一个参数为图像,第二个参数为傅里叶描述子(32位)
ret, descirptor_in_use = fd.fourierDesciptor(roi)
# 计算欧式距离
descirptor_in_use = abs(descirptor_in_use)
# 设置参考点
temp = descirptor_in_use[1]
print("Descirptor length is:" + str(len(descirptor_in_use)))
print("Reference point is:" + str(temp))
print("Eigenvalue division is:")
fd_name = path + str(i) + '_' + str(j) + '.txt'
with open(fd_name, 'w', encoding='utf-8') as f:
for k in range(1, len(descirptor_in_use)):
# 计算特征值比
x_record = int(100 * descirptor_in_use[k] / temp)
print(x_record, end=" ")
f.write(str(x_record))
f.write(' ')
f.write('\n')
print('\n', i, '_', j, 'success')
print('--------------------------------------')
其中,添加噪声的扩展图像明显差值比较大。(扩展模型时,选择性剔除)
另一种方式是:
求取32个特征点的平均值保存到csv中:
(有两个手势集,即保存为2行32列的表格)
import fourierDescriptor as fd # 提取函数
import cv2
import numpy as np
import os
import csv
path = './feature/'
path_img = './image/data_augmention/'#图片集目录
if not os.path.exists(path):
os.mkdir(path)
# 返回单张图像的 32D 特征
def return_32d_features(path_img):
roi = cv2.imread(path_img)
# 第一个参数为图像,第二个参数为傅里叶描述子(32位)
ret, descirptor_in_use = fd.fourierDesciptor(roi)
# 计算欧式距离
descirptor_in_use = abs(descirptor_in_use)
return descirptor_in_use
# 将文件夹中照片特征提取出来, 写入 CSV
def return_features_mean_gestrueX(path_hands_gestureX):
features_list_gestureX = []
photos_list = os.listdir(path_hands_gestureX)
if photos_list:
for i in range(len(photos_list)):
# 调用return_32d_features()得到32d特征
print("%-40s %-20s" % ("正在读的手势图像 / image to read:",
path_hands_gestureX + "/" + photos_list[i])) # 每个手势
features_32d = return_32d_features(
path_hands_gestureX + "/" + photos_list[i])
# print(features_32d)
# 遇到没有检测出手势的图片跳过
if all(features_32d == 0):
i += 1
else:
features_list_gestureX.append(features_32d)
else:
print("文件夹内图像文件为空 / Warning: No images in " +
path_hands_gestureX + '/', '\n')
# 计算 32D 特征的均值
# N x 32D -> 1 x 32D
if features_list_gestureX:
features_mean_gestureX = np.array(features_list_gestureX).mean(axis=0)
else:
features_mean_gestureX = '0'
return features_mean_gestureX
def write_in_csv():
# 读取某手势所有的手势图像数据
handslist = os.listdir(path_img)
handslist.sort()
with open(path + "features_all.csv", "w", newline="") as csvfile:
writer = csv.writer(csvfile)
for gesture in handslist:
print("##### " + gesture + " #####")
# Get the mean/average features of gestureX, it will be a list with
# a length of 32D
features_mean_gestureX = return_features_mean_gestrueX(
path_img + gesture)
writer.writerow(features_mean_gestureX)
print("特征均值 / The mean of features:", list(features_mean_gestureX))
print('\n')
print("写入成功!保存位置:" + path + "features_all.csv")
if __name__ == "__main__":
write_in_csv()
总结,每次图像识别的过程
都是:
接下来就是:对模型训练的思路:
handpose_Keras.h5
模型
参考学习:10分钟教会你搭建CNN模型破解网站验证码
handpose_model.m
模型
Day 14:SVM案例
Dlib模型人脸特征检测原理及demo
这里另起一文说明,关于此部分OpenCV-python手语识别项目的相关代码后面都会放在gitee上。