cv::countNonZero の使い方【OpenCV/C++】〜非ゼロ画素を数える〜
cv::countNonZero は、1チャンネル画像(またはMat)の中でゼロでない画素の数を返す関数です。二値化後の白画素数カウント、差分検出による変化領域の面積計算など、画像処理パイプラインの「判定ステップ」で頻繁に登場します。
最小の使い方はこのとおりです。
int n = cv::countNonZero(mask); // mask は CV_8UC1 など1チャンネルのMat
動作環境
- OpenCV 4.x
- C++17
- ビルド例:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4) -o main
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
// グレースケール画像を読み込む
cv::Mat gray = cv::imread("sample.png", cv::IMREAD_GRAYSCALE);
if (gray.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
// 二値化(画素値 > 127 を白=255、それ以外を黒=0 にする)
cv::Mat binary;
cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY);
// 非ゼロ画素数(白画素数)をカウント
int nonZeroCount = cv::countNonZero(binary);
// 全画素数に対する割合を計算
int totalPixels = binary.rows * binary.cols;
double ratio = static_cast<double>(nonZeroCount) / totalPixels * 100.0;
std::cout << "全画素数 : " << totalPixels << std::endl;
std::cout << "非ゼロ画素数: " << nonZeroCount << std::endl;
std::cout << "白画素の割合: " << ratio << " %" << std::endl;
return 0;
}
実行結果(例):
全画素数 : 307200
非ゼロ画素数: 142300
白画素の割合: 46.3184 %
コード解説
| 行 | 説明 |
|---|---|
cv::imread(..., IMREAD_GRAYSCALE) |
最初から1チャンネルで読み込む。countNonZero は1チャンネル専用なのでここで揃える |
cv::threshold(...) |
二値化してゼロ/非ゼロの境界を明確にする |
cv::countNonZero(binary) |
ゼロでない画素の個数を int で返す |
binary.rows * binary.cols |
全画素数。チャンネル数は含まない点に注意 |
引数と戻り値
int cv::countNonZero(InputArray src);
| 引数/戻り値 | 型 | 説明 |
|---|---|---|
src |
InputArray |
入力配列。1チャンネルのみ対応(CV_8U / CV_16U / CV_32F 等) |
| 戻り値 | int |
ゼロでない要素の数 |
⚠️ src が多チャンネル(CV_8UC3 など)の場合、実行時に assertion エラーが発生します。
実践例
実践例1: 差分検出で変化領域の面積を計算する
背景差分などで変化ピクセル数を定量的に求めるパターンです。
cv::absdiff で差分画像を作り、閾値処理後にカウントします。
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat frame1 = cv::imread("before.png", cv::IMREAD_GRAYSCALE);
cv::Mat frame2 = cv::imread("after.png", cv::IMREAD_GRAYSCALE);
if (frame1.empty() || frame2.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
// 差分画像を生成
cv::Mat diff;
cv::absdiff(frame1, frame2, diff);
// 差分が小さいノイズを除去してから二値化
cv::Mat mask;
cv::threshold(diff, mask, 30, 255, cv::THRESH_BINARY);
int changedPixels = cv::countNonZero(mask);
int totalPixels = mask.rows * mask.cols;
double changeRate = static_cast<double>(changedPixels) / totalPixels * 100.0;
std::cout << "変化画素数: " << changedPixels << std::endl;
std::cout << "変化率 : " << changeRate << " %" << std::endl;
// 変化率が5%を超えたら「動き検出」と判定する例
if (changeRate > 5.0) {
std::cout << ">>> 動きを検出しました" << std::endl;
}
return 0;
}
実行結果(例):
変化画素数: 18432
変化率 : 6.0 %
>>> 動きを検出しました
実践例2: チャンネルごとに非ゼロ画素数を調べる
カラー画像の各チャンネルを個別にカウントしたいときは、cv::split でチャンネルを分解してから渡します。
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat bgr = cv::imread("sample.png");
if (bgr.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
// チャンネルを分解する
std::vector<cv::Mat> channels;
cv::split(bgr, channels);
const char* names[] = {"Blue", "Green", "Red"};
for (int i = 0; i < 3; ++i) {
int n = cv::countNonZero(channels[i]);
std::cout << names[i] << " チャンネル 非ゼロ画素数: " << n << std::endl;
}
return 0;
}
実行結果(例):
Blue チャンネル 非ゼロ画素数: 298765
Green チャンネル 非ゼロ画素数: 301024
Red チャンネル 非ゼロ画素数: 295800
チャンネル分解については cv::split / cv::merge の使い方【OpenCV/C++】〜チャンネルを分解・結合する〜 も参照してください。
つまずきポイント
✅ 多チャンネル Mat をそのまま渡すと assertion エラー
cv::countNonZero は 1チャンネルのみ 受け付けます。カラー画像(CV_8UC3)をそのまま渡すと次のような実行時エラーになります。
OpenCV Error: Assertion failed (src.channels() == 1) in countNonZero
cv::cvtColor でグレースケールに変換するか、cv::split でチャンネルを分解してから渡してください。
✅ 浮動小数点 Mat の「ほぼゼロ」に注意
CV_32F や CV_64F の配列を渡す場合、浮動小数点の演算誤差で「ゼロのつもりだが微小な値が残る」ケースがあります。厳密にゼロ判定したいなら cv::threshold や cv::compare で明示的にマスクを作ってから渡すのが安全です。
✅ 戻り値は int。大きな画像では桁あふれに注意
戻り値は int(32bit符号付き整数)です。8K解像度(7680×4320 ≒ 3,300万画素)など極端に大きい画像では INT_MAX(約21億)を超えることはほぼありませんが、3D ボリュームデータや巨大な行列を扱う場合は念のため上限を意識してください。
関連する関数
- cv::minMaxLoc の使い方【OpenCV/C++】〜最大値・最小値と位置を求める〜
- cv::mean / meanStdDev の使い方【OpenCV/C++】〜平均・標準偏差を求める〜
- cv::absdiff の使い方【OpenCV/C++】〜差分画像を作る〜
- cv::bitwise_and の使い方【OpenCV/C++】〜ビット演算とマスク処理〜
cv::threshold— 二値化。countNonZero と組み合わせて使うことが非常に多いcv::findContours— 非ゼロ領域の輪郭抽出。面積の代わりに形状を取りたい場合に使う
まとめ
cv::countNonZero は1チャンネル画像の非ゼロ画素数を int で返すシンプルな関数です。二値化後の面積計算や差分検出による変化率の判定など、画像処理パイプラインの判断ステップで幅広く活用できます。多チャンネル画像には直接使えない点だけ押さえておけば、迷わず使いこなせます。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

