iOS 사이드 프로젝트를 진행중에 바코드 스캔기능을 적용하면서
혹시나 다른 분들에게 도움이 될까 포스팅 합니다.
참고로 SwiftUI로 프로젝트를 진행 하였고, 구글에 ML Kit을 사용하였습니다.
먼저 ML Kit는 Android 및 iOS 앱에 Google의 기기 내 머신러닝 전문 지식을 제공하는 모바일 SDK입니다.
ML Kit을 사용하면 바코드 스캔 말고도 텍스트 인식 등 다른 여러 API들이 존재 한다고 합니다.
먼저, cocoapod으로 ML Kit을 설치를 진행합니다.
터미널에서 프로젝트 폴더로 이동 후 pod init을 해줍니다.
이후 podfile에
pod 'GoogleMLKit/BarcodeScanning', '3.2.0' 을 포함하고, pod install을 해줍니다.
pod install이 정상적으로 되었다면, xcworkspace 파일을 열어줍니다.
import AVFoundation
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera: CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
import Foundation
import AVFoundation
class CameraModel: NSObject ,ObservableObject {
var session = AVCaptureSession()
var output = AVCaptureVideoDataOutput()
var preview: AVCaptureVideoPreviewLayer!
}
카메라를 사용하기 위해서 AVFoundation을 import 해주고
session과 input 및 output 설정을 해줘야 합니다.
위에 기술된 AVCaptureSession은 AVFoundation 프레임워크에서 제공하는 클래스로, 카메라와 마이크를 포함한 디바이스의 다양한 미디어 입력을 관리하고 출력으로 보낼 수 있는 중요한 기능을 제공합니다. 이를 사용하여 iOS 및 macOS 앱에서 미디어 캡처, 녹화, 스트리밍, 분석 등 다양한 미디어 기능을 구현할 수 있습니다.
그리고 모든 capture sessoin은 적어도 하나의 캡쳐 입력(capture inputs)과 캡쳐 출력(capture outputs)이 필요한데, 캡쳐 입력은 iOS 기기, Mac의 카메라나 마이크와 같은 미디어 자원을 말한다. 캡쳐 출력은 캡쳐 입력으로 부터 받은 데이터를 사용하여 사진, 영상파일 같은 미디어를 생산합니다.
사진이나 영상을 캡쳐하기 위해 카메라를 사용하려면, 적절한 AVCaptureDevice를 선택하여 이와 일치하는 AVCaptureDeviceInput를 생성하고 이를 session에 추가하면 됩니다.
input을 설정 해줬다면 마찬가지로 output도 설정해줘야 한다.
AVCaptureVideoDataOutput 는 비디오를 기록하는, 그리고 프로세싱을 위한 비디오 프레임 접근을 제공하는 캡처 아웃풋이다. 정리하면, 카메라를 통해 들어오는 비디오 정보들을 이 클래스 안에서 처리한다.
setSampleBufferDelegate() 를 통해 델리게이트를 설정할 수 있고, captureOutput 라는 델리게이트 메서드를 통해서 비디오 프레임에 접근할 수 있다.
extension CameraModel: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// input code
}
}
func setUp() {
guard let device = AVCaptureDevice.default(for: .video) else {
print("Failed to access camera.")
return
}
do {
let input = try AVCaptureDeviceInput(device: device)
if session.canAddInput(input) {
session.addInput(input)
}
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
if session.canAddOutput(output) {
session.addOutput(output)
}
} catch {
print("Failed to setup camera input: \(error.localizedDescription)")
}
}
session은 startRunning() 과 stopRunning() 함수를 통해 제어할 수 있습니다.
바코드를 스캔할 때에는 카메라를 이용해서 바로 인식하는 방법과, 사진을 선택하여 사진에 포함된 바코드를 읽는 방법이 있는데요. 카메라를 이용하여 바코드를 읽는 방법을 알아본다면, 사진에 포함된 바코드를 읽는 방법은 느낌적? 으로 알 수 있습니다.
위와 같이 session의 input과 output을 설정을 마친 후, 델리게이트에 captureOutput 메소드안에 카메라를 통해 바코드 인식할 코드를 작성해주어야 합니다.
저는 ML Kit을 사용하여 간단하게 바코드를 인식하는 코드를 작성할 수 있었습니다.
func scanBarcodeOnDevice(in image: VisionImage) {
let format = BarcodeFormat.all
let barcodeOptions = BarcodeScannerOptions(formats: format)
let barcodeScanner = BarcodeScanner.barcodeScanner(options: barcodeOptions)
var barcodes: [Barcode] = []
DispatchQueue(label: "barcodeQueue").async {
do {
barcodes = try barcodeScanner.results(in: image)
} catch {
print(error.localizedDescription)
}
if !barcodes.isEmpty {
for barcode in barcodes {
self.stopSession()
guard let barcodeValue = barcode.displayValue else { return }
print(barcodeValue)
}
}
}
}
format은 인식할 바코드의 종류를 의미합니다. QR code나 code128과 같은 바코드 포맷을 사용할 수 있고, 위 코드와 같이 .all을 사용하고 barcodeOptions에 해당 포맷을 설정하게 된다면 모든 바코드의 종류를 인식할 수 있습니다. 해당 barcodeOptions을 BarcodeScanner의 옵션으로 지정해주면 바코드를 스캔할 카메라의 준비는 끝났습니다.
델리게이트로 돌아가서 델리게이트에서 받아오는 CMSampleBuffer 데이터를 ML Kit에서 제공하는 VisionImage로 형태를 바꿔주는 작업을 해야합니다.
extension CameraModel: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let visionImage = VisionImage(buffer: sampleBuffer)
scanBarcodeOnDevice(in: visionImage)
}
}
VisionImage(buffer: ) 를 사용하여 CMSampleBuffer를 간단하게 VisionImage 타입으로 변환 후 위에 작성한 함수의 매개변수로 넘겨주게 된다면 Live Camera를 이용하여 바코드 값을 읽어올 수 있습니다.
위에서 작성한 함수를 이용한다면 카메라를 이용한 바코드 인식뿐만 아니라 사진에 담긴 바코드를 이용하여 바코드 인식도 가능합니다. 위에서 했던거와 같이 갤러리 에서 들고온 사진을 VisionImage 타입으로 변환 후
scanBarcodeOnDevice 함수 메개변수에 해당 VisionImage를 넘겨주면 됩니다!
쓰다보니 설명이 복잡하게 된거 같아 미리 죄송합니다 ㅠ