1

I am relatively new to coding, and I have recently been working on a program that allows a user to scan a crystal, using the iPhones rear camera, and it will identify what kind of crystal it is. I used CreateML to build the model, and Vision to identify the crystal. What I can't seem to figure out is how to get the results into the UI I built. The results are printing to the Xcode console.

Here's a picture of the Storyboard:

Picture of Storyboard and console

  • 1
    It might be easier for people to help if you could edit your post and provide more specific details. Code snippets, screenshots, etc. are always helpful to give context. – stef Feb 09 '21 at 04:47
  • 1
    @stef true, I figured as much. I'll edit the post with some more info. Thank you. – bigdogg99juherd Feb 09 '21 at 14:39

1 Answers1

1

I assume you want to draw a box around the detected crystal?

You should be getting a boundingBox of your crystal that looks something like this:

(0.166666666666667, 0.35, 0.66666666666667, 0.3)

These are "normalized" coordinates, which means that they are relative to the image that you send to Vision. I explain this more in detail here...

What you are used to What Vision returns

You need to convert these "normalized" coordinates to UIKit coordinates that you can use. To do that, I have this converting function:

func getConvertedRect(boundingBox: CGRect, inImage imageSize: CGSize, containedIn containerSize: CGSize) -> CGRect {
    
    let rectOfImage: CGRect
    
    let imageAspect = imageSize.width / imageSize.height
    let containerAspect = containerSize.width / containerSize.height
    
    if imageAspect > containerAspect { /// image extends left and right
        let newImageWidth = containerSize.height * imageAspect /// the width of the overflowing image
        let newX = -(newImageWidth - containerSize.width) / 2
        rectOfImage = CGRect(x: newX, y: 0, width: newImageWidth, height: containerSize.height)
        
    } else { /// image extends top and bottom
        let newImageHeight = containerSize.width * (1 / imageAspect) /// the width of the overflowing image
        let newY = -(newImageHeight - containerSize.height) / 2
        rectOfImage = CGRect(x: 0, y: newY, width: containerSize.width, height: newImageHeight)
    }
    
    let newOriginBoundingBox = CGRect(
    x: boundingBox.origin.x,
    y: 1 - boundingBox.origin.y - boundingBox.height,
    width: boundingBox.width,
    height: boundingBox.height
    )
    
    var convertedRect = VNImageRectForNormalizedRect(newOriginBoundingBox, Int(rectOfImage.width), Int(rectOfImage.height))
    
    /// add the margins
    convertedRect.origin.x += rectOfImage.origin.x
    convertedRect.origin.y += rectOfImage.origin.y
    
    return convertedRect
}

You can use it like this:

let convertedRect = self.getConvertedRect(
    boundingBox: observation.boundingBox,
    inImage: image.size, /// image is the image that you feed into Vision
    containedIn: self.previewView.bounds.size /// the size of your camera feed's preview view
)
self.drawBoundingBox(rect: convertedRect)

/// draw the rectangle
func drawBoundingBox(rect: CGRect) {
    let uiView = UIView(frame: rect)
    previewView.addSubview(uiView)
        
    uiView.backgroundColor = UIColor.orange.withAlphaComponent(0.2)
    uiView.layer.borderColor = UIColor.orange.cgColor
    uiView.layer.borderWidth = 3
}

Result (I'm doing a VNDetectRectanglesRequest):

If you want to "track" the detected object while your phone is moving, check out my answer here

aheze
  • 24,434
  • 8
  • 68
  • 125
  • 1
    This is definitely helpful, but I added more details in the original post to try and clarify the vague original post. I am trying to make a UI popup when a crystal is scanned to show details on said crystal. – bigdogg99juherd Feb 09 '21 at 16:44
  • @bigdogg99juherd got it. Is `showCrystalInfo` not working? – aheze Feb 09 '21 at 17:02
  • @bigdogg99juherd so what is happening currently? Can you add a screenshot? – aheze Feb 09 '21 at 17:58
  • I got you. Just added it. – bigdogg99juherd Feb 09 '21 at 23:21
  • @bigdogg99juherd so you are calling after `showCrystalInfo` after `print(firstObservation.identifier, firstObservation.confidence)`, right? Your perform segue code seems fine – aheze Feb 10 '21 at 18:19