New Apple Vision API VNTranslationalImageRegistrationRequest - ios

I am trying to play with the Image Alignment Analysis part of the new Vision API but i am struggling with the initialisation of VNTranslationalImageRegistrationRequest. My code is as follows:
import UIKit
import Vision
class ImageTranslation {
var sourceImage: UIImage!
lazy var imageTranslationRequest: VNTranslationalImageRegistrationRequest = {
//This line fails let translationRequest = VNTranslationalImageRegistrationRequest(targetedCGImage: sourceImage.cgImage, completionHandler: self.handleImageTranslationRequest)
return translationRequest
}()
func handleImageTranslationRequest(request: VNRequest, error: Error?) {
guard let observations = request.results as? [VNImageTranslationAlignmentObservation]
else { print("unexpected result type from VNDetectRectanglesRequest")
return
}
guard observations.first != nil else {
return
}
DispatchQueue.main.async {
observations.forEach { observation in
let transform = observation.alignmentTransform
print(transform)
}
}
}
}
But on the marked line above i keep getting the following error and am unsure how to fix it. Instance member 'sourceImage' cannot be used on type 'ImageTranslation'
Can anyone point me in the right direction? Thanks

An example how to use VNTranslationalImageRegistrationRequest on two images to compute the translation between the two images:
import UIKit
import Vision
class ImageTranslation {
func translateImages(for referenceImage: UIImage,
floatingImage: UIImage,
completion: #escaping (_ alignmentTransform : CGAffineTransform?) -> Void) {
let translationRequest = VNTranslationalImageRegistrationRequest(targetedCGImage: floatingImage.cgImage!) { (request: VNRequest, error: Error?) in
completion((request.results?.first as? VNImageTranslationAlignmentObservation)?.alignmentTransform)
}
let vnImage = VNSequenceRequestHandler()
try? vnImage.perform([translationRequest], on: referenceImage.cgImage!)
}
}

Related

Can't get weight samples using anchored query

I'm working on a watchOS App as my first Swift/iOS project ever. I want to fetch the latest body weight sample and use it for some calculation. The result is presented to the user. As soon as a new sample is added, I want to update my UI as well. It works in a completely fresh simulator installation. As soon as I add a sample in the iOS simulator, the app updates its UI in the watchOS simulator. However, it doesn't work on my real device or after resetting the watchOS simulator. And I just don't know why. The HKAnchoredObjectQuery just returns 0 samples but I definitely have some samples stored in health. I can even see them under Settings > Health on my watch. I can't imagine this is related to my code, but here it is:
class WeightProvider: ObservableObject {
private static let weightSampleType = HKSampleType.quantityType(forIdentifier: .bodyMass)!
private static let healthStore: HKHealthStore = .init()
private var previousAnchor: HKQueryAnchor?
private var runningQuery: HKAnchoredObjectQuery?
#Published var bodyWeight: Measurement<UnitMass>?
func getBodyWeight(longRunning: Bool = false) {
let query = HKAnchoredObjectQuery(type: Self.weightSampleType, predicate: nil, anchor: previousAnchor, limit: longRunning ? HKObjectQueryNoLimit : 1, resultsHandler: processQueryResult)
if longRunning {
query.updateHandler = processQueryResult
runningQuery = query
}
Self.healthStore.execute(query)
}
func stopLongRunningQuery() {
if let runningQuery = runningQuery {
Self.healthStore.stop(runningQuery)
self.runningQuery = nil
}
}
private func processQueryResult(_: HKAnchoredObjectQuery, samples: [HKSample]?, _: [HKDeletedObject]?, newAnchor: HKQueryAnchor?, error: Error?) {
guard let samples = samples as? [HKQuantitySample], error == nil else {
fatalError(error?.localizedDescription ?? "Failed to cast [HKSample] to [HKQuantitySample]")
}
previousAnchor = newAnchor
guard let sample = samples.last else {
return
}
DispatchQueue.main.async {
if Locale.current.usesMetricSystem {
let weight = sample.quantity.doubleValue(for: .gramUnit(with: .kilo))
self.bodyWeight = .init(value: weight, unit: UnitMass.kilograms)
} else {
let weight = sample.quantity.doubleValue(for: .pound())
self.bodyWeight = .init(value: weight, unit: UnitMass.pounds)
}
}
}
}
// MARK: - HealthKit Authorization
extension WeightProvider {
private static let typesToRead: Set<HKObjectType> = [
weightSampleType,
]
func authorize(completion: #escaping (Bool, Error?) -> Swift.Void) {
Self.healthStore.requestAuthorization(toShare: nil, read: Self.typesToRead) { success, error in
completion(success, error)
}
}
}
In my Views onAppear I call this function:
private func authorizeHealthKit() {
guard firstRun else {
return
}
firstRun = false
weightProvider.authorize { success, error in
guard success, error == nil else {
return
}
weightProvider.getBodyWeight(longRunning: true)
}
}
HealthKit is properly authorized as I can see in the Settings of my Watch. Any ideas? Any tips for my code in general?
Wow, after all this time I found the issue: The line previousAnchor = newAnchor needs to be after the guard statement. That's it.

Where to store Decoded JSON array from server and how to access it globally in viewControllers?

Currently im creating application which parses JSON from my server. From server I can receive array with JSON models.
Data from this array must be populated in table View.
My question Is simple: where to store decoded array from server, if I want to access it from many viewControllers in my application?
Here is my JSON model, which coming from server.
import Foundation
struct MyModel: Codable {
var settings: Test?
var provider: [Provider]
}
extension MyModel {
struct setting: Codable {
var name: String
var time: Int
}
}
here is how I am decoding it
import Foundation
enum GetResourcesRequest<ResourceType> {
case success([ResourceType])
case failure
}
struct ResourceRequest<ResourceType> where ResourceType: Codable {
var startURL = "https://myurl/api/"
var resourceURL: URL
init(resourcePath: String) {
guard let resourceURL = URL(string: startURL) else {
fatalError()
}
self.resourceURL = resourceURL.appendingPathComponent(resourcePath)
}
func fetchData(completion: #escaping
(GetResourcesRequest<ResourceType>) -> Void ) {
URLSession.shared.dataTask(with: resourceURL) { data, _ , _ in
guard let data = data else { completion(.failure)
return }
let decoder = JSONDecoder()
if let jsonData = try? decoder.decode([ResourceType].self, from: data) {
completion(.success(jsonData))
} else {
completion(.failure)
}
}.resume()
}
}
This is an example of CategoriesProvider. It just stores categories in-memory and you can use them across the app. It is not the best way to do it and not the best architecture, but it is simple to get started.
class CategoriesProvider {
static let shared = CategoriesProvider()
private(set) var categories: [Category]?
private let categoryRequest = ResourceRequest<Category>(resourcePath: "categories")
private let dataTask: URLSessionDataTask?
private init() {}
func fetchData(completion: #escaping (([Category]?) -> Void)) {
guard categories == nil else {
completion(categories)
return
}
dataTask?.cancel()
dataTask = categoryRequest.fetchData { [weak self] categoryResult in
var fetchedCategories: [Category]?
switch categoryResult {
case .failure:
print("error")
case .success(let categories):
fetchedCategories = categories
}
DispatchQueue.main.async {
self?.categories = fetchedCategories
completion(fetchedCategories)
}
}
}
}
I suggest using URLSessionDataTask in order to cancel a previous task. It could happen when you call fetchData several times one after another. You have to modify your ResourceRequest and return value of URLSession.shared.dataTask(...)
Here more details about data task https://www.raywenderlich.com/3244963-urlsession-tutorial-getting-started#toc-anchor-004 (DataTask and DownloadTask)
Now you can fetch categories in CategoriesViewController in this way:
private func loadTableViewData() {
CategoriesProvider.shared.fetchData { [weak self] categories in
guard let self = self, let categories = categories else { return }
self.categories = categories
self.tableView.reloadData()
}
}
In the other view controllers, you can do the same but can check for the 'categories' before making a fetch.
if let categories = CategoriesProvider.shared.categories {
// do something
} else {
CategoriesProvider.shared.fetchData { [weak self] categories in
// do something
}
}
If you really want to avoid duplicate load data() calls, your simplest option would be to cache the data on disk (CoreData, Realm, File, etc.) after parsing it the first time.
Then every ViewController that needs the data, can just query your storage system.
Of course the downside of this approach is the extra code you'll have to write to manage the coherency of your data to make sure it's properly managed across your app.
make a global dictionary array outside any class to access it on every viewcontroller.

Get image URL for a given Google Place ID in iOS

I have a GMSPlace object and I want to get the image URL for that place but the Google Places SDK doesn't seem to allow for that.
loadImageForMetadata only returns an object of type UIImage. I don't need the image just the URL.
How do I find the image url for a given Google Place ID? Here's a class I've written to help me work with the Google Places SDK:
import Foundation
import GooglePlaces
class GoogleImageService: NSObject {
class func loadFirstPhotoForPlace(placeID: String) {
GMSPlacesClient.shared().lookUpPhotos(forPlaceID: placeID) { (photos, error) -> Void in
if let error = error {
print("Error: \(error.localizedDescription)")
}
if let firstPhoto = photos?.results[0] {
self.loadImageForMetadata(photoMetadata: firstPhoto, placeID: placeID)
}
}
}
class func loadImageForMetadata(photoMetadata: GMSPlacePhotoMetadata, placeID: String) {
GMSPlacesClient.shared().loadPlacePhoto(photoMetadata, callback: { (photo, error) -> Void in
if let error = error {
print("Error: \(error.localizedDescription)")
}
// photo parameter in the closure is an UIImage, not helpful
})
}
}

Use of undeclared type in Swift 3

I have the following class to return a list of NOAA weather observation stations. I am using it to learn how to deal with XML. However, I am getting a "Use of undeclared type 'wxObservationStations'" as an error at func returnWxStation() -> (wxObservationStations). I am using SWXMLHash to deserialize the XML, but I don't think that is my problem (though I am just learning, so it may be).
class WxObservationStations {
let wxObserStationsURL = URL(string: "http://w1.weather.gov/xml/current_obs/index.xml")
struct wxStation: XMLIndexerDeserializable {
let stationName: String
let stationState: String
let latitude: Double
let longitude: Double
static func deserialize(_ node: XMLIndexer) throws -> wxStation {
return try wxStation(
stationName: node["station_name"].value(),
stationState: node["state"].value(),
latitude: node["latitude"].value(),
longitude: node["longitude"].value()
)
}
}
public var wxObservationStations: [wxStation] = []
private func getStationNamesAndLocations(url: URL, completion:#escaping (XMLIndexer) -> ()) {
Alamofire.request(url).responseJSON { response in
// print(response) // To check XML data in debug window.
let wxStationList = SWXMLHash.parse(response.data!)
print(wxStationList)
completion(wxStationList)
}
}
//The error is here:
func returnWxStation() -> (wxObservationStations) {
getStationNamesAndLocations(url: wxObserStationsURL!, completion: { serverResponse in
do {
self.wxObservationStations = try serverResponse["wx_station_index"]["station"].value()
} catch {
}
})
return self.wxObservationStations
}
}
Any thoughts? The variable is declared in the class, and I would like to use it to send the data back to the requesting object. Thanks in advance.
The wxObservationStations is not a type, so it doesn't make sense to say
func returnWxStation() -> (wxObservationStations) { ... }
You're returning self.wxObservationStations, which is of type [wxStation]. So the method declaration should be
func returnWxStation() -> [wxStation] { ... }
By the way, your life will be much easier if you stick with Cocoa naming conventions, namely types should start with upper case letters. So rather than the wxStation type, I'd suggest WxStation.
Your following method will not achieve what you want:
func returnWxStation() -> [wxStation] {
getStationNamesAndLocations(url: wxObserStationsURL!, completion: { serverResponse in
do {
self.wxObservationStations = try serverResponse["wx_station_index"]["station"].value()
} catch {
}
})
return self.wxObservationStations
}
The method getStationNamesAndLocations runs asynchronously and your self.wxObservationStations will not be populated by the time that returnWxStation actually returns.
The entire purpose of the getStationNamesAndLocations method is to provide you a nice asynchronous method with completion handler. I would excise returnWxStation from your code entirely. Or do something like:
func returnWxStation(completionHandler: ([wxStation]?) -> Void) {
getStationNamesAndLocations(url: wxObserStationsURL!) { serverResponse in
do {
let stations = try serverResponse["wx_station_index"]["station"].value()
completionHandler(stations)
} catch {
completionHandler(nil)
}
}
}
And you'd use it like so:
returnWxStation() { stations in
guard let stations = stations else {
// handle error here
return
}
// use `stations` here
}
// but not here

lable.text - Fatal Error

I have 2 classes.
In first class I have a label and a button and in second class I have JsonPars function
I need to write a data from second class to the label in first class.
Here is my code:
import UIKit
import CoreLocation
//main class
class ViewController: UIViewController{
#IBOutlet weak var labl: UILabel!
#IBAction func btn(sender: AnyObject) {
Json().Pars()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
and second class:
import Foundation
//Json-class
class Json {
func Pars() {
let url = NSURL(string: "http://my.url.com")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil){
println(error.localizedDescription)
}
var err: NSError?
let parseObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &err)
if let json = parseObj as? NSDictionary{
if let response = json["response"] as? NSDictionary{
if let obj = response["Object"] as? NSDictionary{
if let data = obj["data"] as? NSString {
println(data)//work's and i have a data
ViewController().labl.text = data //fatal error: unexpectedly found nil while unwrapping an Optional value
}
}
}
}
})
task.resume()
}
}
Sorry fo my English
The problem is you're creating a new instance of ViewController when you call ViewController(). Since you haven't presented the view controller it's outlets haven't been set and are nil. Therefore when you try to access labl (an implicitly unwrapped optional), it's equal to nil and your app crashes.
To fix this, perhaps pars() (it's convention to use lower case for methods) could return the data, which you would then have access to in your ViewController class.
Sorry I'm in class on my Lenovo so I can't test this, but try:
import Foundation
import UIKit
I normally do that just in case I need it at some point. Also if that fails maybe try sending data to the viewcontroller then applying it to label.text.
EDIT: You also have this:
})
above task.resume, please review that and see if that doesn't fix it.

Resources