Skip to content

Instantly share code, notes, and snippets.

@himanshu2088
Created September 13, 2023 07:13
Show Gist options
  • Select an option

  • Save himanshu2088/52eb766249ae6de0f147769ac1a4abd2 to your computer and use it in GitHub Desktop.

Select an option

Save himanshu2088/52eb766249ae6de0f147769ac1a4abd2 to your computer and use it in GitHub Desktop.
ARKit: Project Code
import ARKit
import SceneKit
extension ARFaceAnchor {
struct VerticesAndProjection {
var vertex: SIMD3<Float>
var projected: CGPoint
}
func convertVectorsToCGPoints(to view: ARSCNView) -> [VerticesAndProjection] {
let facePointsArray = [200, 210, 420, 850, 648, 767, 372, 803]
var verticesArray = [VerticesAndProjection]()
for x in facePointsArray {
let vertex = geometry.vertices[x]
let col = SIMD4<Float>(SCNVector4())
let pos = SIMD4<Float>(SCNVector4(vertex.x, vertex.y, vertex.z, 1))
let pworld = transform * simd_float4x4(col, col, col, pos)
let vect = view.projectPoint(SCNVector3(pworld.position.x, pworld.position.y, pworld.position.z))
let p = CGPoint(x: CGFloat(vect.x), y: CGFloat(vect.y))
verticesArray.append(VerticesAndProjection(vertex: vertex, projected: p))
}
return verticesArray
}
}
import Foundation
extension Float {
func metersToInches() -> String {
return String(format: "%.2f", self * 39.3701)
}
func metersToCentimeters() -> String {
return String(format: "%.2f", self * 100)
}
}
import SceneKit
extension matrix_float4x4 {
public var position: SCNVector3 {
get{
return SCNVector3(self[3][0], self[3][1], self[3][2])
}
}
}
import UIKit
import ImageIO
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
extension UIImage {
public class func gifImageWithData(_ data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("image doesn't exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
return nil
}
return gifImageWithData(imageData)
}
class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.05
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
to: CFDictionary.self)
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.05 {
delay = 0.05
}
return delay
}
class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
if a < b {
let c = a
a = b
b = c
}
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}
class func gcdForArray(_ array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
import SceneKit
struct VectorHelper {
static func distance(betweenPoints point1: SCNVector3, point2: SCNVector3) -> Float {
let dx = point2.x - point1.x
let dy = point2.y - point1.y
let dz = point2.z - point1.z
return Float(CGFloat(sqrt(dx*dx + dy*dy + dz*dz)))
}
}
import UIKit
import ARKit
import SceneKit
class ViewController: UIViewController {
@IBOutlet var sceneView: ARSCNView!
@IBOutlet weak var bottomRightView: UIView!
@IBOutlet weak var leftEyebrowLenghtLabel: UILabel!
@IBOutlet weak var rightEyebrowLengthLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
guard ARFaceTrackingConfiguration.isSupported else { fatalError() }
sceneView.delegate = self
let configuration = ARFaceTrackingConfiguration()
sceneView.session.run(configuration)
}
}
extension ViewController: ARSCNViewDelegate {
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
guard let device = sceneView.device else {
return nil
}
let faceGeometry = ARSCNFaceGeometry(device: device)
let gifImage = UIImage.gifImageWithName("eyebrow_shimmer")
faceGeometry?.firstMaterial?.transparency = 0.0
let node = SCNNode(geometry: faceGeometry)
return node
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor,
let faceGeometry = node.geometry as? ARSCNFaceGeometry,
let sceneView = renderer as? ARSCNView
else {
return
}
let points: [ARFaceAnchor.VerticesAndProjection] = faceAnchor.convertVectorsToCGPoints(to: sceneView)
DispatchQueue.main.async {
let leftEyebrowDistance1 = VectorHelper.distance(betweenPoints: SCNVector3(points.map{ $0.vertex }[0]), point2: SCNVector3(points.map{ $0.vertex }[1]))
let leftEyebrowDistance2 = VectorHelper.distance(betweenPoints: SCNVector3(points.map{ $0.vertex }[1]), point2: SCNVector3(points.map{ $0.vertex }[2]))
let leftEyebrowTotalDistance = (leftEyebrowDistance1 + leftEyebrowDistance2).metersToCentimeters()
let rightEyebrowDistance1 = Float(VectorHelper.distance(betweenPoints: SCNVector3(points.map{ $0.vertex }[3]), point2: SCNVector3(points.map{ $0.vertex }[4])))
let rightEyebrowDistance2 = Float(VectorHelper.distance(betweenPoints: SCNVector3(points.map{ $0.vertex }[4]), point2: SCNVector3(points.map{ $0.vertex }[5])))
let rightEyebrowTotalDistance = (rightEyebrowDistance1 + rightEyebrowDistance2).metersToCentimeters()
self.leftEyebrowLenghtLabel.text = leftEyebrowTotalDistance + "cm"
self.rightEyebrowLengthLabel.text = rightEyebrowTotalDistance + "cm"
}
faceGeometry.update(from: faceAnchor.geometry)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment