cv::normalize の使い方【OpenCV/C++】〜画像・行列を正規化する〜
cv::normalize は行列や画像の値を指定した範囲やノルムにスケーリングする関数です。「差分画像を 0〜255 に収めたい」「特徴ベクトルをL2正規化したい」といった場面で頻繁に登場します。
最小構成はこれだけです。
cv::normalize(src, dst, 0, 255, cv::NORM_MINMAX, CV_8U);
動作環境
- OpenCV 4.x
- コンパイル:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
まずは浮動小数点の行列を 0〜255 の CV_8U に変換する最もシンプルな例です。
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
// 任意の浮動小数点行列(例: 勾配・スコアマップなど)
cv::Mat src = (cv::Mat_<float>(3, 3) <<
-50.0f, 10.0f, 200.0f,
80.0f, -20.0f, 150.0f,
300.0f, 55.0f, 0.0f);
cv::Mat dst;
// NORM_MINMAX: 最小値→0、最大値→255 に線形スケーリング
cv::normalize(src, dst, 0, 255, cv::NORM_MINMAX, CV_8U);
std::cout << "src:\n" << src << "\n\n";
std::cout << "dst:\n" << dst << "\n";
return 0;
}
実行すると以下のように表示されます。
src:
[-50, 10, 200;
80, -20, 150;
300, 55, 0]
dst:
[ 0, 17, 71;
37, 9, 61;
100, 30, 14]
srcの最小値-50→0、最大値300→255となり、その間は線形補間されます。
引数と戻り値
| 引数 | 型 | 説明 |
|---|---|---|
src |
InputArray |
入力行列 |
dst |
OutputArray |
出力行列 |
alpha |
double |
NORM_MINMAX では出力の最小値、それ以外ではノルム値 |
beta |
double |
NORM_MINMAX では出力の最大値(それ以外は未使用) |
norm_type |
int |
正規化の種類(下表参照) |
dtype |
int |
出力の型(-1 で入力と同じ型) |
mask |
InputArray |
処理対象ピクセルのマスク(省略可) |
norm_type の主な選択肢
| 定数 | 動作 |
|---|---|
cv::NORM_MINMAX |
最小・最大値を alpha/beta にスケーリング |
cv::NORM_L2 |
L2ノルムが alpha になるようにスケーリング |
cv::NORM_L1 |
L1ノルムが alpha になるようにスケーリング |
cv::NORM_INF |
最大絶対値が alpha になるようにスケーリング |
戻り値はありません(dst に結果が書き込まれます)。
実践例
実践例1: 差分画像を見やすく可視化する
cv::absdiff で得た差分は値が小さく、そのまま表示しても真っ暗になりがちです。cv::normalize で引き伸ばすと変化箇所が明確になります。
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat img1 = cv::imread("frame1.png", cv::IMREAD_GRAYSCALE);
cv::Mat img2 = cv::imread("frame2.png", cv::IMREAD_GRAYSCALE);
if (img1.empty() || img2.empty()) {
std::cerr << "画像の読み込みに失敗しました\n";
return 1;
}
cv::Mat diff;
cv::absdiff(img1, img2, diff); // 差分を取る
cv::Mat vis;
// 差分値の最小〜最大を 0〜255 に引き伸ばして可視化
cv::normalize(diff, vis, 0, 255, cv::NORM_MINMAX, CV_8U);
cv::imwrite("diff_vis.png", vis);
std::cout << "diff_vis.png を保存しました\n";
return 0;
}
cv::absdiff の詳細はcv::absdiff の使い方【OpenCV/C++】〜差分画像を作る〜も参照ください。
実践例2: 特徴ベクトルをL2正規化する
マッチングや距離計算の前処理としてベクトルをL2正規化する場面もよくあります。
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
// 1次元の特徴ベクトル(行ベクトル)
cv::Mat feat = (cv::Mat_<float>(1, 5) << 3.0f, 4.0f, 0.0f, -1.0f, 2.0f);
cv::Mat norm_feat;
// L2ノルムが 1.0 になるように正規化
cv::normalize(feat, norm_feat, 1.0, 0.0, cv::NORM_L2);
std::cout << "元ベクトル : " << feat << "\n";
std::cout << "L2正規化後 : " << norm_feat << "\n";
std::cout << "L2ノルム確認: " << cv::norm(norm_feat, cv::NORM_L2) << "\n";
return 0;
}
実行すると以下のように表示されます。
元ベクトル : [3, 4, 0, -1, 2]
L2正規化後 : [0.5547, 0.7303, 0, -0.1826, 0.3651]
L2ノルム確認: 1
実践例3: マスクを使って特定領域だけ正規化する
マスクを渡すと、マスクが 255 のピクセルだけを対象に最小・最大を計算してスケーリングします。マスク外のピクセルは dst に書き込まれません(dst をゼロ初期化しておくと安全です)。
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat src = cv::imread("input.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "画像の読み込みに失敗しました\n";
return 1;
}
// 左半分だけを対象にするマスク
cv::Mat mask = cv::Mat::zeros(src.size(), CV_8U);
mask(cv::Rect(0, 0, src.cols / 2, src.rows)) = 255;
// dst をゼロ初期化してからマスク付き正規化
cv::Mat dst = cv::Mat::zeros(src.size(), CV_8U);
cv::normalize(src, dst, 0, 255, cv::NORM_MINMAX, CV_8U, mask);
cv::imwrite("masked_norm.png", dst);
std::cout << "masked_norm.png を保存しました\n";
return 0;
}
つまずきポイント
⚠️ dtype=-1 のまま整数型に使うと範囲が 0〜1 にならない
NORM_MINMAX で dtype=-1(入力と同じ型)かつ入力が CV_8U の場合、alpha=0/beta=1.0 を指定しても出力は全ピクセルが 0 か 1 になります。CV_8U を 0〜255 に引き伸ばすには beta=255 を指定するか、dtype=CV_8U を明示してください。
// NG: beta=1.0 だと CV_8U では 0 か 1 にしかならない
cv::normalize(src, dst, 0, 1.0, cv::NORM_MINMAX);
// OK
cv::normalize(src, dst, 0, 255, cv::NORM_MINMAX, CV_8U);
⚠️ マルチチャンネル画像は全チャンネルまとめてスケーリングされる
CV_8UC3 のカラー画像を NORM_MINMAX で正規化すると、3チャンネルの全ピクセルを通じた最小・最大でスケーリングされます。チャンネルごとに独立して正規化したい場合は cv::split で分離してから処理してください。
std::vector<cv::Mat> ch;
cv::split(src, ch);
for (auto& c : ch)
cv::normalize(c, c, 0, 255, cv::NORM_MINMAX, CV_8U);
cv::merge(ch, dst);
⚠️ L2正規化で入力がゼロベクトルの場合は NaN になることがある
全要素が 0 のベクトルをL2正規化すると除算がゼロ除算になり、結果が NaN や inf になる可能性があります。実務では正規化前にノルムを確認するか、cv::norm で事前チェックしてください。
if (cv::norm(feat, cv::NORM_L2) > 1e-9)
cv::normalize(feat, feat, 1.0, 0.0, cv::NORM_L2);
関連する関数
- cv::convertScaleAbs — スケーリングと絶対値変換を同時に行いたい場合に使用
- cv::minMaxLoc — 正規化前に最小・最大値を確認したいときに便利
- cv::absdiff の使い方【OpenCV/C++】〜差分画像を作る〜 — 差分画像生成後の可視化に
cv::normalizeをセットで使う - cv::add / addWeighted の使い方【OpenCV/C++】〜画像の加算・ブレンド合成〜 — 値域調整が必要な加算処理と組み合わせることが多い
まとめ
cv::normalizeはNORM_MINMAXで値域を0〜255に収めるのが最もよく使うパターン。- L2正規化や部分マスクにも対応しており、前処理の幅が広い。
dtypeとbetaの指定ミスが典型的なバグ源なので、出力型を明示する習慣をつけると良い。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

