如何在swift中使用视觉框架从其特征点中提取外唇

Posted

技术标签:

【中文标题】如何在swift中使用视觉框架从其特征点中提取外唇【英文标题】:How to extract outer lips from its feature point using vision framework in swift 【发布时间】:2020-03-15 16:41:06 【问题描述】:

我实现了 addFaceLandmarksToImage 函数来裁剪图像的外唇。 addFaceLandmarksToImage 函数首先使用视觉检测图像上的人脸,将人脸边界框大小和原点转换为图像大小和原点。然后我使用面部标志外唇来获取外唇的归一化点,并通过连接所有归一化点在图像的外唇上画线。 然后我实现了裁剪图像的逻辑。我首先将外唇的归一化点转换为图像坐标,并使用查找点方法获取左、右、顶部和最底部的点,并通过裁剪提取外唇并显示在处理后的图像视图中。 此函数的问题是输出following 与预期不符。此外,如果我使用以下链接中的图像以外的图像,它会裁剪除外唇以外的图像。 我不知道我哪里出错了,是在计算裁剪矩形还是 我应该使用另一种方法(使用 OpenCV 和感兴趣区域 (ROI))来提取外唇来自图像? video of an application

func addFaceLandmarksToImage(_ face: VNFaceObservation) 

       
    UIGraphicsBeginImageContextWithOptions(image.size, true, 0.0)
    let context = UIGraphicsGetCurrentContext()

    // draw the image
    image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))

    context?.translateBy(x: 0, y: image.size.height)
    context?.scaleBy(x: 1.0, y: -1.0)

    // draw the face rect
    let w = face.boundingBox.size.width * image.size.width
    let h = face.boundingBox.size.height * image.size.height
    let x = face.boundingBox.origin.x * image.size.width
    let y = face.boundingBox.origin.y * image.size.height
    let cropFace = self.image.cgImage?.cropping(to: CGRect(x: x, y: y, width: w, height: h))
    let ii = UIImage(cgImage: cropFace!)

    // outer lips
    context?.saveGState()
    context?.setStrokeColor(UIColor.yellow.cgColor)

     if let landmark = face.landmarks?.outerLips 
         var actualCordinates = [CGPoint]()
         print(landmark.normalizedPoints)
         for i in 0...landmark.pointCount - 1  
           // last point is 0,0
           let point = landmark.normalizedPoints[i]
           actualCordinates.append(CGPoint(x: x + CGFloat(point.x) * w, y: y + CGFloat(point.y) * h))
           if i == 0 
              context?.move(to: CGPoint(x: x + CGFloat(point.x) * w, y: y + CGFloat(point.y) * h))
            else 
              context?.addLine(to: CGPoint(x: x + CGFloat(point.x) * w, y: y + CGFloat(point.y) * h))
           
      
     // Finding left,right,top,buttom point from actual coordinates points[CGPOINT]

     let leftMostPoint = self.findPoint(points: actualCordinates, position: .leftMost)
     let rightMostPoint = self.findPoint(points: actualCordinates, position: .rightMost)
     let topMostPoint = self.findPoint(points: actualCordinates, position: .topMost)
     let buttonMostPoint = self.findPoint(points: actualCordinates, position: .buttonMost)

     print("actualCordinates:",actualCordinates,
           "leftMostPoint:",leftMostPoint,
           "rightMostPoint:",rightMostPoint,
           "topMostPoint:",topMostPoint,
           "buttonMostPoint:",buttonMostPoint)

     let widthDistance = -(leftMostPoint.x - rightMostPoint.x)
     let heightDistance = -(topMostPoint.y - buttonMostPoint.y)

     //Cropping the image.
     // self.image is actual image 
     let cgCroppedImage = self.image.cgImage?.cropping(to: CGRect(x: leftMostPoint.x,y: leftMostPoint.x - heightDistance,width:1000,height: topMostPoint.y + heightDistance + 500))
     let jj = UIImage(cgImage: cgCroppedImage!)
     self.processedImageView.image = jj    
   
   context?.closePath()
   context?.setLineWidth(8.0)
   context?.drawPath(using: .stroke)
   context?.saveGState()

   // get the final image
   let finalImage = UIGraphicsGetImageFromCurrentImageContext()

   // end drawing context
   UIGraphicsEndImageContext()

   imageView.image = finalImage
 

图像外唇的归一化点:-

[(0.397705078125, 0.3818359375), 
(0.455322265625, 0.390625), 
(0.5029296875, 0.38916015625), 
(0.548828125, 0.40087890625), 
(0.61279296875, 0.3984375), 
(0.703125, 0.37890625), 
(0.61474609375, 0.21875), 
(0.52294921875, 0.1884765625), 
(0.431640625, 0.20166015625), 
(0.33203125, 0.34423828125)]

图像外唇的实际坐标点:-

[(3025.379819973372, 1344.4951847679913),
 (3207.3986613331363, 1372.2607707381248),
 (3357.7955853380263, 1367.633173076436),
 (3502.7936454042792, 1404.6539543699473),
 (3704.8654099646956, 1396.9412916004658),
 (3990.2339324355125, 1335.2399894446135),
 (3711.035540180281, 829.2893117666245),
 (3421.039420047775, 733.6522934250534),
 (3132.5858324691653, 775.3006723802537),
 (2817.9091914743185, 1225.7201781179756)]

我也尝试使用以下方法,使用 CIDetector 获取嘴巴位置并通过裁剪提取外唇。输出不好。

func focusonMouth() 
        let ciimage = CIImage(cgImage: image.cgImage!)
        let options = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: options)!
        let faces = faceDetector.features(in: ciimage)

        if let face = faces.first as? CIFaceFeature 
            if face.hasMouthPosition 
                let crop = image.cgImage?.cropping(to: CGRect(x: face.mouthPosition.x, y: face.mouthPosition.y, width: face.bounds.width - face.mouthPosition.x , height: 200))
                processedImageView.image = imageRotatedByDegrees(oldImage: UIImage(cgImage: crop!), deg: 90)
            
        

    

【问题讨论】:

您使用的裁剪矩形似乎有误。为什么y 使用leftMostPoint.x?为什么宽度是 1000?我的建议是首先将裁剪矩形绘制为图像上的实际矩形,这样您就可以看到实际计算的内容。 我尝试使用不同的值,但无法找出正确的裁剪矩形。所以我使用了我知道这是非常错误的静态宽度。 请记住,Vision 的坐标在左下角有 (0,0),在右上角有 (1,1)。 @MatthijsHollemans 我应该使用 OpenCV,感兴趣区域 (ROI)) 从图像中提取外唇还是使用适当的数学计算来找到裁剪矩形? 我认为使用正确的计算总是一个好主意。 ;-) 无需使用 OpenCV。您已经拥有所需的一切,您只需确保将视觉坐标转换为图像坐标(这已经在绘图代码中发生)。 【参考方案1】:

有两个问题:

    ios 上的输入图像可以用orientation 属性标记旋转方式。 Vision Framework 将完成这项工作,但坐标将被旋转。最简单的解决方案是提供向上(正常旋转)的图像。

    定位标志的位置和大小与检测到的面部标志的位置和大小有关。所以找到的位置和大小应该按找到的人脸的大小和偏移量来缩放,而不是整个图像。

【讨论】:

以上是关于如何在swift中使用视觉框架从其特征点中提取外唇的主要内容,如果未能解决你的问题,请参考以下文章

视觉SLAM特征点提取与匹配

CNN基础框架简介

如何在 Swift 中使用 UITextField 从图像中提取特定文本?

『python』计算机视觉_OpenCV3角点特征Harris提取方法

使用ai软件提取不到人脸

什么库能够在 Python 中提取 SIFT 特征?