OpenCV + OpenVINO实现人脸AR – 请戴上口罩

2021-11-25 14:56:52 浏览数 (1)

前言

最近在看我之前的写的一篇关于人脸landmark的文章,里面有提到OpenVINO自带模型人脸的35个点位,有人问我这个landmark检测有什么用,我斗胆抛砖引玉一下,做了个简单的自动戴口罩的AR演示。

模型介绍

用到两个OpenVINO模型,分别是:

代码语言:javascript复制
face-detection-0204  人脸检测
facial-landmarks-35-adas-0002  landmark检测35点

face-detection-0204模型的输入与输出格式如下:

代码语言:javascript复制
输入:1x3x448x448, BGR顺序
输出:1x1x200x7

facial-landmarks-35-adas-0002模型的输入与输出格式如下:

代码语言:javascript复制
输入:1x3x60x60
输出:1x70

其中70是35个点的xy坐标,取值范围在0~1之间

代码实现

代码实现部分首先检测人脸,然后截取人脸的ROI区域,检测35点landmark坐标,选择(18,34)或者(19、33)两个点位作为起始位置,与对应口罩图象对齐,高度选编码26的点,这样计算X与Y方向的放缩比率,完成对齐,然后贴图口罩到指定的人脸区域。

其中人脸检测的代码如下:

代码语言:javascript复制
# Read IR
net = ie.read_network(model=face_xml, weights=face_bin)

input_blob = next(iter(net.input_info))
out_blob = next(iter(net.outputs))

# 输入设置
n, c, h, w = net.input_info[input_blob].input_data.shape

# 设备关联推理创建
exec_net = ie.load_network(network=net, device_name="CPU")

# 加载landmark模型并设置
landmark_net = ie.read_network(model=landmark_35_xml, weights=landmark_35_bin)
landmark_input_blob = next(iter(landmark_net.input_info))
landmark_out_blob = next(iter(landmark_net.outputs))

# 输入设置
pn, pc, ph, pw = landmark_net.input_info[landmark_input_blob].input_data.shape

# 设备关联推理创建
landmark_exec_net = ie.load_network(network=landmark_net, device_name="CPU")

src = cv.imread("D:/1.jpg")
image = cv.resize(src, (w, h))
image = image.transpose(2, 0, 1)

# 推理
prob = exec_net.infer(inputs={input_blob: [image]})

# 后处理
ih, iw, ic = src.shape
res = prob[out_blob]
if res.ndim == 4:  # SSD
    for obj in res[0][0]:
        if obj[2] > 0.5:
            xmin = int(obj[3] * iw) - 15
            ymin = int(obj[4] * ih) - 15
            xmax = int(obj[5] * iw)   15
            ymax = int(obj[6] * ih)   15
            if xmin < 0:
                xmin = 0
            if ymin < 0:
                ymin = 0
            if xmax >= iw:
                xmax = iw - 1
            if ymax >= ih:
                ymax = ih - 1
            roi = src[ymin:ymax, xmin:xmax, :]
            infer_landmark(roi, landmark_exec_net, landmark_input_blob, landmark_out_blob, pw, ph)
            # cv.rectangle(src, (xmin, ymin), (xmax, ymax), (0, 255, 255), 2, 8)
            # cv.putText(src, str("%.3f" % obj[2]), (xmin, ymin), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, 8)
cv.imshow("Face AR Demo", src)

Landmark检测的方法代码如下:

代码语言:javascript复制
def infer_landmark(faceImg, landmark_exe_net, input_name, output_name, pw, ph):
    # 处理输入图象
    rh, rw, rc = faceImg.shape
    roi = cv.resize(faceImg, (pw, ph))
    roi = roi.transpose(2, 0, 1)

    # 推理
    prob = landmark_exe_net.infer(inputs={input_name: [roi]})
    landmarks = prob[output_name]
    pts = np.reshape(landmarks, (-1, 2))

对齐口罩图象与贴图代码如下:

代码语言:javascript复制
# 寻找点位
for idx, pt in enumerate(pts):
    if idx == 18:
        left_x = 0 # int(pt[0] * rw)
        left_y = int(pt[1] * rh)
        # cv.circle(faceImg, (left_x, left_y), 1, (255, 0, 255), 2, 8, 0)
    if idx == 34:
        right_x = (rw - 1) # int(pt[0] * rw)
        right_y = int(pt[1] * rh)
        # cv.circle(faceImg, (right_x, right_y), 1, (255, 0, 255), 2, 8, 0)
    if idx == 26:
        p26_x = int(pt[0] * rw)
        p26_y = int(pt[1] * rh)

# 对齐与放缩
dx_t = right_x - left_x
dx_m = anchor_pts[2] - anchor_pts[0]
rate_x = dx_t / dx_m
rate_y = (p26_y - left_y) / (anchor_pts[3] - anchor_pts[1])
dst_mask = cv.resize(mask, (0, 0), fx=rate_x, fy=rate_y)
start_x = np.int(anchor_pts[0] * rate_x);
start_y = np.int(anchor_pts[1] * rate_y);
end_x = np.int(anchor_pts[2] * rate_x)
end_y = np.int(anchor_pts[3] * rate_y)

# 贴图
for row in range(end_y - start_y):
    for col in range(end_x - start_x):
        b2, g2, r2 = dst_mask[start_y row, start_x col]
        if b2 < 127 and g2 < 127 and r2 < 127:
            faceImg[row left_y, left_x col] = ( b2, g2, r2)

最终运行结果如下:

0 人点赞