cv::warpAffine の使い方【OpenCV/C++】〜アフィン変換で画像を回転・平行移動する〜
cv::warpAffine は、2×3 のアフィン変換行列を使って画像の回転・平行移動・拡縮・せん断を一括で適用する関数です。
画像の向き補正や Data Augmentation など、現場で最も使われる幾何変換関数のひとつです。
最小構成は次のとおりです。
cv::Mat M = cv::getRotationMatrix2D(center, angle, scale);
cv::warpAffine(src, dst, M, dst.size());
動作環境
- OpenCV 4.x
- コンパイル:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4) -o main - 実行:
./main
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
画像を中心基準で 45° 回転させるサンプルです。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
// 画像読み込み
cv::Mat src = cv::imread("input.jpg");
if (src.empty()) {
std::cerr << "画像を読み込めませんでした。パスを確認してください。" << std::endl;
return 1;
}
// 回転中心を画像の中央に設定
cv::Point2f center(src.cols / 2.0f, src.rows / 2.0f);
// 2×3 の回転行列を生成(角度45°、スケール等倍)
cv::Mat M = cv::getRotationMatrix2D(center, 45.0, 1.0);
// アフィン変換を適用。出力サイズは入力と同じ
cv::Mat dst;
cv::warpAffine(src, dst, M, src.size());
cv::imwrite("output_rotate45.jpg", dst);
std::cout << "変換後サイズ: " << dst.cols << "x" << dst.rows << std::endl;
return 0;
}
実行結果
変換後サイズ: 640x480
入力画像が 640×480 であれば、同サイズの output_rotate45.jpg が生成されます。
45° 回転した画像が保存されており、画面外にはみ出した領域は黒(デフォルトの境界値)で埋められます。
コードのポイント
| 行 | 説明 |
|---|---|
cv::getRotationMatrix2D(center, 45.0, 1.0) |
回転中心・角度・スケールから 2×3 行列を生成。角度は反時計回りが正 |
cv::warpAffine(src, dst, M, src.size()) |
変換行列 M を適用。第4引数で出力サイズを指定 |
引数と戻り値
cv::warpAffine
void cv::warpAffine(
InputArray src, // 入力画像
OutputArray dst, // 出力画像
InputArray M, // 2×3 アフィン変換行列(CV_64F)
Size dsize, // 出力画像のサイズ
int flags = INTER_LINEAR, // 補間方法
int borderMode = BORDER_CONSTANT, // 境界処理方法
Scalar borderValue = Scalar() // borderMode が BORDER_CONSTANT のときの埋め値
);
| 引数 | 型 | 説明 |
|---|---|---|
src |
InputArray |
入力画像(チャンネル数制限なし) |
dst |
OutputArray |
出力画像(dsize と同型で自動生成) |
M |
InputArray |
2×3 アフィン変換行列(通常 CV_64F) |
dsize |
Size |
出力サイズ。入力と異なるサイズも指定可 |
flags |
int |
補間方法。INTER_LINEAR(双線形)/ INTER_NEAREST(最近傍)/ INTER_CUBIC など |
borderMode |
int |
境界外の画素の扱い。BORDER_CONSTANT / BORDER_REFLECT など |
borderValue |
Scalar |
BORDER_CONSTANT 時の埋め色。デフォルトは黒 Scalar(0,0,0) |
戻り値はなし(dst に結果が書き込まれます)。
実践例
実践例 1: 回転後のはみ出しをなくす(出力サイズを拡張する)
単純に元サイズへ収めると画像の四隅が欠けます。回転後の外接矩形に合わせてキャンバスを拡張すれば欠けません。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <cmath>
int main()
{
cv::Mat src = cv::imread("input.jpg");
if (src.empty()) {
std::cerr << "画像を読み込めませんでした。" << std::endl;
return 1;
}
const double angle = 30.0;
const double rad = angle * CV_PI / 180.0;
// 回転後の外接矩形サイズを計算
int newW = static_cast<int>(std::abs(src.cols * std::cos(rad))
+ std::abs(src.rows * std::sin(rad)));
int newH = static_cast<int>(std::abs(src.cols * std::sin(rad))
+ std::abs(src.rows * std::cos(rad)));
// 新しいキャンバスの中心を回転中心にして平行移動成分を調整
cv::Point2f center(src.cols / 2.0f, src.rows / 2.0f);
cv::Mat M = cv::getRotationMatrix2D(center, angle, 1.0);
M.at<double>(0, 2) += (newW / 2.0 - center.x);
M.at<double>(1, 2) += (newH / 2.0 - center.y);
cv::Mat dst;
cv::warpAffine(src, dst, M, cv::Size(newW, newH),
cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
cv::imwrite("output_no_crop.jpg", dst);
std::cout << "拡張後サイズ: " << dst.cols << "x" << dst.rows << std::endl;
return 0;
}
実行結果
拡張後サイズ: 748x583
元画像(640×480)が 30° 回転した外接矩形サイズに拡張されます。四隅の欠けがなくなります。
実践例 2: 平行移動のみ(シフト処理)
平行移動だけが目的なら、変換行列を直接 Mat_<double> で作ると明快です。
#include <opencv2/opencv.hpp>
#include <iostream>
int main()
{
cv::Mat src = cv::imread("input.jpg");
if (src.empty()) {
std::cerr << "画像を読み込めませんでした。" << std::endl;
return 1;
}
// X方向に +50px、Y方向に +30px シフトする変換行列
// [ 1 0 tx ]
// [ 0 1 ty ]
cv::Mat M = (cv::Mat_<double>(2, 3) <<
1.0, 0.0, 50.0,
0.0, 1.0, 30.0);
cv::Mat dst;
cv::warpAffine(src, dst, M, src.size(),
cv::INTER_LINEAR, cv::BORDER_REFLECT);
cv::imwrite("output_shift.jpg", dst);
std::cout << "シフト完了" << std::endl;
return 0;
}
BORDER_REFLECT を指定することで、シフトで生じた境界領域を反転した画素で埋めます。Data Augmentation でよく使います。
つまずきポイント
⚠️ 回転方向が「反時計回りが正」
cv::getRotationMatrix2D の角度は 数学的座標系(Y 軸上向き) に基づき、正値が反時計回りです。
一方、画像座標は Y 軸が下向きのため、画面上では 正値が時計回りに見えます。
「なぜ逆に回るのか」と混乱しやすい点です。時計回りにしたければ角度に負値を渡してください。
⚠️ 変換行列の型は必ず CV_64F(double)
cv::warpAffine に渡す行列は CV_64F でなければなりません。
cv::Mat_<float> や CV_32F で作ると実行時に assertion エラーが発生します。
// NG: float で作るとエラー
cv::Mat M = (cv::Mat_<float>(2, 3) << 1,0,0, 0,1,0);
// OK: double で作る
cv::Mat M = (cv::Mat_<double>(2, 3) << 1.0,0.0,0.0, 0.0,1.0,0.0);
⚠️ 出力サイズ dsize の指定ミス
dsize は cv::Size(width, height) の順です。rows/cols と混同して縦横が逆になるミスが多いです。
src.size() をそのまま渡せばサイズ取り違えは防げますが、拡張キャンバスを手計算する場合は cv::Size(newW, newH) の順序を必ず確認してください。
関連する関数
- cv::getRotationMatrix2D — 回転行列を手軽に生成する関数。本記事とほぼ必ずセットで使います(上記サンプル参照)。
- cv::getAffineTransform — 3点対応からアフィン行列を推定する関数。手動で行列を作らずに済みます。
- cv::warpPerspective — アフィン変換の上位互換にあたる透視変換。4点対応が必要な場合に使います。
まとめ
cv::warpAffineはアフィン変換行列(2×3)を使って回転・平行移動・拡縮をまとめて適用できます。- 回転は
cv::getRotationMatrix2Dで行列を生成し、出力サイズを拡張することで四隅の欠けを防げます。 - 変換行列は
CV_64F(double)で渡す・回転の正方向は画面上で時計回り、の2点を押さえておけば実装上のハマりはほぼなくなります。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

