cv::copyMakeBorder の使い方【OpenCV/C++】〜画像に枠(パディング)を付ける〜
cv::copyMakeBorder は、画像の上下左右に任意のピクセル幅の枠(パディング)を追加する関数です。ニューラルネットワークへの入力サイズ統一、テンプレートマッチングの境界処理、畳み込みの前処理など、実務では頻繁に登場します。
最小限のコードはこれです。
cv::copyMakeBorder(src, dst, top, bottom, left, right, cv::BORDER_CONSTANT, cv::Scalar(0));
動作環境
- OpenCV 4.x
- コンパイル例:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#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::Mat dst;
// 上下左右に20ピクセルの黒枠を追加
int top = 20, bottom = 20, left = 20, right = 20;
cv::copyMakeBorder(
src, dst,
top, bottom, left, right,
cv::BORDER_CONSTANT,
cv::Scalar(0, 0, 0) // 黒(BGR)
);
std::cout << "元サイズ: " << src.cols << "x" << src.rows << std::endl;
std::cout << "出力サイズ: " << dst.cols << "x" << dst.rows << std::endl;
cv::imwrite("output.jpg", dst);
return 0;
}
実行すると、たとえば元画像が 640×480 の場合に次のように表示されます。
元サイズ: 640x480
出力サイズ: 680x520
出力画像は output.jpg に保存されます。
各行の解説
cv::imreadで読み込んだ直後にempty()チェックを行います。パスが正しくても作業ディレクトリが異なると読み込みに失敗するため必須です。top / bottom / left / rightに追加したいピクセル数を整数で指定します。非対称なパディングも指定できます。cv::BORDER_CONSTANTを使うとScalarで指定した色の単色枠になります。- 出力サイズは
src.cols + left + right×src.rows + top + bottomになります。
引数と戻り値
| 引数 | 型 | 説明 |
|---|---|---|
src |
cv::InputArray |
入力画像 |
dst |
cv::OutputArray |
出力画像(パディング済み) |
top |
int |
上辺に追加するピクセル数 |
bottom |
int |
下辺に追加するピクセル数 |
left |
int |
左辺に追加するピクセル数 |
right |
int |
右辺に追加するピクセル数 |
borderType |
int |
境界の補完方法(後述) |
value |
cv::Scalar |
BORDER_CONSTANT 時の色(省略可・デフォルト黒) |
戻り値はありません(void)。
borderType の主な選択肢
| 定数 | 内容 |
|---|---|
cv::BORDER_CONSTANT |
指定色で塗りつぶす(最もよく使う) |
cv::BORDER_REPLICATE |
端のピクセルを繰り返す |
cv::BORDER_REFLECT |
端を軸に鏡像反転(端のピクセルを含む) |
cv::BORDER_REFLECT_101 |
BORDER_REFLECT の改良版(端のピクセルを除いて反転)。多くのフィルタのデフォルト |
cv::BORDER_WRAP |
反対側の端をタイリング |
実践例
実践例 1: アスペクト比を保ちながら正方形にリサイズ
推論エンジンへの入力は正方形(例: 640×640)を要求することが多いです。copyMakeBorder でレターボックス処理を実装します。
#include <opencv2/opencv.hpp>
#include <iostream>
// 画像をアスペクト比を保って targetSize の正方形にパディング
cv::Mat letterbox(const cv::Mat& src, int targetSize) {
// スケールを計算(縦横の小さい方に合わせる)
double scale = static_cast<double>(targetSize) / std::max(src.cols, src.rows);
int newW = static_cast<int>(src.cols * scale);
int newH = static_cast<int>(src.rows * scale);
cv::Mat resized;
cv::resize(src, resized, cv::Size(newW, newH));
// 余白を均等に分配
int padTop = (targetSize - newH) / 2;
int padBottom = targetSize - newH - padTop;
int padLeft = (targetSize - newW) / 2;
int padRight = targetSize - newW - padLeft;
cv::Mat dst;
cv::copyMakeBorder(
resized, dst,
padTop, padBottom, padLeft, padRight,
cv::BORDER_CONSTANT,
cv::Scalar(114, 114, 114) // YOLOシリーズでよく使われるグレー
);
return dst;
}
int main() {
cv::Mat src = cv::imread("input.jpg");
if (src.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return -1;
}
cv::Mat result = letterbox(src, 640);
std::cout << "出力サイズ: " << result.cols << "x" << result.rows << std::endl;
cv::imwrite("letterbox.jpg", result);
return 0;
}
実行すると次のように表示されます(元画像が 1280×720 の場合)。
出力サイズ: 640x640
実践例 2: BORDER_REFLECT_101 で自然なパディング
畳み込みフィルタや射影変換の前処理として、境界を自然に拡張したい場合は BORDER_REFLECT_101 が有効です。
#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::Mat dst;
// 端のピクセル値を鏡像で自然に延長
cv::copyMakeBorder(
src, dst,
50, 50, 50, 50,
cv::BORDER_REFLECT_101
);
cv::imwrite("reflect_border.jpg", dst);
std::cout << "BORDER_REFLECT_101 で保存しました" << std::endl;
return 0;
}
BORDER_REFLECT_101 は OpenCV の多くのフィルタ関数がデフォルトで使うモードです。warpPerspective の前後でパディングを入れる際にも自然な結果が得られます(cv::warpPerspective の使い方 も参照)。
つまずきポイント
⚠️ borderType に cv::BORDER_TRANSPARENT や cv::BORDER_ISOLATED を単独で渡すとエラー
BORDER_TRANSPARENT は一部の関数専用フラグであり、copyMakeBorder では使用できません。コンパイルは通っても実行時に assertion failed が発生します。copyMakeBorder に渡せるのは BORDER_CONSTANT / BORDER_REPLICATE / BORDER_REFLECT / BORDER_REFLECT_101 / BORDER_WRAP のみです。
⚠️ BORDER_CONSTANT のデフォルト色はゼロ(黒)
value 引数を省略すると cv::Scalar() すなわち全チャネル 0(黒)が使われます。グレースケール画像(CV_8UC1)に対して cv::Scalar(255, 255, 255) と書いても白にはなりません。グレースケールの場合は cv::Scalar(255) と 1 チャネル分だけ指定してください。
// グレースケール画像への白枠 → 正しい書き方
cv::copyMakeBorder(gray, dst, 10, 10, 10, 10, cv::BORDER_CONSTANT, cv::Scalar(255));
⚠️ 出力サイズの計算ミスによるダウンストリームのアサーション
copyMakeBorder 後の dst は必ず src.rows + top + bottom × src.cols + left + right になります。matchTemplate や行列演算の次ステップでサイズを前提とするコードがあると、パディング量を変えたときにアサーションで落ちます。サイズは dst.size() から取得するようにしてください。
関連する関数
- cv::resize — リサイズと組み合わせてレターボックス処理を構成する(上記実践例 1 参照)
- cv::warpPerspective の使い方【OpenCV/C++】〜射影変換で画像を補正する〜 — 変換後に黒領域が出る場合、事前パディングで対応できる
- cv::matchTemplate の使い方【OpenCV/C++】〜テンプレートマッチングで物体位置を検出する〜 — 探索画像をパディングしてから渡すと画像端の検出精度が改善する
- cv::filter2D / cv::GaussianBlur — 内部で
borderTypeを同じ定数で扱っており、copyMakeBorderで手動パディングを先に行う際に指定を合わせると一貫した結果になる
まとめ
cv::copyMakeBorder は上下左右のピクセル数と境界モードを指定するだけで画像にパディングを追加できます。推論前のレターボックス処理や畳み込みの前処理など用途は多く、BORDER_CONSTANT(単色)と BORDER_REFLECT_101(鏡像)の 2 種類を使い分けるだけで大半のケースに対応できます。グレースケール画像への Scalar 指定など型の不一致に注意してください。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

