cv::copyMakeBorder の使い方【OpenCV/C++】〜画像に枠(パディング)を付ける〜

OpenCV for C++
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイドC#(OpenCvSharp)セットアップ

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_101OpenCV の多くのフィルタ関数がデフォルトで使うモードです。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::copyMakeBorder は上下左右のピクセル数と境界モードを指定するだけで画像にパディングを追加できます。推論前のレターボックス処理や畳み込みの前処理など用途は多く、BORDER_CONSTANT(単色)と BORDER_REFLECT_101(鏡像)の 2 種類を使い分けるだけで大半のケースに対応できます。グレースケール画像への Scalar 指定など型の不一致に注意してください。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました