HTTPのPOSTメソッドで送信された画像をOpenCVで操作するApacheモジュール

[前回](http://qiita.com/d9magai@github/items/7c2631fd859c989fec89)、AWS SDK for C++を使ってS3にある画像をOpenCVで操作してみました。今度はHTTPのPOSTメソッドで送信された画像をOpenCVで操作するApacheモジュールを作ってみたくなったので試してみます。

# これを作ります
アップロードされた画像の顔を検出して四角で囲んで表示します。
![mov.gif](https://qiita-image-store.s3.amazonaws.com/0/24415/133510a4-d639-3857-6820-09ab2d74c863.gif)

# ApacheのモジュールをC++で書きたい

C++でApacheモジュールを書きたいんです。OpenCVの3.0でC言語関数形式のインターフェイスはメンテナンスが終了しているためC++を使うことが推奨されてますし。[openCV will drop C API support soon](http://answers.opencv.org/question/17546/opencv-will-drop-c-api-support-soon/)
「ApacheのモジュールってCで書かれてるんじゃない?」って私も思っていましたが、どうやらC++で書けるみたいなんですね。[こちら](http://yut.hatenablog.com/entry/20131101/1383260489)が非常に参考になりました。

試しに一丁モジュールを作って動かし、C++を使ってApacheのモジュールが書ける喜びを噛み締めましょう。

# POSTされたデータをパースして画像を`cv::Mat`にいれる

POSTメソッドで送信された画像をOpenCVで操作するApacheモジュールを作るためには、画像を`cv::Mat`にいれてやることになります。
ここの部分が結構手こずったので、詳しく書いてみたいと思います。

## `libapreq2`を使う

調べたところ`libapreq2`を使うのがPOSTされたデータを楽にパースする近道っぽかったです。なので、入れましょう。

```bash
yum install libapreq2-devel
```

[こちら](http://yoshifumi.hateblo.jp/entry/20080604/p1)の`mypost`モジュールを参考にC++で再実装を試してみたところ、見事にPOSTデータのパースに成功しました。
[公式ドキュメント](https://httpd.apache.org/docs/2.4/developer/modguide.html)を読んでもみましたが、`libapreq2`を使わないとなるとかなり骨が折れそうなので、私は素直に使うことにしました。

# POSTされたデータをパースしたい

POSTされたテキストのパースまでは順調に進めたので、

```html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>imagesender</title>
</head>
<body>
<form action="/imagereceiver" method="post" enctype="multipart/form-data">
  <input type="file" name="image" size="30" /><br />
  <input type="submit" value="upload" />
</form>
</body>
</html>
```

こんなフォームを作って今度は画像をPOSTして動かしてみよう、と意気込んてみたはいいものの、ここにかなり手こずってしまいました。詳しく書いてみます。


まず考えたのは「POSTされた画像ってどこにあるんだろう?」ということです。これについてはリファレンスを読むことで辿りつけました。
[Apache 2.X Filter Module](http://httpd.apache.org/apreq/docs/libapreq2/group__mod__apreq2.html)を読み、`apreq_handle_apache2()`を使うことで`request_rec`とcommunicateができるapreqのハンドルである`apreq_handle_t`が作れることが分かりました。
そして、[include/apreq_module.h File Reference](http://httpd.apache.org/apreq/docs/libapreq2/apreq__module_8h.html#7c4cb1c2b91f5539b2231d09ebea2239)に書いてありますが`apreq_body_get()`を使えば`apreq_param_t`構造体のbody paramを`apreq_handle_t`からフェッチできます。`apreq_param_t`はメンバとして`upload`を持っています([apreq_param_t Struct Reference](http://httpd.apache.org/apreq/docs/libapreq2/structapreq__param__t.html))。POSTされた画像はこの中に入っているみたいです。

どうやら`apr_bucket_brigade`構造体である`upload`の中身を`cv::Mat`に入れられれば良さそうだな、というところまではわかりました。
では、どうやって入れるのかという話ですが、私はこの辺からちんぷんかんぷんでした。いろいろ調べてみました。

# `apr_bucket_brigade`や`apr_bucket`について

Apacheモジュールではデータの入出力は双方向循環リストを通して行います。
`apr_bucket_brigade`は内部に`apr_bucket`を持っており、`apr_bucket`は前後の`apr_bucket`へのポインタを持っているリング構造です。
`apr_bucket_brigade`のnextは先頭の`apr_bucket`を指しており、その`apr_bucket`のnextは次の`apr_bucket`を指します。どんどん辿っていくと、先頭の`apr_bucket`を指している`apr_bucket_brigade`に行きつきます。
逆も一緒で、`apr_bucket_brigade`のprevは末尾の`apr_bucket`を指しており、どんどん辿っていくと末尾の`apr_bucket`を指している`apr_bucket_brigade`に行きつきます。`apr_bucket_brigade`が番兵として機能します。

## `upload`を`cv::Mat`に入れる

なので、`apr_bucket_brigade`構造体である`upload`の中身を`cv::Mat`に入れるコードはこんな感じになりました。

```c

cv::Mat bb2Mat(request_rec *r, apr_bucket_brigade *upload) {

    apr_bucket_brigade *bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    apreq_brigade_copy(bb, upload);
    std::vector<char> vec;
    for (apr_bucket *e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) {
        const char *data;
        apr_size_t len;
        if (apr_bucket_read(e, &data, &len, APR_BLOCK_READ) != APR_SUCCESS) {
            throw std::runtime_error("failed to read bucket");
        }
        const char *dup_data = apr_pstrmemdup(r->pool, data, len);
        vec.insert(vec.end(), dup_data, dup_data + len);
        apr_bucket_delete(e);
    }

    cv::Mat ret = cv::imdecode(cv::Mat(vec), CV_LOAD_IMAGE_COLOR);
    if (ret.data == NULL) {
        throw std::runtime_error("buffer is too short or contains invalid data");
    }
    return ret;
}
```

`cv::Mat`に入れられたらあとはこっちのもんです。好きなようにしましょう。


## `cv::Mat`を`apr_bucket_brigade`に入れる

この画像を表示してやるためには、今度は`cv::Mat`を`apr_bucket_brigade`に入れる必要があります。
やり方としては、

1.`cv::Mat`をエンコードして`std::string`にする
2.`std::string`を`apr_bucket_brigade`に入れる

こんな感じになります。`image`が`cv::Mat`の変数です。

```c
    // 1.cv::Matをエンコードしてstd::stringにする
    std::vector<int> params{CV_IMWRITE_JPEG_QUALITY, 100};
    std::vector<unsigned char> buf;
    cv::imencode(".jpg", image, buf, params);
    std::string data(buf.begin(), buf.end());

    // 2.std::stringをapr_bucket_brigadeに入れる
    apr_bucket *bucket = apr_bucket_pool_create(data.c_str(), data.length(), r->pool, r->connection->bucket_alloc);
    apr_bucket_brigade *bucket_brigate = apr_brigade_create(r->pool, r->connection->bucket_alloc);
    APR_BRIGADE_INSERT_TAIL(bucket_brigate, bucket);
```

### 余談

途中、見慣れない単語が出てきたので意味を調べたりしたところ、

* brigade:【陸海軍, 軍事】 旅団
* sentinel:歩哨, 哨兵

と、出てきました。ここにきてApacheという名前の戦闘ヘリがあることを思い出し、「なんて軍事的なんだ・・・」と思ったりもしましたが、"bucket brigade"で一つの言葉で意味はバケツリレー、そしてsentinelとは番兵のことでした。


# Dockerで動かしてみる

目的のApacheモジュールを書いてみました。
さくっと動かせるように、Docker Hubで公開してみました。[こちら](https://hub.docker.com/r/d9magai/mod_imagereceiver/)です(ちなみにmod_imagereceiverって名前はいまいちだったなーと思います。でもいいや、と思っています)。

以下のコマンドで動かせます。

```bash
sudo docker run -dp 8080:80 d9magai/mod_imagereceiver
```

http://localhost:8080/form.html を開けばファイルアップロードのフォームが表示されるので、顔が映っている画像をアップロードしてみましょう。顔を検出して四角で囲んで表示されると思います。

# おわりに

HTTPのPOSTメソッドで送信された画像をOpenCVで操作するApacheモジュールを書けました。またApacheのデータ構造もおぼろげながら理解できたので面白かったです。
そして[前回](http://qiita.com/d9magai@github/items/7c2631fd859c989fec89)と組み合わせることでPOSTされた画像とS3の画像をOpenCVでなんやかんやするApacheモジュールも書けそうです。