示例 - 黑白相机自拍器 - 在两个黑白相机画面里看到你的脸,并按空格键保存。

这个例子需要TK库来运行(用于打开文件对话框)

还需要人脸检测模型,请查看这个教程,学习如何编译一个。

使用这个例子需要 双目相机,如 OAK-D

演示

捕获过程

捕获图片

captured

源代码

import cv2
import depthai
import consts.resource_paths

if not depthai.init_device(consts.resource_paths.device_cmd_fpath):
    raise RuntimeError("Error initializing device. Try to reset it.")

pipeline = depthai.create_pipeline(config={
    'streams': ['left', 'right', 'metaout'],
    'ai': {
        "blob_file": "/path/to/face-detection-retail-0004.blob",
        "blob_file_config": "/path/to/face-detection-retail-0004.json",
    },
    'camera': {'mono': {'resolution_h': 720, 'fps': 30}},
})

if pipeline is None:
    raise RuntimeError('Pipeline creation failed!')

entries_prev = []
face_frame_left = None
face_frame_right = None

while True:
    nnet_packets, data_packets = pipeline.get_available_nnet_and_data_packets()

    for nnet_packet in nnet_packets:
        entries_prev = []
        for e in nnet_packet.entries():
            if e[0]['id'] == -1.0 or e[0]['confidence'] == 0.0:
                break
            if e[0]['confidence'] > 0.5:
                entries_prev.append(e[0])

    for packet in data_packets:
        if packet.stream_name == 'left' or packet.stream_name == 'right':
            frame = packet.getData()

            img_h = frame.shape[0]
            img_w = frame.shape[1]

            for i, e in enumerate(entries_prev):
                left = int(e['left'] * img_w)
                top = int(e['top'] * img_h)
                right = int(e['right'] * img_w)
                bottom = int(e['bottom'] * img_h)

                face_frame = frame[top:bottom, left:right]
                if face_frame.size == 0:
                    continue
                cv2.imshow(f'face-{packet.stream_name}', face_frame)
                if packet.stream_name == 'left':
                    face_frame_left = face_frame
                else:
                    face_frame_right = face_frame

    key = cv2.waitKey(1)
    if key == ord('q'):
        break
    if key == ord(' ') and face_frame_left is not None and face_frame_right is not None:
        from tkinter import Tk, messagebox
        from tkinter.filedialog import asksaveasfilename
        Tk().withdraw()
        filename = asksaveasfilename(defaultextension=".png", filetypes=(("Image files", "*.png"),("All Files", "*.*")))
        joined_frame = cv2.hconcat([face_frame_left, face_frame_right])
        cv2.imwrite(filename, joined_frame)
        messagebox.showinfo("Success", "Image saved successfully!")
        Tk().destroy()

del pipeline

解释

我们的网络会返回它检测到的人脸的边界框(我们将它们存储在 entries_prev 数组中)。所以在这个示例中,我们要做两件主要的事情:裁剪画面,使其只包含人脸,并将其 保存 到用户指定的位置。

进行裁切

裁剪画面 需要我们修改最短工作代码示例,这样我们就不会产生矩形的两个点,而是需要全部四个点:其中两个点决定裁剪的开始(top开始Y轴裁剪,left开始X轴裁剪),另外两个点作为裁剪的结束(bottom结束Y轴裁剪,right结束X轴裁剪)。

                left = int(e['left'] * img_w)
                top = int(e['top'] * img_h)
                right = int(e['right'] * img_w)
                bottom = int(e['bottom'] * img_h)

现在,由于我们的帧是HWC格式(高度、宽度、通道),我们首先裁剪Y轴(高度),然后裁剪X轴(宽度)。所以裁剪代码是这样的。

                face_frame = frame[top:bottom, left:right]

现在,还有一件事要做。因为有时网络可能会产生这样的边界框,当被裁剪后会产生一个空框,我们必须保证自己不受这种情况的影响,因为如果在空框的情况下调用cv2.imshow会抛出一个错误。

                if face_frame.size == 0:
                    continue
                cv2.imshow(f'face-{packet.stream_name}', face_frame)

稍后,由于我们有两台相机同时工作,我们将把显示的画面分配给左面或右面画面变量,这将有助于我们以后保存图像。

                if packet.stream_name == 'left':
                    face_frame_left = face_frame
                else:
                    face_frame_right = face_frame

保存帧

为了 保存图片,我们需要两步:

  • 将左右两台相机的脸部画面合并为一帧。
  • 将准备好的框架保存到磁盘中

值得庆幸的是,OpenCV已经把这一切都解决了,所以对于每一个点,我们只用一行代码就可以了。 调用cv2.hconcat进行帧合并,调用cv2.imwrite存储图像。

其余的代码,利用tkinter包,是可选的,如果你不需要用户交互来保存帧,可以删除。

在这个例子中,我们使用tkinter来保存两个对话框。

  • 获取目标文件路径(存储为filepath),允许我们调用cv2.imwrite,因为它需要路径作为第一个参数。
  • 确认文件已成功保存
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
    if key == ord(' ') and face_frame_left is not None and face_frame_right is not None:
        from tkinter import Tk, messagebox
        from tkinter.filedialog import asksaveasfilename
        Tk().withdraw()
        filename = asksaveasfilename(defaultextension=".png", filetypes=(("Image files", "*.png"),("All Files", "*.*")))
        joined_frame = cv2.hconcat([face_frame_left, face_frame_right])
        cv2.imwrite(filename, joined_frame)
        messagebox.showinfo("Success", "Image saved successfully!")
        Tk().destroy()