When ever I tap the sumitBtn to train the model, I get an error for exampleDrawings.addDrawing(drawing) it is getting the drawing as an image properly so I'm not sure why its getting nil there.
struct ExampleDrawingSet {
/// The desired number of drawings to update the model
private let requiredDrawingCount = 3
/// Collection of the training drawings
private var trainingDrawings = [UserDrawing]()
/// The emoji or sticker that the model should predict when passed similar images
//let emoji: Character
let key = "Test Key"
/// A Boolean that indicates whether the instance has all the required drawings.
var isReadyForTraining: Bool { trainingDrawings.count == requiredDrawingCount }
var count: Int {
trainingDrawings.count
}
/*
init(for emoji: Character) {
self.emoji = emoji
}*/
/// Creates a batch provider of training data given the contents of `trainingDrawings`.
/// - Tag: DrawingBatchProvider
var featureBatchProvider: MLBatchProvider {
var featureProviders = [MLFeatureProvider]()
let inputName = "drawing"
let outputName = "label"
for drawing in trainingDrawings {
let inputValue = drawing.featureValue
let outputValue = MLFeatureValue(string: key)
//let outputValue = MLFeatureValue(string: String(emoji))
let dataPointFeatures: [String: MLFeatureValue] = [inputName: inputValue,
outputName: outputValue]
if let provider = try? MLDictionaryFeatureProvider(dictionary: dataPointFeatures) {
featureProviders.append(provider)
}
}
return MLArrayBatchProvider(array: featureProviders)
}
/// Adds a drawing to the private array, but only if the type requires more.
mutating func addDrawing(_ drawing: UserDrawing) {
if trainingDrawings.count requiredDrawingCount {
trainingDrawings.append(drawing)
}
}
}
class Page6MachinLearning: UIViewController, PKCanvasViewDelegate {
@IBOutlet var canvasView: PKCanvasView!
@IBOutlet var submitBtn: UIButton!
var submitWorkItem: DispatchWorkItem?
var exampleDrawings: ExampleDrawingSet!
var drawingCount = Int()
override func viewDidLoad() {
super.viewDidLoad()
configureCanvasView()
}
@IBAction func submitBtn(_ sender: Any) {
print("User tapped \"Done\"; kicking off model update...")
submitDrawing(canvasView: canvasView)
drawingCount+=1
print(drawingCount)
if drawingCount == 3 {
// Convert the drawings into a batch provider as the update input.
let drawingTrainingData = exampleDrawings.featureBatchProvider
// Update the Drawing Classifier with the drawings.
DispatchQueue.global(qos: .userInitiated).async {
ModelUpdater.updateWith(trainingData: drawingTrainingData) {
DispatchQueue.main.async { self.dismiss(animated: true, completion: nil) }
}
}
} // End If
}
/// Configures the `PKCanvasView`
func configureCanvasView() {
canvasView.backgroundColor = .clear
canvasView.isOpaque = false
canvasView.tool = PKInkingTool(.pen, color: .white, width: 20)
canvasView.delegate = self
canvasView.allowsFingerDrawing = true
}
// MARK: - PKCanvasViewDelegate
func canvasViewDidBeginUsingTool(_ canvasView: PKCanvasView) {
// Cancel the submission of the previous drawing when a user begins drawing
// This lets the user draw another stroke without a time limit of 0.5 seconds
submitWorkItem?.cancel()
}
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
let drawingRect = canvasView.drawing.bounds
guard drawingRect.size != .zero else {
return
}
// Check if the user is crossing out previous stickers
let intersectingViews = canvasView.subviews
.compactMap { $0 as? UILabel }
.filter { $0.frame.intersects(drawingRect) }
guard intersectingViews.isEmpty else {
// If the current drawing intersects with existing stickers,
// remove those stickers
intersectingViews.forEach { $0.removeFromSuperview() }
canvasView.drawing = PKDrawing()
return
}
submitWorkItem = DispatchWorkItem { self.submitDrawing(canvasView: canvasView) }
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 5, execute: submitWorkItem!)
}
func submitDrawing(canvasView: PKCanvasView) {
// Get the rectangle containing the drawing
let drawingRect = canvasView.drawing.bounds.containingSquare
// Turn the drawing into an image
// Because this image may be displayed at a larger scale in the training view,
// a scale of 2.0 is used for smooth rendering.
let image = canvasView.drawing.image(from: drawingRect, scale: UIScreen.main.scale * 2.0)
// Store the white tinted version and the rectangle in a drawing object
let drawing = UserDrawing(image: image.cgImage!, rect: drawingRect)
didProduce(drawing: drawing, sender: self)
DispatchQueue.main.async {
canvasView.drawing = PKDrawing()
}
}
}
extension Page6MachinLearning: DrawingDelegate {
func didProduce(drawing: UserDrawing, sender: Any?) {
DispatchQueue.main.async { self.addDrawing(drawing) }
}
func addDrawing(_ drawing: UserDrawing) {
print("drawing - \(drawing)")
exampleDrawings.addDrawing(drawing) //Error here
}
}