I am trying to use VNDetectRectangleRequest
from Apple's Vision framework to automatically grab a picture of a card. However when I convert the points to draw the rectangle, it is misshapen and does not follow the rectangle is it should. I have been following this article pretty closely
One major difference is I am embedding my CameraCaptureVC in another ViewController so that the card will be scanned only when it is in this smaller window.
Below is how I set up the camera vc in the parent vc (called from viewDidLoad).
func configureSubviews() {
clearView.addSubview(cameraVC.view)
cameraVC.view.autoPinEdgesToSuperviewEdges()
self.addChild(cameraVC)
cameraVC.didMove(toParent: self)
}
Below is the code to draw the rectangle
func createLayer(in rect: CGRect) {
let maskLayer = CAShapeLayer()
maskLayer.frame = rect
maskLayer.cornerRadius = 10
maskLayer.opacity = 0.75
maskLayer.borderColor = UIColor.red.cgColor
maskLayer.borderWidth = 5.0
previewLayer.insertSublayer(maskLayer, at: 1)
}
func removeMask() {
if let sublayer = previewLayer.sublayers?.first(where: { $0 as? CAShapeLayer != nil }) {
sublayer.removeFromSuperlayer()
}
}
func drawBoundingBox(rect : VNRectangleObservation) {
let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -finalFrame.height)
let scale = CGAffineTransform.identity.scaledBy(x: finalFrame.width, y: finalFrame.height)
let bounds = rect.boundingBox.applying(scale).applying(transform)
createLayer(in: bounds)
}
func detectRectangle(in image: CVPixelBuffer) {
let request = VNDetectRectanglesRequest { (request: VNRequest, error: Error?) in
DispatchQueue.main.async {
guard let results = request.results as? [VNRectangleObservation],
let rect = results.first else { return }
self.removeMask()
self.drawBoundingBox(rect: rect)
}
}
request.minimumAspectRatio = 0.0
request.maximumAspectRatio = 1.0
request.maximumObservations = 0
let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: image, options: [:])
try? imageRequestHandler.perform([request])
}
This is my result. The red rectangle should follow the borders of the card but it is much too short and the origin is not at the top of the card even.

I have tried changing values in the drawBoundingBox
function but nothing seems to help. I have also tried converting the bounds in a different way like below but it is the same result, and changing these values gets hacky.
let scaledHeight: CGFloat = originalFrame.width / finalFrame.width * finalFrame.height
let boundingBox = rect.boundingBox
let x = finalFrame.width * boundingBox.origin.x
let height = scaledHeight * boundingBox.height
let y = scaledHeight * (1 - boundingBox.origin.y) - height
let width = finalFrame.width * boundingBox.width
let bounds = CGRect(x: x, y: y, width: width, height: height)
createLayer(in: bounds)
Would appreciate any help. Maybe since I am embedding it as a child VC I need to transform the coordinates a second time? I tried something like this to no avail but maybe I did it wrong or was missing something