AzureとColabを使って英語のプレゼンを翻訳する方法

![translated_notext_translate_test_1p_pdf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/4970b8aa-376e-e5bf-eba5-beed5ad5d79a.png)
(from https://www.slideshare.net/PPerksi/deepgram)

こんにちは！逆瀬川 ( https://twitter.com/gyakuse ) です。
今日は英語のプレゼンをサクッと翻訳する方法を共有します。
前回の[英語論文を翻訳する方法はこちら](https://qiita.com/sakasegawa/items/f50aae7f3acf475411aa)
今日も今日とて実装めちゃ汚いのですが、そのあたりはご容赦ください。

## 概要

Azure FormRecogniterを用いて **文字埋め込みのない** 英語の資料を翻訳します(文字埋め込みのある、背景が黒とか癖強くない資料は前回の方法で翻訳できます)。
今回も翻訳には[フリーのニューラル機械翻訳モデルFuguMT](https://staka.jp/wordpress/?p=413)を使用します。
なお、APIの費用として、1ページあたり6.7円くらいかかります。

## AzureでFormRecognizerを有効化する

Azure FormRecognizerとは、Microsoftが機械学習APIであり、ドキュメントに特化した文字検出が行えます。
今回の用途のためにいくつかOCRシステムを検討したのですが、黒背景の文章の検出を一番うまく行えていました。
また英語文章のみならず、日本語文章の文字識別能力も高かったです。

ほかのものでは、前回の論文の領域検出に用いたDetectron2(layoutparser)を使ってみたところ、黒背景の検出に難がありました。
独自のfine-tuningしたレイアウト検出モデルを使うと良い精度が出るかもしれません。
また、Google Cloud Document AIは精度が高かったのですが、返却されたJSONから領域情報を取り出せませんでした。
自分の勘違いかもしれないので、取り出す方法を知っているかたがいましたら教えて下さい！

FormRecognizerは、Azureに登録後以下のステップを踏むことで有効にできます。

- サインアップしてサブスクリプションを作る
    - https://portal.azure.com/?quickstart=true#blade/Microsoft_Azure_Billing/SubscriptionsBlade
    - https://qiita.com/MMaru76/items/fd462324cfec623eef3e
- リソースグループを作る
    - https://portal.azure.com/?quickstart=true#view/HubsExtension/BrowseResourceGroups
    - https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/management/manage-resource-groups-portal
- FormRecognizerリソースを作る
    - https://portal.azure.com/?quickstart=true#view/Microsoft_Azure_ProjectOxford/AppliedAIHub/~/FormRecognizer
    - https://learn.microsoft.com/ja-jp/azure/applied-ai-services/form-recognizer/create-a-form-recognizer-resource?view=form-recog-3.0.0

作り終わったら、keyとendpointを取得します。

![colabtestxxx_-_Microsoft_Azure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958603/261b3440-ff24-7e8f-6b07-bfb0df6d4d17.png)

## Google Colab

https://colab.research.google.com/drive/1X1kiTrASpWmBbB1HbOK2klj9GN8e3mS9?usp=sharing

## 使い方

- key, endpointを自分のものに置き換えます
- 以降の動かし方は[前回のものと同様](https://qiita.com/sakasegawa/items/f50aae7f3acf475411aa#%E4%BD%BF%E3%81%84%E6%96%B9)です

## 処理の流れ

基本的に[英語論文翻訳](https://qiita.com/sakasegawa/items/f50aae7f3acf475411aa)と同じです。

- PdfReaderから取ったbase_pdfをページごとにループさせる
- ページごとに以下の処理を行う
    - Azure RecognizerでOCRをかける
    - 文章をFuguMTで翻訳する
    - レイアウトの対象部分をいい感じの色の長方形で覆う
    - その上に文章を詰め込み、reportlabで書き込む

論文翻訳との主な差分としては、以下があります。

- 文字列埋め込みがないので、LayoutParserではなく、FormRecognizerを使う
    - このおかげで文字を元のpdfから取ってきたりする処理がなくなる
- プレゼンファイルは論文と異なり、背景色が変わるので背景に敷き詰めるボックスカラーを同じ色にする必要がある
- 背景色が変わるので、文字色をいい感じにする必要がある

## 主要な実装

### pdfの特定の範囲から色を抽出する

pdfの画像と領域情報を渡して、背景色を抽出します。
絶対もっといい感じのライブラリや実装方法あるので、もし知ってたら教えて下さい！！

```py
def pick_background_color_from_pdfimage(pdf_image, x_1, y_1, x_2, y_2):
    """Pick the background color from a region of a PDF image.

    Parameters:
        pdf_image (numpy.ndarray): The image in numpy array format.
        x_1 (int): The x coordinate of the top-left corner of the region.
        y_1 (int): The y coordinate of the top-left corner of the region.
        x_2 (int): The x coordinate of the bottom-right corner of the region.
        y_2 (int): The y coordinate of the bottom-right corner of the region.

    Returns:
        tuple: The most common color in the region, represented as an (R, G, B) tuple.
    """
    # 画像をnp.asarray形式で読み込む
    image = pdf_image

    # 特定領域を選択する
    region = image[y_1:y_2, x_1:x_2]

    # 選択された領域内のR、G、Bの値をそれぞれ集める
    r, g, b = region[:,:,0], region[:,:,1], region[:,:,2]

    # R、G、Bの値をそれぞれカウントする
    r_counts = Counter(r.flatten())
    g_counts = Counter(g.flatten())
    b_counts = Counter(b.flatten())

    # 最も多い色のR、G、Bの値を取得する
    most_common_r = r_counts.most_common(1)[0][0]
    most_common_g = g_counts.most_common(1)[0][0]
    most_common_b = b_counts.most_common(1)[0][0]

    # 最も多い色をRGB値で表す
    most_common_color = (most_common_r, most_common_g, most_common_b)
    return most_common_color
```

### 文字色を決定する

いろいろ書いてみましたが、https://stackoverflow.com/questions/946544/good-text-foreground-color-for-a-given-background-color/946734#946734 を最終的に採用しました。
そもそも元の文字色を抽出できればいいのですが、難しそうだったので(領域内の背景色と距離が遠くかつそこそこ使われてる成分を抽出するとか、文字輪郭を頑張ってOCRで抽出してそこの成分を抽出するとか)、シンプルなものにしました。

```py
import colorsys

def calculate_contrast_ratio(color1, color2):
    """
    二つの色のコントラスト比を計算する関数。
    color1: tuple(int, int, int) -- コントラスト比を計算したい色1。RGB値を0~255の範囲で指定する。
    color2: tuple(int, int, int) -- コントラスト比を計算したい色2。RGB値を0~255の範囲で指定する。
    return: float -- color1とcolor2のコントラスト比。
    """
    # RGB 値を 0.0～1.0 の間の値に変換する
    r1, g1, b1 = [x / 255 for x in color1]
    r2, g2, b2 = [x / 255 for x in color2]

    # 輝度を計算する
    l1 = 0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1
    l2 = 0.2126 * r2 + 0.7152 * g2 + 0.0722 * b2

    # コントラスト比を計算する
    contrast_ratio = (l1 + 0.05) / (l2 + 0.05)

    return contrast_ratio

# 背景色からテキスト色(白または黒)を決定する関数
# 輝度考慮してW3C準拠なんだけどなんか(33, 33, 33)渡したら(0, 0, 0)が帰ってくる(見づらい)ので一番下の簡単な関数を使う
def determine_text_color_deprecated(bg_color):
    """
    背景色に対して、コントラストの良いテキスト色を決定する関数。
    bg_color: tuple(int, int, int) -- 背景色。RGB値を0~255の範囲で指定する。
    return: tuple(int, int, int) -- テキスト色。RGB値を0~255の範囲で返す。
    """
    # 背景色と白色のコントラスト比を計算する
    white_contrast = calculate_contrast_ratio(bg_color, (255, 255, 255))

    # 背景色と黒色のコントラスト比を計算する
    black_contrast = calculate_contrast_ratio(bg_color, (0, 0, 0))

    # コントラスト比がより大きな色をテキスト色とする
    text_color = None
    if white_contrast > black_contrast:
        text_color = (255, 255, 255)
    else:
        text_color = (0, 0, 0)
    return text_color

# https://stackoverflow.com/questions/946544/good-text-foreground-color-for-a-given-background-color/946734#946734
# also read: https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
def determine_text_color(bg_color):
    if (33*0.299 + 33*0.587 + 33*0.114) > 186:
        return (0, 0, 0) # black
    else:
        return (255, 255, 255) # white
```

## 既知のバグや課題

- 背景色をうまく取る必要がある
- 文字色もうまく取りたい
- 領域がガバガバで埋め尽くしすぎたりする
    - 一番上の画像も、Solutionがちょっと潰れてる

## 後記

- AzureやGoogleのAPIはすごいので、無理に頑張らなくてもいい場合はドシドシ使っていきましょう
- それはそれとして自分でmodelを作ったり、fine-tuningしたりするの楽しいので、OCRやっていきしましょう
- レイアウト検出・OCRはメチャクチャ面白く、最近はTransformerベースのシステムも増えています
    - https://github.com/clovaai/donut
    - https://github.com/microsoft/unilm/tree/master/layoutlmv3
        - 非常に強力なDocument AIの基盤モデルです。現在は中国語の事前学習モデルがありますが、今後増やしていくとのことです。
        - ライセンスが微妙にわかりづらいですが、事前学習モデルは非商用でコードはApacheらしいです
            - https://github.com/microsoft/unilm/issues/873#issuecomment-1406588487
        - 実運用してみたい人はLayerXさんに入ろう！
            - https://tech.layerx.co.jp/entry/2022/11/24/130000