티스토리 뷰
Google MLKit ?
/* Google MLKit은 머신러닝 전문 지식을 앱에 제공하는 모바일 SDK
MLKit 중 Text Recognize v2(Beta)를 사용할 것이다. */
/* 카메라에서 셔터버튼을 누르면 누른 순간의 텍스트를 인식해오는 실습 */
How to use ?
// Podfile
pod 'GoogleMLKit/TextRecognitionKorean', '2.6.0'
// Camera
/* 카메라 사용을 위한 CameraView */
import SwiftUI
import AVFoundation
struct ScanIdCameraView: UIViewRepresentable {
let viewModel: ScanIdViewModel
class VideoPreviewView: UIView {
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
return layer as! AVCaptureVideoPreviewLayer
}
}
func makeUIView(context: Context) -> VideoPreviewView {
let view = VideoPreviewView()
view.backgroundColor = .black
view.videoPreviewLayer.videoGravity = .resizeAspectFill
view.videoPreviewLayer.cornerRadius = 0
view.videoPreviewLayer.session = viewModel.session
view.videoPreviewLayer.connection?.videoOrientation = .portrait
viewModel.requestAndCheckPermissions()
return view
}
func updateUIView(_ uiView: VideoPreviewView, context: Context) {
}
}
// ViewModel
import MLKitTextRecognitionKorean
import MLKitVision
import AVFoundation
class ScanIdViewModel: NSObject, ObservableObject {
private let koreanOptions: KoreanTextRecognizerOptions
private let koreanTextRecognizer: TextRecognizer
override init() {
self.koreanOptions = KoreanTextRecognizerOptions()
self.koreanTextRecognizer = TextRecognizer.textRecognizer(options: self.koreanOptions)
}
@Published private var model: IdCardModel = IdCardModel()
@Published var session: AVCaptureSession = AVCaptureSession()
var isConfigure = false
let output = AVCapturePhotoOutput()
var name: String {
model.name ?? ""
}
var address: String {
model.address ?? ""
}
// 권한 체크
func requestAndCheckPermissions() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .notDetermined:
// 권한 요청
AVCaptureDevice.requestAccess(for: .video) { authStatus in
if authStatus {
DispatchQueue.main.async {
self.setupCamera()
}
}
}
case .restricted:
break
case .authorized:
// 이미 권한 받은 경우 셋업
setupCamera()
default:
// 거절했을 경우
print("Permession declined")
}
}
func setupCamera() {
if isConfigure == false {
initInput()
initOutput()
isConfigure = true
}
}
func initInput() {
// Video 방식으로 촬영하겠다.
guard let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
return
}
do {
let input = try AVCaptureDeviceInput(device: captureDevice)
session.addInput(input)
} catch {
print("error")
}
}
func initOutput() {
session.addOutput(output)
}
func capturePhoto() {
let photoSettings = AVCapturePhotoSettings()
output.capturePhoto(with: photoSettings, delegate: self)
}
func imageOrientation(deviceOrientation: UIDeviceOrientation, cameraPosition: AVCaptureDevice.Position) -> UIImage.Orientation {
switch deviceOrientation {
case .portrait:
return cameraPosition == .front ? .leftMirrored : .right
case .landscapeLeft:
return cameraPosition == .front ? .downMirrored : .up
case .portraitUpsideDown:
return cameraPosition == .front ? .rightMirrored : .left
case .landscapeRight:
return cameraPosition == .front ? .upMirrored : .down
case .faceDown, .faceUp, .unknown:
return .up
}
}
func process(_ imageData: Data) {
guard let image = UIImage(data: imageData) else { return }
let visionImage = VisionImage(image: image)
visionImage.orientation = imageOrientation(deviceOrientation: UIDevice.current.orientation, cameraPosition: .back)
koreanTextRecognizer.process(visionImage) { result, error in
guard error == nil, let result = result else {
// Error handling
print("error")
return
}
self.model.name = result.text
print("resultText: \\(result.text)")
}
}
}
extension ScanIdViewModel: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
AudioServicesDisposeSystemSoundID(1108)
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
guard let imageData = photo.fileDataRepresentation() else { return }
process(imageData)
}
}
// View
import SwiftUI
struct ScanIdView: View {
@ObservedObject private var viewModel: ScanIdViewModel
init() {
viewModel = ScanIdViewModel()
}
var body: some View {
VStack {
Spacer()
ScanIdCameraView(viewModel: viewModel)
.ignoresSafeArea()
.onAppear {
viewModel.session.startRunning()
}
.onDisappear {
viewModel.session.stopRunning()
}
// 카메라 셔터 버튼
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.orange)
.onTapGesture {
viewModel.capturePhoto()
}
Text(viewModel.name)
Spacer()
}
}
}
struct ScanIdView_Previews: PreviewProvider {
static var previews: some View {
ScanIdView()
}
}
Review
/* OCR 기술을 사용하고 싶은데 어떤 것을 써야할 지 모르겠어서 하나씩 다 맛만봤다.
Tesseract나 SwiftOCR은 실제로 적용하는 것도 쉽지않았고, 성능도 전혀 만족스럽지 않았다.
현재 사용한 MLKit은 베타버전이고 실제로 사용하려면 더 다듬어야겠지만 성능도 만족스럽고 사용성도 좋았다. */
Reference
https://developers.google.com/ml-kit/vision/text-recognition/v2/ios
'iOS > iOS' 카테고리의 다른 글
[iOS] 다크모드 미 지원 (0) | 2022.06.15 |
---|---|
[WWDC2022] Platforms State of the Union (0) | 2022.06.09 |
[iOS] OCR(2) - SwiftOCR (0) | 2022.05.22 |
[iOS] OCR(1) - Tesseract (0) | 2022.05.22 |
[iOS] MVC, MVVM 패턴 (1) | 2022.04.21 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- enumerations
- 의미있는이름
- rxswift
- AWS
- ObservedObject
- 곰튀김
- Swift
- ChatGPT
- SwiftUI
- MVVM
- Git
- docker
- IOS
- delegate
- onTapGesture
- CodingTest
- Custom
- kakao
- Login
- tabview
- strcut
- 카메라
- navigation
- Xcode
- AWS Fargate
- Protocol
- OCR
- frame과 bounds 차이
- file private
- Generic
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
글 보관함