当前位置: 首页 > article >正文

【机器学习实战中阶】使用Python和OpenCV进行手语识别

在这里插入图片描述

手语识别与机器学习

项目构思精要:本项目旨在帮助聋哑人,通过创建一个能够识别手语的检测器,促进他们与他人之间的交流。此手语识别系统利用机器学习技术,为聋哑人群体提供了一个宝贵的沟通工具。

使用 Python 和 OpenCV 进行手语识别

BY DATAFLAIR TEAM

多种技术进步和大量研究致力于帮助聋哑人,为了进一步促进这一事业,深度学习和计算机视觉也能够起到重要作用。这对手语识别尤其有用,因为并不是每个人都能理解手语。通过这项技术,我们可以延伸到创建自动编辑器,使得聋哑人能够仅通过手势轻松书写。

项目概览
在这个手语识别项目中,我们创建了一个可以检测从1到10的手势识别器,这个项目很容易扩展至更多其他的手势和动作,包括字母表上的手势。

本项目采用 Python 的 OpenCV 和 Keras 模块开发。

先决条件
手语识别项目所需的软件环境与库包括:

  • Python (3.7.4)
  • IDE (Jupyter)
  • Numpy (版本 1.16.5)
  • cv2 (OpenCV) (版本 3.4.2)
  • Keras (版本 2.3.1)
  • Tensorflow (Keras 后端以及图像预处理所依赖的框架) (版本 2.0.0)

下载手语识别项目代码
请下载机器学习手语识别项目源代码:
链接: 使用Python和OpenCV进行手语识别 源代码 sign-language-recognition-project

手语识别项目的开发分为三个部分:

  1. 创建数据集
  2. 在捕获的数据集上训练 CNN
  3. 预测数据

上述所有步骤都在三个独立的 .py 文件中完成。文件结构如下所示:

项目文件结构

1. 创建手语检测数据集

在互联网上获取所需的数据集是有可能的,但在本项目中,我们将自行创建数据集。我们将从视频相机获取实时视频流,并将视频流中每一帧检测到的手部手势保存在一个目录(本例中为 gesture 目录)下的两个文件夹 train 和 test 中,每个文件夹再细分为 10 个子文件夹,每个子文件夹内存放着使用 create_gesture_data.py 捕获的图像。

目录结构

gesture
├── test
│   ├── 1
│   ├── 2
│   ├── 3
│   └── ...
└── train
    ├── 1
    ├── 2
    ├── 3
    └── ...

目录树结构如上述所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建 ROI (感兴趣区域)
为了从背景中分离出手部,我们使用 OpenCV 获取网络摄像头的实时视频流,并创建一个感兴趣区域 (ROI)。ROI 是一个特定的矩形区域,在其中我们想要检测手势的发生。
在这里插入图片描述

创建好 ROI 后,利用摄像头获取实时视频流,ROI 以一个红色矩形框的形式显示在窗口中。

计算背景权重
为了区分背景与前景,我们计算背景图像的累加加权平均值,然后从含有前景对象的帧中减去这个背景平均值,来区分前景中的手部。

累加加权平均值是通过在一些帧(这里使用60帧)上计算得到,以便对背景有一个累积的平均值。在获取了背景的累加平均值后,我们从后续的每一帧中减去背景以检查是否有前景对象,即手部。

# 导入必要的库
import tensorflow as tf
from tensorflow import keras
from keras.models import Sequential
from keras.layers import Activation, Dense, Flatten, BatchNormalization, Conv2D, MaxPool2D, Dropout
from keras.optimizers import Adam, SGD
from keras.metrics import categorical_crossentropy
from keras.preprocessing.image import ImageDataGenerator
import warnings
import numpy as np
import cv2
from keras.callbacks import ReduceLROnPlateau
from keras.callbacks import ModelCheckpoint, EarlyStopping
warnings.simplefilter(action='ignore', category=FutureWarning)  # 忽略 FutureWarning 警告
background = None
accumulated_weight = 0.5  # 定义背景的累计加权值
# 创建 ROI 的维度...
ROI_top = 100  # ROI 区域的顶部位置
ROI_bottom = 300  # ROI 区域的底部位置
ROI_right = 150  # ROI 区域的右侧位置
ROI_left = 350  # ROI 区域的左侧位置
def cal_accum_avg(frame, accumulated_weight):
    # 定义全局变量背景
    global background
    # 如果初始化过程中背景为空,则将帧复制给背景,并返回 None
    if background is None:
        background = frame.copy().astype("float")
        return None
    # 对帧应用加权平均以得到背景
    cv2.accumulateWeighted(frame, background, accumulated_weight)

在这里插入图片描述

计算阈值并找到轮廓
现在我们为每一帧计算阈值,使用 cv2.findContours 确定轮廓,并通过函数 segment 返回最大的轮廓(即最外层的轮廓)。通过这些轮廓,我们能够判断 ROI 中是否有前景对象,即手势的出现。

def segment_hand(frame, threshold=25):
    # 定义全局变量背景
    global background
    # 计算背景与当前帧之间的差异
    diff = cv2.absdiff(background.astype("uint8"), frame)
    # 设置差异图像的阈值
    _ , thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)
    # 获取差异图像中的外部轮廓
    image, contours, hierarchy = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 如果没有检测到轮廓,返回 None
    if len(contours) == 0:
        return None
    else:
        # 找到轮廓中面积最大的(即手势)
        hand_segment_max_cont = max(contours, key=cv2.contourArea)
        # 返回处理后的阈值图像和对应的手部最大轮廓
        return (thresholded, hand_segment_max_cont)

当轮廓被检测到(即手势存在时),我们将 ROI 的图像分别保存至训练集与测试集,用于识别我们想要检测的手势。

# 初始化摄像头
cam = cv2.VideoCapture(0)
num_frames = 0  # 初始化帧数
element = 10  # 待检测的手势元素
num_imgs_taken = 0  # 训练图像数量初始化
while True:
    ret, frame = cam.read()  # 读取一帧
    frame = cv2.flip(frame, 1)  # 水平翻转图像以纠正倒置
    frame_copy = frame.copy()  # 复制当前帧,用于显示
    roi = frame[ROI_top:ROI_bottom, ROI_right:ROI_left]  # 定义 ROI 区域
    gray_frame = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
    gray_frame = cv2.GaussianBlur(gray_frame, (9, 9), 0)  # 应用高斯模糊
    # 在前60帧内获取背景
    if num_frames < 60:
        cal_accum_avg(gray_frame, accumulated_weight)
        if num_frames <= 59:
            # 在 ROI 区域获取背景时显示等待文本
            cv2.putText(frame_copy, "获取背景...请稍等",
                       (80, 400), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,0,255), 2)
    # 在第60至300帧时配置手部
    elif num_frames <= 300:
        hand = segment_hand(gray_frame)  # 检测手部
        cv2.putText(frame_copy, "调整手部...对 " + str(element) + " 的手势",
                    (200, 400), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
        if hand is not None:
            # 分割手部区域
            thresholded, hand_segment = hand
            # 在手部轮廓周围画出轮廓线
            cv2.drawContours(frame_copy, [hand_segment + (ROI_right, ROI_top)], -1, (255, 0, 0), 1)
            # 显示当前帧数
            cv2.putText(frame_copy, str(num_frames) + " 对 " + str(element),
                        (70, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
            # 显示阈值处理后的手部图像
            cv2.imshow("阈值手部图像", thresholded)
    # 在第300帧后保存手部图像
    else:
        hand = segment_hand(gray_frame)
        if hand is not None:
            thresholded, hand_segment = hand
            # 在手部轮廓周围画出轮廓线
            cv2.drawContours(frame_copy, [hand_segment + (ROI_right, ROI_top)], -1, (255, 0, 0), 1)
            # 显示当前帧数和已保存图像数
            cv2.putText(frame_copy, str(num_frames), (70, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
            cv2.putText(frame_copy, str(num_imgs_taken) + " 图像 用于 " + str(element), (200, 400), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
            cv2.imshow("Thresholded Hand Image", thresholded)
            # 保存图像
            if num_imgs_taken <= 300:
                cv2.imwrite(r"D:\\gesture\\train\\" + str(element) + "\\" + str(num_imgs_taken + 300) + '.jpg', thresholded)
            else:
                break
            num_imgs_taken += 1
        else:
            # 没有检测到手部时显示文本
            cv2.putText(frame_copy, "没有检测到手部...", (200, 400), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
    # 在当前帧上绘制 ROI
    cv2.rectangle(frame_copy, (ROI_left, ROI_top), (ROI_right, ROI_bottom), (255, 128, 0), 3)
    # 显示程序项目名称
    cv2.putText(frame_copy, "DataFlair 手势识别...", (10, 20), cv2.FONT_ITALIC, 0.5, (51, 255, 51), 1)
    # 增加帧数用于追踪
    num_frames += 1
    # 显示含手势分割的当前帧
    cv2.imshow("手势检测", frame_copy)
    # 使用 Esc 键关闭所有窗口(或其他按键,如 ord('Q') 也可以)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
# 释放摄像头并销毁所有窗口
cv2.destroyAllWindows()
cam.release()

在这里插入图片描述
在这里插入图片描述

2. 训练 CNN

现在我们使用创建的数据集来训练一个 CNN。

首先,我们使用 Keras 的 ImageDataGenerator 加载数据,通过这个生成器我们可以使用 flow_from_directory 函数来加载训练集和测试集数据,每个数字文件夹的名称将作为加载图像的类名。

train_path = r'D:\gesture\train'
test_path = r'D:\gesture\test'
train_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input).flow_from_directory(directory=train_path, target_size=(64,64), class_mode='categorical', batch_size=10, shuffle=True)
test_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input).flow_from_directory(directory=test_path, target_size=(64,64), class_mode='categorical', batch_size=10, shuffle=True)

代码总结:这段代码的目的是使用 Keras 的 ImageDataGenerator 从指定的目录加载训练集和测试集数据。flow_from_directory 函数会自动根据目录结构生成标签,preprocess_input 函数用于预处理图像数据,使其符合 VGG16 模型的要求。target_size 参数设置图像的大小为 64x64 像素,class_mode 参数设置标签的类型为分类,batch_size 参数设置每次批量加载的图像数量为 10 张,shuffle 参数设置是否在加载时打乱数据。

# 加载数据集并获取一批图像和标签
imgs, labels = next(train_batches)

代码总结:这段代码的目的是从训练生成器中获取一批图像和标签数据,以便在接下来的步骤中进行可视化和模型训练。

# 定义 plotImages 函数用于绘制数据集中的图像
def plotImages(images_arr):
    fig, axes = plt.subplots(1, 10, figsize=(30,20))
    axes = axes.flatten()
    for img, ax in zip( images_arr, axes):
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 将图像从 BGR 转换为 RGB
        ax.imshow(img)  # 绘制图像
        ax.axis('off')  # 关闭坐标轴
    plt.tight_layout()  # 调整子图之间的间距
    plt.show()  # 显示图像

代码总结:这段代码定义了一个 plotImages 函数,用于绘制数据集中的图像。函数通过 plt.subplots 创建一个 1x10 的图像网格,然后遍历图像数组并将其绘制在相应的子图中。cv2.cvtColor 函数用于将图像从 BGR 格式转换为 RGB 格式,以便在 Matplotlib 中正确显示。

# 调用 plotImages 函数绘制一批图像
plotImages(imgs)
# 打印图像的形状和标签信息
print(imgs.shape)
print(labels)

代码总结:这段代码的目的是调用 plotImages 函数绘制一批图像,并打印这些图像的形状和标签信息,以便验证数据加载是否正确。
在这里插入图片描述

# 设计 CNN 模型
model = Sequential()
# 添加第一个卷积层,输入形状为 64x64x3,使用 32 个 3x3 的卷积核,激活函数为 ReLU
model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(64,64,3)))
# 添加第一个最大池化层,池化窗口大小为 2x2,步长为 2
model.add(MaxPool2D(pool_size=(2, 2), strides=2))
# 添加第二个卷积层,使用 64 个 3x3 的卷积核,激活函数为 ReLU,填充方式为 same
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding = 'same'))
# 添加第二个最大池化层,池化窗口大小为 2x2,步长为 2
model.add(MaxPool2D(pool_size=(2, 2), strides=2))
# 添加第三个卷积层,使用 128 个 3x3 的卷积核,激活函数为 ReLU,填充方式为 valid
model.add(Conv2D(filters=128, kernel_size=(3, 3), activation='relu', padding = 'valid'))
# 添加第三个最大池化层,池化窗口大小为 2x2,步长为 2
model.add(MaxPool2D(pool_size=(2, 2), strides=2))
# 将特征图展平为一维向量
model.add(Flatten())
# 添加第一个全连接层,输出维度为 64,激活函数为 ReLU
model.add(Dense(64, activation='relu'))
# 添加第二个全连接层,输出维度为 128,激活函数为 ReLU
model.add(Dense(128, activation='relu'))
# 去掉了Dropout层
#model.add(Dropout(0.2))
# 添加第三个全连接层,输出维度为 128,激活函数为 ReLU
model.add(Dense(128, activation='relu'))
# 去掉了Dropout层
#model.add(Dropout(0.3))
# 添加最后一个全连接层,输出维度为 10,激活函数为 Softmax
model.add(Dense(10, activation='softmax'))
# 打印模型的结构
model

代码总结:这段代码的目的是设计一个卷积神经网络(CNN)模型。模型包含多个卷积层和最大池化层,用于提取图像特征。最后,模型使用全连接层进行分类。Dropout 层被注释掉了,这意味着模型不会在训练过程中随机丢弃某些神经元,以减少过拟合。
在这里插入图片描述

# 编译模型,使用 Adam 优化器,学习率为 0.001,损失函数为分类交叉熵,评估指标为准确率
model.compile(optimizer=Adam(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])
# 定义 ReduceLROnPlateau 回调函数,当验证集损失不再下降时减少学习率
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=1, min_lr=0.0001)
# 定义 EarlyStopping 回调函数,当验证集损失连续几个 epoch 不下降时停止训练
early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=2, verbose=0, mode='auto')
# 重新编译模型,这次使用 SGD 优化器,学习率为 0.001
model.compile(optimizer=SGD(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'])
# 重新定义 ReduceLROnPlateau 回调函数,学习率的最小值设置为 0.0005
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=1, min_lr=0.0005)
# 重新定义 EarlyStopping 回调函数
early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=2, verbose=0, mode='auto')

代码总结:这段代码的目的是编译模型并定义回调函数。首先使用 Adam 优化器编译模型,然后定义了 ReduceLROnPlateau 回调函数用于在验证集损失不再下降时减少学习率,EarlyStopping 回调函数用于在验证集损失连续几个 epoch 不下降时停止训练。接下来,使用 SGD 优化器重新编译模型,并重新定义了 ReduceLROnPlateauEarlyStopping 回调函数。
在这里插入图片描述

# 训练模型,使用训练生成器的数据,训练 10 个 epoch,使用定义的回调函数,并在每个 epoch 使用验证集评估模型
history2 = model.fit(train_batches, epochs=10, callbacks=[reduce_lr, early_stop], validation_data=test_batches)

代码总结:这段代码的目的是训练编译好的 CNN 模型。模型使用训练生成器的数据进行训练,训练 10 个 epoch(根据用户选择的参数可能有所不同)。训练过程中使用了 ReduceLROnPlateauEarlyStopping 回调函数来监控验证集损失,并在必要时调整学习率或停止训练。

# 从测试集中获取一批图像并评估模型,打印准确率和损失分数
imgs, labels = next(test_batches)
scores = model.evaluate(imgs, labels, verbose=0)
print(f'{model.metrics_names[0]} of {scores[0]}; {model.metrics_names[1]} of {scores[1]*100}%')

代码总结:这段代码的目的是从测试生成器中获取一批图像并评估模型的性能。评估结果包括损失分数和准确率,这些信息将被打印出来,以便查看模型在测试集上的表现。

# 保存训练好的模型
model.save('best_model_dataflair3.h5')

代码总结:这段代码的目的是将训练好的模型保存到指定的文件中,以便在后续的模块中使用。

3. 预测手势

在这个部分,我们创建一个检测 ROI(感兴趣区域)的边界框,并计算 accumulated_avg,就像我们在创建数据集时所做的那样。这是为了识别任何前景对象。

现在我们找到最大的轮廓,如果检测到轮廓,说明手被识别了,那么 ROI 的阈值图像将作为测试图像。

我们使用 keras.models.load_model 加载之前保存的模型,并将手的阈值图像作为输入传递给模型进行预测。

# 获取 model_for_gesture.py 所需的导入
import numpy as np
import cv2
import keras
from keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf

代码总结:这段代码的目的是导入所需的库,以便在 model_for_gesture.py 中使用。这些库包括 NumPy、OpenCV、Keras 和 TensorFlow。

# 加载之前训练好的模型
model = keras.models.load_model(r"C:\Users\abhij\best_model_dataflair3.h5")
# 初始化背景变量和 ROI 尺寸
background = None
accumulated_weight = 0.5
ROI_top = 100
ROI_bottom = 300
ROI_right = 150
ROI_left = 350

代码总结:这段代码的目的是加载之前训练好的模型,并初始化一些变量。background 变量用于存储背景图像,accumulated_weight 用于计算背景的加权平均,ROI_topROI_bottomROI_rightROI_left 变量用于定义 ROI 的位置。

# 计算背景的加权平均值
def cal_accum_avg(frame, accumulated_weight):
    global background
    
    if background is None:
        background = frame.copy().astype("float")  # 如果背景为空,初始化背景
        return None
    cv2.accumulateWeighted(frame, background, accumulated_weight)  # 计算加权平均

代码总结:这段代码定义了一个 cal_accum_avg 函数,用于计算背景的加权平均值。如果背景为空,则初始化背景;否则,使用 cv2.accumulateWeighted 函数计算当前帧与背景的加权平均值。

# 分割手部区域,获取最大轮廓和手部检测的阈值图像
def segment_hand(frame, threshold=25):
    global background
    
    diff = cv2.absdiff(background.astype("uint8"), frame)  # 计算背景与当前帧的差值
    
    _, thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)  # 使用阈值将差值图像转化为二值图像
    
    # 在帧中获取轮廓(这些轮廓可以是手或其他前景对象)
    image, contours, hierarchy = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # 如果轮廓列表的长度为 0,说明没有检测到任何轮廓
    if len(contours) == 0:
        return None
    else:
        # 最大的外部轮廓应该是手
        hand_segment_max_cont = max(contours, key=cv2.contourArea)
        
        # 返回手部区域(最大轮廓)和手部检测的阈值图像
        return (thresholded, hand_segment_max_cont)

代码总结:这段代码定义了一个 segment_hand 函数,用于分割手部区域。函数首先计算背景与当前帧的差值,然后使用阈值将差值图像转化为二值图像。接着,使用 cv2.findContours 函数在二值图像中查找轮廓,最大的外部轮廓被认为是手。函数返回手部区域的最大轮廓和阈值图像。

# 在实时摄像头上检测手部动作
cam = cv2.VideoCapture(0)
num_frames = 0
while True:
    ret, frame = cam.read()  # 读取摄像头的一帧图像
    # 翻转图像以防止捕获的图像是倒置的
    frame = cv2.flip(frame, 1)
    frame_copy = frame.copy()  # 复制一帧图像用于显示
    # 从帧中提取 ROI
    roi = frame[ROI_top:ROI_bottom, ROI_right:ROI_left]
    gray_frame = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)  # 将 ROI 转换为灰度图像
    gray_frame = cv2.GaussianBlur(gray_frame, (9, 9), 0)  # 对灰度图像进行高斯模糊
    
    if num_frames < 70:
        # 计算背景的加权平均值
        cal_accum_avg(gray_frame, accumulated_weight)
        
        cv2.putText(frame_copy, "FETCHING BACKGROUND...PLEASE WAIT", (80, 400), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,0,255), 2)  # 显示提示信息
    
    else: 
        # 分割手部区域
        hand = segment_hand(gray_frame)
        
        # 检查是否检测到手部
        if hand is not None:
            thresholded, hand_segment = hand
            # 在手部区域周围绘制轮廓
            cv2.drawContours(frame_copy, [hand_segment + (ROI_right, ROI_top)], -1, (255, 0, 0), 1)
            
            cv2.imshow("Thresholded Hand Image", thresholded)  # 显示手部检测的阈值图像
            
            # 调整阈值图像的大小并转换格式
            thresholded = cv2.resize(thresholded, (64, 64))
            thresholded = cv2.cvtColor(thresholded, cv2.COLOR_GRAY2RGB)
            thresholded = np.reshape(thresholded, (1, thresholded.shape[0], thresholded.shape[1], 3))
            
            # 使用模型进行预测
            pred = model.predict(thresholded)
            # 在图像上显示预测结果
            cv2.putText(frame_copy, word_dict[np.argmax(pred)], (170, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
    
    # 在复制的帧上绘制 ROI
    cv2.rectangle(frame_copy, (ROI_left, ROI_top), (ROI_right, ROI_bottom), (255,128,0), 3)
    # 增加帧数计数器
    num_frames += 1
    # 显示带有分割手部的图像
    cv2.putText(frame_copy, "DataFlair hand sign recognition_ _ _", (10, 20), cv2.FONT_ITALIC, 0.5, (51,255,51), 1)
    cv2.imshow("Sign Detection", frame_copy)
    
    # 按下 Esc 键关闭窗口
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
# 释放摄像头并销毁所有窗口
cam.release()
cv2.destroyAllWindows()

代码总结:这段代码的目的是在实时摄像头上检测手势。首先打开摄像头并读取图像帧,然后翻转图像以防止倒置。接着,提取 ROI 并进行灰度和高斯模糊处理。在前 70 帧中,计算背景的加权平均值。此后,对 ROI 进行手部分割,如果检测到手部,绘制手部轮廓并显示阈值图像。最后,将阈值图像调整大小并转换格式,作为输入传递给模型进行预测,并在图像上显示预测结果。

手势识别输出

手势识别输出
在这里插入图片描述

总结

我们已经成功开发了一个手势识别项目。这是一个有趣的 Python 机器学习项目,可以帮助你提升技能。该项目可以进一步扩展,用于检测英文字符。

参考资料

参考资料链接
Keras 文档https://keras.io/
TensorFlow 文档https://www.tensorflow.org/
OpenCV 文档https://docs.opencv.org/
卷积神经网络基础https://www.jianshu.com/p/5b4f31316d4b
图像数据增强https://blog.csdn.net/weixin_42665933/article/details/103929667
ReduceLROnPlateau 回调函数https://keras.io/callbacks/#reducelronplateau
EarlyStopping 回调函数https://keras.io/callbacks/#earlystopping
CNN 训练技巧https://www.analyticsvidhya.com/blog/2022/01/building-a-convolutional-neural-network-cnn-for-image-classification/
手势识别技术综述https://www.sciencedirect.com/science/article/pii/S092523121931150X
OpenCV 手势识别https://www.pyimagesearch.com/2018/06/18/rotation-invariance-gradient-features-and-hog/
手语识别基于深度学习的综述https://www.cnblogs.com/DeepLearn/archive/2022/07/01/16188783.html
OpenCV 官方网站https://opencv.org/
Keras: 内置于 TensorFlow 的深度学习 API 介绍https://tensorflow.google.cn/guide/keras/overview?hl=zh_cn
使用 Python 进行手语识别https://www.datacamp.com/community/tutorials/opencv-hand-gesture-recognition?utm_source=adwords_ppc&utm_campaignid=15652612787&utm_adgroupid=125893662092&utm_device=c&utm_keyword=python%20hand%20gesture%20recognition&utm_matchtype=b&utm_network=g&utm_adpostion=&utm_creative=536767165181&utm_targetid=aud-1051414126416:cat-sci-tech-1&utm_loc_interest_ms=&utm_loc_physical_ms=9061664&gclid=Cj0KCQiA-sr-BRD8ARIsAHOHK-h4bxpeedMOhT84J_c6L1iPQf98KXpBp_5FW_t7JdmjOwCaYQyiBapaAsWSEALw_wcB
OpenCV 3.4.2 安装指南https://docs.opencv.org/3.4.2/df/d68/tutorial_table_of_content_introduction.html
TensorFlow 2.0.0 更新日志https://github.com/tensorflow/tensorflow/releases/tag/v2.0.0-rc0
基于 Python 的手势识别算法https://towardsdatascience.com/hand-gesture-recognition-machine-learning-with-python-part-1-113d32a3dc39
创建手势识别数据集的方法https://machinelearningmastery.com/how-to-create-a-machine-learning-dataset/
OpenCV 图像预处理https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_table_of_contents_imgproc/py_table_of_contents_imgproc.html#py-table-of-contents-imgproc
Keras Callbackshttps://keras.io/api/callbacks/
深度学习论文——手语识别https://ieeexplore.ieee.org/document/8859885
手部轮廓检测https://learnopencv.com/hand-gesture-recognition-using-opencv/
使用 OpenCV 和 Python 创建手语识别应用程序https://developer.mozilla.org/en-US/docs/Web/Extensions/Python
机器学习新手指南https://www.dataquest.io/blog/machine-learning-python/
计算机视觉在手语识别中的应用https://visionlab.github.io/teaching/vis2015-hand.pdf

http://www.kler.cn/a/512590.html

相关文章:

  • 基于python的博客系统设计与实现
  • 光谱相机在智能冰箱的应用原理与优势
  • Linux 如何使用parted进行磁盘分区?
  • CentOS部署FastDFS+Nginx并实现远程访问本地服务器中文件
  • 2024年第十五届蓝桥杯青少组国赛(c++)真题—快速分解质因数
  • vue2 - Day05 - VueX
  • 欧拉(Euler 22.03)安装ProxySQL
  • Flutter项目和鸿蒙平台的通信
  • Skyeye 云 VUE 版本 v3.15.5 发布
  • 计算机基础问答-面试总结
  • 路径参数和查询参数分别是什么
  • 什么是股指期货的到期风险?
  • 从前端视角看设计模式之结构型模式篇
  • Springboot和Android项目接入Firebase Cloud Message(FCM)
  • 抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目
  • 【Flutter】platform_view之AppKitView在哪个flutter版本添加的
  • 使用 Python 获取 1688 商品快递费用 API 接口的示例代码解析
  • [苍穹外卖] 1-项目介绍及环境搭建
  • 提升开发效率:Bash 脚本自动化环境搭建与依赖安装
  • 【Java面试】RabbitMQ
  • JSON解析时如何处理异常?
  • SpringBoot 接入 豆包 火山方舟大模型
  • Debian 上安装PHP
  • 【深度解析Java 20天速成】04_IDEA的安装与使用
  • ChromeOS 132 版本更新
  • 一文夯实垃圾收集的理论基础