今週の内容 z 静止画 グレースケールへの変換 入力と出力の両方を表示 グレースケール画像のエッジ抽出 ぼかし 1. 2. 3. 4. OpenCV 2.x 演習 z 動画 (ファイル) 知的計算システム研究室 1. グレースケールへの変換 (contributor: 加島 隆博) z 動画 (カメラ) 1. グレースケールへの変換 2 カラーからグレースケールへの変換 カラーからグレースケールへの変換 z 概要 z グレースケール画像の特徴 ¾ 1 チャンネルのみ ¾ 白から黒のグレースケール画像に変換 カラー 一般的には各画素を 0 ~ 255 の 256 段階で表わす 値が大きいほど明るい グレースケール 変換 0 3 255 4 カラーからグレースケールへの変換 カラーからグレースケールへの変換 z 色空間変換関数 z 色空間変換関数 ¾ 変換コード void cvtColor(const Mat &src, Mat &dst, int code, int dstCn = 0) CV_BGR2GRAY z BGR → グレースケール CV_BGR2HSV z BGR → HSV CV_HSV2BGR ¾ 引数 z HSV → BGR src: 入力画像 dst: 出力画像 code: 変換コード dstCn: 出力画像のチャンネル数 (省略可能) CV_BGR2YCbCr z BGR → YCbCr CV_YCbCr2BGR z YCbCr → BGR 6 5 カラーからグレースケールへの変換 入力と出力の両方を表示 z 関数の使い方 z 入力と出力の両方を表示 // カラー画像 cv::Mat colorImage = ...; ¾ 簡単に比較できるように // ⼊⼒画像 cv::imshow("Input", colorImage); // グレースケール画像 cv::Mat grayImage; // 変換 cv::cvtColor(colorImage, grayImage, CV_BGR2GRAY); z 演習 ¾ カラー画像をグレースケール画像に変換して表示するプ ログラムを作成せよ 7 // 出⼒画像 cv::imshow("Output", grayImage); ウィンドウ タイトル 画像 8 グレースケール画像のエッジ抽出 グレースケール画像のエッジ抽出 z 概要 z Laplacian ¾ グレースケール画像のエッジを抽出 元画像 void Laplacian( // ⼊⼒画像 const Mat& src, // 出⼒画像 Mat& dst, // ビット深度 int ddepth, // アパーチャサイズ。正の奇数 (省略可能) int ksize=1, // スケール ファクタ (省略可能) double scale=1, // 結果に⾜される値 (省略可能) double delta=0, // ピクセル外挿⼿法 (省略可能) int borderType=BORDER_DEFAULT) エッジ 抽出 9 10 グレースケール画像のエッジ抽出 グレースケール画像のエッジ抽出 z Sobel z 使用例 void Sobel( // ⼊⼒画像 const Mat& src, // 出⼒画像 Mat& dst, // 出⼒画像のビット深度 int ddepth, // xに関する微分の次数 int xorder, // yに関する微分の次数 int yorder, // 拡張Sobelカーネルのサイズ (省略可能) int ksize=3, // スケール ファクタ (省略可能) double scale=1, // 結果に⾜される値 (省略可能) double delta=0, // ピクセル外挿⼿法 (省略可能) int borderType=BORDER_DEFAULT) ¾ Laplacian cv::Laplacian(grayImage, edgeImage, CV_8U, 3); ¾ Sobel cv::Sobel(grayImage, edgeImage, CV_8U, 1, 0); z 演習 ¾ グレースケール画像のエッジを抽出して表示するプ ログラムを作成せよ Laplacian と Sobel 両方を試すこと 11 12 ぼかし ぼかし z 概要 z blur ¾ ぼかす 元画像 void blur( // ⼊⼒画像 const Mat& src, // 出⼒画像 Mat& dst, // 平滑化カーネルサイズ Size ksize, // アンカー点 (省略可能) Point anchor=Point(‐1, ‐1), // 画像外ピクセルの外挿モード (省略可能) int borderType=BORDER_DEFAULT) ぼかされた画像 処理 13 14 ぼかし 動画ファイルのグレースケールへの変換 z 使用例 z 演習 ¾ 動画ファイル (.avi) をグレースケールに変換しながら 表示するプログラムを作成せよ cv::blur(originalImage, blurredImage, cv::Size(7, 7)); z 演習 ¾ 画像をぼかして表示するプログラムを作成せよ カラー画像とグレースケール画像両方で試すこと 15 16 カメラの動画のグレースケールへの変換 メモリ上に画像を作成 z 演習 z メモリ上に画像の領域を作成する ¾ カメラからの動画をグレースケールに変換しながら 表示するプログラムを作成せよ ¾ メモリにアクセスして画像を作成・加工・検査 ¾ 新しい画像を作成するには、まずメモリ領域を確保 する必要 ¾ 作成時には主に以下のパラメータを指定 大きさ ビット深度 チャンネル数 17 18 cv::Mat クラス 作成時の引数 z 画像 (や行列) を表すクラス z 作成時の引数は z 画像領域を確保するには cv::Mat image; … image.create(cv::Size(640, 480), CV_8UC3); ¾ 変数作成と同時にコンストラクタで作成する cv::Mat image(cv::Size(640, 480), CV_8UC3); ¾ 変数作成後に作成・再作成する 画像の大きさ (ピクセル数) (縦幅, 横幅) cv::Mat image; … image.create(cv::Size(640, 480), CV_8UC3); 19 ビット数 型 チャンネル数 20 ビット数と型とチャンネル数 ビット数・型・チャンネル数の指定 z ビット数 (深度) z 組み合わせて指定 ¾ 色の値を表すために何ビット使うか ビット数が大きいほど正確・多くのメモリ領域が必要 ¾ 例: 8 ビット、16 ビット、32 ビット prefix ビット数 型 チャンネル数 CV_ 8 16 32 64 U S F C1 C2 C3 C4 z型 ¾ 整数型 符号なし (負数なし) → U 符号あり (負数あり) → S ¾ 浮動小数点数型 → F z チャンネル数 ¾ グレースケールなら 1 つ、カラー画像なら 3 つなど ビット数×チャンネル数 = bits per pixel (bpp) 21 よく使われるもの 22 CV_8UC1 のメモリ上の画像表現 z CV_8UC3 ゴミ バイト 4 ‐ (w mod 4) 個 ¾ 各 8 ビットの RGB カラー画像など (24 bpp) 1 ピクセル z CV_8UC1 0 1 2 3 4 5 ¾ 二値画像やグレースケール画像 (8 bpp) w‐6 w‐4 w‐2 w‐5 w‐3 w‐1 0 1 2 ... これらの情報は •メモリ領域取得時に何バイト取得するか •ライブラリの画像処理関数側が、指定された画像がどのような 形式で表現されているか/どのような形式で出力するか などを決定するために使われる h ‐ 1 23 24 メモリ上の画像表現の詳細 CV_8UC3 のメモリ上の画像表現 ゴミ バイト 4 ‐ ((w * 3) mod 4) 個 1 ピクセル 0 1 ... w ‐ 2 z ピクセルの並び順 ¾ トップ ダウン (top-down) 左上から 1 行ずつ右下に向かって並ぶ 一般的な画像処理はこっち w ‐ 1 ¾ ボトム アップ (bottom-up) 0 右下から 1 行ずつ右上に向かって並ぶ BMP ファイルや Windows 上ではこっちが多い 1 z RGB 色空間の場合 ¾ 逆順に BGR と並んでいる 2 ... z 1 行ずつ終端にゴミのバイトが付く場合がある ¾ 1 行のバイト数が 4 で割り切れないとき、4 で割り切れ るようにするために付けられる h ‐ 1 25 画像の大きさの取得方法 CV_8UC4 のメモリ上の画像表現 z幅 32 bpp などの画像の場合、4 で絶対に割り切れるのでゴミのバイトがない (ので楽 & 高速) 1 ピクセル 0 1 ... w ‐ 2 26 ¾ cols メンバ変数 w ‐ 1 z 高さ 0 ¾ rows メンバ変数 1 cv::Mat image; … int pixels = image.cols * image.rows; 2 ... h ‐ 1 27 28 ピクセルへのアクセス ピクセルへのアクセス z ptr メンバ関数 (メソッド) ptr() ¾ ピクセル データへのポインタを取得 または ptr(0) uchar 型 (8 ビット符号なし整数型) のポインタ このポインタを使ってピクセルを読み書き ¾ ptr 関数に引数を… 入れなければ、画像の先頭画素へのポインタを取得 整数の引数を入れると、その行の先頭画素へのポインタ を取得 ptr(1) ptr(2) 29 30 ポインタについて 実際のポインタの使い方 z メモリのアドレスを指し示すもの z ポインタの前に * を付けることで、そのアドレス の値を読み書き可能 z ポインタに対して整数の加減算を行うと、その分 だけ指し示すメモリのアドレスが移動 z 全ての画素値を 0 にする 31 ¾ (CV_8UC1 の場合) for (int y = 0; y < image.rows; ++y) { // y ⾏⽬の先頭画素へのポインタを取得 uchar *p = image.ptr(y); for (int x = 0; x < image.cols; ++x) { // ポインタが指し⽰すアドレスに 0 を書き込み *p = 0; // 1 を⾜して次の画素を指し⽰すようにする p = p + 1; } } 32 実際のポインタの使い方 別の書き方 z 全ての画素値を 0 にする z 以下のように書けるので *(p + n) ¾ (CV_8UC3 の RGB 画像の場合) for (int y = 0; y < image.rows; ++y) { // y ⾏⽬の先頭画素へのポインタを取得 uchar *p = image.ptr(y); for (int x = 0; x < image.cols; ++x) { // それぞれの RGB 値を 0 にする *p = 0; p = p + 1; // B *p = 0; p = p + 1; // G *p = 0; p = p + 1; // R } } p[n] for (int y = 0; y < image.rows; ++y) { // y ⾏⽬の先頭画素へのポインタを取得 uchar *p = image.ptr(y); for (int x = 0; x < image.cols; ++x) { // それぞれの RGB 値を 0 にする p[2] = 0; // R p[1] = 0; // G p[0] = 0; // B p += 3; } } 33 34 別の例 演習: グレースケール化 z 画像を加工して別領域に書き込む z 画像をグレースケール化して表示するプログラ ムを作成せよ for (int y = 0; y < srcImage.rows; ++y) { const uchar *srcp = srcImage.ptr(y); uchar *dstp = dstImage.ptr(y); for (int x = 0; x < srcImage.cols; ++x, srcp += 3, dstp += 3) { dstp[2] = 255 ‐ srcp[2]; dstp[1] = 255 ‐ srcp[1]; dstp[0] = 255 ‐ srcp[0]; } } ¾ 変換処理は前述のポインタを使って作成すること z 「グレースケール化」…と言っても ¾ いくつかアルゴリズムが存在する 35 36 RGB の合計を 3 で割る方法 中間値を使う方法 z (R + G + B) / 3 z (max(R, G, B) + min(R, G, B)) / 2 カラー画像 グレースケール画像 カラー画像 グレースケール画像 37 38 係数 (NTSC) を使う方法 ポスタライズ (posterization) z R * 0.298912 + G * 0.586611 + B * 0.114478 z 減色処理 カラー画像 ¾ 色を減らす (16,777,216 色 → 16 色など) グレースケール画像 元画像 39 減色された画像 40 演習 ポインタを使った画素処理の詳細 z 画像をポスタライズして表示するプログラムを作 成せよ z cv::Mat から得られる 情報 ¾ ポインタを使った画素処理を用いて作成すること 先頭画素への ポインタ: ptr() 1 行分のバイト数: step [byte] 1 行分の画素数 (幅): cols [px] z 動画 (AVI) ファイルをグレースケール化して表示 するプログラムを作成せよ ¾ ポインタを使った画素処理を用いて作成すること z カメラからの画像をグレースケール化して表示 するプログラムを作成せよ 行数 (高さ): rows ¾ ポインタを使った画素処理を用いて作成すること 41 チャンネル数: channels() ポインタを使った画素処理の詳細 ポインタを使った画素処理の詳細 z 「任意位置の画素へのアクセス処理」の一般化 z 先程の式を利用した、全画素へのアクセス ¾ cv::Mat を img、画素の位置を x, y とすると uchar *p = img.ptr() + img.step * y + img.channels() * x; ¾ ptr 関数に行数 (y) を入れて、行頭の画素へのポイ ンタを得ることもできるので… uchar *p = img.ptr(y) + img.channels() * x; 43 42 for (int y = 0; y < img.rows; ++y) { for (int x = 0; x < img.cols; ++x) { uchar *p = img.ptr() + img.step * y + img.channels() * x; *(p + 2) = 0; *(p + 1) = 0; *(p + 0) = 0; } } 44 ポインタを使った画素処理の詳細 ポインタを使った画素処理の詳細 z 少し簡略化 & 高速化を考えてみると z x 位置の算出で掛け算 (×3) をしたくないなら ¾ 代わりに画素ごとに + 3 していく for (int y = 0; y < img.rows; ++y) { uchar *p = img.ptr(y); for (int x = 0; x < img.cols; ++x) { *(p + 3 * x + 2) = 0; *(p + 3 * x + 1) = 0; *(p + 3 * x + 0) = 0; } } for (int y = 0; y < img.rows; ++y) { uchar *p = img.ptr(y); for (int x = 0; x < img.cols; ++x, p += 3) { *(p + 2) = 0; *(p + 1) = 0; *(p + 0) = 0; } } 45 46 ポインタを使った処理の更なる例 キーボードからの入力 z 画素ごとに処理しない z int cv::waitKey(int delay = 0) ¾ 全画素の RGB を 0 にするだけなら、データを 0 で埋 めればよい uchar *p for (int i < p[i] } ¾ キーボードからの入力を待つ ¾ 引数: delay 入力を待つ時間 (ミリ秒) デフォルトは 0 0 の場合 (デフォルト)、入力があるまで永遠に待つ = img.ptr(); i = 0; img.rows * img.cols; ++i) { = 0; ¾ 戻り値は入力されたキーボードの文字 何も入力されなかったら ‐1 ¾ 注意点 何か画像が表示 (cv::imshow) されていないと機能しない 画像のウィンドウにフォーカスを当てて入力 47 48 キーボードからの入力 画像ファイルの保存 z 特定のキーの入力に反応する例 z bool cv::imwrite( const std::string &filename, const cv::Mat &img) ¾ 画像を指定されたファイル名として保存する ¾ 引数: filename if (cv::waitKey() == 'a') { // ... } ファイル名 ¾ 引数: img 画像 z例 cv::imwrite("hoge.bmp", img); 49 50 演習 キー入力文字に応じた多方向分岐 z 演習(カメラ入力のファイル保存) z 文字ごとに if文を用いるとプログラムが複雑に z switch(‐case)文が便利 switch文からの脱出 ¾ カメラからの入力画像を表示し、キーボードから特定のキー (例えば文字`s’)が押されたら、その時の画像をファイル(例 えば“hoge.bmp”)に保存するプログラムを作成せよ ¾ なお,キーボードから文字`e’または`q’が押されたらプログラ ム終了せよ flagc = cv::waitKey(33); switch (flagc) { これが無いと、下に そのまま進む case ‘a’: 文字aに対する処理内容; break; case ‘b’: 文字bに対する処理内容; break; case ‘d’: case ‘D’: ...; break; default : ...; end(0); } 51 何も記述しないと下に進む 即ち、’d’ または ‘D’ の処理 の記述を1回で済ませられる プログラムの(正常)終了 cv::waitKey() はキー入力が無い時は ‐1 を返す事に注意 52 演習 演習(カメラ入力のファイル保存)の擬似コード // 入力画像を保存するプログラム // Input: カメラ入力,キーボード // Outpu:カメラ入力画像,保存画像 // キー入力= // s ... 入力画像をファイルに保存 // e または q ... プログラムの終了 main{ InImg宣言; // 入力画像 SaveImg宣言; // 保存画像 cap(0)宣言; // カメラ cap >> InImg; // カメラから1フレーム取得 SaveImg生成; // InImgの画像サイズ情報を利用 for(;;){ // プログラム終了まで繰り返し cap >> InImg; // カメラから1フレーム取得 char = cv::waitKey(33); // キー入力を33msec待つ switch (char){ // 入力文字により多方向分岐 case 's': InImg を SaveImg にコピー; SaveImg を ファイル"hoge.bmp" に保存; case 'e' または 'q': プログラム終了; } // end switch InImg をウィンドウに表示; 最後に保存された SaveImg を ウィンドウに表示; } z 演習(カメラ入力フレーム間の差分表示) ¾ カメラからの入力画像を表示し、キーボードから特定のキー (例えば文字`s’)が押されたら、前回押されたときの画像と入 力画像との差分を表示するプログラムを作成せよ ¾ 差分は前回画像を a,入力画像を b,差分画像を c とする時 ,c(青)=|b(青)-a(青) |, c(緑)=|b(緑)-a(緑) |, c(赤 )=|b(赤)-a(赤) |と定義する ¾ 表示は「入力画像」,「前回画像」,「差分画像」の3つ ¾ なお,キーボードから文字`s’以外が押されたらプログラム終 了せよ } 53 演習(入力フレーム間の差分表示)の擬似コード // 入力画像を保存するプログラム // Input: カメラ入力,キーボード // Output:カメラ入力画像, 前回画像,差分画像 // キー入力= // s ... 入力画像をファイルに保存 // s以外 ... プログラムの終了 main{ InImg宣言; // 入力画像 SaveImg宣言; // 前回画像 DiffImg宣言; // 差分画像 cap(0)宣言; // カメラ cap >> InImg; // カメラから1フレーム取得 SaveImg生成; DiffImg生成; // InImgの画像サイズを利用 for(;;){ cap >> InImg; char = cv::waitKey(33); switch (char){ case 's': InImg を SaveImg にコピー; case ‘s’ 以外: プログラム終了; } // end switch for(各行){ for(各列){ DiffImg(青)=|InImg(青)‐SaveImg(青)|; DiffImg(緑)=|InImg(緑)‐SaveImg(緑)|; DiffImg(赤)=|InImg(赤)‐SaveImg(赤)|; } } // end for of 各行 InImg をウィンドウに表示; SaveImg をウィンドウに表示; DiffImg をウィンドウに表示; } // end for(;;) } 55 54
© Copyright 2024 Paperzz