You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Vision API实现人脸边缘裁剪的技术求助

Hey 兄弟!我太懂你这种没人带、刚摸iOS几个月就要啃Vision API的痛苦了——计算机视觉这块对新手来说确实有点门槛。我给你整理了一套亲测能用的沿人脸边缘裁剪的步骤,一步步来肯定能搞定:

使用Vision API沿人脸边缘裁剪人脸的分步指南

1. 先配置人脸轮廓检测请求

和普通的人脸矩形检测不同,要沿边缘裁剪得用人脸 landmark 检测,也就是VNDetectFaceLandmarksRequest,它能拿到人脸的精准轮廓点。先写个基础的请求初始化代码:

import Vision
import UIKit

func detectAndCropFaceContour(from image: UIImage) -> UIImage? {
    guard let ciImage = CIImage(image: image) else {
        print("无法将UIImage转为CIImage")
        return nil
    }
    
    // 创建人脸轮廓检测请求
    let faceLandmarkRequest = VNDetectFaceLandmarksRequest { [weak self] request, error in
        if let error = error {
            print("人脸检测出错:\(error.localizedDescription)")
            return
        }
        // 拿到结果后处理裁剪逻辑
        self?.processFaceDetectionResults(request, originalImage: image)
    }
    
    // 执行请求
    let requestHandler = VNImageRequestHandler(ciImage: ciImage, options: [:])
    do {
        try requestHandler.perform([faceLandmarkRequest])
    } catch {
        print("执行检测请求失败:\(error.localizedDescription)")
        return nil
    }
    return nil // 这里后续可以改成直接返回裁剪后的图,先按流程走
}

2. 提取轮廓点并转换坐标系

Vision用的是Core Image坐标系(原点在左下角),但UIKit的坐标系原点在左上角,所以必须转换坐标,不然裁剪出来的位置完全不对。然后把轮廓点连成路径:

private func processFaceDetectionResults(_ request: VNRequest, originalImage: UIImage) {
    guard let faceObservations = request.results as? [VNFaceObservation],
          let firstFace = faceObservations.first,
          let faceLandmarks = firstFace.landmarks,
          let faceContour = faceLandmarks.faceContour else {
        print("没检测到人脸或者人脸轮廓")
        return
    }
    
    let imageSize = originalImage.size
    // 坐标系转换:垂直翻转并平移
    let coordinateTransform = CGAffineTransform(scaleX: 1, y: -1)
        .translatedBy(x: 0, y: -imageSize.height)
    
    // 把Vision的点转换成UIKit坐标系的点
    let contourPoints = faceContour.pointsInImage(imageSize: imageSize)
        .map { $0.applying(coordinateTransform) }
    
    // 用轮廓点创建UIBezierPath
    let facePath = UIBezierPath()
    if let startPoint = contourPoints.first {
        facePath.move(to: startPoint)
        contourPoints.dropFirst().forEach { facePath.addLine(to: $0) }
        facePath.close()
    }
    
    // 调用裁剪方法
    if let croppedFace = cropImage(toContourPath: facePath, originalImage: originalImage) {
        // 这里就拿到了沿边缘裁剪后的人脸图,可以用它做后续操作
        print("裁剪成功!")
        // 比如显示到UIImageView:imageView.image = croppedFace
    }
}

3. 根据轮廓路径裁剪图片

用Core Graphics把原图按路径裁剪,生成新的UIImage:

private func cropImage(toContourPath path: UIBezierPath, originalImage: UIImage) -> UIImage? {
    // 开启图形上下文
    UIGraphicsBeginImageContextWithOptions(originalImage.size, false, originalImage.scale)
    guard let context = UIGraphicsGetCurrentContext() else {
        UIGraphicsEndImageContext()
        return nil
    }
    
    // 设置裁剪区域为轮廓路径
    context.addPath(path.cgPath)
    context.clip()
    
    // 把原图绘制到裁剪区域内
    originalImage.draw(at: .zero)
    
    // 获取裁剪后的图片
    let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    return croppedImage
}

几个新手容易踩的坑

  • 坐标系转换不能忘:这是最容易出错的地方,要是没转换,裁剪出来的图要么上下颠倒,要么位置完全不对。
  • 处理多个人脸:上面的代码只处理了第一张人脸,如果要处理多张,记得循环faceObservations数组。
  • 后台执行请求:如果是处理相机实时画面,一定要把检测请求放到后台队列,不然会卡顿主线程。

内容的提问来源于stack exchange,提问作者Ashish Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:40:07