虚苦心観察ブログ

ブログ管理者である虚苦心が私利私欲に基づいて書いているブログです。主にガジェットのレビューだったり、画像処理のことだったりを記事にしています。

へっぽこ知識で作る画像処理プログラム 「自炊本のページ抜けチェック」 3.5日目

はじめに

2.5日目につづき今回もPython版を作成したので解説していこうと思います。 ただし、アルゴリズムについてはC++版を参考にしてもらうことにして、ここではC++版とPython版での違いについて紹介していきます。

C++Pythonでのピクセルアクセス方法の違い

C++ではOpenCVのクラスcv::Matに画像データを保持しますが、Pythonにはcv::Matは存在しません。
では何を使うのかというと、Python数値計算ライブラリであるnumpyのndarrayを利用します。

numpyのデータ構造の一つであるndarrayはC++のcv::Matに比べてかなり柔軟な書き方ができます。
例えば今回の場合だと、文字のない範囲を塗りつぶす、という処理を行っているわけですが、C++版とPython版を比べてみましょう。

C++

//文字のない範囲を3チャンネルの原画像に書き込む
    for (size_t i = 0; i < verticalRanges.size(); i++){
        Range& r = verticalRanges[i];
        //文字のない範囲を3チャンネルの原画像から切り出す
        cv::Rect rect(0, r.start, verticalRangeDst.cols, r.end - r.start + 1);
        cv::Mat roi(verticalRangeDst, rect);
        //切り出した画像を1色で塗りつぶす
        roi = cv::Scalar(0, 0, 255);
    }

Python

    for r in verticalRanges:
        verticalRangeDst[r.start:r.end, :, :] = (0, 0, 255)

C++版はちょっと冗長な書き方をしていますが、、、どうでしょう。Python版はものすごくシンプルで、なんと矩形の塗りつぶしだけなら1行で書けてしまいます!
ndarrayでの各ピクセルへのアクセス方法はPythonの配列と同等の方法になります。
そして":"はPythonのスライスと呼ばれる書き方です。 スライスを使うことでアクセスしたい配列の範囲を指定することができます。 スライスで指定した範囲に対して上記のように代入することができるため、C++に比べて非常に柔軟な書き方が可能です。

別の例として1チャンネルの画像から3チャンネルの画像を作る方法。cv::mergeを使わずに実装することができます。

color = numpy.zeros((gray.shape[0], gray.shape[1], 3), dtype=gray.dtype)
color[:,:,0] = gray
color[:,:,1] = gray
color[:,:,2] = gray

ただPython版のOpenCVにはcv2.merge関数が用意されていてそっちの方が簡単です。あくまで参考。

Pythonではrangeという組み込み関数が存在するので注意

Python版を作るうえで今回注意した点が一つあります。 それはrangeという名前の関数や変数を作らないことです。 なぜかというと、Pythonにはrangeという組み込み関数が存在し、誤って上書きしてしまうとrange関数を使っている場所で予期しない動作をする可能性があります。 今回の場合はrangeという変数名の代わりに頭文字のrをつかって対処しました。 PythonC++に比べて柔軟な書き方が出来て便利な反面、こういった落とし穴もあるので注意が必要です。

出来上がったプログラム

今回もタグを用意しています。

github.com

今回はここまで。