iOS Swift/Library, Framework 연동

[iOS/Swift] 네이버 Clova OCR Api 예제

주니어코더 2023. 1. 2. 13:09

 

 

 

 

 

Swift 언어를 사용하여

네이버 Clova OCR 엔진을 사용하는 예제를 포스팅한다!

 

 

네이버 Clovar OCR Api 사용 방법부터 진행해야 하는 분은

이전 글부터 읽어야 한다

 

 

 

 네이버 Clova OCR 사용 예제 

 

 

2022.12.07 - [프로그래밍/기타] - [OCR] 네이버 Naver Clova OCR 사용

 

[OCR] 네이버 Naver Clova OCR 예제

이번에 OCR 관련 프로젝트를 진행하며, Google OCR, Naver Clova OCR, ios 내장 text 인식 세 가지를 사용해 보았다. 그중, 한글 인식률이 가장 좋았던 Naver Clova OCR 사용 방법을 소개한다 Chap1. Clova OCR 이용신

ohwhatisthis.tistory.com

 

 

 

api 사용 완료 했다면,

이제부터 예제를 스탭별로 따라 하면 된다!

 

 

 

Chap1.  번역할 이미지를 넣는다


 

 

저는 김수영 시인의 '사랑' 이라는 시를 번역해 보겠습니다

 

 

 

번역할 이미지를 Asset에 넣어줍니다.

 

 

 

이미지를 변수에 저장합니다!

 

    var image = UIImage(named: "love")

 

여기까지 하면 이미지 준비는 '완'

 

 

 

 

Chap2.  네이버 Clova Platform에서 생성한 Secret Key와 Api URL 을 저장한다


 

 

Secret Key 와 Api URL 은 변하지 않음으로 let 변수에 저장하여 반복적으로 사용합니다

    let secretKey = "scretkey"
    let apiURL = "https://3f1iycdv15.apigw.ntruss.com/custom/v1/19260/general"

 

 

 

 

Chap3.  Request ,  Response 모델을 Codable 형식으로 제작합니다


 

 

 

Request, Response를 할 때 Json 데이터가 필요하다

 

Swift에서는 쉽게 Json <-> Model 할 수 있는 함수를 제공한다!

 

Encode - 모델을 Json 데이터로 변환

Decode - Json 데이터를 모델로 변환

 

Encode, Decode 하기 위해서는 Codable 모델이 필요한데,

Json 데이터를 Codable 모델로 바꾸는 과정을 진행하겠습니다~

 

 

https://app.quicktype.io/

 

Instantly parse JSON in any language | quicktype

 

app.quicktype.io

위 사이트에 json 데이터를 입력하면 자동으로 Codable 데이터로 변환해 줍니다

 

 

 

 

Request 데이터

 

 

[ Request Json ]

{
    "images": [
      {
        "format": "png",
        "name": "medium",
        "data": "image64",
      }
    ],
    "lang": "ko",
    "requestId": "string",
    "resultType": "string",
    "timestamp": {{$timestamp}},
    "version": "V2"
}

 

[ Request Codable ]

import Foundation

struct Naver: Codable {
    let images: [images]

    let lang: String
    let requestId: String
    let timestamp: String
    let version: String

}
struct images: Codable {
    let format: String
    let name: String
    let data: String
}

 

 

 

Response 데이터

 

 

[ Response Json ]

{
    "version": "V2",
    "requestId": "string",
    "timestamp": 1670386441771,
    "images": [
        {
            "uid": "d3c3f8cc84e84e878f07474b573d0fd2",
            "name": "medium",
            "inferResult": "SUCCESS",
            "message": "SUCCESS",
            "validationResult": {
                "result": "NO_REQUESTED"
            },
            "convertedImageInfo": {
                "width": 700,
                "height": 400,
                "pageIndex": 0,
                "longImage": false
            },
            "fields": [
                {
                    "valueType": "ALL",
                    "boundingPoly": {
                        "vertices": [
                            {
                                "x": 345.0,
                                "y": 201.0
                            },
                            {
                                "x": 434.0,
                                "y": 120.0
                            },
                            {
                                "x": 463.0,
                                "y": 152.0
                            },
                            {
                                "x": 375.0,
                                "y": 233.0
                            }
                        ]
                    },
                    "inferText": "사랑",
                    "inferConfidence": 0.9995,
                    "type": "NORMAL",
                    "lineBreak": true
                }
                
                }
            ]
        }
    ]
}

 

 

[ Response Codable ] 

import Foundation

struct Response: Codable {
    let version, requestID: String
    let timestamp: Int
    let images: [Image]

    enum CodingKeys: String, CodingKey {
        case version
        case requestID = "requestId"
        case timestamp, images
    }
}

struct Image: Codable {
    let uid, name, inferResult, message: String
    let validationResult: ValidationResult
    let convertedImageInfo: ConvertedImageInfo
    let fields: [Field]
}

struct ConvertedImageInfo: Codable {
    let width, height, pageIndex: Int
    let longImage: Bool
}

struct Field: Codable {
    let valueType: String
    let boundingPoly: BoundingPoly
    let inferText: String
    let inferConfidence: Double
    let type: String
    let lineBreak: Bool
}

struct BoundingPoly: Codable {
    let vertices: [Vertex]
}

struct Vertex: Codable {
    let x, y: Int
}

struct ValidationResult: Codable {
    let result: String
}

 

 

 

 

Chap4.  Model 을 Encode 한다


 

 

 

Model 을 encode 하기 전 사진 파일을 String 형식으로 바꿔야 한다

 

UIImage에 base64EncodedString 를 적용하면 쉽게 바꿀 수 있다!

 

    fileprivate func changePngToString() {
        if let data = image?.pngData() { // image = UIImage
            let base64 = data.base64EncodedString()
            image64 = base64
        }
    }

 

 

모든 준비가 완료되었으니 Model을 Json 형식으로 Encode 한다

 

    fileprivate func encodeJson(){
        //객체 생성
        let naver = Naver(images: [images(format: "png", name: "love", data: image64)]
                          , lang: "ko", requestId: "string", timestamp: "0", version: "V2")
        
        let encoder = JSONEncoder()
        
        let jsonData = try? encoder.encode(naver)
        let jsonString = String(bytes: jsonData!, encoding: .utf8)

        requestBody = jsonString!
        
        print("Encode Finish")
    }

 

 

 

 

Chap5.  Model을 Request 한 후 Response 데이터를 Decode 한다


 

 

 

네이버 url로 encode 한 json을 보내고

 

미리 정의 한 Response Model로 json을 파싱 한다

 

    fileprivate func requestPost(){
        encodeJson()
        
        //URL
        let url = URL(string : apiURL);
         
        //URLRequest 생성
        var request = URLRequest(url: url!)
        request.httpMethod = "POST"
        request.setValue(secretKey, forHTTPHeaderField: "X-OCR-SECRET")
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = requestBody.data(using: .utf8)
         
        
        //POST전송
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            //에러처리
            if let e = error {
                NSLog("An error has occurred: \(e.localizedDescription)")
                return;
            }
            
            // response status code 검증
            guard let httpResponse = response as? HTTPURLResponse,
                  (200..<300).contains(httpResponse.statusCode) else {
                      print("Response error")
                
                      return
                  }

            // data 검증 및 사용
            if let data = data {
                DispatchQueue.main.async {
                    do {
                        let decoder = JSONDecoder()
                        var result = ""
                        if let json = try? decoder.decode(Response.self, from: data) {
                            print(json.timestamp)
                            
                            // field 갯수만큼 for 문 돌려서 추출
                            for i in 0...json.images[0].fields.count - 1{
                                result += json.images[0].fields[i].inferText
                                
                                print(json.images[0].fields[i].inferText)
                            }
                                                
                            print("전체 결과 : ", result)
                        
                        }

                    } catch let e as NSError {
                        NSLog("An error has occurred: \(e.localizedDescription)")
                    }
                }
            }
        }
        task.resume();
    }

 

 

 

 

 

전체 코드


 

import UIKit

class NaverViewController: UIViewController{
    
    let secretKey = "R1lLZUxIZkNtalVaU05rSkFlcHNScXF3SG1ialNyclI="
    let apiURL = "https://3f1iycdv15.apigw.ntruss.com/custom/v1/19260/d965e0173ed48371cfca72258ce8dc822ede325e69df2196b5a9f3187a375ea0/general"
    
    var image = UIImage(named: "love")
    var image64: String = ""
    
    var stringResult: String = ""
    
    private lazy var topSafeAreaView: UIView = {
        var view = UIView()
        view.backgroundColor = UIColor(named: "#f8f8fa")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private let labelTitle: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        label.textAlignment = .center
        label.text = "네이버 결과"
        return label
    }()
    
    private let imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.image = UIImage(named: "love")
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()
    
    private let lbResult: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        label.textAlignment = .center
        label.text = ""
        return label
    }()
    
    private lazy var button: UIButton = {
        var button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("변환", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(topSafeAreaView)
        topSafeAreaView.addSubview(labelTitle)
        topSafeAreaView.addSubview(imageView)
        topSafeAreaView.addSubview(lbResult)
        topSafeAreaView.addSubview(button)
                
        applyConstraints()
        setupAction()
        changePngToString()
    }
    
    // MARK: - Contstraints
    fileprivate func applyConstraints() {
        let topSafeAreaViewConstraints = [
            topSafeAreaView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            topSafeAreaView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            topSafeAreaView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            topSafeAreaView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ]
        
        let labelTitleConstraints = [
            labelTitle.topAnchor.constraint(equalTo: topSafeAreaView.topAnchor, constant: 20),
            labelTitle.leadingAnchor.constraint(equalTo: topSafeAreaView.leadingAnchor, constant: 20),
            labelTitle.trailingAnchor.constraint(equalTo: topSafeAreaView.trailingAnchor, constant: -20)
        ]
        
        let imageViewConstraints = [
            imageView.topAnchor.constraint(equalTo: labelTitle.bottomAnchor, constant: 20),
            imageView.leadingAnchor.constraint(equalTo: topSafeAreaView.leadingAnchor, constant: 20),
            imageView.trailingAnchor.constraint(equalTo: topSafeAreaView.trailingAnchor, constant: -20),
            imageView.heightAnchor.constraint(equalToConstant: 300)
        ]
        
        let buttonConstraints = [
            button.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 20),
            button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
        ]
        
        let lbResultConstraints = [
            lbResult.topAnchor.constraint(equalTo: button.bottomAnchor, constant: 20),
            lbResult.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            lbResult.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
        ]
            
        NSLayoutConstraint.activate(topSafeAreaViewConstraints)
        NSLayoutConstraint.activate(labelTitleConstraints)
        NSLayoutConstraint.activate(imageViewConstraints)
        NSLayoutConstraint.activate(lbResultConstraints)
        NSLayoutConstraint.activate(buttonConstraints)
    }
    
    fileprivate func setupAction() {
        button.isUserInteractionEnabled = true

        button.addTarget(self, action: #selector(changeImageToText), for: .touchUpInside)
    }
    
    @objc func changeImageToText() {
        requestPost()
    }
    
    fileprivate func requestPost(){
        
        let naver = Naver(images: [images(format: "png", name: "1", data: image64)], lang: "ko", requestId: "string", timestamp: "0", version: "V2")
        
        let encoder = JSONEncoder()
        
        let jsonData = try? encoder.encode(naver)
        let jsonString = String(bytes: jsonData!, encoding: .utf8)

        var requestBody = jsonString!
        
        //URL
        let url = URL(string : apiURL);
         
        //URLRequest 생성
        var request = URLRequest(url: url!)
        request.httpMethod = "POST"
        request.setValue(secretKey, forHTTPHeaderField: "X-OCR-SECRET")
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = requestBody.data(using: .utf8)
         
        //POST전송
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            //에러처리
            if let e = error {
                NSLog("An error has occurred: \(e.localizedDescription)")
                return;
            }
            
            // response status code 검증
            guard let httpResponse = response as? HTTPURLResponse,
                  (200..<300).contains(httpResponse.statusCode) else {
                      print("Response error")
                      return
            }

            // data 검증 및 사용
            if let data = data {
                DispatchQueue.main.async {
                    do {
                        let decoder = JSONDecoder()
                        if let json = try? decoder.decode(Response.self, from: data) {
                            // field 갯수만큼 for 문 돌려서 추출
                             
                            for i in 0...json.images[0].fields.count - 1{
                                self.stringResult += json.images[0].fields[i].inferText
                                
                                print(json.images[0].fields[i].inferText)
                            }
                            
                            self.lbResult.text = self.stringResult
                                                    
                        }

                    } catch let e as NSError {
                        NSLog("An error has occurred: \(e.localizedDescription)")
                    }
                }
            }
        }
        task.resume();
    }
        
    fileprivate func changePngToString(){
        if let data = image?.pngData() {
            let image64 = data.base64EncodedString()
        }
    }
}

 

 

 

 

 

 

반응형