使用OpenCV进行人脸检测和人脸关键点检测

使用opencv haar cascades级联检测器识别人脸,使用opencv facemark LBF识别人脸关键点。

  1. 在本地电脑新建一个目录(如face_recognition_demo),用来存放新的python项目。创建完成后进入该目录。
    mkdir face_recognition_demo
    cd face_recognition_demo
  2. 为新的python项目建立一个python虚拟环境(在.venv子目录下),并激活。
    python3 -m venv .venv
    source .venv/bin/activate
    激活虚拟环境后,.venv/bin这个目录会加入PATH环境变量的头部,这个目录下包含python, pip等命令。所以后面执行python或pip命令时,就会执行虚拟环境下bin目录里对应的命令。
    如果想关闭虚拟环境,在项目根目录执行:deactivate。
  3. 安装依赖包,准备facemark LBF模型文件,准备人脸照片。
    pip install opencv-contrib-python
    注意:opencv-contrib-python已经包含了opencv核心,所以不需要再安装opencv-python包。2个都安装会造成重复。
    新建model子目录,将lbfmodel.yaml放入其中。
    将人脸照片(这里命名为face9.jpeg),放到项目根目录。
  4. 启动VScode,打开上面的python项目的根目录。新建1个python文件(这里文件名为demo.py)。
    以下列出代码。顺便说一下obsidian里关于代码块的注意事项:
    (1)obsidian里,代码块用3个反引号开始,后面紧跟编程语言的名字(如python),用3个反引号结束。
    (2)粘贴代码到代码块时,如果想保留原先代码里的缩进,粘贴时按住 Command + Shift + V (MacOS系统) 或 Control + Shift + V (windows系统)。
from pathlib import Path

import cv2


def resolve_haarcascade(base_dir: Path) -> str:
    candidates = []

    if hasattr(cv2, "data") and hasattr(cv2.data, "haarcascades"):
        candidates.append(Path(cv2.data.haarcascades) / "haarcascade_frontalface_alt2.xml")

    cv2_root = Path(cv2.__file__).resolve().parent
    print(f"cv2_root:{cv2_root}")
    
    candidates.extend(
        [
            base_dir / "haarcascade_frontalface_alt2.xml",
            Path("/opt/homebrew/Cellar/opencv/4.13.0_10/share/opencv4/haarcascades/haarcascade_frontalface_alt2.xml"),
            cv2_root / "data" / "haarcascade_frontalface_alt2.xml",
            cv2_root.parent.parent.parent / "share" / "opencv4" / "haarcascades" / "haarcascade_frontalface_alt2.xml",
        ]
    )

    for candidate in candidates:
        if candidate.exists():
            return str(candidate)

    raise FileNotFoundError(
        "Cannot find haarcascade_frontalface_alt2.xml. "
        "Download it from https://raw.githubusercontent.com/opencv/opencv/4.x/data/haarcascades/haarcascade_frontalface_alt2.xml "
        "and place it next to demo.py."
    )


def main() -> None:
    base_dir = Path(__file__).resolve().parent
    image_path = base_dir / "face9.jpeg"
    model_path = base_dir / "model" / "lbfmodel.yaml"
    output_path = base_dir / "face9_landmarks.jpg"

    image = cv2.imread(str(image_path))
    if image is None:
        raise FileNotFoundError(f"Cannot read image: {image_path}")

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    cascade_path = resolve_haarcascade(base_dir)
    detector = cv2.CascadeClassifier(cascade_path)
    if detector.empty():
        raise RuntimeError(f"Failed to load Haar cascade: {cascade_path}")

    faces = detector.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(80, 80),
    )
    if len(faces) == 0:
        raise RuntimeError("No face detected in face4.jpeg")

    facemark = cv2.face.createFacemarkLBF()
    facemark.loadModel(str(model_path))

    ok, landmarks = facemark.fit(gray, faces)
    if not ok:
        raise RuntimeError("Facemark fitting failed")

    for (x, y, w, h), face_landmarks in zip(faces, landmarks):
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        # for px, py in face_landmarks[0]:
        #     cv2.circle(image, (int(px), int(py)), 2, (0, 0, 255), -1)
        for i, (px, py) in enumerate(face_landmarks[0]):
            x, y = int(px), int(py)
            cv2.circle(image, (x, y), 2, (0, 0, 255), -1)
            cv2.putText(image, str(i), (x + 2, y - 2),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)

    cv2.imwrite(str(output_path), image)

    print(f"Detected {len(faces)} face(s)")
    print(f"Saved result to: {output_path}")


if __name__ == "__main__":
    main()

  1. 运行程序。
    在虚拟环境已经激活的情况下(如未激活,则需要执行上面提及的source命令),在终端或命令行中,在项目的根目录下,执行:python demo.py
    目录下会生成一张标记人脸及人脸关键点的图片。人脸用绿色方框标出,人脸关键点用红色圆点标出,每个关键点会标出序号,从0~67。因为我们使用的是人脸68点标记。

Mac系统 (m系列芯片) 安装d2l (Dive into Deep Learning|动手学深度学习)

  1. 安装miniforge
    不要用文档上说的miniconda。
    下载最新版mac系统arm版本的miniforge,比如:Miniforge3-26.3.2-2-MacOSX-arm64.sh。
    执行bash ./Miniforge3-26.3.2-2-MacOSX-arm64.sh, 默认会安装到/Users/xxx/miniforge3,安装过程中可以自定义安装路径。
  2. 新建1个虚拟环境并激活
    安装miniforge完成后,关闭终端,重新打开一个终端。
    conda create -n d2l python=3.10
    conda activate d2l
    这里,python版本选择3.10,其它版本会报错。
  3. 安装依赖
    conda install pytorch torchvision torchaudio -c pytorch
    pip install d2l
  4. 下载代码并运行
    按照文档,执行以下命令。
    mkdir d2l-zh && cd d2l-zh
    curl https://zh-v2.d2l.ai/d2l-zh-2.0.0.zip -o d2l-zh.zip
    unzip d2l-zh.zip && rm d2l-zh.zip
    cd pytorch
    jupyter notebook
    Web浏览器中打开http://localhost:8888(通常会自动打开)。

使用css创建水平菜单的3种方法

使用html中的<ul>,<li>和<a>等元素创建出的菜单,默认情况下,每个item前面有黑色marker,并且每个item占据一行。如何通过设定css,将其转变为水平菜单?有如下3种方法。

1. 使用float属性:对<li>元素设置CSS: float: left。这样,<li>元素将水平排列,不会换行。注意:使用float时,必须对容器(这里是<ul>)设置overflow:hidden。

      <!DOCTYPE html>
      <html>
      <style>
      ul {
        list-style-type: none;
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: #333;
      }
      
      ul li {
        float: left;
      }
      
      ul li a {
        display: block;
        padding: 10px 15px;
        text-decoration: none;
        color: white;
      }
      
      ul li a:hover {
        background-color: red;
      }
      
      
      </style>
      <body>
      
      <ul>
      <li><a href="#">item1</a></li>
      <li><a href="#">item2</a></li>
      <li><a href="#">item3</a></li>
      </ul>
      </body>
      </html>

      2. 将<li>元素设置CSS: display: inline-block。因为<li>是block level元素,会以一个新行开始,并且占用全部宽度。通过对其设置 display: inline-block,它就会变得像inline元素一样,只会占据必要宽度,从而多个<li>元素就能够实现水平排列。

      <!DOCTYPE html>
      <html>
      <style>
      ul {
        list-style-type: none;
        margin: 0;
        padding: 0;
        background-color: #333;
      }
      
      ul li {
        display: inline-block;
      }
      
      ul li a {
        display: block;
        padding: 10px 15px;
        text-decoration: none;
        color: white;
      }
      
      ul li a:hover {
        background-color: red;
      }
      
      
      </style>
      <body>
      
      <ul>
      <li><a href="#">item1</a></li>
      <li><a href="#">item2</a></li>
      <li><a href="#">item3</a></li>
      </ul>
      </body>
      </html>
      

      3. 对<ul>使用flex布局。<ul>包含的<li>就会水平排列。

      <!DOCTYPE html>
      <html>
      <style>
      ul {
        list-style-type: none;
        margin: 0;
        padding: 0;
        background-color: #333;
        display: flex;
      }
      
      ul li a {
        display: block;
        padding: 10px 15px;
        text-decoration: none;
        color: white;
      }
      
      ul li a:hover {
        background-color: red;
      }
      
      
      </style>
      <body>
      
      <ul>
      <li><a href="#">item1</a></li>
      <li><a href="#">item2</a></li>
      <li><a href="#">item3</a></li>
      </ul>
      </body>
      </html>
      

      发表在 CSS

      gogs迁移服务器

      1. 新服务器安装gogs,参考https://cloud.tencent.com/developer/article/1626705。只安装,到浏览器打开3000端口首次设置时停止。
      2. 旧服务器上运行./gogs backup 命令,会生成一个zip文件。
      3. 将zip文件使用scp命令传输到新服务器,使用./gogs restore –from=”zip file name”,恢复数据。
      4. 修改DNS设置,gogs服务器域名指向新服务器ip地址,参考旧服务器nginx配置文件,在新服务器nginx配置文件里设置gogs相关网址。
      5. 浏览器打开gogs网址,登录后右上角“用户设置”-“SSH密钥”,删除旧密钥,重新上传公钥文件内容(以前的公钥文件要保留)。

      使用rufus制作Windows To Go系统(Windows 11)

      使用rufus制作WTG(Windows To Go)的好处:1. 绕过第一次启动windows 11时必须联网;2. 绕过第一次启动windows 11时必须登录微软帐户。

      在rufus界面需要作如下设置:1. 指定要安装WTG的移动硬盘;2. 指定下载好的windows镜像文件;3. 设定要安装的是:普通windows系统还是windows to go系统;4. 设定UEFI启动;5. 设定文件系统NTFS;6. 设定分区格式GPT,等。最后点击开始,弹出对话框,勾选绕过联网/绕过登录微软帐户等选项。点击确定,等待几分钟就可以制作完成。