OpenCV/C++で画像処理入門 vol.6 〜画像を行列にしてみよう②〜

2019.01.31

 

こんにちは、代表の大野です。

 

OpenCV/C++で画像処理入門シリーズの第6回目です。

 

画像を行列にするにあたり、
前回は、デジタル画像と行列の関係性について見てきました。

 

今回は、実際モノクロ画像を行列に変換する
という操作を、OpenCVを使ってやってみたいと思います。

 

環境があれば、ぜひみなさんも一緒に
プログラムを書いて体験してみてくださいね。

 

 

バックナンバー

vol1. 画像を表示してみよう
vol2. 画像を加工してみよう

vol3. 画像に図形を描画してみよう

vol4. 画像に文字を書き込んでみよう

vol5. 画像を行列にしてみよう①

 

今回やること

  • デジタル画像に行列にしてみる
  • デジタル画像の一部分を行列として取得してみる

 

 

環境

マシン  :mac OS Mojave 10.14.1
言語   :C++
ライブラリ:OpenCV 3.4.3
コンパイラ:Clang / LLVM

 

 

準備 & 画像の用意

OpenCVのインストールなどは
バックナンバーなどから、参考にしてください。

 

今回のサンプルは
スワワちゃんが愛用しているスマートフォンである
「スワートフォン(通称スワホ)」を用いることにします。

 

 

スワートフォン(swartphone.jpg)

width : 300px

height : 300px

 

 

これを見た瞬間に

 

300 × 300行列だな
スワホ本体の領域には、数値が低いものが多そうだな

 

と思った方は、前回の内容が
十分に理解されていますね。

 

 

では、いってみましょう。

 

 

デジタル画像に行列にしてみる

 

さて、さっそくですが、
今日はプログラムから見てみたいと思います。

 

行列にするのはそんなに難しくありません。
なぜなら、これまでのブログで
すでにやっている操作だからです。

 

それではみてみましょう。

 

 

<ソースプログラム>

matrix.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
  cv::Mat img, dst;

  img = cv::imread("swartphone.jpg");
  cv::cvtColor(img, dst, cv::COLOR_BGR2GRAY);

  std::cout << dst << std::endl;

  return 0;
}

 

さて、ここまでOpenCV/C++入門ブログを愛読いただいた方なら
このままビルドして、すでに実行してしまっているかもしれませんが、

 

ビルドはできますが、
ターミナルの画面がひどいことになります(笑)

 

 

まずソースコードから説明しますね。

 

1
2
3
4
cv::Mat img, dst;

img = cv::imread("swartphone.jpg");
cv::cvtColor(img, dst, cv::COLOR_BGR2GRAY);

 

これは、「vol2. 画像を加工してみよう」

でやった、
画像を白黒にする処理です。

 

まずは、モノクロ画像を行列にするため
cv::cvtColor()を使って、スワホ画像を白黒にしています。

 

もし、cv::imshow()するなら以下の感じになっているはずです。

 

 

 

続いて、

 

1
  std::cout << dst << std::endl;

 

あれ、いきなり標準出力のcoutしちゃっています。

 

行列にする処理は
どこで行なったのでしょうか?

 

 

そう、実は、OpenCVでは、
cv::imread()をして、デジタル画像を読み込んだタイミングで
行列として、読み込んでいます。

 

これはものすごく当たり前のことを言っているんですが
なぜなら、cv::imread()で格納する先は、

 

cv::Mat img

 

だからです。

 

 

cv::Matは、Matrixの略です。
つまり、Matrix(行列)なんです。

 

 

じゃあ、何も難しいことはありません。

 

このままビルド&実行してしまえば
当然行列が取れます。

 

 

やめたほうがいいですが、
きっとやってみたい気持ちMAXだと思うので(笑)

 

300×300行列 = 90000の数値

 

をターミナルに表示してやりましょう。

 

 

<ビルド & 実行>

ターミナル

1
2
c++ $(pkg-config --cflags --libs opencv) matrix.cpp
./a.out

 

 

結果は、書きません(笑)

 

 

読めるかこんなもん!
って感じかと思います…

 

 

デジタル画像の一部分を行列として取得してみる

 

ということで、デジタル画像処理をするには、
この行列に、四則演算をしていけばいいのですが、

 

もっとわかりやすいように
まずはデジタル画像の一部分だけを取り出して
行列として表示してみるのが、わかりやすいでしょう。

 

いきなり、300 × 300行列だと
標準出力でとんでもないことになるので、

 

1行目だけを取り出してみたいと思います。
それでも300の数値がありますけどね。

 

 

<ソースプログラム>

matrix_part.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
  cv::Mat img, dst;

  img = cv::imread("swartphone.jpg");
  cv::cvtColor(img, dst, cv::COLOR_BGR2GRAY);

    std::cout << dst.row(0) << std::endl;

  return 0;
}

(ビルドは、先ほどの手順と同じ)

 

 

どうですか?

 

300ならなんとか表示画面に
おさまったのではないかと思います。
(おさまらない場合は表示文字サイズの縮小を)

 

 

 

あれ、全部255だ
と思った方もいると思いますので、
画像をもう一度みてみましょう。

 

 

 

 

縦幅300pxあるので、
一番上の1行目は、全部白であることがわかりますよね?

 

なので、全部255で表示されているのは正常なんです。

 

ソースコードの該当部分をみてみると

 

1
std::cout << dst.row(0) << std::endl;

 

 

この、dstというcv::Matクラスのrow()を使って、
引数に行数を入力すれば、その行のみを取得できます。

 

画像の行列は、
1行目、1列目は0から始まります。

 

横幅300pxなら、0から299です。

 

 

今回は、引数が0なので
1行目だけの行列が取得されています。

 

 

では、真ん中の150行目あたりを表示すれば、
255以外の値も、行列から取得できそうなのでやってみましょう。

 

 

matrix_part2.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
  cv::Mat img, dst;

  img = cv::imread("swartphone.jpg");
  cv::cvtColor(img, dst, cv::COLOR_BGR2GRAY);

  std::cout << dst.row(149) << std::endl;

  return 0;
}

(ビルドは、先ほどの手順と同じ)

 

 

どうですか?

 

 

 

 

こんな感じになりました。

 

 

同様に列だけ取り出したい場合は

 

1
std::cout << dst.col(149) << std::endl;

 

で取得することができます。
ここでは、やりませんが、

 

ぜひ自身でやってみてください。

 

 

 

では、最後に
部分行列を指定した場所から取り出す方法をみてみたいと思います。

 

 

 

こちらのスワートフォンの画像のうち
スワワちゃんの右目の部分のみの行列を
取り出してみます。

 

右目は、この画像でいくと
だいたい

 

(x, y) = (95, 115)

 

あたりを左上頂点座標として、

 

 

width 30px × height 40px = 1200行列

 

で取得できると思います。

 

 

<ソースプログラム>

matrix_part3.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
  cv::Mat img, dst;

  img = cv::imread("swartphone.jpg");
  cv::cvtColor(img, dst, cv::COLOR_BGR2GRAY);

  std::cout << dst(cv::Rect(95,115,30,40)) << std::endl;

  return 0;
}

(ビルドは、先ほどの手順と同じ)

 

 

 

<結果表示>

 

 

いかがでしょうか?

 

目の画像のように
丸い円状に、高い数値が出力されている行列になっていますよね?

 

 

1
  std::cout << dst(cv::Rect(95,115,30,40)) << std::endl;

 

このコードの通り、cv::Mat型のdstを
cv::Rect型を使って切り取っています。

 

 

cv::Rect型は

 

第一引数 x座標

第二引数 y座標

第三引数 width(横幅)

第四引数 height(縦幅)

 

でしたね?

 

 

この引数の値を変更することで
任意の好きな画像領域から、

 

行列が取得できることがわかりました。

 

 

 

まとめ

 

いかがでしたでしょうか?

 

 

時間があるときに、様々な画像を使って
デジタル画像を行列として取得してみるのも面白いと思いますので
ぜひ試してみてください。

 

 

それでは。


Top