Pausing Animation in SceneKit - swift

I loaded into SceneKit a .usdz file which has an animation attached to it. I want to stop this animation to play.. but I can't find the right way.
I load the .usdz asset file with the following method:
func loadIdle() {
let urlfile = Bundle.main.path(forResource: "armi_idle",
ofType: "usdz",
inDirectory: "Asset.scnassets")!
let scene = try! SCNScene(url: URL(string: urlfile)!)
guard let findNode = scene.rootNode.childNode(withName: "armi_idle",
recursively: true)
else {
print("err finde idle")
return
}
// try to pause, but not work
findNode.isPaused = true
// add to main scene
self.scene.rootNode.addChildNode(findNode)
}
A picture of the asset:

Declare the following properties:
import SceneKit
#IBOutlet var sceneView: SCNView!
var notWalking: Bool = true
var animations = [String: CAAnimation]()
var node = SCNNode()
override func viewDidLoad() {
super.viewDidLoad()
sceneView.scene = SCNScene()
self.animation()
}
Then try the following logic (you need Idle and Walking files) :
fileprivate func loadAnimation(withKey: String, scene: String, id: String) {
let url = Bundle.main.url(forResource: scene, withExtension: "dae")!
let source = SCNSceneSource(url: url, options: nil)
guard let character = source?.entryWithIdentifier(id,
withClass: CAAnimation.self) else { return }
character.fadeInDuration = 0.5
character.fadeOutDuration = 0.5
self.animations[withKey] = character
}
then:
fileprivate func animation() {
let standStill = SCNScene(named: "art.scnassets/Idle")!
for childNode in standStill.rootNode.childNodes {
self.node.addChildNode(childNode)
}
sceneView.scene.rootNode.addChildNode(self.node)
self.loadAnimation(withKey: "walking", scene: "art.scnassets/Walking",
id: "Walking-1")
}
and then:
func playWalking(key: String) {
sceneView.scene.rootNode.addAnimation(animations[key]!, forKey: key)
}
func stopWalking(key: String) {
sceneView.scene.rootNode.removeAnimation(forKey: key,
blendOutDuration: 0.75)
}
and at last:
#IBAction func pressed(_ sender: UIButton) {
notWalking ? playWalking(key: "walking") : stopWalking(key: "walking")
notWalking.toggle()
}

Related

How to properly place SCNNode on top of the QR Code?

I want to detect QR codes in the vertical plane and place a node on top of the detected QR. For QR detection, I used Vision framework and Arkit to place the nodes as below code. Whenever placing the node, it is not attached to the QR code and placed somewhere else.
Could someone help to figure out what I have done wrong?
class ViewController: UIViewController, ARSCNViewDelegate,ARSessionDelegate{
#IBOutlet var sceneView: ARSCNView!
var qrRequests = [VNRequest]()
var detectedDataAnchor: ARAnchor?
var processing = false
let configuration = ARWorldTrackingConfiguration()
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.delegate = self
self.sceneView.session.delegate = self
self.sceneView.session.run(configuration)
startQrCodeDetection()
}
func startQrCodeDetection() {
let request = VNDetectBarcodesRequest(completionHandler: self.requestHandler)
self.qrRequests = [request]
}
public func session(_ session: ARSession, didUpdate frame: ARFrame) {
DispatchQueue.global(qos: .userInitiated).async {
do {
if self.processing {
return
}
self.processing = true
let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: frame.capturedImage,
options: [:])
try imageRequestHandler.perform(self.qrRequests)
} catch {
}
}
}
func requestHandler(request: VNRequest, error: Error?) {
if let results = request.results, let result = results.first as? VNBarcodeObservation {
guard let payload = result.payloadStringValue else {return}
var rect = result.boundingBox
let center = CGPoint(x: rect.midX, y: rect.midY)
DispatchQueue.main.async {
self.hitTestQrCode(center: center)
self.processing = false
}
} else {
self.processing = false
}
}
func hitTestQrCode(center: CGPoint) {
print("Hit Test")
if let hitTestResults = self.sceneView?.hitTest(center, types: [.featurePoint, .existingPlaneUsingExtent] ),
let hitTestResult = hitTestResults.first {
if let detectedDataAnchor = self.detectedDataAnchor,
let node = self.sceneView.node(for: detectedDataAnchor) {
node.transform = SCNMatrix4(hitTestResult.worldTransform)
} else {
self.detectedDataAnchor = ARAnchor(transform: hitTestResult.worldTransform)
self.sceneView.session.add(anchor: self.detectedDataAnchor!)
}
}
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
if self.detectedDataAnchor?.identifier == anchor.identifier {
let sphere = SCNSphere(radius: 0.02)
sphere.firstMaterial?.diffuse.contents = UIColor.red
let sphereNode = SCNNode(geometry: sphere)
sphereNode.transform = SCNMatrix4(anchor.transform)
return sphereNode
}
return nil
}
}

How to prevent song item to start over again after being selected?

I have a collection view, that has a few songs, and upon selecting these songs, the app moves into a playerVC where the pause button is and the song image.
If the user selects song a for example, the music starts so "player.play()" is true, and then the user is moved into a playerVC with it's image etc, now say the user leaves this playerVC and wants to return to it to pause the song, the whole song restarts. How can I prevent this from happening? I know I have to implement an If statement to do something is "player.isPlaying" but do not know what to put in it.
This is the class where the audio is initialised
class SoundManager {
public var position = 0
public var songs: [Song] = []
var player: AVAudioPlayer?
func playNavPlayer() {
let song = songs[position]
let urlString = Bundle.main.path(forResource: song.trackName, ofType: "mp3")
do {
try AVAudioSession.sharedInstance().setMode(.default) ///app needs to know what mode we are working on
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
guard let urlString = urlString else { return }
player = try AVAudioPlayer(contentsOf: URL(string: urlString)!)
guard let player = player else {return}
player.volume = 0.5
// player.play()
// player.delegate = self
player.numberOfLoops = -1
} catch {
print ("Error")
}
}
}
Updated Singleton Class
class SoundManager {
private init(){ }
static let shared = SoundManager()
public var position = 0
public var songs: [Song] = []
var player: AVAudioPlayer?
var songURL: URL!
func playNavPlayer() {
let song = songs[position]
let urlString = Bundle.main.path(forResource: song.trackName, ofType: "mp3")
do {
try AVAudioSession.sharedInstance().setMode(.default) ///app needs to know what mode we are working on
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
guard let urlString = urlString else { return }
player = try AVAudioPlayer(contentsOf: URL(string: urlString)!)
guard let player = player else {return}
player.volume = 0.5
player.play()
//player.delegate = self
player.numberOfLoops = -1
}
catch {
print ("Error")
}
}
How this class is called
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
//present player
let position = indexPath.item
guard let vc = storyboard?.instantiateViewController(identifier: "player") as? PlayerViewController else {
return
}
func push() {
vc.songs = songs
vc.position = position
vc.soundManager = SoundManager.shared
navigationController?.pushViewController(vc, animated: true)
}
SoundManager.shared.songs = songs
SoundManager.shared.position = position
if SoundManager.shared.player?.isPlaying == true && SoundManager.shared.player?.url (This is where I need to see compare song playing with song clicked) {
push()
}
else {
SoundManager.shared.playNavPlayer()
push()
}
SoundManager.shared.setupRemoteTransportControls()
SoundManager.shared.setupNowPlaying()
//songs
}
Song Declaration
var songs = [Song]()
func configureSongs() {
songs.append(Song(name: "Empire", imageName: "cover1", trackName: "Empire"))
songs.append(Song(name: "FirstSample", imageName: "cover2", trackName: "FirstSample"))
songs.append(Song(name: "TBH", imageName: "cover3", trackName: "TBH"))
songs.append(Song(name: "Trials", imageName: "cover4", trackName: "Trials"))
songs.append(Song(name: "Magic Spells", imageName: "cover5", trackName: "MagicSpells"))
songs.append(Song(name: "Still Goin", imageName: "cover6", trackName: "StillGoin"))
}
Song, is a struct
import Foundation
struct Song {
let name: String
let imageName: String
let trackName: String
}
So the Song struct should conform the Equatable protocol, in order for us to use a function in which we compare two instances of our Song struct, like this
struct Song: Equatable {
let name: String
let imageName: String
let trackName: String
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.trackName == rhs.trackName
}
}
And then when we select a song(item) in didSelect we implement an if function to check wether the player is playing and also to compare the two track names, the one that is playing and the one we just clicked on, then if true we specify what we want to do in the curly brackets.
if SoundManager.shared.player?.isPlaying == true && songs[position] == SoundManager.shared.songs[SoundManager.shared.position] {
//Whatever you want to do
}

How can I record AVDepthData video and save in the gallery?

I am developing an application to record RGB-D sequences with the iPhone by using the DualRearCamera or the TrueDepthCamera. I can capture and visualize the RGB frame and depth frames and I developed a version where I can compress this data and save in the internal files of the iPhone. Nevertheless, my idea is to save both sequences (RGB and depth map sequences) in the gallery, but I am having problems to use AVAssetWritter and create a depth map video.
I am using the iPhone X, Xcode 10.2.1 and swift 5
import UIKit
import AVFoundation
import AssetsLibrary
var noMoreSpace = false
class ViewController: UIViewController{
#IBOutlet weak var previewView: UIImageView!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var previewModeControl: UISegmentedControl!
let session = AVCaptureSession()
let dataOutputQueue = DispatchQueue(label: "video data queue")
let videoOutput = AVCaptureVideoDataOutput()
let movieOutput = AVCaptureMovieFileOutput()
let depthOutput = AVCaptureDepthDataOutput()
let depthCapture = DepthCapture()
var previewLayer = AVCaptureVideoPreviewLayer()
var inputDevice: AVCaptureDeviceInput!
let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInTrueDepthCamera], mediaType: .video, position: .unspecified)
var Timestamp: String {
let currentDate = NSDate()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "ddMM_HHmmss"
return "\(dateFormatter.string(from: currentDate as Date))"
}
var isRecording = false
var time = 0
var timer = Timer()
enum PreviewMode: Int {
case original
case depth
}
var previewMode = PreviewMode.original
var depthMap: CIImage?
var scale: CGFloat = 0.0
//let sessionQueue = DispatchQueue(label: "session queue")
override func viewDidLoad() {
super.viewDidLoad()
timeLabel.isHidden = true //TODO: Disable the rest of the UI
previewMode = PreviewMode(rawValue: previewModeControl.selectedSegmentIndex) ?? .original
configureCaptureSession()
session.startRunning()
}
func configureCaptureSession() {
session.beginConfiguration()
let camera = AVCaptureDevice.default(.builtInTrueDepthCamera, for: .video, position: .unspecified)!
do {
let cameraInput = try AVCaptureDeviceInput(device: camera)
if session.canAddInput(cameraInput){
session.sessionPreset = .vga640x480
session.addInput(cameraInput)
self.inputDevice = cameraInput
}
if session.canAddOutput(videoOutput){
videoOutput.setSampleBufferDelegate(self, queue: dataOutputQueue)
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
session.addOutput(videoOutput)
let videoConnection = videoOutput.connection(with: .video)
videoConnection?.videoOrientation = .portrait
//previewLayer = AVCaptureVideoPreviewLayer(session: session)
//previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
//previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
//previewView.layer.addSublayer(previewLayer)
//previewLayer.position = CGPoint(x: self.previewView.frame.width / 2, y: self.previewView.frame.height / 2)
//previewLayer.bounds = previewView.frame
}
//Add Depth output to the session
if session.canAddOutput(depthOutput){
session.addOutput(depthOutput)
depthOutput.setDelegate(self, callbackQueue: dataOutputQueue)
depthOutput.isFilteringEnabled = true
let depthConnection = depthOutput.connection(with: .depthData)
depthConnection?.videoOrientation = .portrait
}
/*if session.canAddOutput(movieOutput){
session.addOutput(movieOutput)
}*/
} catch {
print("Error")
}
let outputRect = CGRect(x: 0, y: 0, width: 1, height: 1)
let videoRect = videoOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
let depthRect = depthOutput.outputRectConverted(fromMetadataOutputRect: outputRect)
// Calculate the scaling factor between videoRect and depthRect
scale = max(videoRect.width, videoRect.height) / max(depthRect.width, depthRect.height)
// Change the AVCaptureDevice configuration, so you need to lock it
do{
try camera.lockForConfiguration()
// Set the AVCaptureDevice‘s minimum frame duration (which is the inverse of the maximum frame rate) to be equal to the supported frame rate of the depth data
if let frameDuration = camera.activeDepthDataFormat?.videoSupportedFrameRateRanges.first?.minFrameDuration{
camera.activeVideoMinFrameDuration = frameDuration
}
// Unlock the configuration you locked
camera.unlockForConfiguration()
}catch{
fatalError(error.localizedDescription)
}
session.commitConfiguration()
}
#IBAction func startStopRecording(_ sender: Any) {
if isRecording{
stopRecording()
} else {
startRecording()
}
}
func startRecording(){
timeLabel.isHidden = false
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.timerAction), userInfo: nil, repeats: true)
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let flagTime = Timestamp
let auxStr = flagTime+"_output.mp4"
let fileUrl = paths[0].appendingPathComponent(auxStr)
depthCapture.prepareForRecording(timeFlag: flagTime)
movieOutput.startRecording(to: fileUrl, recordingDelegate: self)
print(fileUrl.absoluteString)
print("Recording started")
self.isRecording = true
}
func stopRecording(){
timeLabel.isHidden = true
timer.invalidate()
time = 0
timeLabel.text = "0"
movieOutput.stopRecording()
print("Stopped recording!")
self.isRecording = false
do {
try depthCapture.finishRecording(success: { (url: URL) -> Void in
print(url.absoluteString)
})
} catch {
print("Error while finishing depth capture.")
}
}
#objc func timerAction() {
time += 1
timeLabel.text = String(time)
}
#IBAction func previeModeChanged(_ sender: UISegmentedControl) {
previewMode = PreviewMode(rawValue: previewModeControl.selectedSegmentIndex) ?? .original
}
#IBAction func switchCamera(_ sender: Any) {
let currentDevice = self.inputDevice.device
let currentPosition = currentDevice.position
let preferredPosition: AVCaptureDevice.Position
let preferredDeviceType: AVCaptureDevice.DeviceType
let devices = self.videoDeviceDiscoverySession.devices
var newVideoDevice: AVCaptureDevice? = nil
switch currentPosition {
case .unspecified, .front:
preferredPosition = .back
preferredDeviceType = .builtInDualCamera
case .back:
preferredPosition = .front
preferredDeviceType = .builtInTrueDepthCamera
#unknown default:
preferredPosition = .back
preferredDeviceType = .builtInDualCamera
}
// First, seek a device with both the preferred position and device type. Otherwise, seek a device with only the preferred position. ENTENDER MEJOR LQS CONDICIONES
if let device = devices.first(where: { $0.position == preferredPosition && $0.deviceType == preferredDeviceType }) {
newVideoDevice = device
} else if let device = devices.first(where: { $0.position == preferredPosition }) {
newVideoDevice = device
}
if let videoDevice = newVideoDevice {
do {
let cameraInput = try AVCaptureDeviceInput(device: videoDevice)
self.session.beginConfiguration()
self.session.removeInput(self.inputDevice)
if self.session.canAddInput(cameraInput) {
session.sessionPreset = .vga640x480
self.session.addInput(cameraInput)
self.inputDevice = cameraInput
}else {
self.session.addInput(self.inputDevice)
}
self.session.commitConfiguration()
} catch{
print("Error occurred while creating video device input: \(error)")
}
}
}
}
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate{
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let image = CIImage(cvPixelBuffer: pixelBuffer!)
let previewImage: CIImage
switch previewMode {
case .original:
previewImage = image
case .depth:
previewImage = depthMap ?? image
}
let displayImage = UIImage(ciImage: previewImage)
DispatchQueue.main.async {[weak self] in self?.previewView.image = displayImage}
}
}
extension ViewController: AVCaptureDepthDataOutputDelegate{
func depthDataOutput(_ output: AVCaptureDepthDataOutput, didOutput depthData: AVDepthData, timestamp: CMTime, connection: AVCaptureConnection) {
var convertedDepth: AVDepthData
// Ensure the depth data is the format you need: 32 bit FP disparity.???
if depthData.depthDataType != kCVPixelFormatType_DepthFloat16{
convertedDepth = depthData.converting(toDepthDataType: kCVPixelFormatType_DepthFloat32)
}else{
convertedDepth = depthData
}
// You save the depth data map from the AVDepthData object as a CVPixelBuffer
let pixelBuffer = convertedDepth.depthDataMap
//Using an extension, you then clamp the pixels in the pixel buffer to keep them between 0.0 and 1.0.
pixelBuffer.clamp()
// Convert the pixel buffer into a CIImage
let depthMap = CIImage(cvPixelBuffer: pixelBuffer)
// You store depthMap in a class variable for later use
DispatchQueue.main.async {
[weak self] in self?.depthMap = depthMap
}
}
}

Can I get 3d models from web servers on Swift?

I'm working on an application with Arkit. There are many 3D models and the size is big in my app. Can I get these models out of another server (outside sites)? I'm new on swift, I can't seem to find anything on loading a 3d model from a web server.
is it enough to change the model path there? Thank you
func loadModel() {
guard let virtualObjectScene = SCNScene(named: "\(modelName).\(fileExtension)", inDirectory: "Models.scnassets/\(modelName)") else {
return
}
let wrapperNode = SCNNode()
for child in virtualObjectScene.rootNode.childNodes {
let defaults = UserDefaults.standard
wrapperNode.addChildNode(child)
}
self.addChildNode(wrapperNode)
}
All code:
import UIKit
import SceneKit
import ARKit
class VirtualObject: SCNNode {
var modelName: String = ""
var fileExtension: String = ""
var thumbImage: UIImage!
var title: String = ""
var viewController: ViewController?
override init() {
super.init()
self.name = "Virtual object root node"
}
init(modelName: String, fileExtension: String, thumbImageFilename: String, title: String) {
super.init()
self.name = "Virtual object root node"
self.modelName = modelName
self.fileExtension = fileExtension
self.thumbImage = UIImage(named: thumbImageFilename)
self.title = title
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadModel() {
guard let virtualObjectScene = SCNScene(named: "\(modelName).\(fileExtension)", inDirectory: "Models.scnassets/\(modelName)") else {
return
}
let wrapperNode = SCNNode()
for child in virtualObjectScene.rootNode.childNodes {
let defaults = UserDefaults.standard
wrapperNode.addChildNode(child)
}
self.addChildNode(wrapperNode)
}
func unloadModel() {
self.removeFromParentNode()
for child in self.childNodes {
child.removeFromParentNode()
}
}
func translateBasedOnScreenPos(_ pos: CGPoint, instantly: Bool, infinitePlane: Bool) {
guard let controller = viewController else {
return
}
let result = controller.worldPositionFromScreenPosition(pos, objectPos: self.position, infinitePlane: infinitePlane)
controller.moveVirtualObjectToPosition(result.position, instantly, !result.hitAPlane)
}
}
extension VirtualObject {
static func isNodePartOfVirtualObject(_ node: SCNNode) -> Bool {
if node.name == "Virtual object root node" {
return true
}
if node.parent != nil {
return isNodePartOfVirtualObject(node.parent!)
}
return false
}
static let availableObjects: [VirtualObject] = [
Anatomy()
]
}
you can load an scn file from a webserver with ip addresses like this (i used a fake ip below)
let myURL = NSURL(string: “http://110.151.153.202:80/scnfiles/myfile.scn”)
let scene = try! SCNScene(url: myURL! as URL, options:nil)
Edit:
Here’s a simple Swift PlayGrounds which pulls a test cube scn file from my github repo. You just tap anywhere and the cube loads.
import ARKit
import SceneKit
import PlaygroundSupport
class ViewController: NSObject {
var sceneView: ARSCNView
init(sceneView: ARSCNView) {
self.sceneView = sceneView
super.init()
self.setupWorldTracking()
self.sceneView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(_:))))
}
private func setupWorldTracking() {
if ARWorldTrackingConfiguration.isSupported {
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
configuration.isLightEstimationEnabled = true
self.sceneView.session.run(configuration, options: [])
}
}
#objc func handleTap(_ gesture: UITapGestureRecognizer) {
let results = self.sceneView.hitTest(gesture.location(in: gesture.view), types: ARHitTestResult.ResultType.featurePoint)
guard let result: ARHitTestResult = results.first else {
return
}
// pulls cube.scn from github repo
let myURL = NSURL(string: "https://raw.githubusercontent.com/wave-electron/scnFile/master/cube.scn")
let scene = try! SCNScene(url: myURL! as URL, options: nil)
let node = scene.rootNode.childNode(withName: "SketchUp", recursively: true)
node?.scale = SCNVector3(0.01,0.01,0.01)
let position = SCNVector3Make(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z)
node?.position = position
self.sceneView.scene.rootNode.addChildNode(node!)
}
}
let sceneView = ARSCNView()
let viewController = ViewController(sceneView: sceneView)
sceneView.autoenablesDefaultLighting = true
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = viewController.sceneView

Objects Track using vision framework in iOS 11

I want to detect object and track that object using vision framework. I am successfully done with detect objects and little bit with tracking also but I don't get so much accuracy with tracking.
I want much more accuracy while converting frames as its frequently lost the accuracy while track the objects.
Please check the below code for detect and track the objects:
import UIKit
import AVFoundation
import Vision
class ViewController: UIViewController {
private lazy var captureSession: AVCaptureSession = {
let session = AVCaptureSession()
session.sessionPreset = AVCaptureSession.Preset.photo
guard let backCamera = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: backCamera) else
{
return session
}
session.addInput(input)
return session
}()
private lazy var cameraLayer: AVCaptureVideoPreviewLayer =
AVCaptureVideoPreviewLayer(session: self.captureSession)
private let handler = VNSequenceRequestHandler()
fileprivate var lastObservation: VNDetectedObjectObservation?
lazy var highlightView: UIView = {
let view = UIView()
view.layer.borderColor = UIColor.red.cgColor
view.layer.borderWidth = 4
view.backgroundColor = .clear
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.layer.addSublayer(cameraLayer)
view.addSubview(highlightView)
let output = AVCaptureVideoDataOutput()
output.setSampleBufferDelegate(self, queue: DispatchQueue(label:
"queue"))
captureSession.addOutput(output)
captureSession.startRunning()
let tapGestureRecognizer = UITapGestureRecognizer(target: self,
action: #selector(tapAction))
view.addGestureRecognizer(tapGestureRecognizer)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
cameraLayer.frame = view.bounds
}
// MARK: - Actions
#objc private func tapAction(recognizer: UITapGestureRecognizer) {
highlightView.frame.size = CGSize(width: 120, height: 120)
highlightView.center = recognizer.location(in: view)
let originalRect = highlightView.frame
var convertedRect =
cameraLayer.metadataOutputRectConverted(fromLayerRect:
originalRect)
convertedRect.origin.y = 1 - convertedRect.origin.y
lastObservation = VNDetectedObjectObservation(boundingBox:
convertedRect)
}
fileprivate func handle(_ request: VNRequest, error: Error?) {
DispatchQueue.main.async {
guard let newObservation = request.results?.first as?
VNDetectedObjectObservation else {
return
}
self.lastObservation = newObservation
var transformedRect = newObservation.boundingBox
transformedRect.origin.y = 1 - transformedRect.origin.y
let convertedRect =
self.cameraLayer.layerRectConverted(fromMetadataOutputRect:
transformedRect)
self.highlightView.frame = convertedRect
}
}
}
extension ViewController:
AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer:
CMSampleBuffer, from connection: AVCaptureConnection) {
guard let pixelBuffer =
CMSampleBufferGetImageBuffer(sampleBuffer),
let observation = lastObservation else {
return
}
let request = VNTrackObjectRequest(detectedObjectObservation:
observation) { [unowned self] request, error in
self.handle(request, error: error)
}
request.trackingLevel = .accurate
do {
try handler.perform([request], on: pixelBuffer)
}
catch {
print(error)
}
}
}
Any help will be appreciated!!
Thanks.
I am not so good at vision and core ml, but apparently your code looks fine. One thing you can do is check when vision does not get any tracking in the buffer, you have to mark its property isLastFrame true if tracking request confidence value falls to 0.
if !trackingRequest.isLastFrame {
if observation.confidence > 0.7 {
trackingRequest.inputObservation = observation
} else {
trackingRequest.isLastFrame = true
}
newTrackingRequests.append(trackingRequest)
}
This way its easy to find out whether vision tracking request lost tracking object or it just tracking the wrong object.