OCRで名刺データをスキャンしてみる

# はじめに
CamCard SDKを利用して、名刺データを読み取るサンプルをご紹介します。
詳細は、下記のサイトを参考にしてください。
https://dev.camcard.com/

REST APIの利用も出来ますが、オフラインでも利用するため、SDKを使いました。

## 1.導入
### 1.1.フレームワークをプロジェクトにコピーする。

・ISBusinessCardSDK.framework
・ISOpenSDKFoundation.embeddedframework
・IS_BCRAllTemplete.dat
・IS_BCRTemplete_AddressParse.dat

### 1.2. Linkied Frameworks and Librariesに下記を追加する。

・libstds++.tbd
・UIKit.framework
・CoreLocation.framework

### 1.3. Build Settings > Other Linker Flags に-ObjCをつける。
SDKは、Objective-C製です。

```objective_c:ISRecogenizeCard.h
//
//  ISRecogenizeCard.h
//  ISBusinessCardSDK
//
//  Created by Simon liu on 17/02/15.
//  Copyright (c) 2017年 xzliu. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <ISOpenSDKFoundation/ISOpenSDKFoundation.h>
#import "ISBCRDefine.h"

typedef void(^ConstructResourcesFinishHandler)(ISOpenSDKStatus status);

@interface ISRecogenizeCard : NSObject

/**
 *  A singleton method for ISRecogenizeCard, you should use this singleton to call other methods.
 *
 *  @return A ISRecogenizeCard instance.
 */
+ (ISRecogenizeCard *)sharedISOpenSDKController;

/*
 You should call this method to construct resources before call other APIs in the SDK, calling this API will cause an online authorization for the first time this SDK is used.Once authorized successfully, this API will be returned immediately within expire time and try to update authorization status in background
 */
/**
 *  Use this method to construct resources and authorize SDK. You should call this method before any other API.
 *
 *  @param appKey    The app key you apply for.
 *  @param subAppKey For further use, now is nil.
 *  @param handler   The construct result block. The status is defined in header file 'ISOpenSDKStatus.h'
 */
- (void)constructResourcesWithAppKey:(NSString *)appKey
                           subAppkey:(NSString *)subAppKey
                       finishHandler:(ConstructResourcesFinishHandler)handler;

/**
 *  Recognize card with recognize result content, if recognize failed, then return error.
 *
 *  @param cardImage              The image that need to be recognized.
 *  @param recognizeLanguages     Recognized languages that need to be preset, e.g. @[@(ISOCRLanguageTypeEnglish), @(ISOCRLanguageTypeChineseSimp), @(ISOCRLanguageTypeJapanese)].
 *  @param needProcess            If need to process image, please set to YES.
 *  @param recognizeResultHandler Return recognized result content and error if recognize failed.
 *  @param processResultHandler   Return processed image and error if process failed.
 *
 *  @return The authorize status, defined in header file 'ISOpenSDKStatus.h'
 */
- (ISOpenSDKStatus)recognizeCardWithImage:(UIImage *)cardImage
                        recognizeLanguage:(NSArray *)recognizeLanguages
                         needImageProcess:(BOOL)needProcess
                   recognizeResultHandler:(ISRecognizeResultHanler)recognizeResultHandler
                imageProcessResultHandler:(ISProcessCardResultHandler)processResultHandler;

/**
 *  Release resources, call when you want to finish recognize.
 */
- (void)destructResources;

/**
 *  Use this method to get version information
 */
+ (NSString *)getSDKVersion;
@end 
```

### 1.4. Build Settingsにある下記の設定をNoに変更する。

・Compress PNG Files
・Remove Text Metadata From PNG files

Compress PNG File Errorが出たため、上記の対応を行いました。

## 2.実装

### 2.1 名刺データ保存用の型を用意する。

```swift:BussinessCard.swift
import UIKit
import ISOpenSDKFoundation
import ISBusinessCardSDK

struct BussinessCard {

    //性    
    var firstName = ""

    //名
    var lastName = ""
    
    //電話番号
    var cellNumbers = [String]()
    var phoneNumbers = [String]()
    
    //会社情報
    var companies = [ISContactCompanyInfo]()
    
    //メールアドレス
    var emails = [String]()
    
    //URL
    var urls = [String]()
    
    //住所
    var address = [ISContactAddress]()
    
}
```
### 2.2. 名刺データの解析用のロジッククラスを用意する。

```swift:ORCManager.swift
import UIKit
import ISOpenSDKFoundation
import ISBusinessCardSDK

protocol OCRManagerDelegate: class {
    func success(cardInfo: BussinessCard)
    func failure()
}

final class ORCManager {
    
    //MARK: - Property
    let appkey = "" //this key just for test
    let subKey = "" //reserved for future use
    
    let languagesArray = [
        Int(ISOCRLanguageType.english.rawValue),
        Int(ISOCRLanguageType.japanese.rawValue)
    ]

    weak var delegate: OCRManagerDelegate?
    
    /// 認証
    func verify() {
        ISRecogenizeCard.sharedISOpenSDKController()
            .constructResources(withAppKey: appkey, subAppkey: subKey) { status in
                
                if case ISOpenSDKStatus.success = status {
                    print("Authorized success")
                } else {
                    let statusInt = String.init(status.rawValue)
                    let statusAnalysis = IntSigSDKStatus.statusAnalysisEnglish(status: status)
                    let statusInfo = "Construct resource failed with:" + "[" + statusInt + "]" + "<" + statusAnalysis + ">"
                    print(statusInfo)
                }
        }
    }
    
    /// 名刺データを解析する
    func analyze(originImage: UIImage) {
                
        ISRecogenizeCard
            .sharedISOpenSDKController()
            .recognizeCard(with: originImage,
                           recognizeLanguage: languagesArray,
                           needImageProcess: true,
                           recognizeResultHandler: { info, error in
                            
                            if let error = error {
                                self?.delegate?.failure()
                                return
                            }
                            
                            if let info = info {
                                let cardInfo = self.parse(info: info)
                                self?.delegate?.success(cardInfo: cardInfo)
                            }
            }, imageProcessResultHandler: nil)
    }
}

extension ORCManager {
    
    private func parse(info: ISContactInfo) -> BussinessCard {
        
        var result = BussinessCard()
        
        /// 姓
        if let firstName = info.firstName {
            result.firstName = firstName
            print(firstName)
        }
        
        /// 名
        if let lastName = info.lastName {
            result.lastName = lastName
        }
        
        // 会社名、部署、役職
        if let companies = info.companies as? [ISContactCompanyInfo] {
            result.companies = companies
        }
        
        /// 電話番号
        if let phones = info.phones as? [ISContactItem] {
            
            for phone in phones {
                
                // 携帯電話
                if phone.itemLabel == "CELL" {
                    if let itemInfo = phone.itemInfo {
                        result.cellNumbers.append(itemInfo.description)
                    }
                } else if phone.itemLabel == "WORK" {

                    // 固定電話                    
                    if let itemInfo = phone.itemInfo {
                        result.phoneNumbers.append(itemInfo.description)
                    }
                }
            }
        }
        
        /// メールアドレス
        if let emails = info.emails as? [ISContactItem] {
            
            for email in emails {
                
                if email.itemLabel == "WORK" {
                    
                    if let itemInfo = email.itemInfo {
                        result.emails.append(itemInfo.description)
                    }
                }
            }
        }
        
        /// URL
        if let urls = info.urls as? [ISContactItem] {
            
            for url in urls {
                
                if url.itemLabel == "HOMEPAGE" {
                    
                    if let itemInfo = url.itemInfo {
                        result.urls.append(itemInfo.description)
                    }
                }
            }
        }
        
        /// 住所
        if let addresses = info.addresses as? [ISContactAddress] {
            result.address = addresses
        }
        return result
    }
}
```

### 2.3. 利用する。

```swift:ViewController.swift
import UIKit

final class ViewController: UIViewController {
    
    private let ocrManager = ORCManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        ocrManager.verify()
    }

    @IBAction func clickTakePhotoButtonMethod(_ sender: Any) {
        
        guard let image = UIImage(named: "demo.jpg") else {
            return
        }
        
        ocrManager.analyze(originImage: image)        
    }
}
```
# まとめ
OCRの精度については、お試しあれ！
参考までに