I am currently trying to build an app that uses google's autoML feature. I have trained a model and published it on google firebase and have integrated the necessary code into my app following the documentation:
https://firebase.google.com/docs/ml-kit/ios/label-images-with-automl
I am using a remote model instead of making it local. However when I try running the code, then choose an image in the simulator, an empty list of predictions is output in the console.
I have also turned on the debugging feature, but this has not helped me fix my error. This is the code I am running in ViewController:
import UIKit
import CoreML
import Vision
import Firebase
import FirebaseMLCommon
var serverImage: UIImage? = nil
var topResult = ""
class ViewController: UIViewController {
#IBOutlet var skinDiseaseImageView: UIImageView!
#IBOutlet var result1Label: UILabel!
#IBOutlet var result1Confidence: UILabel!
#IBOutlet var result2Label: UILabel!
#IBOutlet var result2Confidence: UILabel!
#IBOutlet var result3Label: UILabel!
#IBOutlet var result3Confidence: UILabel!
override func viewDidLoad() {
let initialConditions = ModelDownloadConditions(allowsCellularAccess: true,
allowsBackgroundDownloading: true)
let updateConditions = ModelDownloadConditions(allowsCellularAccess: false,
allowsBackgroundDownloading: true)
let remoteModel = RemoteModel(
name: "skinDiseaseModel", // The name you assigned in the console.
allowsModelUpdates: true,
initialConditions: initialConditions,
updateConditions: updateConditions
)
ModelManager.modelManager().register(remoteModel)
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController {
#IBAction func selectImage(_ sender: Any) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = .savedPhotosAlbum
present(pickerController, animated: true)
}
}
extension ViewController: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
dismiss(animated: true)
guard let skinImage = info[UIImagePickerControllerOriginalImage] as? UIImage else {
fatalError("Error Retrieving Image Line 95")
}
var skinImageToDiagnose = VisionImage(image: skinImage)
serverImage = skinImage
skinDiseaseImageView.image = skinImage
let labelerOptions = VisionOnDeviceAutoMLImageLabelerOptions(
remoteModelName: "skinDiseaseModel", // Or nil to not use a remote model
localModelName: nil // Or nil to not use a bundled model
)
labelerOptions.confidenceThreshold = 0 // Evaluate your model in the Firebase console
// to determine an appropriate value.
let labeler = Vision.vision().onDeviceAutoMLImageLabeler(options: labelerOptions)
var topThreeResults = [String]()
var topThreeConfidences = [String]()
labeler.process(skinImageToDiagnose) { labels, error in
guard error == nil, let labels = labels
else {
print(error)
return
}
//task succeeded
print("1")
print(labels)
var counter = 0
for label in labels {
topThreeResults.append(String(describing: label))
topThreeConfidences.append(String(describing: label.confidence))
counter = counter + 1
print("counter")
if counter == 3 {
break
}
}
}
result1Label.text = topThreeResults[0]
result1Confidence.text = (topThreeConfidences[0] + "%")
result2Label.text = topThreeResults[1]
result2Confidence.text = (topThreeConfidences[1] + "%")
result3Label.text = topThreeResults[2]
result3Confidence.text = (topThreeConfidences[2] + "%")
}
}
This is the error I recieved:
Fatal error: Index out of range
2019-08-31 19:50:19.763469-0700 medicalAppFinal[13776:2281569] Fatal error: Index out of range
(lldb)
I reasoned that the index out of range problem is due to the list of labels(output predictions) being empty after having printed it. Thus I understand why it is index out of range, but I do not know why I am recieving an empty list after passing in the image into labeler.process() How do I solve this error? Tell me if you need more information
This seems to be duplicate of the following question (which was answered by the author):
Not Retriveing Output Prediction List from Remote Firebase Automl custom model
Related
What am I doing wrong?
I get this error:
let letterString = sender.title(for: .normal)! // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
when I tried to get the title of a button in swift like below:
import UIKit
class ViewController: UIViewController {
// My IBOutlets
#IBOutlet var treeImageView: UIImageView!
#IBOutlet var correctWordLabel: UILabel!
#IBOutlet var scoreLabel: UILabel!
// My Outlet Collection
#IBOutlet var letterButtons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Begin the round.
newRound()
}
var listOfWords = ["estufa", "nevera", "computadora", "empanada", "chuleta", "camarones", "brincar", "correr", "caminar", "tigre", "jirafa", "mono", "kisseemmee", "Tampa", "Orlando"]
let incorrectMovesAllowed = 7
let totalWins = 0
let totalLosses = 0
// My IBActions
#IBAction func letterButtonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)! // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
}
var currentGame: Game!
func newRound() {
let newWord = listOfWords.removeFirst()
currentGame = Game(word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
updateUI()
}
func updateUI() {
scoreLabel.text = "Wins: \(totalWins), Losses: \(totalLosses)"
treeImageView.image = UIImage(named: "Tree \(currentGame.incorrectMovesRemaining)")
}
}
// Game.swift file code:
import Foundation
struct Game {
var word: String
var incorrectMovesRemaining: Int
var guessedLetters: [Character]
mutating func playerGuessed(letter: Character) {
guessedLetters.append(letter)
if !word.contains(letter) {
incorrectMovesRemaining -= 1
}
}
}
I'm a newbie. This is my first program. I appreciate if you code the solution.
You can get the title of the UIButton using titleLabel property. Check the below code.
sender.titleLabel?.text
As the above code returns optional, you can use optional chain to safely get the string
if let titleLabel = sender.titleLabel {
let title = titleLabel.text
}
OR
You can also use the currentTitle property as below.
sender.currentTitle
You can use:
sender.titleLabel.text
As an exercise to learn Swift, I'm creating a simple app where you use ImagePickerController to select a photo and get data about the photo. For now, I'm just pulling pixelWidth and pixelHeight data from photo's PHAsset.
My Setup: I have a ViewController class which I created using Storyboard that includes the UI, ImagePickerController and it's delegate which after selecting photo, will update data in another class called TestGlobalData.
The problem I'm running into is that while I'm able to update variables from ViewController to TestGlobalData, I can't get it to update back on ViewController
Here is my code. Any help would be appreciated, I'm totally stumped (As mentioned I'm also new to Swift, so pointing out any fundamental things I'm not getting would be appreciated too! )
// TestViewController.swift
class TestViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var testPhotoView: UIImageView!
#IBOutlet weak var testWidthLabel: UILabel!
#IBOutlet weak var testHeightLabel: UILabel!
var testWidthText: String?
var testHeightText: String?
var selectionFromPicker: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
}
// Get imagePickerController ///////////////////////////////////////////////////////////////////
#IBAction func getPhotoButton(_ sender: Any) {
getImagePicker()
}
func getImagePicker() {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.sourceType = .photoLibrary
imagePickerController.allowsEditing = false
present (imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
defer { dismiss (animated: true, completion: nil)}
guard let selectedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
guard let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset else { return }
selectionFromPicker = selectedImage
let data = TestGlobalData()
data.testData = asset // Updates PHAsset
data.updateData() // Data shows as updated here
data.pushData() // Data shows as updated here too
self.updateTestPhoto() // Photo updates successfully (photo does not get passed)
self.textToLabel() // Assigns text to UILobel
self.checkData() // Data is lost and shows as nil here
}
// Functions //////////////////////////////////////////////////////////////////////////////
// Assign Text To Label
func textToLabel() {
testWidthLabel.text = testWidthText
testHeightLabel.text = testHeightText
}
// Update Photo To Selected
func updateTestPhoto() {
testPhotoView.image = selectionFromPicker
}
// Final Check
// TestGlobalData.swift
class TestGlobalData {
var testData: PHAsset?
var testWidth = Int()
var testHeight = Int()
var widthInString = String()
var heightInString = String()
func updateData() {
testWidth = testData!.pixelWidth
testHeight = testData!.pixelHeight
widthInString = String(testWidth)
heightInString = String(testHeight)
//widthInString and testWidth updated successfully at this point
}
func pushData() {
let vc = TestViewController()
vc.testWidthText = widthInString
vc.testHeightText = heightInString
//vc.testWidthText show as updated successfully here
}
}
The problem is you are creating a new instance of the TestViewController in the TestGlobalData class, specifically in the pushData() function.
Instead change the pushData to:
func pushData(vc: UIViewController) {
vc.testWidthText = widthInString
vc.testHeightText = heightInString
}
and change when you call the method as well to:
data.pushData(self)
Here is some additional resources that should help you understand everything better :)
https://code.tutsplus.com/tutorials/swift-from-scratch-an-introduction-to-classes-and-structures--cms-23197
https://www.python-course.eu/python3_class_and_instance_attributes.php
I am currently trying to build an app that uses google's autoML feature. I have trained a model and published it on google firebase and have integrated the necessary code into my app following the documentation:
https://firebase.google.com/docs/ml-kit/ios/label-images-with-automl
I am using a remote model instead of making it local. However when I try running the code, then choose an image in the simulator, an empty list of predictions is output in the console.
I have also turned on the debugging feature, but this has not helped me fix my error. This is the code I am running in ViewController:
import UIKit
import CoreML
import Vision
import Firebase
import FirebaseMLCommon
var serverImage: UIImage? = nil
var topResult = ""
class ViewController: UIViewController {
#IBOutlet var skinDiseaseImageView: UIImageView!
#IBOutlet var result1Label: UILabel!
#IBOutlet var result1Confidence: UILabel!
#IBOutlet var result2Label: UILabel!
#IBOutlet var result2Confidence: UILabel!
#IBOutlet var result3Label: UILabel!
#IBOutlet var result3Confidence: UILabel!
override func viewDidLoad() {
let initialConditions = ModelDownloadConditions(allowsCellularAccess: true,
allowsBackgroundDownloading: true)
let updateConditions = ModelDownloadConditions(allowsCellularAccess: false,
allowsBackgroundDownloading: true)
let remoteModel = RemoteModel(
name: "skinDiseaseModel", // The name you assigned in the console.
allowsModelUpdates: true,
initialConditions: initialConditions,
updateConditions: updateConditions
)
ModelManager.modelManager().register(remoteModel)
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController {
#IBAction func selectImage(_ sender: Any) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = .savedPhotosAlbum
present(pickerController, animated: true)
}
}
extension ViewController: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
dismiss(animated: true)
guard let skinImage = info[UIImagePickerControllerOriginalImage] as? UIImage else {
fatalError("Error Retrieving Image Line 95")
}
var skinImageToDiagnose = VisionImage(image: skinImage)
serverImage = skinImage
skinDiseaseImageView.image = skinImage
let labelerOptions = VisionOnDeviceAutoMLImageLabelerOptions(
remoteModelName: "skinDiseaseModel", // Or nil to not use a remote model
localModelName: nil // Or nil to not use a bundled model
)
labelerOptions.confidenceThreshold = 0 // Evaluate your model in the Firebase console
// to determine an appropriate value.
let labeler = Vision.vision().onDeviceAutoMLImageLabeler(options: labelerOptions)
var topThreeResults = [String]()
var topThreeConfidences = [String]()
labeler.process(skinImageToDiagnose) { labels, error in
guard error == nil, let labels = labels
else {
print(error)
return
}
//task succeeded
print("1")
print(labels)
var counter = 0
for label in labels {
topThreeResults.append(String(describing: label))
topThreeConfidences.append(String(describing: label.confidence))
counter = counter + 1
print("counter")
if counter == 3 {
break
}
}
}
result1Label.text = topThreeResults[0]
result1Confidence.text = (topThreeConfidences[0] + "%")
result2Label.text = topThreeResults[1]
result2Confidence.text = (topThreeConfidences[1] + "%")
result3Label.text = topThreeResults[2]
result3Confidence.text = (topThreeConfidences[2] + "%")
}
}
This is the error I recieved:
Fatal error: Index out of range
2019-08-31 19:50:19.763469-0700 medicalAppFinal[13776:2281569]
(lldb)
I reasoned that the index out of range problem is due to the list of labels(output predictions) being empty after having printed it. Thus I understand why it is index out of range, but I do not know why I am recieving an empty list after passing in the image into labeler.process() How do I solve this error? Tell me if you need more information
It was a logic error. This part of the code:
result1Label.text = topThreeResults[0]
result1Confidence.text = (topThreeConfidences[0] + "%")
result2Label.text = topThreeResults[1]
result2Confidence.text = (topThreeConfidences[1] + "%")
result3Label.text = topThreeResults[2]
result3Confidence.text = (topThreeConfidences[2] + "%")
should have been inside the labeler.process() function. Otherwise it was running the above code without having even retrieved a list of predictions, thus causing the fatal error. By placing it inside, I ensure it has retrieved the list of predictions and only then runs the above code to find specific values in the list.
I'm creating an app that plays a sound when a button is clicked. It consists of UIButton to play the sound, UIImageView to display the associated image, and another UIButton which I'm using like a label to describe the button. I want to be able to configure all three parameters so I can change them remotely from Firebase. So far I figured out how to change the label, but I want to be able to change the URL that the sound and image load from. Here is my code:
import UIKit
import Firebase
import AVKit
class FirebaseViewController: UIViewController, AVAudioPlayerDelegate {
//These variables are for my sound when I click a button
var firesound1 = AVPlayer()
var firesound2 = AVPlayer()
var firesound3 = AVPlayer()
//These outlets reference the labels(UIButton) and UIImageView in the storyboard
#IBOutlet weak var firelabel1: UIButton!
#IBOutlet weak var firelabel2: UIButton!
#IBOutlet weak var firelabel3: UIButton!
#IBOutlet weak var fireimage1: UIImageView!
#IBOutlet weak var fireimage2: UIImageView!
#IBOutlet weak var fireimage3: UIImageView!
func updateViewWithRCValues() {
//These remote config options allow me to change the text of the UIButton, which here I'm using like a UILabel
firelabel1.setTitle(buttonLabel1, for: .normal)
let buttonLabel2 = RemoteConfig.remoteConfig().configValue(forKey: "label2").stringValue ?? ""
firelabel2.setTitle(buttonLabel2, for: .normal)
let buttonLabel3 = RemoteConfig.remoteConfig().configValue(forKey: "label3").stringValue ?? ""
firelabel3.setTitle(buttonLabel3, for: .normal)
let url = RemoteConfig.remoteConfig().configValue(forKey: "url1").stringValue ?? ""
firelabel3.setTitle(buttonLabel3, for: .normal)
}
func setupRemoteConfigDefaults() {
let defaultValues = [
"label1": "" as NSObject,
"label2": "" as NSObject,
"label3": "" as NSObject
]
RemoteConfig.remoteConfig().setDefaults(defaultValues)
}
func fetchRemoteConfig() {
// Remove this before production!!
let debugSettings = RemoteConfigSettings(developerModeEnabled: true)
RemoteConfig.remoteConfig().configSettings = debugSettings!
RemoteConfig.remoteConfig().fetch(withExpirationDuration: 0) { [unowned self] (status, error) in guard error == nil else {
print ("Error fetching remote values: \(String(describing: error))")
return
}
print("Retrieved values from the cloud")
RemoteConfig.remoteConfig().activateFetched()
self.updateViewWithRCValues()
}
}
override func viewDidLoad() {
super.viewDidLoad()
setupRemoteConfigDefaults()
fetchRemoteConfig()
//This code loads an image from a url into a UIImageView. I want to be able to configure the url like a parameter so I can change the url from the firebase website.
let url = URL(string: "https://ichef-1.bbci.co.uk/news/976/media/images/83351000/jpg/_83351965_explorer273lincolnshirewoldssouthpicturebynicholassilkstone.jpg")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if (error != nil)
{
print("ERROR")
}
else
{
var documentsDirectory: String?
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if paths.count > 0
{
documentsDirectory = paths [0]
let savePath = documentsDirectory! + "/ImageOne"
FileManager.default.createFile(atPath: savePath, contents: data, attributes: nil)
DispatchQueue.main.async
{
self.fireimage1.image = UIImage(named: savePath)
}
}
}
}
task.resume()
}
//This code plays the sounds. I also want to be able to configure the url like a parameter.
#IBAction func soundpressed1(_ sender: Any) {
let sound1 = AVPlayerItem(url: URL(string: "https://firebasestorage.googleapis.com/v0/b/mlg-soundboard-2018-edition.appspot.com/o/hitmarker.mp3?alt=media&token=e5d342d6-4074-4c50-ad9d-f1e41662d9e9")!)
firesound1 = AVPlayer(playerItem: sound1)
firesound1.play()
}
override func didReceiveMemoryWarning() {
}
}
Basically I want to be able to swap out the URLs with Remote Config.
You can either create separate keys in Remote config for Text, Sound URL and Image URL.
Or you can create a key called button_config and supply all the three params in a JSON
button_config = {"text" : "My button label", "sound_url" : "https://foo.com/sound.mp3", "image_url" : "https://foo.com/image.png"}
I have added a button, that adds points to a label.
Everything works fine, and the label is then persisted into core data and appears in a tableViewCell.
When I get back to my detailsVC, I get my label with the persisted number, but when I click on the button again to increment the points, the label goes back to zero.
Here's a part of my code:
import UIKit
import CoreData
class GoalDetailsVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// IBOutlets:
#IBOutlet weak var titleTF: UITextField!
#IBOutlet weak var detailsTextView: UITextView!
#IBOutlet weak var pointsLabel: UILabel!
#IBOutlet weak var dateOfEntry: UILabel!
#IBOutlet weak var thumbImage: UIImageView!
// properties
var currentScore = 0
var goalToEdit: Goal? // goalToEdit is now an optional, and it needs to be unwrapped when used.
var imagePicker: UIImagePickerController!
override func viewDidLoad() {
super.viewDidLoad()
if let topItem = self.navigationController?.navigationBar.topItem {
topItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
}
// now we need to say that if there is a goal to edit ( not equal to nil), then we load the Goal data with the loadGoalData() function.
if goalToEdit != nil {
loadGoalData()
}
imagePicker = UIImagePickerController()
imagePicker.delegate = self
}
// when button is pressed, I need to
// 1 : add a point to the pointsLabel
// 2 : put the current date to the dateLabel
// 3 : persist the new points and date labels.
#IBAction func plusOneBtnPressed(_ sender: UIButton) {
currentScore += 1
pointsLabel.text = "\(currentScore)"
}
#IBAction func minusOneBtnPressed(_ sender: Any) {
}
#IBAction func savePressed(_ sender: Any) {
var goal: Goal!
let picture = Image(context: context) // Image = Entity
picture.image = thumbImage.image // image = attribute
if goalToEdit == nil {
goal = Goal(context: context)
} else {
goal = goalToEdit
}
goal.toImage = picture
// this is unwrapping because the original goalToEdit is an optional.
if let title = titleTF.text {
goal.title = title
}
// we saveed, or persisted the TITLE
if let points = pointsLabel.text {
goal.plusOnes = (points as NSString).intValue
}
// we saveed, or persisted the POINTS
if let details = detailsTextView.text {
goal.details = details
}
// we saved, or persisted the DETAILS
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE MMM d yyyy"
if let date = dateFormatter.date(from: dateFormatter.dateFormat) {
goal.lastEntry = date as NSDate
}
// we saved, or persisted the DATE
ad.saveContext()
_ = navigationController?.popViewController(animated: true)
}
func loadGoalData() {
if let goal = goalToEdit {
titleTF.text = goal.title
pointsLabel.text = "\(goal.plusOnes)"
detailsTextView.text = goal.details
dateOfEntry.text = (String(describing: goal.lastEntry))
thumbImage.image = goal.toImage?.image as? UIImage
}
}
When you get the persisted number you should also set currentScore to that value (if greater than 0). I believe currently you only set it to 0 that's why the incrementation starts over.