Rustで学術論文からテキストを抽出する #7 - Hough変換による表領域の抽出①

:::note
## Summary
- RustでOpenCVが使える！
- OpenCVのHough変換を使うことによって，表を構成する直線をある程度検出できる
- とはいえ，検出結果にはかなりノイズがのっている

GiHub -> https://github.com/akitenkrad/rsrpp
crates.io -> https://crates.io/crates/rsrpp

## ToDo
- [x] pdftotextで論文から単語単位のテキストと位置情報を取得する (`Word`,`Line`,`Block`,`Page`)
- [x] テキストの属性 (本文, タイトル, 脚注, etc.) を判定する
    - [x] テキストが含まれるエリアを抽出する
        - [x] 2段組みを扱えるようにする
    - [x] セクションのタイトルを識別する
- [ ] 図表に含まれるテキストを除外する
    - [ ] 表を除外する
        - [x] PDFを画像に変換
        - [ ] 画像処理で表の位置を特定
## 今日のファイル
```text
rsrpp
├── Cargo.toml
├── rsrpp
│   ├── Cargo.toml
│   └── src
│       ├── lib.rs
│       └── parser
│           ├── mod.rs <-- today
│           ├── structs.rs <-- today
│           └── tests.rs
└── rsrpp-cli
    ├── Cargo.toml
    └── src
        └── main.rs
```
:::

## 前回までのあらすじ

[前回：Rustで学術論文からテキストを抽出する #6](https://qiita.com/akitenkrad/items/3d278d40f23ef63221c4)

表を検出するにあたって，PDFの画像に対してHough変換を適用することで表領域の検出を試みることにしました．
まずはHough変換を使い倒すところから．

```mermaid
graph LR
    A[要件定義]

    subgraph Popplerの観察
    B[Popperの観察]
    B1[pdftotext]
    B2[pdfinfo]
    B3[pdftohtml]
    B4[pdftocairo]
    end

    subgraph テキストの属性判定
    C[テキストの属性判定]
    C1[テキストエリアの抽出]
    end

    subgraph 図表の処理
    D[図表の処理]
    subgraph 表の検出
    subgraph 今日はここ
    D1[表の検出]
    end
    end
    end
    
    E[テキスト抽出PoC]
    
    A-->B
    B1-->C
    B3-->C
    C1-->D
    D-->E
    B4-->D1
```

## OpenCVの導入

Rustによる画像処理のために[OpenCV](https://opencv.org/)を使います．
クレートの追加だけではなく，事前にOpenCV本体のインストールが必要です．

```bash
> sudo apt install libopencv-dev clang libclang-dev
> cargo add opencv
```

詳しい説明はできませんが，OpenCVは画像処理を行うためのライブラリの代表格の一つです．
様々な言語でラップされていて，Pythonでも使えます．
Rustでも使えるようになっているとは[^1]．
Rustはコアライブラリが薄い言語なのですが，最近は周辺が充実してきて楽しくなってきてますね．

OpenCVによるHough変換の手順は以下の通りです．

```rust
    // read the image
    let _src = imgcodecs::imread(image_path, imgcodecs::IMREAD_COLOR).unwrap();
    let mut src = Mat::zeros(width, height, _src.typ()).unwrap().to_mat().unwrap();

    let dst_size = opencv::core::Size::new(width, height);
    // reshape
    imgproc::resize(&_src, &mut src, dst_size, 0.0, 0.0, imgproc::INTER_LINEAR).unwrap();

    // convert the image to grayscale
    let mut src_gray = Mat::default();
    imgproc::cvt_color_def(&src, &mut src_gray, imgproc::COLOR_BGR2GRAY).unwrap();

    // apply Canny edge detector
    let mut edges = Mat::default();
    imgproc::canny_def(&src_gray, &mut edges, 50.0, 200.0).unwrap();

    // apply Hough Line Transform
    let min_line_length = src.size().unwrap().width as f64 / 10.0;
    let mut s_lines = Vector::<Vec4f>::new();
    imgproc::hough_lines_p(
        &edges,
        &mut s_lines,
        2.,
        PI / 180.,
        100,
        min_line_length,
        3.,
    )
    .unwrap();
```

上記はそれぞれのパラメータを調整した後のものになりますが，正直この辺りは手探りだったので，何か上手い調整方法があれば追々アップデートをかけたいところです．

この設定でHough変換を実行した結果がこちら．
検出した直線を緑色のラインで表示しています．

![houghlines.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933655/3d4d4771-43b0-27c2-c74c-79cd8f0abefd.jpeg)

ターゲットの表に関しては，一番外側のラインは取れていますが，中のラインに関しては短いものを落としていたりします．
また，表でない箇所を直線として誤検知箇所がたくさんあります．
パラメータを変えることによって，特定の画像に対してはある程度最適化が可能なのですが，論文のPDFはどんな品質で上がってくるか未知数なので，Hough変換の最適化を極めるよりは，ノイズが乗った図のようなアウトプットからいかにして表領域を抽出するか，という方向に注力した方が生産的かなと判断しました．

これくらいのノイズに収めるためにめちゃくちゃ試行錯誤したのでその辺りもぜひ記事にしたいところなのですが，全く面白みがないので泣く泣くカットすることにしました．

話がさらに脱線しますが，Rustで画像を扱えることがわかったので，やれることの幅が大きく広がりました．
どうしてもテキストだけで解決できない課題は思い切って画像処理を挟んでみると意外とうまくハマることがあるようなないような．

## 次回

Hough変換である程度表を構成する直線を検出できることがわかったので，次は検出結果をもとにして表領域を確定させに行きます．

[次回：Rustで学術論文からテキストを抽出する #8](https://qiita.com/akitenkrad/items/b09446fecca9db045bf9)

[^1]: https://crates.io/crates/opencv