Microblink: reading an image - ios

I'm asking the Microblink care reader to look at a photo of a card, rather than using the camera:
lazy var blinkCardRecognizer: MBCBlinkCardRecognizer = {
return MBCBlinkCardRecognizer()
}()
lazy var recognizerCollection: MBCRecognizerCollection = {
blinkCardRecognizer.extractCvv = false
blinkCardRecognizer.extractIban = false
blinkCardRecognizer.extractExpiryDate = false
let recognizerList = [blinkCardRecognizer]
return MBCRecognizerCollection(recognizers: recognizerList)
}()
My class has declared these two delegates:
MBCBlinkCardOverlayViewControllerDelegate, MBCScanningRecognizerRunnerDelegate
I'm sure that I'm passing this function a correct UIImage, and I do get to the processImage call:
func prepareToReadImage(_ theImage: UIImage?) {
let recognizerRunner: MBCRecognizerRunner = MBCRecognizerRunner(recognizerCollection: recognizerCollection)
recognizerRunner.scanningRecognizerRunnerDelegate = self
var image: MBCImage? = nil
if let anImage = theImage {
image = MBCImage(uiImage: anImage)
}
image?.cameraFrame = true
image?.orientation = MBCProcessingOrientation.left
let _serialQueue = DispatchQueue(label: "com.microblink.DirectAPI-sample-swift")
_serialQueue.async(execute: {() -> Void in
recognizerRunner.processImage(image!)
})
}
But this callback is not being hit:
func recognizerRunner(_ recognizerRunner: MBCRecognizerRunner, didFinishScanningWith state: MBCRecognizerResultState) {
if state == .valid {
print (state)
}
}
Can you see why it isn't? Does it matter that I see the log warning You are using time-limited license key!?

From the presented code, I can see that the recognizerRunner and the prepareToReadImage methods have been entered correctly.
However, in the first block of code, where you're defining the recognizer and the recognizerCollection, I can see that the issue could be with the MBCRecognizerCollection since its parameter, recognizers, is of type [MBCRecognizer], and you're placing there [MBCBlinkCardRecognizer]. I can suggest this solution to see if it works:
blinkCardRecognizer = MBCBlinkCardRecognizer()
var recognizerList = [MBCRecognizer]()
let recognizerCollection: MBCRecognizerCollection = {
blinkCardRecognizer.extractCvv = false
blinkCardRecognizer.extractIban = false
blinkCardRecognizer.extractExpiryDate = false
recognizerList.append(blinkCardRecognizer!)
return MBCRecognizerCollection(recognizers: recognizerList)
}()
recognizerRunner = MBCRecognizerRunner(recognizerCollection: recognizerCollection)
The only difference is that I've previously defined the BlinkCardRecognizer and the RecognizerRunner, so that should not make any difference:
private var recognizerRunner: MBCRecognizerRunner?
private var blinkCardRecognizer: MBCBlinkCardRecognizer?
Just to add here, it does not matter if you see the You are using time-limited license key!, it is simply an indicator that you using a time-limited key and it should not affect the scanning process.

Related

How to get a ordinary Mixamo character animation working in SceneKit?

Go to mixamo.com, pick a character, tap animations, pick one, simply download as .dae.
Have the file on your Mac desktop; tap file info). It will perfectly animate the character move.
Xcode, drag in the folder. Tap the .dae file, tap the Play icon at the bottom. It will perfectly animate the character move.
Now, add the character to your existing SceneKit scene. For example:
let p = Bundle.main.url(forResource: "File Name", withExtension: "dae")!
modelSource = SCNSceneSource(url: p, options: nil)!
let geom = modelSource.entryWithIdentifier("geometry316",
withClass: SCNGeometry.self)! as SCNGeometry
theModel = SCNNode(geometry: geom)
.. your node .. .addChildNode(theModel)
(To get the geometry name, just look in the .dae text example )
You will PERFECTLY see the character, in T pose
However it seems impossible to run the animation on the character.
Code would look something like ...
theAnime = amySource.entryWithIdentifier("unnamed_animation__0", withClass: CAAnimation.self)!
theModel.addAnimation(theAnime, forKey:"aKey")
No matter what I try it just doesn't animate.
At the moment you addAnimation, the character jumps to a different static position, and does nothing. (If you arrange to "end" the animation removeAllAnimations(), it simply returns to the T-pose.)
Clearly the dae file is perfect since the shows the animation perfectly simply in the Mac finder, and perfectly on the actual screen of the .dae file in Xcode!
In short, from the mixamo image above, has anyone been able to get the animation to run, actually, in a SceneKit scene?
(PS not ARKit .. scene kit.)
First, you need your character in the T-Position only. Download that file as Collada (DAE) with the Skin. Do NOT include any animations to this File. No Further modifications are required to this file then.
Then, for any animation effect you will implement like walking, running, dancing, or whatever - do it like so:
Test/Apply your desired animation in Mixamo on the character, adjust the settings as you want then download it. Here it is very important to Download as Collada (DAE) and choose WITHOUT Skin!!! Leave Framerate and keyframe reduction default.
This will give you a single DAE File for each animation you want to implement. This DAE contains no mesh data and no rig. It only contains the deformations of the Model to which it belongs (this is why you choose to download it without Skin).
Then you need to do two additional operations on all DAE Files which contains animations.
First, you need to pretty-print the XML structure of each DAE containing an animation. You can do this i.Ex. using the XML Tools in Notepad++ or you open a terminal on your Mac and use this command:
xmllint —-format my_anim_orig.dae > my_anim.dae
Then install this Tool here on your Mac.
(https://drive.google.com/file/d/0B1_uvI21ZYGUaGdJckdwaTRZUEk/edit?usp=sharing)
Convert all of your DAE Animations with this converter:
(But do NOT convert your T-Pose Model using this tool!!!)
No we are ready to setup the Animation:
you should organise the DAE's within the art.scnassets folder
Let's configure this:
I usually organise this within a struct called characters. But any other implementation will do
add this:
struct Characters {
// MARK: Characters
var bodyWarrior : SCNNode!
private let objectMaterialWarrior : SCNMaterial = {
let material = SCNMaterial()
material.name = "warrior"
material.diffuse.contents = UIImage.init(named: "art.scnassets/warrior/textures/warrior_diffuse.png")
material.normal.contents = UIImage.init(named: "art.scnassets/warrior/textures/warrior_normal.png")
material.metalness.contents = UIImage.init(named: "art.scnassets/warrior/textures/warrior_metalness.png")
material.roughness.contents = UIImage.init(named: "art.scnassets/warrior/textures/warrior_roughness.png")
material.ambientOcclusion.contents = UIImage.init(named: "art.scnassets/warrior/textures/warrior_AO.png")
material.lightingModel = .physicallyBased
material.isDoubleSided = false
return material
}()
// MARK: MAIN Init Function
init() {
// Init Warrior
bodyWarrior = SCNNode(named: "art.scnassets/warrior/warrior.dae")
bodyWarrior.childNodes[1].geometry?.firstMaterial = objectMaterialWarrior // character body material
print("Characters Init Completed.")
}
}
Then you can init the struct i.Ex. in the viewDidLoad
var characters = Characters()
Pay Attention to use the correct childNodes!
in this case the childNodes[1] is the visible mesh and childNodes[0] then will be the animation Node.
you might also implement this SceneKit extension to your code, it is very useful to import Models. (attention, it will organise the model nodes as Childs from a new node!)
extension SCNNode {
convenience init(named name: String) {
self.init()
guard let scene = SCNScene(named: name) else {return}
for childNode in scene.rootNode.childNodes {addChildNode(childNode)}
}
}
also add that extension below. You'll need it for the animation player later.
extension SCNAnimationPlayer {
class func loadAnimation(fromSceneNamed sceneName: String) -> SCNAnimationPlayer {
let scene = SCNScene( named: sceneName )!
// find top level animation
var animationPlayer: SCNAnimationPlayer! = nil
scene.rootNode.enumerateChildNodes { (child, stop) in
if !child.animationKeys.isEmpty {
animationPlayer = child.animationPlayer(forKey: child.animationKeys[0])
stop.pointee = true
}
}
return animationPlayer
}
}
Handle Character setup and Animation like so:
(here is a simplified version of my Class)
class Warrior {
// Main Nodes
var node = SCNNode()
private var animNode : SCNNode!
// Control Variables
var isIdle : Bool = true
// For Initial Warrior Position and Scale
private var position = SCNMatrix4Mult(SCNMatrix4MakeRotation(0,0,0,0), SCNMatrix4MakeTranslation(0,0,0))
private var scale = SCNMatrix4MakeScale(0.03, 0.03, 0.03) // default size ca 6m height
// MARK: ANIMATIONS
private let aniKEY_NeutralIdle : String = "NeutralIdle-1" ; private let aniMAT_NeutralIdle : String = "art.scnassets/warrior/NeutralIdle.dae"
private let aniKEY_DwarfIdle : String = "DwarfIdle-1" ; private let aniMAT_DwarfIdle : String = "art.scnassets/warrior/DwarfIdle.dae"
private let aniKEY_LookAroundIdle : String = "LookAroundIdle-1" ; private let aniMAT_LookAroundIdle : String = "art.scnassets/warrior/LookAround.dae"
private let aniKEY_Stomp : String = "Stomp-1" ; private let aniMAT_Stomp : String = "art.scnassets/warrior/Stomp.dae"
private let aniKEY_ThrowObject : String = "ThrowObject-1" ; private let aniMAT_ThrowObject : String = "art.scnassets/warrior/ThrowObject.dae"
private let aniKEY_FlyingBackDeath : String = "FlyingBackDeath-1" ; private let aniMAT_FlyingBackDeath : String = "art.scnassets/warrior/FlyingBackDeath.dae"
// MARK: MAIN CLASS INIT
init(index: Int, scaleFactor: Float = 0.03) {
scale = SCNMatrix4MakeScale(scaleFactor, scaleFactor, scaleFactor)
// Config Node
node.index = index
node.name = "warrior"
node.addChildNode(GameViewController.characters.bodyWarrior.clone()) // childNodes[0] of node. this holds all subnodes for the character including animation skeletton
node.childNodes[0].transform = SCNMatrix4Mult(position, scale)
// Set permanent animation Node
animNode = node.childNodes[0].childNodes[0]
// Add to Scene
gameScene.rootNode.addChildNode(node) // add the warrior to scene
print("Warrior initialized with index: \(String(describing: node.index))")
}
// Cleanup & Deinit
func remove() {
print("Warrior deinitializing")
self.animNode.removeAllAnimations()
self.node.removeAllActions()
self.node.removeFromParentNode()
}
deinit { remove() }
// Set Warrior Position
func setPosition(position: SCNVector3) { self.node.position = position }
// Normal Idle
enum IdleType: Int {
case NeutralIdle
case DwarfIdle // observe Fingers
case LookAroundIdle
}
// Normal Idles
func idle(type: IdleType) {
isIdle = true // also sets all walking and running variabled to false
var animationName : String = ""
var key : String = ""
switch type {
case .NeutralIdle: animationName = aniMAT_NeutralIdle ; key = aniKEY_NeutralIdle // ; print("NeutralIdle ")
case .DwarfIdle: animationName = aniMAT_DwarfIdle ; key = aniKEY_DwarfIdle // ; print("DwarfIdle ")
case .LookAroundIdle: animationName = aniMAT_LookAroundIdle ; key = aniKEY_LookAroundIdle // ; print("LookAroundIdle")
}
makeAnimation(animationName, key, self.animNode, backwards: false, once: false, speed: 1.0, blendIn: 0.5, blendOut: 0.5)
}
func idleRandom() {
switch Int.random(in: 1...3) {
case 1: self.idle(type: .NeutralIdle)
case 2: self.idle(type: .DwarfIdle)
case 3: self.idle(type: .LookAroundIdle)
default: break
}
}
// MARK: Private Functions
// Common Animation Function
private func makeAnimation(_ fileName : String,
_ key : String,
_ node : SCNNode,
backwards : Bool = false,
once : Bool = true,
speed : CGFloat = 1.0,
blendIn : TimeInterval = 0.2,
blendOut : TimeInterval = 0.2,
removedWhenComplete : Bool = true,
fillForward : Bool = false
)
{
let anim = SCNAnimationPlayer.loadAnimation(fromSceneNamed: fileName)
if once { anim.animation.repeatCount = 0 }
anim.animation.autoreverses = false
anim.animation.blendInDuration = blendIn
anim.animation.blendOutDuration = blendOut
anim.speed = speed; if backwards {anim.speed = -anim.speed}
anim.stop()
print("duration: \(anim.animation.duration)")
anim.animation.isRemovedOnCompletion = removedWhenComplete
anim.animation.fillsForward = fillForward
anim.animation.fillsBackward = false
// Attach Animation
node.addAnimationPlayer(anim, forKey: key)
node.animationPlayer(forKey: key)?.play()
}
}
you can then initialise the Class Object after you initialised the characters struct.
the rest you'll figure out, come back on me, if you have questions or need a complete example App :)

MDCTextInputController error messages cover entered text

Using material-components/material-components-ios v85.8.0
import MaterialComponents
....
var usernameTextField = MDCTextField()
var userNameTextLayout = MDCTextInputControllerUnderline()
usernameTextField = {
let usernameTextEdit = MDCTextField()
usernameTextEdit.translatesAutoresizingMaskIntoConstraints = false
usernameTextEdit.clearButtonMode = .unlessEditing
usernameTextEdit.backgroundColor = .white
return usernameTextEdit
}()
userNameTextLayout.textInput = usernameTextField
userNameTextLayout.placeholderText = "Username"
// add to view
....
private func isUserNameValid() -> Bool {
let enteredUsername = usernameTextField.text ?? ""
if (!enteredUsername.isValidEmail) {
userNameTextLayout.setErrorText("Invalid e-mail address",
errorAccessibilityValue: nil)
return false
}
}
Error messages cover the text entered and it looks bad:
Android material design does however place the error underneath the line:
Was wondering if there's a way to do that, or if I'm doing it wrong.
I followed their tutorial: https://codelabs.developers.google.com/codelabs/mdc-101-swift/#2
I had set the height of the MDCTextField too small. In increasing it from 50->80pts it did the trick and moved the error message below the line.

iOS - Type 'DispatchQueue' has no member 'GlobalAttributes'

I have the following error:
Type 'DispatchQueue' has no member 'GlobalAttributes'
In my code. Do you know why?
public typealias Classification = (Label: DigitLabel, Confidence: CGFloat, BestPrototypeIndex: Int)
public func classifyDigit(digit: DigitStrokes, votesCounted: Int = 5, scoreCutoff: CGFloat = 0.8) -> Classification? {
if let normalizedDigit = normalizeDigit(inputDigit: digit) {
let serviceGroup = DispatchGroup()
let queue = ***DispatchQueue.global(attributes: DispatchQueue.GlobalAttributes.qosUserInitiated)***
let serialResultsQueue = DispatchQueue(label: "collect_results")
var bestMatches = SortedMinArray<CGFloat, (DigitLabel, Int)>(capacity: votesCounted)
for (label, prototypes) in self.normalizedPrototypeLibrary {
queue.async(group: serviceGroup) {
var localBestMatches = SortedMinArray<CGFloat, (DigitLabel, Int)>(capacity: votesCounted)
var index = 0
for prototype in prototypes {
if prototype.count == digit.count {
let score = self.classificationScore(sample: normalizedDigit, prototype: prototype)
//if score < scoreCutoff {
localBestMatches.add(value: score, element: (label, index))
//}
}
index += 1
}
serialResultsQueue.async(group: serviceGroup) {
for (score, bestMatch) in localBestMatches {
bestMatches.add(value: score, element: bestMatch)
}
}
}
}
I also attached the file, just in case.
According to Apple's documentation, the message is correct.
Maybe you want to use DispatchQueue.Attributes?
EDIT:
I just had a closer look at the documentation:
DispatchQueue.global() seems to be changed. The documentation shows this declaration:
class func global(qos: DispatchQoS.QoSClass = default) -> DispatchQueue
I tested some variations, Xcode proposed and found this line:
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated)
I did not test, if this works 100% with your example, but Xcode will compile it

Using a Bool in a Swift condition statement gives error: "initialization of variable newCard was never used"

Essentially what I want to do is have the bool variable newCard be set to true when playButton runs, that way when firstCardPressed runs, it will only set the image if its True, and then reset it back to false.
The issue is that I'm getting an error stating
initialization of variable newCard was never used
But I can't seem to figure out how to initialize it correctly.
#IBAction func playButton(sender: UIButton) {
// Randomize the face up stack
let firstRandomNumber:Int = Int(arc4random_uniform(52))
let firstCardString:String = self.deckArray[firstRandomNumber]
self.faceUpStack.image = UIImage(named: firstCardString)
var newCard = true
}
#IBAction func firstCardPressed(sender: UIButton) {
let cardKept = self.faceUpStack.image
if newCard == true {
self.firstCard.image = cardKept
newCard = false
}
}
var newCard = false
#IBAction func playButton(sender: UIButton) {
// Randomize the face up stack
let firstRandomNumber:Int = Int(arc4random_uniform(52))
let firstCardString:String = self.deckArray[firstRandomNumber]
self.faceUpStack.image = UIImage(named: firstCardString)
newCard = true
}
#IBAction func firstCardPressed(sender: UIButton) {
let cardKept = self.faceUpStack.image
if newCard {
self.firstCard.image = cardKept
newCard = false
}
}
newCard in your playButtonFunc is a local variable. It appears when the func starts, and it disappears without a track when the func finishes. "var newCard = true" as the very last statement in your func is pointless, as the compiler tells you, because the local variable newCard disappears an instance later.
You probably want it to be an instance variable in your class. There's a good chance that it already is, because otherwise firstCardPressed wouldn't compile.

AVAudioPlayerDelegate doesn't call the method

Here is the method inside a class:
import UIKIt
import Foundation
class notMoving {
var drumPlayerObject = drumPlayer()
var fileManagerObject = fileManager1()
let drumStrength = 1
var bassStrength = 1
var synthStrength = 1
var indexToPlay: Int = 0
// here we start the drum player.
func startToPlay() {
fileManagerObject.clearPlayedListDrum(drumStrength, KeyNoteOfInstDrum: "C")
if let indexToPlay = fileManager1().randomizeTheNextInstrument(fileManager1().drums, Strength: drumStrength, KeyNote: "C") {
fileManager1().drums[indexToPlay].4 = true
self.indexToPlay = indexToPlay
}
let instrument = fileManager1().drums[self.indexToPlay].0
let name = fileManager1().drums[self.indexToPlay].1
let length = fileManager1().drums[self.indexToPlay].2
let power = fileManager1().drums[self.indexToPlay].3
let ifplayed = fileManager1().drums[self.indexToPlay].4
let tempo = Double(fileManager1().drums[self.indexToPlay].5)
let bridge: Bool = false
let extention = fileManagerObject.extentionOfFile
let loops = fileManager1().drumNumberOfLoops()
drumPlayerObject.play(instrument, name: name, extentionOfFile: extention,
length: length, power: power, ifplayed: ifplayed, tempo: tempo, loops:
loops, bridge: bridge)
fileManager1().clearPlayedListDrum(drumStrength, KeyNoteOfInstDrum: "C")
}
}
And here is AVAudioPlayerDelegate extension for a drumPlayer class.
extension drumPlayer : AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
println("finished playing \(flag)")
var notMovingObject = notMoving()
notMovingObject.startToPlay()
}
func audioPlayerDecodeErrorDidOccur(player: AVAudioPlayer!, error: NSError!) {
println("\(error.localizedDescription)")
}
}
But audioPlayerDidFinishPlaying doesn't call the startToPlay method after the file is finished.It just only prints "finished playing true"
What I'm doing wrong?
Your notMovingObject is not retained anywhere, thus after program exits audioPlayerDidFinishPlaying() object is deallocated.
I found a solution by creating an instance self.notMovingObject = NotMoving() inside the "func play" of my player. So the instance is created only after the player starts.

Resources