OpenCVで画像をリサイズする方法メモ。
画像全体をリサイズ
resize(src, dst, cv::Rect(320, 240), dst_size, 0, 0, INTER_LINEAR);
imwrite("scale.jpg", dst);
とりあえずこれで320x240にリサイズできる。
ただ、アスペクト比が維持されない。
(たとえば512x512とかの画像がsrcに入っていると、横に引き伸ばされて見える)
画像の一部を切り取る
cv::Mat cut_img(src, cv::Rect(0, 0, 320, 240));
imwrite("cut.jpg", cut_img);
リサイズとは若干アプローチが異なるけど、これで画像の一部を切り取れる。
この例では320x240の矩形をsrcから切り取っている。
切り取りとリサイズの組み合わせで期待した結果が得られる。
たとえば、640x480(アスペクト比4:3)の画像をsrcに格納し、640x360(16:9)に切り出す場合、上下60ドットを切り落とせばいいのでコードはこうなる。
cv::Mat crop_img(src, cv::Rect(0, 60, 640, 360));
画像の一部を任意の色で埋める
上記の切り取りとは逆のケース。
640x360(16:9)の画像を640x480(アスペクト比4:3)になるように、不足部分60ドットを黒で埋める。
手法1
cv::Mat restore_aspect_img(cv::Size(640, 480), CV_8UC3, CV_RGB(0,0,0));
cv::Mat dar16_9_roi(restore_aspect_img, cv::Rect(0, 60, 640, 360));
crop_img.copyTo(dar16_9_roi);
imwrite("restore_dar.jpg", restore_aspect_img);
この方法では、最初に黒色の画像を作る。
作成した黒色の画像の部分領域(ROI = 640x380)を定義し、そこにソース画像を転送する。
結果、中央部分にソース画像, 上下に黒帯という結果が得られる。
この手法は、画像全体を塗りつぶすため、上から画像が重ねられる部分の塗りつぶし処理が無駄になってしまうこと。(ROIで上書きされる部分)
ゲームなどのようにフレームごとに必ずリフレッシュするのならばよいかもしれないが、速度を重視する場合は更新箇所だけ更新したい。必要な処理だけ順に行うようにしたコードは以下の手法2のところ。
手法2
cv::Mat restore_aspect_img(cv::Size(640, 480), CV_8UC3);
cv::Mat dar16_9_roi(restore_aspect_img, cv::Rect(0, 60, 640, 360));
crop_img.copyTo(dar16_9_roi);
rectangle(restore_aspect_img, cv::Point(0,0), cv::Point(640, 60), CV_RGB(0,0,0), CV_FILLED);
rectangle(restore_aspect_img, cv::Point(0,420), cv::Point(640, 480), CV_RGB(0,0,0), CV_FILLED);
imwrite("restore_dar2.jpg", restore_aspect_img);
この手法では、次のようなステップで処理を行う
- 出力画像を作る(640x480)
- 16:9の画像を載せるROIを定義する
- 2で定義したROIに画像をコピーする
- ROI以外の部分をrectangle関数で塗りつぶす(上60ドット)
- ROI以外の部分をrectangle関数で塗りつぶす(下60ドット)
考察
opencvではMatクラスを使いこなすことが上達への第一歩らしい。
opencvでリサイズとかやってみるとMatに詳しくなるよということでやってみた。
おっしゃるとおり、詳しくなった。Matをこうやって重ねていくと、レイヤーとかリージョンを扱うような感覚で色々なことができるわけですね。
何よりもMatは通常は浅いコピーを行うというのがこのあたりのポイントだと思う。
あとは性能面での話とか気になるけども、汎用性を気にするなら手法1がよさげ。
複数の画像を乗っけるときとか。(昔のゲームでいうとVRAMにスプライト書くようなイメージ)