英語の論文を楽に訳したい．

# はじめに
英語の論文を読む時，いちいち論文の文章をコピーして[DeepL](https://www.deepl.com/translator)にペーストするのが面倒だなと思ったので，訳したい箇所の画像をいれると翻訳されたテキストが返ってくるようなプログラムがほしいなと思って自作しました!(本当は原文をそのまま読めなきゃいけないというお叱りはなしでお願いします．)  
ここのコードは[github](https://github.com/zakkii-k/Translate_English_img)にもあげています．

# 注意
ただの大学生が自分用に作ったコードなので，コードが汚かったり文章が雑だったりはありますが気にしない人だけ読み進めてください．

# 動機
冒頭にも書きましたが，DeepLにいちいち**コピペして整形するのが面倒**だと感じたのがきっかけです．
DeepLにはpdfごと訳してくれる機能もあるのですが，型が崩れたり図表みたいな余分なところも訳したりと個人的にあまり好きじゃなかったのと，ファイル数にも制限があるので(お金がないので無料プラン)，毎回該当箇所をコピーして貼り付けてました．
ただ，コピペするにしても
- 文章中の数式に使われてる斜体の英字とかだと全く別の文字として認識される．
- まるまる一文訳が抜ける．
- そもそも論文をコピーしようとした時に変な範囲が選択される．
- いちいちコピペするのが面倒．

などの理由で効率が悪かったです．特に**斜体の英字**についてが一番面倒に感じており，最初はそれを改善するコードだけを書いていたのですが，その後Pythonならそもそもまとめて訳まで出来そうだと思い，なにか方法はないかと考えていた時DeepLのPython向けAPIを見つけました．
このAPIでは，**１ヶ月に50万字まで無料**で使えるらしいので，これを使って　自分で自分の思うように作っちゃおうと思いました．

# 事前準備
あらかじめpip(またはpip3)でライブラリをインストールしておいてください．
以下のコマンドでできます．
```
pip install tqdm
pip install deepl
pip install pyocr
pip install nltk
```
また，DeepLの開発者向けプランに登録し，APIの認証コードを取得しておく必要があります．
プランは有料でも無料でも問題ないです(無料は月50万字まで翻訳可能)．



# 実装
コードは，関数をまとめたtools.pyとそれを使うmain.pyに分けてます．多分main.pyの中の処理も1つの関数にまとめるのが普通なのかなとか思いながら長々と書いてます．
実行環境は
- Macbookpro (m1)
- Python 3.10

です．

なお，forループで`tqdm`というものを使ってますが，これはプログレスバーが出た方が進捗がわかりやすいし見た目がかっこいいからです．
```main.py
import tools
import glob
import tqdm
files = glob.glob("./paper_fig/[1-5].png")
Entxt = "En_1.txt"
Jatxt = "Ja_1.txt"
text_l = []
files.sort()
for i in files:
    print(i)
    text_l.extend(list(tools.img_to_str(i).split()))


f = open(Entxt, "w")
print("Making English document...")
string = tools.to_str(text_l)
f.write(tools.divide_sentence(string))
f.close()

print("translating English into Japanese...")

out_f = open(Jatxt, "w")
# cnt = 0
f = open(Entxt, "r")
f_list = f.readlines()
for i in tqdm.tqdm(range(len(f_list))):
    out_f.write(tools.translate(f_list[i]))

    #=====debug=====
    # cnt+=1
    # if(cnt >= 10):
    #     break
f.close()
out_f.close()
```

```tools.py
import sys
from PIL import Image
import pyocr
import pyocr.builders
from nltk.tokenize import sent_tokenize
import deepl
import tqdm
def img_to_str(filename):
    tools = pyocr.get_available_tools()
    if len(tools) == 0:
        print("No OCR tool found")
        sys.exit(1)

    tool = tools[0]
    # print("Will use tool '%s'" % (tool.get_name()))

    txt = tool.image_to_string(
        #文字認識対象の画像image.pngを用意する
        Image.open(filename),
        lang="eng",
        builder=pyocr.builders.TextBuilder(tesseract_layout=6)
    )
    #print( txt )
    return txt
    
def divide_sentence(text): #文章を1文ずつ改行する．
    a = sent_tokenize(text)
    #print(a)
    ans = ""
    for i in a:
        ans += i
        ans += "\n"
    return ans

    

def translate(text):
    translator = deepl.Translator("hogehoge")#APIの認証コードを入力
    result = translator.translate_text(text, target_lang="JA") 
    translated_text = result.text
    return translated_text


def to_str(l): #リストで受け取った単語を繋げて文にする．
    s = ""
    set_L = set(range(119860, 119886))
    set_S = set(range(119886, 119912))

    for i in tqdm.tqdm(range(len(l))):
        tmp = list(map(ord, list(l[i])))
        if(len(set(tmp)&set_L) > 0):
            tmp_s = ""
            for j in range(len(tmp)):
                if(tmp[j] >= 119860 and tmp[j] < 119886):
                    tmp[j] = tmp[j]-119795
                tmp_s += chr(tmp[j])
            l[i] = tmp_s
        elif(len(set(tmp)&set_S) > 0):
            tmp_s = ""
            for j in range(len(tmp)):
                if(tmp[j] >= 119886 and tmp[j] < 119912):
                    tmp[j] = tmp[j]-119789
                tmp_s += chr(tmp[j])
            l[i] = tmp_s
        if(l[i][-1] == "-" and "state" not in l[i] and "of" not in l[i]):
            l[i] = l[i][:-1]
            s += l[i]
            continue
        s += l[i]
        s += " "
    return s
```

## 動作の流れ
1. main.pyでは，読み込みたい画像ファイルと，出力する時のファイル名をそれぞれ`files`，`Entxt`, `Jatxt`で指定します．画像ファイルの名前は，読み込んでほしい順に，**1.png, 2.png, ..., n.png**としておくことで，仮に読み込んでほしい順番に読み込まれなかったとしてもそのあとの`files.sort()`処理で順番に並ぶようにしています．

2. ソートされたファイルから，`tools.img_to_str`で画像中の文字を抽出します．
img_to_strは[【python】文字認識で画像からテキストを出力するやり方をわかりやすく【Tesseract&PyOCR】](https://www.kzyrepository.com/2020/11/python%E6%96%87%E5%AD%97%E8%AA%8D%E8%AD%98%E3%81%A7%E7%94%BB%E5%83%8F%E3%81%8B%E3%82%89%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%92%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B%E3%82%84%E3%82%8A%E6%96%B9%E3%82%92%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%8Ftesseractpyocr/)のものを参考にさせていただきました．この関数で文字列を抽出して返します．返ってきた文字列は，空白区切りで分割したリストにし，結合していきます．結合しているのは，1文が2つの画像にまたがっている時にそれらを1文として繋げて処理できるようにするためです．
例えば，
    ```
    Doraemon is one of my favori-
    te characters.
    ```
    という文字列(favoriteの途中で改行が挟まれているとする)は
    ```
    ["Doraemon", "is", "one", "of", "my", "favori-", "te", "charachers."]
    ```
    という配列になります．

3. 2で作成したリストから，文を繋げてまずは英語のテキストファイルを作ります(わざわざ英語のテキストファイルを作る必要はないのですが，画像から抽出した文字が一部間違っていた場合などに修正しやすいように作っています)．
tools.to_strは，単語ごとに分割されていたリストを1つの文章にする関数です．これは元々DeepLにコピペする際に数式で使われていた斜体の文字や，改行によって2つに分割されてしまった単語(favori- te)などを修正するために使っていたのですが，画像から文字を読み取る時ため基本的に斜体の文字の修正は不要なので，後者の2つに分割された単語を修正するために使っています．
2で示した例の場合，
    ```
    Doraemon is one of my favorite characters.
    ```
    という文字列が返されます．

4. 3で返された文字列は改行がない横1列に長い文章なので，見やすさのために1文ずつ改行を挟むのが`tools.devide_sentence`です．
この関数に入れられた文字列は，文末のピリオドと文中のピリオド(Washington D.C.など)を区別するためにsent_tokenizeで構文解析が行われ，文末のピリオドの後ろには改行を示す"\n"が追加されて返されます．
(日本語に訳してから改行を入れると文末は必ず句点なのでピリオドとの区別をつけるための構文解析は不要だったのですが，英文のtxtファイルも作成するためにここで構文解析を用いて改行を入れています．)

5. ここまでの1~4で，
画像から文字列を抽出→文字列を整形→txtファイルに出力
という処理が完了しました．
次はこれを日本語に翻訳して出力します．

6. f.readLines()で，英語のファイルの各行を1つの要素とするリスト`f_list`を作成します．
例えば，
    ```
    Hello, my name is zakkii.
    Nice to meet you.
    ```
    という文章の場合，
    ```
    f_list = ["Hello, my name is zakkii.", "Nice to meet you."]
    ```
    となります．
    f_listの中身を1つずつ`tools.translate`に入れることで，一文ずつの逐語訳が行われます．
    translate関数については[DeepLのAPIに関するページ](https://www.deepl.com/ja/blog/announcing-python-client-library-for-deepl-api)に記載されているものをそのまま使っています．
返された文字列をそのままファイルに書き込んでいくことで日本語に翻訳したtxtファイルが完成します．

# 実験
適当な英文として，[この論文](https://papers.dice-research.org/2021/KCAP2021_KG_Partitioning/public.pdf)のAbstractの部分の画像を使います．![スクリーンショット 2022-11-14 17.16.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2635963/ca787763-ad86-cd08-5e64-5e9af6a7491d.png)

出力結果としては以下のようになりました．
```En.txt
Data partitioning is an effective way to manage large datasets.
While a broad range of RDF graph partitioning techniques has been proposed in previous works, little attention has been given to workload-aware RDF graph partitioning.
In this paper, we propose two techniques that make use of the querying workload to detect the portions of RDF graphs that are often queried concurrently.
Our techniques leverage predicate co-occurrences in SPARQL queries.
By detecting highly co-occurring predicates, our techniques can keep data pertaining to these predicates in the same data partition.
We evaluate the proposed partitioning techniques using various real-data and query benchmarks generated by the FEASIBLE SPARQL benchmark generation framework.
Our evaluation results show the superiority of the proposed techniques in comparison to previous techniques in terms of better query runtime performances.
```
```Ja.txt
データ分割は、大規模なデータセットを管理するための有効な手段である。
これまでの研究において、広範なRDFグラフ分割技術が提案されているが、作業負荷を考慮したRDFグラフ分割にはほとんど注意が払われていない。
本論文では、問い合わせの作業負荷を利用して、RDFグラフのうち、同時問い合わせが多い部分を検出する2つの技術を提案する。
この技術は、SPARQLクエリにおける述語の共起を利用するものである。
共起度の高い述語を検出することで、その述語に関連するデータを同一のデータパーティションに保持することができる。
FEASIBLE SPARQLベンチマーク生成フレームワークで生成した様々な実データとクエリベンチマークを用いて、提案するパーティショニング技術を評価する。
評価結果から、提案技術は従来技術と比較して、クエリ実行性能の点で優れていることが示された。
```
問題なく訳せていそうです．

# 課題
- タイトルの太字を認識して改行をいくつか増やすなど，章の変わり目をわかりやすくしたい．
- 画像から文字列を抽出する時の精度がよりよいパッケージがほしい(数式のeを€って呼んでる箇所が少しだけあった)
- できればやっぱりpdfのままやってほしい．

他にもこうした方が良いとかこのライブラリの方が良いとかあれば教えてください!
ありがとうございました．