Created
September 13, 2023 07:13
-
-
Save himanshu2088/52eb766249ae6de0f147769ac1a4abd2 to your computer and use it in GitHub Desktop.
ARKit: Project Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| extension Float { | |
| func metersToInches() -> String { | |
| return String(format: "%.2f", self * 39.3701) | |
| } | |
| func metersToCentimeters() -> String { | |
| return String(format: "%.2f", self * 100) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SceneKit | |
| extension matrix_float4x4 { | |
| public var position: SCNVector3 { | |
| get{ | |
| return SCNVector3(self[3][0], self[3][1], self[3][2]) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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))) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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