PythonとWinRT OCRで文字認識


# インストール

```bash
pip install winocr
```

https://github.com/GitHub30/winocr

# Usage

### winocrをインポート
```python
import winocr
```
![Screenshot from 2021-06-05 02-17-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/c25df582-6f8d-0529-fc85-34fb6e8e2445.png)


### Pillow使い
**lang**パラメータ(第2引数)で認識対象の言語を指定可能です。

```python
from PIL import Image

img = Image.open('test.jpg')
(await winocr.recognize_pil(img, 'ja')).text
```
![Screenshot from 2021-06-05 02-17-47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/370a54c8-b56a-53b8-6f25-6433c16088fa.png)

### OpenCV使い
```python
import cv2

img = cv2.imread('test.jpg')
(await winocr.recognize_cv2(img, 'ja')).text
```
![Screenshot from 2021-06-05 02-19-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/e8013c86-a832-987c-57c8-158565f41042.png)


### Colaboratoryへのローカルランタイムへの接続

Jupyter起動

```powershell
pip install jupyterlab jupyter_http_over_ws
jupyter serverextension enable --py jupyter_http_over_ws
jupyter notebook --NotebookApp.allow_origin='https://colab.research.google.com' --ip=0.0.0.0 --port=8888 --NotebookApp.port_retries=0
```

UbuntuやMacから使用する場合は、[VMS](https://developer.microsoft.com/ja-jp/microsoft-edge/tools/vms/)などからWindowsをVirtualBoxなどにインストールし、ポートフォワーディングなどを行う。
![Screenshot from 2021-06-05 02-09-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/a0bbeb5b-36f4-d88c-83ad-75f65e6cc7e9.png)

![Screenshot from 2021-06-05 01-58-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/015bc355-d50f-3b9d-7ad1-a34d4a561f10.png)

Jupyter/JupyterLabは、何も設定せずに使えます!

### API経由で使う人
予めOCR Serverを起動させること！

```powershell
pip install winocr[api]
winocr_serve
```

#### Python
```python
import requests

f = open('test.jpg', 'rb')
requests.post('http://localhost:8000/?lang=ja', f.read()).json()['text']
```
![Screenshot from 2021-06-05 02-21-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/d71b3516-2dfe-ad91-bfa5-f47f7b86c2cc.png)
`./ngrok http 8000` でColaboratoryのランタイムでOCRを実行できます。

```python
from PIL import Image
from io import BytesIO

img = Image.open('test.jpg')
# 前処理する
buf = BytesIO()
img.save(buf, format='JPEG')
requests.post('https://15a5fabf0d78.ngrok.io/?lang=ja', buf.getvalue()).json()['text']
```
![Screenshot from 2021-06-05 12-30-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/390d9597-1d55-5d1c-8520-15c4a8118ddd.png)

```python
import cv2
import requests

img = cv2.imread('test.jpg')
# 前処理する
requests.post('https://15a5fabf0d78.ngrok.io/?lang=ja', cv2.imencode('.jpg', img)[1].tobytes()).json()['text']
```
![Screenshot from 2021-06-05 12-30-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/9a3da54a-66d6-9a82-708f-dd47c2821900.png)

Windows ServerでOCR Serverを動かすことも可能です。

#### Curl
```bash
curl localhost:8000?lang=ja --data-binary @test.jpg
```
![Screenshot from 2021-06-05 02-23-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/fd1845fc-e647-0d7f-536e-64e3c02cc8a1.png)

#### JavaScript
Chromeかつ英語だけの認識だけでよければ、[Text Detection API](https://www.chromestatus.com/feature/5644087665360896)も検討できます。

```javascript
// input要素の画像から認識
const file = document.querySelector('[type=file]').files[0]
await fetch('http://localhost:8000/', {method: 'POST', body: file}).then(r => r.json())

// 画像をダウンロードして認識
const blob = await fetch('https://image.itmedia.co.jp/ait/articles/1706/15/news015_16.jpg').then(r=>r.blob())
await fetch('http://localhost:8000/?lang=ja', {method: 'POST', body: blob}).then(r => r.json())
```


### 取得可能な情報
**angle**, **text**, **line**, **word**, **BoundingBox**が取得できます。

```python
import pprint

result = await winocr.recognize_pil(img, 'ja')
pprint.pprint({
    'text_angle': result.text_angle,
    'text': result.text,
    'lines': [{
        'text': line.text,
        'words': [{
            'bounding_rect': {'x': word.bounding_rect.x, 'y': word.bounding_rect.y, 'width': word.bounding_rect.width, 'height': word.bounding_rect.height},
            'text': word.text
        } for word in line.words]
    } for line in result.lines]
})
```
![Screenshot from 2021-06-05 02-48-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/cf547b11-108d-cc53-e8c3-16f37ed28ac5.png)

### 言語のインストール

```powershell
# 管理者のPowerShellでインストールできます
Add-WindowsCapability -Online -Name "Language.OCR~~~en-US~0.0.1.0"
Add-WindowsCapability -Online -Name "Language.OCR~~~ja-JP~0.0.1.0"

# インストールした言語の検索
Get-WindowsCapability -Online -Name "Language.OCR*"
# State: NotPresentの言語はインストール出来ていないので必要に応じてインストールしてください
Name         : Language.OCR~~~hu-HU~0.0.1.0
State        : NotPresent
DisplayName  : ハンガリー語の光学式文字認識
Description  : ハンガリー語の光学式文字認識
DownloadSize : 194407
InstallSize  : 535714

Name         : Language.OCR~~~it-IT~0.0.1.0
State        : NotPresent
DisplayName  : イタリア語の光学式文字認識
Description  : イタリア語の光学式文字認識
DownloadSize : 159875
InstallSize  : 485922

Name         : Language.OCR~~~ja-JP~0.0.1.0
State        : Installed
DisplayName  : 日本語の光学式文字認識
Description  : 日本語の光学式文字認識
DownloadSize : 1524589
InstallSize  : 3398536

Name         : Language.OCR~~~ko-KR~0.0.1.0
State        : NotPresent
DisplayName  : 韓国語の光学式文字認識
Description  : 韓国語の光学式文字認識
DownloadSize : 3405683
InstallSize  : 7890408
```

宗教上の理由でPythonが使えない、PowerShellだけで認識したい場合は、 [こちら](https://gist.github.com/GitHub30/8bc1e784148e4f9801520c7e7ba191ea)

# 高速化
並列処理することで3倍高速になりました。コア数を増やせばさらに高速化できます！

```python
from PIL import Image

images = [Image.open('testocr.png') for i in range(1000)]
```

## 1コア 48秒
CPUを使い切れていません。
![Screenshot from 2021-06-08 12-12-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/fa2c2f52-9f86-d452-d2c7-ab3c0e0af87d.png)

```python
import winocr

[(await winocr.recognize_pil(img)).text for img in images]
```
![Screenshot from 2021-06-08 12-13-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/eee6cc20-7fc5-7db7-84db-caeae2defa99.png)

ちなみに、Tesseractで同じ画像を同じ枚数認識すると、223秒かかります
![Screenshot from 2021-06-09 00-24-57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/d86bbb20-524e-93e6-1448-afb9e2539fbd.png)

## 4コア 16秒
CPUを100%使用しています。電気代に注意してください！
![Screenshot from 2021-06-08 12-17-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/7ceebf1e-f22f-50e5-965a-402ffc78645b.png)


workerモジュールを作成

```python
%%writefile worker.py
import winocr
import asyncio

async def ensure_coroutine(awaitable):
    return await awaitable

def recognize_pil_text(img):
    return asyncio.run(ensure_coroutine(winocr.recognize_pil(img))).text
```

```python
import worker
import concurrent.futures

with concurrent.futures.ProcessPoolExecutor() as executor:
  # https://stackoverflow.com/questions/62488423
  results = executor.map(worker.recognize_pil_text, images)
list(results)
```

![Screenshot from 2021-06-08 12-18-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208363/a7167571-aa5d-cb15-ada1-a2d200e58eb8.png)