<commit_before>//
//  Copyright (c) 2018 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import AVFoundation
import CoreVideo

import FirebaseMLVision
import FirebaseMLVisionObjectDetection
import FirebaseMLCommon
import FirebaseMLVisionAutoML

@objc(CameraViewController)
class CameraViewController: UIViewController {
  private let detectors: [Detector] = [.onDeviceAutoMLImageLabeler,
                                       .onDeviceFace,
                                       .onDeviceText,
                                       .onDeviceObjectProminentNoClassifier,
                                       .onDeviceObjectProminentWithClassifier,
                                       .onDeviceObjectMultipleNoClassifier,
                                       .onDeviceObjectMultipleWithClassifier]
  private var currentDetector: Detector = .onDeviceFace
  private var isUsingFrontCamera = true
  private var previewLayer: AVCaptureVideoPreviewLayer!
  private lazy var captureSession = AVCaptureSession()
  private lazy var sessionQueue = DispatchQueue(label: Constant.sessionQueueLabel)
  private lazy var vision = Vision.vision()
  private var lastFrame: CMSampleBuffer?
  private var areAutoMLModelsRegistered = false
  private lazy var modelManager = ModelManager.modelManager()
  @IBOutlet var downloadProgressView: UIProgressView!

  private lazy var previewOverlayView: UIImageView = {

    precondition(isViewLoaded)
    let previewOverlayView = UIImageView(frame: .zero)
    previewOverlayView.translatesAutoresizingMaskIntoConstraints = false
    return previewOverlayView
  }()

  private lazy var annotationOverlayView: UIView = {
    precondition(isViewLoaded)
    let annotationOverlayView = UIView(frame: .zero)
    annotationOverlayView.translatesAutoresizingMaskIntoConstraints = false
    return annotationOverlayView
  }()

  // MARK: - IBOutlets

  @IBOutlet private weak var cameraView: UIView!

  // MARK: - UIViewController

  override func viewDidLoad() {
    super.viewDidLoad()

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    setUpPreviewOverlayView()
    setUpAnnotationOverlayView()
    setUpCaptureSessionOutput()
    setUpCaptureSessionInput()
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    startSession()
  }

  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    stopSession()
  }

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    previewLayer.frame = cameraView.frame
  }

  // MARK: - IBActions

  @IBAction func selectDetector(_ sender: Any) {
    presentDetectorsAlertController()
  }

  @IBAction func switchCamera(_ sender: Any) {
    isUsingFrontCamera = !isUsingFrontCamera
    removeDetectionAnnotations()
    setUpCaptureSessionInput()
  }

  // MARK: - On-Device AutoML Detections

  private func detectImageLabelsAutoMLOndevice(
    in visionImage: VisionImage,
    width: CGFloat,
    height: CGFloat) {
    registerAutoMLModelsIfNeeded()

    let options = VisionOnDeviceAutoMLImageLabelerOptions(
      remoteModelName: Constant.remoteAutoMLModelName,
      localModelName: Constant.localAutoMLModelName
    )
    options.confidenceThreshold = Constant.labelConfidenceThreshold
    let autoMLOnDeviceLabeler = vision.onDeviceAutoMLImageLabeler(options: options)
    print("labeler: \(autoMLOnDeviceLabeler)\n")

    let group = DispatchGroup()
    group.enter()

    autoMLOnDeviceLabeler.process(visionImage) { detectedLabels, error in
      defer { group.leave() }

      self.updatePreviewOverlayView()
      self.removeDetectionAnnotations()

      if let error = error {
        print("Failed to detect labels with error: \(error.localizedDescription).")
        return
      }

      guard let labels = detectedLabels, !labels.isEmpty else {
        return
      }

      let annotationFrame = self.annotationOverlayView.frame
      let resultsRect = CGRect(
        x: annotationFrame.origin.x + Constant.padding,
        y: annotationFrame.size.height - Constant.padding - Constant.resultsLabelHeight,
        width: annotationFrame.width - 2 * Constant.padding,
        height: Constant.resultsLabelHeight
      )
      let resultsLabel = UILabel(frame: resultsRect)
      resultsLabel.textColor = .yellow
      resultsLabel.text = labels.map { label -> String in
        return "Label: \(label.text), Confidence: \(label.confidence ?? 0)"
        }.joined(separator: "\n")
      resultsLabel.adjustsFontSizeToFitWidth = true
      resultsLabel.numberOfLines = Constant.resultsLabelLines
      self.annotationOverlayView.addSubview(resultsLabel)
    }

    group.wait()
  }

  private func registerAutoMLModelsIfNeeded() {
    if areAutoMLModelsRegistered {
      return
    }

    let initialConditions = ModelDownloadConditions()
    let updateConditions = ModelDownloadConditions(
      allowsCellularAccess: false,
      allowsBackgroundDownloading: true
    )
    let remoteModel = RemoteModel(
      name: Constant.remoteAutoMLModelName,
      allowsModelUpdates: true,
      initialConditions: initialConditions,
      updateConditions: updateConditions
    )
    modelManager.register(remoteModel)
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(remoteModelDownloadDidSucceed(_:)),
      name: .firebaseMLModelDownloadDidSucceed,
      object: nil
    )
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(remoteModelDownloadDidFail(_:)),
      name: .firebaseMLModelDownloadDidFail,
      object: nil
    )
    DispatchQueue.main.async {
      self.downloadProgressView.isHidden = false
      self.downloadProgressView.observedProgress = self.modelManager.download(remoteModel)
    }

    guard let localModelFilePath = Bundle.main.path(
      forResource: Constant.localModelManifestFileName,
      ofType: Constant.autoMLManifestFileType
      ) else {
        print("Failed to find AutoML local model manifest file.")
        return
    }
    let localModel = LocalModel(name: Constant.localAutoMLModelName, path: localModelFilePath)
    modelManager.register(localModel)
    areAutoMLModelsRegistered = true
  }

  // MARK: - Notifications

  @objc
  private func remoteModelDownloadDidSucceed(_ notification: Notification) {
    let notificationHandler = {
      self.downloadProgressView.isHidden = true
      guard let userInfo = notification.userInfo,
        let remoteModel =
        userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel
        else {
          print("firebaseMLModelDownloadDidSucceed notification posted without a RemoteModel instance.")
          return
      }
      print("Successfully downloaded the remote model with name: \(remoteModel.name). The model is ready for detection.")
    }
    if Thread.isMainThread { notificationHandler(); return }
    DispatchQueue.main.async { notificationHandler() }
  }

  @objc
  private func remoteModelDownloadDidFail(_ notification: Notification) {
    let notificationHandler = {
      self.downloadProgressView.isHidden = true
      guard let userInfo = notification.userInfo,
        let remoteModel =
        userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel,
        let error = userInfo[ModelDownloadUserInfoKey.error.rawValue] as? NSError
        else {
          print("firebaseMLModelDownloadDidFail notification posted without a RemoteModel instance or error.")
          return
      }
      print("Failed to download the remote model with name: \(remoteModel.name), error: \(error).")
    }
    if Thread.isMainThread { notificationHandler(); return }
    DispatchQueue.main.async { notificationHandler() }
  }

  // MARK: Other On-Device Detections

  private func detectFacesOnDevice(in image: VisionImage, width: CGFloat, height: CGFloat) {
    let options = VisionFaceDetectorOptions()

    // When performing latency tests to determine ideal detection settings,
    // run the app in 'release' mode to get accurate performance metrics
    options.landmarkMode = .none
    options.contourMode = .all
    options.classificationMode = .none

    options.performanceMode = .fast
    let faceDetector = vision.faceDetector(options: options)

    var detectedFaces: [VisionFace]? = nil
    do {
      detectedFaces = try faceDetector.results(in: image)
    } catch let error {
      print("Failed to detect faces with error: \(error.localizedDescription).")
    }
    guard let faces = detectedFaces, !faces.isEmpty else {
      print("On-Device face detector returned no results.")
      DispatchQueue.main.sync {
        self.updatePreviewOverlayView()
        self.removeDetectionAnnotations()
      }
      return
    }

    DispatchQueue.main.sync {
      self.updatePreviewOverlayView()
      self.removeDetectionAnnotations()
      for face in faces {
        let normalizedRect = CGRect(
          x: face.frame.origin.x / width,
          y: face.frame.origin.y / height,
          width: face.frame.size.width / width,
          height: face.frame.size.height / height
        )
        let standardizedRect =
          self.previewLayer.layerRectConverted(fromMetadataOutputRect: normalizedRect).standardized
        UIUtilities.addRectangle(
          standardizedRect,
          to: self.annotationOverlayView,
          color: UIColor.green
        )
        self.addContours(for: face, width: width, height: height)
      }
    }
  }

  private func recognizeTextOnDevice(in image: VisionImage, width: CGFloat, height: CGFloat) {
    let textRecognizer = vision.onDeviceTextRecognizer()
    textRecognizer.process(image) { text, error in
      self.removeDetectionAnnotations()
      self.updatePreviewOverlayView()
      guard error == nil, let text = text else {
        print("On-Device text recognizer error: " +
          "\(error?.localizedDescription ?? Constant.noResultsMessage)")
        return
      }
      // Blocks.
      for block in text.blocks {
        let points = self.convertedPoints(from: block.cornerPoints, width: width, height: height)
        UIUtilities.addShape(
          withPoints: points,
          to: self.annotationOverlayView,
          color: UIColor.purple
        )

        // Lines.
        for line in block.lines {
          let points = self.convertedPoints(from: line.cornerPoints, width: width, height: height)
          UIUtilities.addShape(
            withPoints: points,
            to: self.annotationOverlayView,
            color: UIColor.orange
          )

          // Elements.
          for element in line.elements {
            let normalizedRect = CGRect(
              x: element.frame.origin.x / width,
              y: element.frame.origin.y / height,
              width: element.frame.size.width / width,
              height: element.frame.size.height / height
            )
            let convertedRect = self.previewLayer.layerRectConverted(
              fromMetadataOutputRect: normalizedRect
            )
            UIUtilities.addRectangle(
              convertedRect,
              to: self.annotationOverlayView,
              color: UIColor.green
            )
            let label = UILabel(frame: convertedRect)
            label.text = element.text
            label.adjustsFontSizeToFitWidth = true
            self.annotationOverlayView.addSubview(label)
          }
        }
      }
    }
  }

  // MARK: Object Detection

  private func detectObjectsOnDevice(in image: VisionImage,
                                     width: CGFloat,
                                     height: CGFloat,
                                     options: VisionObjectDetectorOptions) {
    let detector = vision.objectDetector(options: options)

    var detectedObjects: [VisionObject]? = nil
    do {
      detectedObjects = try detector.results(in: image)
    } catch let error {
      print("Failed to detect object with error: \(error.localizedDescription).")
      return
    }
    guard let objects = detectedObjects, !objects.isEmpty else {
      print("On-Device object detector returned no results.")
      DispatchQueue.main.sync {
        self.updatePreviewOverlayView()
        self.removeDetectionAnnotations()
      }
      return
    }

    DispatchQueue.main.sync {
      self.updatePreviewOverlayView()
      self.removeDetectionAnnotations()
      for object in objects {
        let normalizedRect = CGRect(
          x: object.frame.origin.x / width,
          y: object.frame.origin.y / height,
          width: object.frame.size.width / width,
          height: object.frame.size.height / height
        )
        let standardizedRect =
          self.previewLayer.layerRectConverted(fromMetadataOutputRect: normalizedRect).standardized
        UIUtilities.addRectangle(
          standardizedRect,
          to: self.annotationOverlayView,
          color: UIColor.green
        )
        let label = UILabel(frame: standardizedRect)
        label.numberOfLines = 2
        var description = ""
        if let trackingID = object.trackingID {
          description = "ID:" + trackingID.stringValue + "\n"
        }
        if let objectLabel = object.label {
          description += objectLabel
        }
        label.text = description

        label.adjustsFontSizeToFitWidth = true
        self.annotationOverlayView.addSubview(label)
      }
    }
  }

  // MARK: - Private

  private func setUpCaptureSessionOutput() {
    sessionQueue.async {
      self.captureSession.beginConfiguration()
      // When performing latency tests to determine ideal capture settings,
      // run the app in 'release' mode to get accurate performance metrics
      self.captureSession.sessionPreset = AVCaptureSession.Preset.medium

      let output = AVCaptureVideoDataOutput()
      output.videoSettings =
        [(kCVPixelBufferPixelFormatTypeKey as String): kCVPixelFormatType_32BGRA]
      let outputQueue = DispatchQueue(label: Constant.videoDataOutputQueueLabel)
      output.setSampleBufferDelegate(self, queue: outputQueue)
      guard self.captureSession.canAddOutput(output) else {
        print("Failed to add capture session output.")
        return
      }
      self.captureSession.addOutput(output)
      self.captureSession.commitConfiguration()
    }
  }

  private func setUpCaptureSessionInput() {
    sessionQueue.async {
      let cameraPosition: AVCaptureDevice.Position = self.isUsingFrontCamera ? .front : .back
      guard let device = self.captureDevice(forPosition: cameraPosition) else {
        print("Failed to get capture device for camera position: \(cameraPosition)")
        return
      }
      do {
        self.captureSession.beginConfiguration()
        let currentInputs = self.captureSession.inputs
        for input in currentInputs {
          self.captureSession.removeInput(input)
        }

        let input = try AVCaptureDeviceInput(device: device)
        guard self.captureSession.canAddInput(input) else {
          print("Failed to add capture session input.")
          return
        }
        self.captureSession.addInput(input)
        self.captureSession.commitConfiguration()
      } catch {
        print("Failed to create capture device input: \(error.localizedDescription)")
      }
    }
  }

  private func startSession() {
    sessionQueue.async {
      self.captureSession.startRunning()
    }
  }

  private func stopSession() {
    sessionQueue.async {
      self.captureSession.stopRunning()
    }
  }

  private func setUpPreviewOverlayView() {
    cameraView.addSubview(previewOverlayView)
    NSLayoutConstraint.activate([
      previewOverlayView.topAnchor.constraint(greaterThanOrEqualTo: cameraView.topAnchor),
      previewOverlayView.centerYAnchor.constraint(equalTo: cameraView.centerYAnchor),
      previewOverlayView.leadingAnchor.constraint(equalTo: cameraView.leadingAnchor),
      previewOverlayView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor),
      previewOverlayView.bottomAnchor.constraint(lessThanOrEqualTo: cameraView.bottomAnchor),
      ])
  }
<commit_msg>fix image aspect not correct<commit_after>//
//  Copyright (c) 2018 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import AVFoundation
import CoreVideo

import FirebaseMLVision
import FirebaseMLVisionObjectDetection
import FirebaseMLCommon
import FirebaseMLVisionAutoML

@objc(CameraViewController)
class CameraViewController: UIViewController {
  private let detectors: [Detector] = [.onDeviceAutoMLImageLabeler,
                                       .onDeviceFace,
                                       .onDeviceText,
                                       .onDeviceObjectProminentNoClassifier,
                                       .onDeviceObjectProminentWithClassifier,
                                       .onDeviceObjectMultipleNoClassifier,
                                       .onDeviceObjectMultipleWithClassifier]
  private var currentDetector: Detector = .onDeviceFace
  private var isUsingFrontCamera = true
  private var previewLayer: AVCaptureVideoPreviewLayer!
  private lazy var captureSession = AVCaptureSession()
  private lazy var sessionQueue = DispatchQueue(label: Constant.sessionQueueLabel)
  private lazy var vision = Vision.vision()
  private var lastFrame: CMSampleBuffer?
  private var areAutoMLModelsRegistered = false
  private lazy var modelManager = ModelManager.modelManager()
  @IBOutlet var downloadProgressView: UIProgressView!

  private lazy var previewOverlayView: UIImageView = {

    precondition(isViewLoaded)
    let previewOverlayView = UIImageView(frame: .zero)
    previewOverlayView.contentMode = UIViewContentMode.scaleAspectFill
    previewOverlayView.translatesAutoresizingMaskIntoConstraints = false
    return previewOverlayView
  }()

  private lazy var annotationOverlayView: UIView = {
    precondition(isViewLoaded)
    let annotationOverlayView = UIView(frame: .zero)
    annotationOverlayView.translatesAutoresizingMaskIntoConstraints = false
    return annotationOverlayView
  }()

  // MARK: - IBOutlets

  @IBOutlet private weak var cameraView: UIView!

  // MARK: - UIViewController

  override func viewDidLoad() {
    super.viewDidLoad()

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    setUpPreviewOverlayView()
    setUpAnnotationOverlayView()
    setUpCaptureSessionOutput()
    setUpCaptureSessionInput()
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    startSession()
  }

  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    stopSession()
  }

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    previewLayer.frame = cameraView.frame
  }

  // MARK: - IBActions

  @IBAction func selectDetector(_ sender: Any) {
    presentDetectorsAlertController()
  }

  @IBAction func switchCamera(_ sender: Any) {
    isUsingFrontCamera = !isUsingFrontCamera
    removeDetectionAnnotations()
    setUpCaptureSessionInput()
  }

  // MARK: - On-Device AutoML Detections

  private func detectImageLabelsAutoMLOndevice(
    in visionImage: VisionImage,
    width: CGFloat,
    height: CGFloat) {
    registerAutoMLModelsIfNeeded()

    let options = VisionOnDeviceAutoMLImageLabelerOptions(
      remoteModelName: Constant.remoteAutoMLModelName,
      localModelName: Constant.localAutoMLModelName
    )
    options.confidenceThreshold = Constant.labelConfidenceThreshold
    let autoMLOnDeviceLabeler = vision.onDeviceAutoMLImageLabeler(options: options)
    print("labeler: \(autoMLOnDeviceLabeler)\n")

    let group = DispatchGroup()
    group.enter()

    autoMLOnDeviceLabeler.process(visionImage) { detectedLabels, error in
      defer { group.leave() }

      self.updatePreviewOverlayView()
      self.removeDetectionAnnotations()

      if let error = error {
        print("Failed to detect labels with error: \(error.localizedDescription).")
        return
      }

      guard let labels = detectedLabels, !labels.isEmpty else {
        return
      }

      let annotationFrame = self.annotationOverlayView.frame
      let resultsRect = CGRect(
        x: annotationFrame.origin.x + Constant.padding,
        y: annotationFrame.size.height - Constant.padding - Constant.resultsLabelHeight,
        width: annotationFrame.width - 2 * Constant.padding,
        height: Constant.resultsLabelHeight
      )
      let resultsLabel = UILabel(frame: resultsRect)
      resultsLabel.textColor = .yellow
      resultsLabel.text = labels.map { label -> String in
        return "Label: \(label.text), Confidence: \(label.confidence ?? 0)"
        }.joined(separator: "\n")
      resultsLabel.adjustsFontSizeToFitWidth = true
      resultsLabel.numberOfLines = Constant.resultsLabelLines
      self.annotationOverlayView.addSubview(resultsLabel)
    }

    group.wait()
  }

  private func registerAutoMLModelsIfNeeded() {
    if areAutoMLModelsRegistered {
      return
    }

    let initialConditions = ModelDownloadConditions()
    let updateConditions = ModelDownloadConditions(
      allowsCellularAccess: false,
      allowsBackgroundDownloading: true
    )
    let remoteModel = RemoteModel(
      name: Constant.remoteAutoMLModelName,
      allowsModelUpdates: true,
      initialConditions: initialConditions,
      updateConditions: updateConditions
    )
    modelManager.register(remoteModel)
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(remoteModelDownloadDidSucceed(_:)),
      name: .firebaseMLModelDownloadDidSucceed,
      object: nil
    )
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(remoteModelDownloadDidFail(_:)),
      name: .firebaseMLModelDownloadDidFail,
      object: nil
    )
    DispatchQueue.main.async {
      self.downloadProgressView.isHidden = false
      self.downloadProgressView.observedProgress = self.modelManager.download(remoteModel)
    }

    guard let localModelFilePath = Bundle.main.path(
      forResource: Constant.localModelManifestFileName,
      ofType: Constant.autoMLManifestFileType
      ) else {
        print("Failed to find AutoML local model manifest file.")
        return
    }
    let localModel = LocalModel(name: Constant.localAutoMLModelName, path: localModelFilePath)
    modelManager.register(localModel)
    areAutoMLModelsRegistered = true
  }

  // MARK: - Notifications

  @objc
  private func remoteModelDownloadDidSucceed(_ notification: Notification) {
    let notificationHandler = {
      self.downloadProgressView.isHidden = true
      guard let userInfo = notification.userInfo,
        let remoteModel =
        userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel
        else {
          print("firebaseMLModelDownloadDidSucceed notification posted without a RemoteModel instance.")
          return
      }
      print("Successfully downloaded the remote model with name: \(remoteModel.name). The model is ready for detection.")
    }
    if Thread.isMainThread { notificationHandler(); return }
    DispatchQueue.main.async { notificationHandler() }
  }

  @objc
  private func remoteModelDownloadDidFail(_ notification: Notification) {
    let notificationHandler = {
      self.downloadProgressView.isHidden = true
      guard let userInfo = notification.userInfo,
        let remoteModel =
        userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel,
        let error = userInfo[ModelDownloadUserInfoKey.error.rawValue] as? NSError
        else {
          print("firebaseMLModelDownloadDidFail notification posted without a RemoteModel instance or error.")
          return
      }
      print("Failed to download the remote model with name: \(remoteModel.name), error: \(error).")
    }
    if Thread.isMainThread { notificationHandler(); return }
    DispatchQueue.main.async { notificationHandler() }
  }

  // MARK: Other On-Device Detections

  private func detectFacesOnDevice(in image: VisionImage, width: CGFloat, height: CGFloat) {
    let options = VisionFaceDetectorOptions()

    // When performing latency tests to determine ideal detection settings,
    // run the app in 'release' mode to get accurate performance metrics
    options.landmarkMode = .none
    options.contourMode = .all
    options.classificationMode = .none

    options.performanceMode = .fast
    let faceDetector = vision.faceDetector(options: options)

    var detectedFaces: [VisionFace]? = nil
    do {
      detectedFaces = try faceDetector.results(in: image)
    } catch let error {
      print("Failed to detect faces with error: \(error.localizedDescription).")
    }
    guard let faces = detectedFaces, !faces.isEmpty else {
      print("On-Device face detector returned no results.")
      DispatchQueue.main.sync {
        self.updatePreviewOverlayView()
        self.removeDetectionAnnotations()
      }
      return
    }

    DispatchQueue.main.sync {
      self.updatePreviewOverlayView()
      self.removeDetectionAnnotations()
      for face in faces {
        let normalizedRect = CGRect(
          x: face.frame.origin.x / width,
          y: face.frame.origin.y / height,
          width: face.frame.size.width / width,
          height: face.frame.size.height / height
        )
        let standardizedRect =
          self.previewLayer.layerRectConverted(fromMetadataOutputRect: normalizedRect).standardized
        UIUtilities.addRectangle(
          standardizedRect,
          to: self.annotationOverlayView,
          color: UIColor.green
        )
        self.addContours(for: face, width: width, height: height)
      }
    }
  }

  private func recognizeTextOnDevice(in image: VisionImage, width: CGFloat, height: CGFloat) {
    let textRecognizer = vision.onDeviceTextRecognizer()
    textRecognizer.process(image) { text, error in
      self.removeDetectionAnnotations()
      self.updatePreviewOverlayView()
      guard error == nil, let text = text else {
        print("On-Device text recognizer error: " +
          "\(error?.localizedDescription ?? Constant.noResultsMessage)")
        return
      }
      // Blocks.
      for block in text.blocks {
        let points = self.convertedPoints(from: block.cornerPoints, width: width, height: height)
        UIUtilities.addShape(
          withPoints: points,
          to: self.annotationOverlayView,
          color: UIColor.purple
        )

        // Lines.
        for line in block.lines {
          let points = self.convertedPoints(from: line.cornerPoints, width: width, height: height)
          UIUtilities.addShape(
            withPoints: points,
            to: self.annotationOverlayView,
            color: UIColor.orange
          )

          // Elements.
          for element in line.elements {
            let normalizedRect = CGRect(
              x: element.frame.origin.x / width,
              y: element.frame.origin.y / height,
              width: element.frame.size.width / width,
              height: element.frame.size.height / height
            )
            let convertedRect = self.previewLayer.layerRectConverted(
              fromMetadataOutputRect: normalizedRect
            )
            UIUtilities.addRectangle(
              convertedRect,
              to: self.annotationOverlayView,
              color: UIColor.green
            )
            let label = UILabel(frame: convertedRect)
            label.text = element.text
            label.adjustsFontSizeToFitWidth = true
            self.annotationOverlayView.addSubview(label)
          }
        }
      }
    }
  }

  // MARK: Object Detection

  private func detectObjectsOnDevice(in image: VisionImage,
                                     width: CGFloat,
                                     height: CGFloat,
                                     options: VisionObjectDetectorOptions) {
    let detector = vision.objectDetector(options: options)

    var detectedObjects: [VisionObject]? = nil
    do {
      detectedObjects = try detector.results(in: image)
    } catch let error {
      print("Failed to detect object with error: \(error.localizedDescription).")
      return
    }
    guard let objects = detectedObjects, !objects.isEmpty else {
      print("On-Device object detector returned no results.")
      DispatchQueue.main.sync {
        self.updatePreviewOverlayView()
        self.removeDetectionAnnotations()
      }
      return
    }

    DispatchQueue.main.sync {
      self.updatePreviewOverlayView()
      self.removeDetectionAnnotations()
      for object in objects {
        let normalizedRect = CGRect(
          x: object.frame.origin.x / width,
          y: object.frame.origin.y / height,
          width: object.frame.size.width / width,
          height: object.frame.size.height / height
        )
        let standardizedRect =
          self.previewLayer.layerRectConverted(fromMetadataOutputRect: normalizedRect).standardized
        UIUtilities.addRectangle(
          standardizedRect,
          to: self.annotationOverlayView,
          color: UIColor.green
        )
        let label = UILabel(frame: standardizedRect)
        label.numberOfLines = 2
        var description = ""
        if let trackingID = object.trackingID {
          description = "ID:" + trackingID.stringValue + "\n"
        }
        if let objectLabel = object.label {
          description += objectLabel
        }
        label.text = description

        label.adjustsFontSizeToFitWidth = true
        self.annotationOverlayView.addSubview(label)
      }
    }
  }

  // MARK: - Private

  private func setUpCaptureSessionOutput() {
    sessionQueue.async {
      self.captureSession.beginConfiguration()
      // When performing latency tests to determine ideal capture settings,
      // run the app in 'release' mode to get accurate performance metrics
      self.captureSession.sessionPreset = AVCaptureSession.Preset.medium

      let output = AVCaptureVideoDataOutput()
      output.videoSettings =
        [(kCVPixelBufferPixelFormatTypeKey as String): kCVPixelFormatType_32BGRA]
      let outputQueue = DispatchQueue(label: Constant.videoDataOutputQueueLabel)
      output.setSampleBufferDelegate(self, queue: outputQueue)
      guard self.captureSession.canAddOutput(output) else {
        print("Failed to add capture session output.")
        return
      }
      self.captureSession.addOutput(output)
      self.captureSession.commitConfiguration()
    }
  }

  private func setUpCaptureSessionInput() {
    sessionQueue.async {
      let cameraPosition: AVCaptureDevice.Position = self.isUsingFrontCamera ? .front : .back
      guard let device = self.captureDevice(forPosition: cameraPosition) else {
        print("Failed to get capture device for camera position: \(cameraPosition)")
        return
      }
      do {
        self.captureSession.beginConfiguration()
        let currentInputs = self.captureSession.inputs
        for input in currentInputs {
          self.captureSession.removeInput(input)
        }

        let input = try AVCaptureDeviceInput(device: device)
        guard self.captureSession.canAddInput(input) else {
          print("Failed to add capture session input.")
          return
        }
        self.captureSession.addInput(input)
        self.captureSession.commitConfiguration()
      } catch {
        print("Failed to create capture device input: \(error.localizedDescription)")
      }
    }
  }

  private func startSession() {
    sessionQueue.async {
      self.captureSession.startRunning()
    }
  }

  private func stopSession() {
    sessionQueue.async {
      self.captureSession.stopRunning()
    }
  }

  private func setUpPreviewOverlayView() {
    cameraView.addSubview(previewOverlayView)
    NSLayoutConstraint.activate([
      previewOverlayView.centerXAnchor.constraint(equalTo: cameraView.centerXAnchor),
      previewOverlayView.centerYAnchor.constraint(equalTo: cameraView.centerYAnchor),
      previewOverlayView.leadingAnchor.constraint(equalTo: cameraView.leadingAnchor),
      previewOverlayView.trailingAnchor.constraint(equalTo: cameraView.trailingAnchor),

      ])
  }

{'fraction_non_alphanumeric': 0.077293812880294, 'fraction_numerical': 0.0007831797096210615, 'mean_word_length': 2.7572430964237213, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 62, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2974985', 'n_tokens_mistral': 8731, 'n_tokens_neox': 8556, 'n_words': 2032}