• OpenCV实现人脸关键点检测


    目录

    实现过程

    1,代码解读

    1.1 导入工具包

    1.2导入所需图像,以及训练好的人脸预测模型

    1.3 将 dlib 的关键点对象转换为 NumPy 数组,以便后续处理

    1.4图像上可视化面部关键点

    1.5# 读取输入数据,预处理

    1.6进行人脸检测

    1.7遍历检测到的框

    1.8遍历每个面部

    2,所有代码

    3,结果展示


    实现过程

    1. 导入工具包:首先导入所需的Python库,包括dlib用于人脸检测和关键点定位,以及OpenCV用于图像处理。

    2. 参数解析:使用argparse库解析命令行参数,以指定面部关键点预测器的路径和输入图像的路径。

    3. 定义关键点范围:定义了两个字典(FACIAL_LANDMARKS_68_IDXS和FACIAL_LANDMARKS_5_IDXS),它们包含了不同面部部位的关键点索引范围,用于标识人脸的不同部分。

    4. 图像预处理:加载输入图像,将其缩放为指定宽度(500像素),并将其转换为灰度图像。这些预处理步骤有助于提高人脸检测的性能和稳定性。

    5. 人脸检测:使用dlib库的人脸检测器检测灰度图像中的人脸。检测结果是一个包含人脸边界框的列表。

    6. 遍历检测到的人脸:对于每个检测到的人脸,使用面部关键点定位器获取关键点的坐标。然后,对不同的面部部位进行循环处理。

    7. 绘制关键点:为了可视化,代码使用OpenCV在图像上绘制关键点。每个关键点以红色圆圈的形式标记在图像上,并标注了各个部位的名称。

    8. 提取ROI区域:在每个部位上,代码还提取了一个感兴趣区域(ROI),这是通过计算关键点的包围矩形来实现的。ROI区域随后可以用于进一步的分析或显示。

    9. 调整ROI尺寸:最后,代码调整了ROI区域的尺寸,以确保它们具有一致的宽度(250像素),同时保持高宽比例不变。

    1,代码解读

    1.1 导入工具包

    • collections.OrderedDict: 用于创建有序的字典。
    • numpy: 用于处理数值计算。
    • argparse: 用于处理命令行参数。
    • dlib: 一个图像处理库,用于人脸检测和关键点定位。
    • cv2 (OpenCV): 用于图像处理。

    1.2导入所需图像,以及训练好的人脸预测模型

    # 参数
    ap = argparse.ArgumentParser()
    ap.add_argument("-p", "--shape-predictor", required=True,
    help="path to facial landmark predictor")
    ap.add_argument("-i", "--image", required=True,
    help="path to input image")

    1.3 将 dlib 的关键点对象转换为 NumPy 数组,以便后续处理

    '''这个函数用于将 dlib 的关键点对象转换为 NumPy 数组,以便后续处理。
    它遍历关键点对象中的每个点,提取其 x 和 y 坐标,然后将坐标保存在 NumPy 数组中。'''
    def shape_to_np(shape, dtype="int"):
    # 创建68*2
    coords = np.zeros((shape.num_parts, 2), dtype=dtype)
    # 遍历每一个关键点
    # 得到坐标
    for i in range(0, shape.num_parts):
    coords[i] = (shape.part(i).x, shape.part(i).y)
    return coords

    1.4图像上可视化面部关键点

    这个函数用于在图像上可视化面部关键点。
    它接受输入图像、关键点坐标、可选颜色和透明度参数。
    在输入图像上绘制关键点,可以为不同面部部位指定不同的颜色。
    最后,将可视化的图像与原图像混合以得到输出图像。'''
    def visualize_facial_landmarks(image, shape, colors=None, alpha=0.75):
    # 创建两个copy
    # overlay and one for the final output image
    overlay = image.copy()
    output = image.copy()
    # 设置一些颜色区域
    if colors is None:
    colors = [(19, 199, 109), (79, 76, 240), (230, 159, 23),
    (168, 100, 168), (158, 163, 32),
    (163, 38, 32), (180, 42, 220)]
    # 遍历每一个区域
    for (i, name) in enumerate(FACIAL_LANDMARKS_68_IDXS.keys()):
    # 得到每一个点的坐标
    (j, k) = FACIAL_LANDMARKS_68_IDXS[name]
    pts = shape[j:k]
    # 检查位置
    if name == "jaw":
    # 用线条连起来
    for l in range(1, len(pts)):
    ptA = tuple(pts[l - 1])
    ptB = tuple(pts[l])
    cv2.line(overlay, ptA, ptB, colors[i], 2)
    # 计算凸包
    else:
    hull = cv2.convexHull(pts)
    cv2.drawContours(overlay, [hull], -1, colors[i], -1)
    # 叠加在原图上,可以指定比例
    cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0, output)
    return output

    1.5# 读取输入数据,预处理

    image = cv2.imread(args["image"])
    (h, w) = image.shape[:2]
    width=500#这一行定义了一个新的宽度,即将图像调整为的目标宽度。
    r = width / float(w)
    '''这一行创建一个新的图像维度 dim,它是一个元组,包含了目标宽度 width 和一个计算出的新高度。
    新高度是原始高度 h 乘以比例 r 并取整数部分'''
    dim = (width, int(h * r))
    '''最后一行使用OpenCV的 cv2.resize 函数,
    将原始图像 image 调整为新的维度 dim,以实现目标宽度为500像素,同时保持高宽比例不变。
    interpolation 参数指定了插值方法,这里使用了 cv2.INTER_AREA,它适合缩小图像。'''
    image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    1.6进行人脸检测

    '''1 是一个可选参数,它控制人脸检测的程度。
    通常,值为 1 表示对图像进行一次粗略的检测。
    你也可以尝试使用不同的值,以获得更灵敏或更宽松的人脸检测结果'''
    rects = detector(gray, 1)

    1.7遍历检测到的框

    for (i, rect) in enumerate(rects):
    # 对人脸框进行关键点定位
    # 转换成ndarray
    shape = predictor(gray, rect)
    shape = shape_to_np(shape)

    1.8遍历每个面部

    # 遍历每一个部分
    #这段代码针对每个面部部位执行一系列操作
    for (name, (i, j)) in FACIAL_LANDMARKS_68_IDXS.items():
    clone = image.copy() #这一行创建了图像的一个副本 clone,以便在副本上绘制标记,以保持原始图像不受影响。
    cv2.putText(clone, name, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
    0.7, (0, 0, 255), 2)
    '''
    这一行在图像上标记面部部位的名称,使用 OpenCV 的 cv2.putText 函数。
    name 是部位的名称。
    (10, 30) 是文本的起始坐标。
    cv2.FONT_HERSHEY_SIMPLEX 是用于文本的字体。
    0.7 是字体的比例因子。
    (0, 0, 255) 是文本的颜色(蓝色)。
    2 是文本的线宽。'''

    # 根据位置画点
    for (x, y) in shape[i:j]:
    cv2.circle(clone, (x, y), 3, (0, 0, 255), -1)
    ''' 这个循环遍历给定部位的关键点坐标 (x, y),并在 clone 图像上绘制红色的小圆圈,以标记关键点的位置。
    (x, y) 是关键点的坐标。
    3 是圆圈的半径。
    (0, 0, 255) 是红色的颜色。'''

    # 提取ROI区域
    (x, y, w, h) = cv2.boundingRect(np.array([shape[i:j]]))

    2,所有代码

    1. #导入工具包
    2. from collections import OrderedDict
    3. import numpy as np
    4. import argparse
    5. import dlib
    6. import cv2
    7. #https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/
    8. #http://dlib.net/files/
    9. # 参数
    10. ap = argparse.ArgumentParser()
    11. ap.add_argument("-p", "--shape-predictor", required=True,
    12. help="path to facial landmark predictor")
    13. ap.add_argument("-i", "--image", required=True,
    14. help="path to input image")
    15. args = vars(ap.parse_args())
    16. '''这两个字典包含了不同面部部位的关键点索引范围,用于标识人脸的不同部分,例如嘴巴、眼睛、鼻子等。'''
    17. FACIAL_LANDMARKS_68_IDXS = OrderedDict([
    18. ("mouth", (48, 68)),
    19. ("right_eyebrow", (17, 22)),
    20. ("left_eyebrow", (22, 27)),
    21. ("right_eye", (36, 42)),
    22. ("left_eye", (42, 48)),
    23. ("nose", (27, 36)),
    24. ("jaw", (0, 17))
    25. ])
    26. FACIAL_LANDMARKS_5_IDXS = OrderedDict([
    27. ("right_eye", (2, 3)),
    28. ("left_eye", (0, 1)),
    29. ("nose", (4))
    30. ])
    31. '''这个函数用于将 dlib 的关键点对象转换为 NumPy 数组,以便后续处理。
    32. 它遍历关键点对象中的每个点,提取其 x 和 y 坐标,然后将坐标保存在 NumPy 数组中。'''
    33. def shape_to_np(shape, dtype="int"):
    34. # 创建68*2
    35. coords = np.zeros((shape.num_parts, 2), dtype=dtype)
    36. # 遍历每一个关键点
    37. # 得到坐标
    38. for i in range(0, shape.num_parts):
    39. coords[i] = (shape.part(i).x, shape.part(i).y)
    40. return coords
    41. '''
    42. 这个函数用于在图像上可视化面部关键点。
    43. 它接受输入图像、关键点坐标、可选颜色和透明度参数。
    44. 在输入图像上绘制关键点,可以为不同面部部位指定不同的颜色。
    45. 最后,将可视化的图像与原图像混合以得到输出图像。'''
    46. def visualize_facial_landmarks(image, shape, colors=None, alpha=0.75):
    47. # 创建两个copy
    48. # overlay and one for the final output image
    49. overlay = image.copy()
    50. output = image.copy()
    51. # 设置一些颜色区域
    52. if colors is None:
    53. colors = [(19, 199, 109), (79, 76, 240), (230, 159, 23),
    54. (168, 100, 168), (158, 163, 32),
    55. (163, 38, 32), (180, 42, 220)]
    56. # 遍历每一个区域
    57. for (i, name) in enumerate(FACIAL_LANDMARKS_68_IDXS.keys()):
    58. # 得到每一个点的坐标
    59. (j, k) = FACIAL_LANDMARKS_68_IDXS[name]
    60. pts = shape[j:k]
    61. # 检查位置
    62. if name == "jaw":
    63. # 用线条连起来
    64. for l in range(1, len(pts)):
    65. ptA = tuple(pts[l - 1])
    66. ptB = tuple(pts[l])
    67. cv2.line(overlay, ptA, ptB, colors[i], 2)
    68. # 计算凸包
    69. else:
    70. hull = cv2.convexHull(pts)
    71. cv2.drawContours(overlay, [hull], -1, colors[i], -1)
    72. # 叠加在原图上,可以指定比例
    73. cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0, output)
    74. return output
    75. # 加载人脸检测与关键点定位
    76. detector = dlib.get_frontal_face_detector()
    77. predictor = dlib.shape_predictor(args["shape_predictor"])
    78. # 读取输入数据,预处理
    79. image = cv2.imread(args["image"])
    80. (h, w) = image.shape[:2]
    81. width=500#这一行定义了一个新的宽度,即将图像调整为的目标宽度。
    82. r = width / float(w)
    83. '''这一行创建一个新的图像维度 dim,它是一个元组,包含了目标宽度 width 和一个计算出的新高度。
    84. 新高度是原始高度 h 乘以比例 r 并取整数部分'''
    85. dim = (width, int(h * r))
    86. '''最后一行使用OpenCV的 cv2.resize 函数,
    87. 将原始图像 image 调整为新的维度 dim,以实现目标宽度为500像素,同时保持高宽比例不变。
    88. interpolation 参数指定了插值方法,这里使用了 cv2.INTER_AREA,它适合缩小图像。'''
    89. image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
    90. gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    91. # 人脸检测
    92. '''1 是一个可选参数,它控制人脸检测的程度。
    93. 通常,值为 1 表示对图像进行一次粗略的检测。
    94. 你也可以尝试使用不同的值,以获得更灵敏或更宽松的人脸检测结果'''
    95. rects = detector(gray, 1)
    96. # 遍历检测到的框
    97. for (i, rect) in enumerate(rects):
    98. # 对人脸框进行关键点定位
    99. # 转换成ndarray
    100. shape = predictor(gray, rect)
    101. shape = shape_to_np(shape)
    102. # 遍历每一个部分
    103. #这段代码针对每个面部部位执行一系列操作
    104. for (name, (i, j)) in FACIAL_LANDMARKS_68_IDXS.items():
    105. clone = image.copy() #这一行创建了图像的一个副本 clone,以便在副本上绘制标记,以保持原始图像不受影响。
    106. cv2.putText(clone, name, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
    107. 0.7, (0, 0, 255), 2)
    108. '''
    109. 这一行在图像上标记面部部位的名称,使用 OpenCV 的 cv2.putText 函数。
    110. name 是部位的名称。
    111. (10, 30) 是文本的起始坐标。
    112. cv2.FONT_HERSHEY_SIMPLEX 是用于文本的字体。
    113. 0.7 是字体的比例因子。
    114. (0, 0, 255) 是文本的颜色(蓝色)。
    115. 2 是文本的线宽。'''
    116. # 根据位置画点
    117. for (x, y) in shape[i:j]:
    118. cv2.circle(clone, (x, y), 3, (0, 0, 255), -1)
    119. ''' 这个循环遍历给定部位的关键点坐标 (x, y),并在 clone 图像上绘制红色的小圆圈,以标记关键点的位置。
    120. (x, y) 是关键点的坐标。
    121. 3 是圆圈的半径。
    122. (0, 0, 255) 是红色的颜色。'''
    123. # 提取ROI区域
    124. (x, y, w, h) = cv2.boundingRect(np.array([shape[i:j]]))
    125. roi = image[y:y + h, x:x + w]
    126. (h, w) = roi.shape[:2]
    127. width=250
    128. r = width / float(w)
    129. dim = (width, int(h * r))
    130. roi = cv2.resize(roi, dim, interpolation=cv2.INTER_AREA)
    131. # 显示每一部分
    132. cv2.imshow("ROI", roi)
    133. cv2.imshow("Image", clone)
    134. cv2.waitKey(0)
    135. # 展示所有区域
    136. output = visualize_facial_landmarks(image, shape)
    137. cv2.imshow("Image", output)
    138. cv2.waitKey(0)

    3,结果展示

  • 相关阅读:
    route/router区别/参数
    2.2 Pthreads是什么
    Win11提示无法安全下载软件怎么办?Win11无法安全下载软件
    LeetCode刷题复盘笔记——93. 复原 IP 地址(一文搞懂回溯解决把一长串数字字符串转换成IP地址问题)
    EL表达式 Jstl (附上代码理解)
    从电路设计的角度入门VerilogHDL——学习记录
    【postgresql 基础入门】从了解数据库访问权限,访问数据库,到认识数据库的所有者及属性,从此打开了数据库使用的大门
    本地事务与分布式事务
    单链表的排序操作
    JavaScript学习笔记
  • 原文地址:https://blog.csdn.net/qq_53545309/article/details/133808373