Multiple Vision Requests per Frame - ios

I am working with the Vision and CoreML frameworks. I have a real time video feed. For every frame, I first detect rectangles using VNDetectRectanglesRequest. For every rectangle I detect, I crop out that part of the image and perform a VNCoreMLRequest to classify that part of the image. After classifying the object, if it is the object type I am looking for, I draw the rectangle. It's like I built an object detector when I don't have data to train an actual neural network for detection.
Generally, I detect around 1 to 3 rectangles. Not that many. So for every VNDetectRectanglesRequest, I have 1 to 3 additional VNCoreMLRequest per frame to perform. However, performing all these requests make my video stream very laggy. It's quite noticeable when I point my camera at rectangularly shaped objects. I guess I should add that this video footage is coming from ARKit, so whatever background operations ARKit is performing might have made the lag worse.
I tried to optimize the code using DispatchQueue. Below is my pseudo-code. I'm happy with what the code is doing, but I need to get rid of the lag.
DispatchQueue.global(qos: .background).async {
let request = VNDetectRectanglesRequest(completionHandler: { (request, error) in
// ...
for observation in request.results {
let mlRequest = VNCoreMLRequest(model: model){ (request, error) in
// classify ... if is object I want then jump back to main queue and draw
DispatchQueue.main.async {
// draw rectangles
}
})
}
})
}

I don't think there's anything wrong with your code, just that making all of those requests for every frame is too high of a load for the device to handle.
Try reducing the frequency of the requests, and maybe add a few conditions to check before making the request in the first place. Simply adding a check to see if you're already waiting for other requests to finish might noticeably reduce the load. You could also check if the user is holding the device steady, the frame is in focus, the lighting is adequate, etc.

The lag is potentially caused by creation of new request objects (better check it in Instruments/Time Profiler, but object creation is often the first suspect).
In a similar situation (where I need to run a request on the entire image, then other requests on its parts) I am using https://github.com/maxvol/RxVision which does not re-create requests and hence does not introduce the lag.
let mlRequest: RxVNCoreMLRequest<CGImage> = VNCoreMLRequest.rx.request(model: model, imageCropAndScaleOption: .scaleFit)
mlRequest
.observable
.subscribe { [unowned self] (event) in
switch event {
case .next(let completion):
let cgImage = completion.value // NB you can easily pass the value along to the completion handler
if let result = completion.request.results?[0] as? VNClassificationObservation {
os_log("results: %#", type: .debug, result.identifier)
}
default:
break
}
}
.disposed(by: disposeBag)
let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, orientation: .up, options: requestOptions)
do {
try imageRequestHandler.rx.perform([mlRequest], with: cgImage) // NB you can easily pass the value along to the completion handler
} catch {
print(error)
}

Related

How can I show translucent 3D Model which have been loaded from a file path similar to AR Quick Look?

I have a use case of showing translucent 3D Model in my AR app unless the plane gets detected. I am using ARKit and RealityKit. This experience already exists in AR Quick Look as shown in the below image.
Any idea how Apple is doing this?
I don't see any opacity api in ModelEntity
I am loading the model using below code:
ModelEntity.loadModelAsync(contentsOf: fileURL).sink(
receiveCompletion: {
[weak self] completion in
if case let .failure(error) = completion {
print("Unable to load a model due to error \(error)")
}
self?.cancellableLoadRequest?.cancel()
self?.cancellableLoadRequest = nil
},
receiveValue: {
[weak self] modelEntity in
self?.arView?.addModelEntity(newModelEntity: modelEntity)
})

Make multiple asynchronous requests but wait for only one

I have a question concerning asynchronous requests. I want to request data from different sources on the web. Each source might have the data I want but I do not know that beforehand. Because I only want that information once, I don't care about the other sources as soon as one source has given me the data I need. How would I go about doing that?
I thought about doing it with a didSet and only setting it once, something like this:
var dogPicture : DogPicture? = nil {
didSet {
// Do something with the picture
}
}
func findPictureOfDog(_ sources) -> DogPicture? {
for source in sources {
let task = URL.Session.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
dogPicture = data.getPicture()
}
}
task.resume()
}
}
sources = ["yahoo.com", "google.com", "pinterest.com"]
findPictureOfDog(sources)
However it would be very helpful, if I could just wait until findPictureOfDog() is finished, because depending on if I find something or not, I have to ask the user for more input.
I don't know how I could do it in the above way, because if I don't find anything the didSet will never be called, but I should ask the user for a picture then.
A plus: isWhatIWanted() is rather expensive, so If there was a way to abort the execution of the handler once I found a DogPicture would be great.
I hope I made myself clear and hope someone can help me out with this!
Best regards and thank you for your time
A couple of things:
First, we’re dealing with asynchronous processes, so you shouldn’t return the DogPicture, but rather use completion handler pattern. E.g. rather than:
func findPictureOfDog(_ sources: [String]) -> DogPicture? {
...
return dogPicture
}
You instead would probably do something like:
func findPictureOfDog(_ sources: [String], completion: #escaping (Result<DogPicture, Error>) -> Void) {
...
completion(.success(dogPicture))
}
And you’d call it like:
findPictureOfDog(sources: [String]) { result in
switch result {
case .success(let dogPicture): ...
case .failure(let error): ...
}
}
// but don’t try to access the DogPicture or Error here
While the above was addressing the “you can’t just return value from asynchronous process”, the related observations is that you don’t want to rely on a property as the trigger to signal when the process is done. All of the “when first process finishes” logic should be in the findPictureOfDog routine, and call the completion handler when it’s done.
I would advise against using properties and their observers for this process, because it begs questions about how one synchronizes access to ensure thread-safety, etc. Completion handlers are unambiguous and avoid these secondary issues.
You mention that isWhatIWanted is computationally expensive. That has two implications:
If it is computationally expensive, then you likely don’t want to call that synchronously inside the dataTask(with:completionHandler:) completion handler, because that is a serial queue. Whenever dealing with serial queues (whether main queue, network session serial queue, or any custom serial queue), you often want to get in and out as quickly as possible (so the queue is free to continue processing other tasks).
E.g. Let’s imagine that the Google request came in first, but, unbeknownst to you at this point, it doesn’t contain what you wanted, and the isWhatIWanted is now slowly checking the result. And let’s imagine that in this intervening time, the Yahoo request that came in. If you call isWhatIWanted synchronously, the result of the Yahoo request won’t be able to start checking its result until the Google request has failed because you’re doing synchronous calls on this serial queue.
I would suggest that you probably want to start checking results as they came in, not waiting for the others. To do this, you want a rendition of isWhatIWanted the runs asynchronously with respect to the network serial queue.
Is the isWhatIWanted a cancelable process? Ideally it would be, so if the Yahoo image succeeded, it could cancel the now-unnecessary Pinterest isWhatIWanted. Canceling the network requests is easy enough, but more than likely, what we really want to cancel is this expensive isWhatIWanted process. But we can’t comment on that without seeing what you’re doing there.
But, let’s imagine that you’re performing the object classification via VNCoreMLRequest objects. You might therefore cancel any pending requests as soon as you find your first match.
In your example, you list three sources. How many sources might there be? When dealing with problems like this, you often want to constrain the degree of concurrency. E.g. let’s say that in the production environment, you’d be querying a hundred different sources, you’d probably want to ensure that no more than, say, a half dozen running at any given time, because of the memory and CPU constraints.
All of this having been said, all of these considerations (asynchronous, cancelable, constrained concurrency) seem to be begging for an Operation based solution.
So, in answer to your main question, the idea would be to write a routine that iterates through the sources, and calling the main completion handler upon the first success and make sure you prevent any subsequent/concurrent requests from calling the completion handler, too:
You could save a local reference to the completion handler.
As soon as you successfully find a suitable image, you can:
call that saved completion handler;
nil your saved reference (so in case you have other requests that have completed at roughly the same time, that they can’t call the completion handler again, eliminating any race conditions); and
cancel any pending operations so that any requests that have not finished will stop (or have not even started yet, prevent them from starting at all).
Note, you’ll want to synchronize the the above logic, so you don’t have any races in this process of calling and resetting the completion handler.
Make sure to have a completion handler that you call after all the requests are done processing, in case you didn’t end up finding any dogs at all.
Thus, that might look like:
func findPictureOfDog(_ sources: [String], completion: #escaping DogPictureCompletion) {
var firstCompletion: DogPictureCompletion? = completion
let synchronizationQueue: DispatchQueue = .main // note, we could have used any *serial* queue for this, but main queue is convenient
let completionOperation = BlockOperation {
synchronizationQueue.async {
// if firstCompletion not nil by the time we get here, that means none of them matched
firstCompletion?(.failure(DogPictureError.noneFound))
}
print("done")
}
for source in sources {
let url = URL(string: source)!
let operation = DogPictureOperation(url: url) { result in
if case .success(_) = result {
synchronizationQueue.async {
firstCompletion?(result)
firstCompletion = nil
Queues.shared.cancelAllOperations()
}
}
}
completionOperation.addDependency(operation)
Queues.shared.processingQueue.addOperation(operation)
}
OperationQueue.main.addOperation(completionOperation)
}
So what might that DogPictureOperation might look like? I might create an asynchronous custom Operation subclass (I just subclass a general purpose AsynchronousOperation subclass, like the one here) that will initiate network request and then run an inference on the resulting image upon completion. And if canceled, it would cancel the network request and/or any pending inferences (pursuant to point 3, above).
If you care about only one task use a completion handler, call completion(nil) if no picture was found.
var dogPicture : DogPicture?
func findPictureOfDog(_ sources, completion: #escaping (DogPicture?) -> Void) {
for source in sources {
let task = URL.Session.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
let picture = data.getPicture()
completion(picture)
}
}
task.resume()
}
}
sources = ["yahoo.com", "google.com", "pinterest.com"]
findPictureOfDog(sources) { [weak self] picture in
if let picture = picture {
self?.dogPicture = picture
print("picture set")
} else {
print("No picture found")
}
}
You can use DispatchGroup to run a check when all of your requests have returned:
func findPictureOfDog(_ sources: [String]) -> DogPicture? {
let group = DispatchGroup()
for source in sources {
group.enter()
let task = URLSession.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
dogPicture = data.getPicture()
}
group.leave()
}
task.resume()
}
group.notify(DispatchQueue.main) {
if dogPicture == nil {
// all requests came back but none had a result.
}
}
}

Completion handler? async_dispatch?

These are the methods and processes which need to happen:
Download images from Parse
Download associated data (which matches images)
Plot this data on a map
Here is my code from view did load:
override func viewDidLoad() {
imageDownload { () -> () in
print("5-----inside closure")
self.queryParseandSave(callback: self.plotImages)
}
}
Image download function:
func imageDownload(completed: #escaping FinishedDownload){
print("1-----Started Image download")
// Query for places
let query = PFQuery(className:"ViewFinderObjects")
query.whereKey("ImageVerified", equalTo: true)
query.whereKey("coordinates", nearGeoPoint:myGeoPoint)
query.limit = 10
query.findObjectsInBackground { (objects, error) -> Void in
if (error == nil) {
for object in objects! {
print("2-----inside object for block")
let imageFromParse = object["image"] as! PFFile
imageFromParse.getDataInBackground(block: {(imageData, error) -> Void in
print("Searching for Image")
if error == nil {
let obsImage:UIImage = UIImage(data: imageData!)!
self.imageToShow = obsImage
self.closestImage.append(self.imageToShow!)
print("There are \(self.closestImage.count) images in the image array")
}
})
print("3-----Completed object loop")
}
}
print("4-----Calling completed statement")
completed()
}
}
Which then calls another function queryParseandSave(callback: self.plotImages)
with the self.plotImages plotting the images on a map.
I have 1 huge issue:
self.plotImahes is always called before the images have finished downloading
I have researched async_dispatch but have no idea if this is the right thing to do.
I'm not familiar with the implementations of the query.findObjectsInBackground and imageFromParse.getDataInBackground methods, but their naming implies that they both happen asynchronously. Also judging from what you're provided above, the former retrieves the object data, while the latter does the actual image data download. If that is indeed the case, then it looks like you're calling your completion handler inside the body of the first asynchronous method instead of waiting for the second method (what appears to be the actual image download).
A couple of ideas for how to resolve this:
You could move your completion handler into the imageFromParse.getDataInBackground block, but this would only make sense if you're comfortable calling the completion block multiple times, after each image finishes downloading.
You could create your own dispatch or operation queue and wait until all tasks complete, then call the completion handler.
You could set up an observer or notification pattern that will call your completion handler at the appropriate time.
There are many different ways to address the issue, but the key thing to remember is that it sounds like you want to call your completion handler after all of the asynchronous operations have completed. Right now you're calling it after you've retrieved the objects, but all of your images are still downloading in the background when your completion handler is called.

iOS Motion Detection: Count of times the device moves up and down

How can i use the gravity or motion sensors inside iPhone to calculate how many times the device moved up and down. e.g. as if it were lifted like a dumbbell for a couple of times, i wanted to count it.
Forgive me if this is something very simply achievable but I'm pretty new to iOS development and hence the question.
You need to use the Core Motion framework to access the gyroscope and accelerometer data.
let manager = CMMotionManager()
if manager.gyroAvailable {
// CMMotionManager is available.
manager.gyroUpdateInterval = 0.1
manager.startGyroUpdates()
// get gyro data...
let queue = NSOperationQueue.mainQueue
manager.startGyroUpdatesToQueue(queue) {
(data, error) in
// ... get data here
}
}
// accelerometer data
if manager.accelerometerAvailable {
manager.accelerometerUpdateInterval = 0.01
manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) {
[weak self] (data: CMAccelerometerData!, error: NSError!) in
// get data here ...
}
}
Combining those 2 you can get the detection going. Experiment with the results until you get the right motion you want.
I experimented a bit with the HTML5 methods on the iPhone. I wanted to build something similar to you, an app that would count the number of chin-ups or sit-ups automatically. I tried a combination of these:
navigator.geolocation
window.ondevicemotion
window.ondeviceorientation
The app would count up when moving in one direction, then wait after movement in the opposite direction ends, then start counting again. I get a count, but it lacks precision. Calibration is difficult... Code available if needed.

progressBlock in getDataInBackgroundWithBlock not called

My Swift app uses Parse.com as the backend. Among other things, I store images for user profiles. I store two versions of the same image: one thumbnail and one full sized image. When the user taps on the thumbnail, it is enlarged. The app then loads the full sized version and presents it on top of the enlarged thumbnail when loading is complete. Since the thumbnail image looks blurry while the full sized one is loading, I've implemented a circular progress bar that shows the loading progress, giving visual feedback of what's going on.
This is the method that handles this process (progressCircle is a CAShapeLayer):
func fetchFullImage() {
photoObject.fetchIfNeededInBackgroundWithBlock { (fetchedObject: PFObject!, error: NSError!) -> Void in
if error == nil {
// After fetching object, now we get the full sized image
let userFullImageFile = fetchedObject["fullImage"] as PFFile
userFullImageFile.getDataInBackgroundWithBlock( {
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
println(">> Success! Full image retrieved.")
self.fullImageView.image = UIImage(data: imageData)
// Hide the progress indicator
self.progressCircle.hidden = true
// Fade in and show the image
self.showFullImage()
}
},
progressBlock: {(percentDone: CInt) -> Void in
println("Loading... \(percentDone)")
// Prints out progress percentage under wifi, but not under 3G!
self.progressCircle.strokeEnd = CGFloat(percentDone) / 100.0
})
} else {
println(error)
}
}
}
Now this is the funny part. When I run the app on a real device connected via wifi, everything goes as expected: the progress circle grows as the image is loaded, etc. Now when I turn off wifi and the same device is connected via 3G, the progress circle does not grow. Essentially, what happens is that the progressBlock closure is not called (I can see this because the println statement produces no result). The image loads after a while (the completion closure is called), and is presented correctly, but there is no way for me to show the progress.
I am trying to find the logic behind this. I could almost understand it if it was the opposite (image loading so fast over wifi that the progress was not visible), but over 3G images take a few seconds to load, during which period nothing seems to happen.
Does anyone have an idea of what can keep progressBlock from being called here? I want to be able to provide the "loading in progress" visual feedback.
Thanks in advance!

Resources