OpenCV-python提取特征(批量处理数据)

文章目录

  • 一、提取特征
  • 二、保存特征点

一、提取特征

傅里叶描述子特征点进行提取
提取手部轮廓原理:

  • 加载图像(opencv,截图保存saveROI)
  • 肤色检测(YCrCb颜色空间的Cr分量+Otsu法阈值分割算法)
  • 图像去噪(numpy二值化处理)
  • 轮廓提取(canny检测,cv2.findContours->傅里叶描述子Laplacian)
  • 二次去噪(numpy二值化处理)
  • 绘制轮廓(cv2.drawContours)
    OpenCV-python提取特征(批量处理数据)_第1张图片

由视频中保存手势图像参考:python+opencv提取视频中手部轮廓
图片数据集增强参考:OpenCV-python增强数据集(几何变换原理)

现有手势库手势“1”测试图的147份图片:
OpenCV-python提取特征(批量处理数据)_第2张图片
目录:
OpenCV-python提取特征(批量处理数据)_第3张图片

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

二、保存特征点

第一种方式:txt文本格式保存,命名格式为x_i
OpenCV-python提取特征(批量处理数据)_第4张图片

feature.py 特征提取,内容:

  • 图片路径
  • 描述子个数
  • 取每张图的第二个点为参考描述子点
  • 其余31个各描述子点与参考描述点的比值
    OpenCV-python提取特征(批量处理数据)_第5张图片
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('--------------------------------------')

其中,添加噪声的扩展图像明显差值比较大。(扩展模型时,选择性剔除)
OpenCV-python提取特征(批量处理数据)_第6张图片
另一种方式是:
求取32个特征点的平均值保存到csv中:
(有两个手势集,即保存为2行32列的表格)
OpenCV-python提取特征(批量处理数据)_第7张图片在这里插入图片描述

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()

总结,每次图像识别的过程都是:

  • 准备过程:
    导入图像、图像处理、提取特征值、特征值处理、模型训练(制定判别参数)
  • 识别:
    导入图像、图像处理、提取特征值、特征值与判别参数对比、输出结果

接下来就是:对模型训练的思路:

  1. 构建CNN神经网络创建handpose_Keras.h5模型

    参考学习:10分钟教会你搭建CNN模型破解网站验证码

  2. 构建SVM分类训练创建handpose_model.m模型

    Day 14:SVM案例

  3. 计算特征数据集的欧氏距离作对比

    Dlib模型人脸特征检测原理及demo

这里另起一文说明,关于此部分OpenCV-python手语识别项目的相关代码后面都会放在gitee上。

你可能感兴趣的:(Python学习,#,Python)