Vision框架詳細解析(九) —— 基于Vision的QR掃描(二)

版本記錄

版本號 時間
V1.0 2020.10.15 星期四

前言

iOS 11+macOS 10.13+ 新出了Vision框架徒恋,提供了人臉識別暴浦、物體檢測、物體跟蹤等技術(shù)官觅,它是基于Core ML的刑峡⊙竺觯可以說是人工智能的一部分玄柠,接下來幾篇我們就詳細的解析一下Vision框架突梦。感興趣的看下面幾篇文章。
1. Vision框架詳細解析(一) —— 基本概覽(一)
2. Vision框架詳細解析(二) —— 基于Vision的人臉識別(一)
3. Vision框架詳細解析(三) —— 基于Vision的人臉識別(二)
4. Vision框架詳細解析(四) —— 在iOS中使用Vision和Metal進行照片堆疊(一)
5. Vision框架詳細解析(五) —— 在iOS中使用Vision和Metal進行照片堆疊(二)
6. Vision框架詳細解析(六) —— 基于Vision的顯著性分析(一)
7. Vision框架詳細解析(七) —— 基于Vision的顯著性分析(二)
8. Vision框架詳細解析(八) —— 基于Vision的QR掃描(一)

源碼

1. Swift

首先看下工程組織結(jié)構(gòu):

下面就是源碼了

1. ViewController.swift
import UIKit
import Vision
import AVFoundation
import SafariServices

class ViewController: UIViewController {
  // MARK: - Private Variables
  var captureSession = AVCaptureSession()

  // TODO: Make VNDetectBarcodesRequest variable
  lazy var detectBarcodeRequest = VNDetectBarcodesRequest { request, error in
    guard error == nil else {
      self.showAlert(withTitle: "Barcode error", message: error?.localizedDescription ?? "error")
      return
    }
    self.processClassification(request)
  }

  // MARK: - Override Functions
  override func viewDidLoad() {
    super.viewDidLoad()
    checkPermissions()
    setupCameraLiveView()
  }
  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // TODO: Stop Session
    captureSession.stopRunning()
  }
}


extension ViewController {
  // MARK: - Camera
  private func checkPermissions() {
    // TODO: Checking permissions
    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .notDetermined:
      AVCaptureDevice.requestAccess(for: .video) { [self] granted in
        if !granted {
          self.showPermissionsAlert()
        }
      }
    case .denied, .restricted:
      showPermissionsAlert()
    default:
      return
    }
  }

  private func setupCameraLiveView() {
    // TODO: Setup captureSession
    captureSession.sessionPreset = .hd1280x720

    // TODO: Add input
    let videoDevice = AVCaptureDevice
      .default(.builtInWideAngleCamera, for: .video, position: .back)

    guard
      let device = videoDevice,
      let videoDeviceInput = try? AVCaptureDeviceInput(device: device),
      captureSession.canAddInput(videoDeviceInput) else {
        showAlert(
          withTitle: "Cannot Find Camera",
          message: "There seems to be a problem with the camera on your device.")
        return
      }

    captureSession.addInput(videoDeviceInput)

    // TODO: Add output
    let captureOutput = AVCaptureVideoDataOutput()
    // TODO: Set video sample rate
    captureOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]
    captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: DispatchQoS.QoSClass.default))
    captureSession.addOutput(captureOutput)

    configurePreviewLayer()

    // TODO: Run session
    captureSession.startRunning()
  }

  // MARK: - Vision
  func processClassification(_ request: VNRequest) {
    // TODO: Main logic
    guard let barcodes = request.results else { return }
    DispatchQueue.main.async { [self] in
      if captureSession.isRunning {
        view.layer.sublayers?.removeSubrange(1...)

        for barcode in barcodes {
          guard
            // TODO: Check for QR Code symbology and confidence score
            let potentialQRCode = barcode as? VNBarcodeObservation,
            potentialQRCode.symbology == .QR,
            potentialQRCode.confidence > 0.9
            else { return }

          observationHandler(payload: potentialQRCode.payloadStringValue)
        }
      }
    }
  }

  // MARK: - Handler
  func observationHandler(payload: String?) {
    // TODO: Open it in Safari
    guard
      let payloadString = payload,
      let url = URL(string: payloadString),
      ["http", "https"].contains(url.scheme?.lowercased())
    else { return }

    let config = SFSafariViewController.Configuration()
    config.entersReaderIfAvailable = true

    let safariVC = SFSafariViewController(url: url, configuration: config)
    safariVC.delegate = self
    present(safariVC, animated: true)
  }
}


// MARK: - AVCaptureDelegation
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    // TODO: Live Vision
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

    let imageRequestHandler = VNImageRequestHandler(
      cvPixelBuffer: pixelBuffer,
      orientation: .right)

    do {
      try imageRequestHandler.perform([detectBarcodeRequest])
    } catch {
      print(error)
    }
  }
}


// MARK: - Helper
extension ViewController {
  private func configurePreviewLayer() {
    let cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    cameraPreviewLayer.videoGravity = .resizeAspectFill
    cameraPreviewLayer.connection?.videoOrientation = .portrait
    cameraPreviewLayer.frame = view.frame
    view.layer.insertSublayer(cameraPreviewLayer, at: 0)
  }

  private func showAlert(withTitle title: String, message: String) {
    DispatchQueue.main.async {
      let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
      alertController.addAction(UIAlertAction(title: "OK", style: .default))
      self.present(alertController, animated: true)
    }
  }

  private func showPermissionsAlert() {
    showAlert(
      withTitle: "Camera Permissions",
      message: "Please open Settings and grant permission for this app to use your camera.")
  }
}


// MARK: - SafariViewControllerDelegate
extension ViewController: SFSafariViewControllerDelegate {
  func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
    captureSession.startRunning()
  }
}

后記

本篇主要講述了基于Vision的QR掃描羽利,感興趣的給個贊或者關(guān)注~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宫患,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子这弧,更是在濱河造成了極大的恐慌娃闲,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匾浪,死亡現(xiàn)場離奇詭異皇帮,居然都是意外死亡,警方通過查閱死者的電腦和手機蛋辈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門属拾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來将谊,“玉大人,你說我怎么就攤上這事渐白∽鹋ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵纯衍,是天一觀的道長栋齿。 經(jīng)常有香客問我,道長襟诸,這世上最難降的妖魔是什么瓦堵? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮歌亲,結(jié)果婚禮上谷丸,老公的妹妹穿的比我還像新娘。我一直安慰自己应结,他們只是感情好刨疼,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹅龄,像睡著了一般揩慕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扮休,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天迎卤,我揣著相機與錄音,去河邊找鬼玷坠。 笑死蜗搔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的八堡。 我是一名探鬼主播樟凄,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼兄渺!你這毒婦竟也來了缝龄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤挂谍,失蹤者是張志新(化名)和其女友劉穎叔壤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體口叙,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡炼绘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了妄田。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俺亮。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡仗哨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铅辞,到底是詐尸還是另有隱情厌漂,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布斟珊,位于F島的核電站苇倡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏囤踩。R本人自食惡果不足惜旨椒,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望堵漱。 院中可真熱鬧综慎,春花似錦、人聲如沸勤庐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愉镰。三九已至米罚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丈探,已是汗流浹背录择。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碗降,地道東北人隘竭。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像讼渊,于是被迫代替她去往敵國和親动看。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345