using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NUnit.Framework;
using OpenCvSharp;
namespace TIASshot.Tests {
/// <summary>
/// ImageStats のテスト(ピュアロジック層)
///
/// GUIDE_08: ハードウェア非依存の計算ロジックを NUnit で検証する。
/// ShotWhiteBoard・TryDisableColorEnhancement・Form1 ボタンはハードウェア結合層につき対象外。
///
/// 飽和定義: 輝度(BGR2GRAY: 0.114*B + 0.587*G + 0.299*R)が 254 以上の画素を飽和とカウントする。
/// TECH_02 Q8 では「チャンネル別 max」も記録対象とするが、飽和率の算出は輝度ベース(現実装の契約)。
/// </summary>
[TestFixture]
public class ImageStatsTests {
// ---------------------------------------------------------------
// ImageStats.Calc — ch別 mean / max
// ---------------------------------------------------------------
/// <summary>
/// Calc_全画素同一値_chMeanとchMaxが正しく返る:
/// 全画素 B=100, G=100, R=100 → MeanB/G/R=100, MaxB/G/R=100
/// </summary>
[Test]
public void Calc_全画素同一値_chMeanとchMaxが正しく返る() {
// Arrange: 2x2 の全画素が B=100, G=100, R=100
using (var mat = new Mat(2, 2, MatType.CV_8UC3, new Scalar(100, 100, 100))) {
// Act
var stats = ImageStats.Calc(mat);
// Assert: ch別 mean / max
Assert.That(stats.MeanB, Is.EqualTo(100.0).Within(1e-3), "MeanB = 100");
Assert.That(stats.MeanG, Is.EqualTo(100.0).Within(1e-3), "MeanG = 100");
Assert.That(stats.MeanR, Is.EqualTo(100.0).Within(1e-3), "MeanR = 100");
Assert.That(stats.MaxB, Is.EqualTo(100.0).Within(1e-3), "MaxB = 100");
Assert.That(stats.MaxG, Is.EqualTo(100.0).Within(1e-3), "MaxG = 100");
Assert.That(stats.MaxR, Is.EqualTo(100.0).Within(1e-3), "MaxR = 100");
}
}
/// <summary>
/// Calc_ch別に異なる値_各chのMeanとMaxが分離される:
/// 全画素 B=50, G=120, R=200 → MeanB=50, MeanG=120, MeanR=200(ch間の混在なし)
/// </summary>
[Test]
public void Calc_ch別に異なる値_各chのMeanとMaxが分離される() {
// Arrange: 3x3 の全画素が B=50, G=120, R=200(BGR 順)
using (var mat = new Mat(3, 3, MatType.CV_8UC3, new Scalar(50, 120, 200))) {
// Act
var stats = ImageStats.Calc(mat);
// Assert
Assert.That(stats.MeanB, Is.EqualTo(50.0).Within(1e-3), "MeanB = 50");
Assert.That(stats.MeanG, Is.EqualTo(120.0).Within(1e-3), "MeanG = 120");
Assert.That(stats.MeanR, Is.EqualTo(200.0).Within(1e-3), "MeanR = 200");
Assert.That(stats.MaxB, Is.EqualTo(50.0).Within(1e-3), "MaxB = 50");
Assert.That(stats.MaxG, Is.EqualTo(120.0).Within(1e-3), "MaxG = 120");
Assert.That(stats.MaxR, Is.EqualTo(200.0).Within(1e-3), "MaxR = 200");
}
}
/// <summary>
/// Calc_2行の異なる画素_chMaxが最大値を返す:
/// 行0: B=10, G=20, R=30、行1: B=50, G=60, R=70 → MaxB=50, MaxG=60, MaxR=70
/// MeanB=(10+50)/2=30(画素数=各行1画素の1x1ではなく2行1列の計2画素の平均)
/// </summary>
[Test]
public void Calc_2行異なる画素_chMaxが最大値を返す() {
// Arrange: 2行1列(2画素)、行ごとに値が異なる
using (var mat = new Mat(2, 1, MatType.CV_8UC3)) {
mat.At<Vec3b>(0, 0) = new Vec3b(10, 20, 30); // B=10, G=20, R=30
mat.At<Vec3b>(1, 0) = new Vec3b(50, 60, 70); // B=50, G=60, R=70
// Act
var stats = ImageStats.Calc(mat);
// Assert: MaxB=50, MaxG=60, MaxR=70(2画素の最大値)
Assert.That(stats.MaxB, Is.EqualTo(50.0).Within(1e-3), "MaxB は 2 画素の最大値 50");
Assert.That(stats.MaxG, Is.EqualTo(60.0).Within(1e-3), "MaxG は 2 画素の最大値 60");
Assert.That(stats.MaxR, Is.EqualTo(70.0).Within(1e-3), "MaxR は 2 画素の最大値 70");
// Assert: MeanB=(10+50)/2=30, MeanG=(20+60)/2=40, MeanR=(30+70)/2=50
Assert.That(stats.MeanB, Is.EqualTo(30.0).Within(1e-3), "MeanB = (10+50)/2 = 30");
Assert.That(stats.MeanG, Is.EqualTo(40.0).Within(1e-3), "MeanG = (20+60)/2 = 40");
Assert.That(stats.MeanR, Is.EqualTo(50.0).Within(1e-3), "MeanR = (30+70)/2 = 50");
}
}
// ---------------------------------------------------------------
// ImageStats.Calc — 飽和率(輝度ベース・BGR2GRAY)
// ---------------------------------------------------------------
/// <summary>
/// Calc_全画素同一値100_飽和率0:
/// 全画素 (B,G,R)=(100,100,100) → 輝度=100 → 飽和なし → SaturatedRatio=0.0
/// </summary>
[Test]
public void Calc_全画素同一値100_飽和率0() {
using (var mat = new Mat(2, 2, MatType.CV_8UC3, new Scalar(100, 100, 100))) {
var stats = ImageStats.Calc(mat);
Assert.That(stats.SaturatedRatio, Is.EqualTo(0.0).Within(1e-6), "全輝度 100 → 飽和率 0");
}
}
/// <summary>
/// Calc_全画素255_飽和率1_0:
/// 全画素 (B,G,R)=(255,255,255) → 輝度=255 → 全画素飽和 → SaturatedRatio=1.0
/// </summary>
[Test]
public void Calc_全画素255_飽和率1_0() {
using (var mat = new Mat(2, 2, MatType.CV_8UC3, new Scalar(255, 255, 255))) {
var stats = ImageStats.Calc(mat);
Assert.That(stats.SaturatedRatio, Is.EqualTo(1.0).Within(1e-6), "全画素 255 → 飽和率 1.0");
}
}
/// <summary>
/// Calc_全画素255_p99は255:
/// 全画素 255 → p99=255 であること
/// </summary>
[Test]
public void Calc_全画素255_p99は255() {
using (var mat = new Mat(2, 2, MatType.CV_8UC3, new Scalar(255, 255, 255))) {
var stats = ImageStats.Calc(mat);
Assert.That(stats.P99, Is.EqualTo(255.0).Within(1e-3), "全画素 255 → p99 = 255");
}
}
/// <summary>
/// Calc_全画素同一値100_p99は100:
/// 全画素 (B,G,R)=(100,100,100) → 輝度=100 → p99=100
/// </summary>
[Test]
public void Calc_全画素同一値100_p99は100() {
using (var mat = new Mat(2, 2, MatType.CV_8UC3, new Scalar(100, 100, 100))) {
var stats = ImageStats.Calc(mat);
Assert.That(stats.P99, Is.EqualTo(100.0).Within(1e-3), "全輝度 100 → p99 = 100");
}
}
/// <summary>
/// Calc_飽和画素割合の境界_SaturatedRatioが正しい割合:
/// 4画素のうち1画素だけ輝度 >= 254(全ch=255)、残り3画素は輝度 100
/// → SaturatedRatio = 1/4 = 0.25
///
/// 飽和の定義: 輝度(BGR2GRAY: 0.114*B + 0.587*G + 0.299*R)が ≥ 254 のとき飽和。
/// ch別に255であっても輝度 < 254 なら非飽和(例: B=255, G=0, R=0 → 輝度 ≒ 29)。
/// </summary>
[Test]
public void Calc_飽和画素1つ4画素中_SaturatedRatioが0_25() {
// Arrange: 2x2(4画素)、(0,0)のみ全ch=255(確実に輝度 >= 254)、残りは 100
using (var mat = new Mat(2, 2, MatType.CV_8UC3, new Scalar(100, 100, 100))) {
mat.At<Vec3b>(0, 0) = new Vec3b(255, 255, 255); // 輝度 = 255 >= 254 → 飽和
// Act
var stats = ImageStats.Calc(mat);
// Assert: 4画素中1画素飽和 → 1/4 = 0.25
Assert.That(stats.SaturatedRatio, Is.EqualTo(0.25).Within(1e-6), "4画素中1画素飽和 → SaturatedRatio = 0.25");
}
}
/// <summary>
/// Calc_R=255のみ高輝度_輝度ベースで飽和判定されないこと:
/// B=0, G=0, R=255 → BGR2GRAY ≒ 0.299*255 ≒ 76.2 → 輝度 < 254 → 飽和なし
///
/// これは「チャンネル別に255でも輝度ベースでは非飽和」という定義の明示的な固定。
/// </summary>
[Test]
public void Calc_R255のみ_輝度ベースでは飽和しない() {
// Arrange: B=0, G=0, R=255(輝度 ≒ 76.2 → 254 未満)
using (var mat = new Mat(1, 1, MatType.CV_8UC3, new Scalar(0, 0, 255))) {
var stats = ImageStats.Calc(mat);
// Assert: 輝度ベースでは飽和なし
Assert.That(stats.SaturatedRatio, Is.EqualTo(0.0).Within(1e-6),
"R=255のみ → BGR2GRAY ≒ 76 < 254 → 輝度ベース飽和率 = 0");
}
}
/// <summary>
/// Calc_G=255のみ_輝度ベースで飽和しない:
/// B=0, G=255, R=0 → BGR2GRAY ≒ 0.587*255 ≒ 149.7 → 輝度 < 254 → 飽和なし
/// </summary>
[Test]
public void Calc_G255のみ_輝度ベースでは飽和しない() {
using (var mat = new Mat(1, 1, MatType.CV_8UC3, new Scalar(0, 255, 0))) {
var stats = ImageStats.Calc(mat);
Assert.That(stats.SaturatedRatio, Is.EqualTo(0.0).Within(1e-6),
"G=255のみ → BGR2GRAY ≒ 150 < 254 → 飽和率 = 0");
}
}
// ---------------------------------------------------------------
// ImageStats.CalcPercentile — 通常系
// ---------------------------------------------------------------
/// <summary>
/// CalcPercentile_全画素同一値150_p50は150:
/// 全画素 = 150 → p50 = 150
/// </summary>
[Test]
public void CalcPercentile_全画素同一値150_p50は150() {
using (var gray = new Mat(4, 4, MatType.CV_8U, new Scalar(150))) {
double p50 = ImageStats.CalcPercentile(gray, 50.0);
Assert.That(p50, Is.EqualTo(150.0).Within(1e-3), "全画素 150 → p50 = 150");
}
}
/// <summary>
/// CalcPercentile_全画素同一値200_p99は200:
/// 全画素 = 200 → p99 = 200
/// </summary>
[Test]
public void CalcPercentile_全画素同一値200_p99は200() {
using (var gray = new Mat(4, 4, MatType.CV_8U, new Scalar(200))) {
double p99 = ImageStats.CalcPercentile(gray, 99.0);
Assert.That(p99, Is.EqualTo(200.0).Within(1e-3), "全画素 200 → p99 = 200");
}
}
/// <summary>
/// CalcPercentile_全画素同一値100_p100は100:
/// 全画素 = 100 → p100 = 100
/// </summary>
[Test]
public void CalcPercentile_全画素同一値100_p100は100() {
using (var gray = new Mat(4, 4, MatType.CV_8U, new Scalar(100))) {
double p100 = ImageStats.CalcPercentile(gray, 100.0);
Assert.That(p100, Is.EqualTo(100.0).Within(1e-3), "全画素 100 → p100 = 100");
}
}
/// <summary>
/// CalcPercentile_2種の輝度値_p50境界:
/// 2画素: 値=100 と 値=200 → p50 は threshold=2*0.5=1.0
/// bin[100]=1.0 で cumulative=1.0 >= 1.0 → p50=100
/// </summary>
[Test]
public void CalcPercentile_2画素100と200_p50は100() {
// Arrange: 1行2列(2画素)
using (var gray = new Mat(1, 2, MatType.CV_8U)) {
gray.At<byte>(0, 0) = 100;
gray.At<byte>(0, 1) = 200;
// Act
double p50 = ImageStats.CalcPercentile(gray, 50.0);
// Assert: threshold = 2 * 0.5 = 1.0。bin[100]=1.0 → cumulative=1.0 >= 1.0 → 100
Assert.That(p50, Is.EqualTo(100.0).Within(1e-3), "p50: bin[100]=1 → cumulative=1 >= threshold=1 → p50=100");
}
}
/// <summary>
/// CalcPercentile_2画素100と200_p99は200:
/// threshold = 2 * 0.99 = 1.98。bin[100]=1.0 → cumulative=1.0 < 1.98。
/// bin[200]=1.0 → cumulative=2.0 >= 1.98 → p99=200
/// </summary>
[Test]
public void CalcPercentile_2画素100と200_p99は200() {
using (var gray = new Mat(1, 2, MatType.CV_8U)) {
gray.At<byte>(0, 0) = 100;
gray.At<byte>(0, 1) = 200;
double p99 = ImageStats.CalcPercentile(gray, 99.0);
Assert.That(p99, Is.EqualTo(200.0).Within(1e-3), "p99=200(上位1%に 200 が含まれるため)");
}
}
// ---------------------------------------------------------------
// ImageStats.CalcPercentile — 空 Mat
// ---------------------------------------------------------------
/// <summary>
/// CalcPercentile_空Mat_0を返す:
/// 0行0列(画素数 = 0)→ total = 0 → 0.0 を返す
/// </summary>
[Test]
public void CalcPercentile_空Mat_0を返す() {
// Arrange: 0行0列の CV_8U
using (var gray = new Mat(0, 0, MatType.CV_8U)) {
double result = ImageStats.CalcPercentile(gray, 99.0);
Assert.That(result, Is.EqualTo(0.0).Within(1e-6), "空 Mat → 0.0 を返す");
}
}
// ---------------------------------------------------------------
// ImageStats.BuildJson — 構造・キー・値の検証
// ---------------------------------------------------------------
/// <summary>
/// BuildJson_metaのみstatsなし_有効なJSONが生成される:
/// meta に string / int / bool / double を混在させ、JSON として parse できること
/// </summary>
[Test]
public void BuildJson_metaのみ_有効なJSON文字列が生成される() {
// Arrange
var meta = new Dictionary<string, object> {
{ "capture_datetime", "2026-06-07 12:00:00" },
{ "num_images", 5 },
{ "color_enhancement_off", true },
{ "exposure_us", 12345.6 },
};
// Act
string json = ImageStats.BuildJson(meta, null);
// Assert: null / 空でない
Assert.That(json, Is.Not.Null, "json が null であってはならない");
Assert.That(json, Is.Not.Empty, "json が空文字列であってはならない");
// Assert: '{' / '}' で囲まれている(最低限の JSON 外枠)
Assert.That(json.TrimStart().StartsWith("{"), Is.True, "JSON は '{' で始まる");
Assert.That(json.TrimEnd().EndsWith("}"), Is.True, "JSON は '}' で終わる");
}
/// <summary>
/// BuildJson_metaの各型_キーと値が正しくシリアライズされる:
/// bool → true/false、int → 数値、double → G6/InvariantCulture 書式、string → クォート
/// </summary>
[Test]
public void BuildJson_bool値_truefalseがシリアライズされる() {
var meta = new Dictionary<string, object> {
{ "flag_on", true },
{ "flag_off", false },
};
string json = ImageStats.BuildJson(meta, null);
// bool は JSON の true / false リテラルになること
Assert.That(json, Does.Contain("\"flag_on\": true"), "true が literal true になること");
Assert.That(json, Does.Contain("\"flag_off\": false"), "false が literal false になること");
}
/// <summary>
/// BuildJson_int値_数値リテラルがシリアライズされる
/// </summary>
[Test]
public void BuildJson_int値_数値リテラルがシリアライズされる() {
var meta = new Dictionary<string, object> {
{ "num_images", 10 },
};
string json = ImageStats.BuildJson(meta, null);
// int は引用符なしの数値リテラル
Assert.That(json, Does.Contain("\"num_images\": 10"), "int は数値リテラルになること");
}
/// <summary>
/// BuildJson_double値_G6InvariantCultureで書式化される:
/// 1.23456789 → G6 書式(6桁有効数字)= "1.23457"(InvariantCulture: 小数点は '.')
/// </summary>
[Test]
public void BuildJson_double値_G6InvariantCultureで書式化される() {
var meta = new Dictionary<string, object> {
{ "val", 1.23456789 },
};
string json = ImageStats.BuildJson(meta, null);
// G6 + InvariantCulture: 1.23456789 → "1.23457"(四捨五入6桁)
Assert.That(json, Does.Contain("\"val\": 1.23457"), "double は G6 InvariantCulture で書式化される");
// ロケール依存のカンマ区切り(例: "1,23457")が混入していないこと
Assert.That(json.Contains("1,23457"), Is.False, "小数点はカンマではなくピリオドを使う(InvariantCulture)");
}
/// <summary>
/// BuildJson_string値_ダブルクォートでエスケープされる:
/// 値中の '"' は '\"' にエスケープされること
/// </summary>
[Test]
public void BuildJson_string値中のダブルクォート_エスケープされる() {
var meta = new Dictionary<string, object> {
{ "desc", "say \"hello\"" },
};
string json = ImageStats.BuildJson(meta, null);
// '"' が '\\"' にエスケープされた文字列として含まれること
Assert.That(json, Does.Contain("say \\\"hello\\\""), "\" は \\\" にエスケープされること");
}
/// <summary>
/// BuildJson_string値中のバックスラッシュ_エスケープされる:
/// 値中の '\' は '\\' にエスケープされること
/// </summary>
[Test]
public void BuildJson_string値中のバックスラッシュ_エスケープされる() {
var meta = new Dictionary<string, object> {
{ "path", @"C:\TIAS_Data" },
};
string json = ImageStats.BuildJson(meta, null);
// '\' が '\\' にエスケープされた文字列として含まれること
Assert.That(json, Does.Contain(@"C:\\TIAS_Data"), @"\ は \\ にエスケープされること");
}
/// <summary>
/// BuildJson_string値中の改行とタブ_エスケープされる:
/// RFC 8259 §7 により制御文字は生で出力できないため '\n' '\t' にエスケープされること
/// </summary>
[Test]
public void BuildJson_string値中の改行とタブ_エスケープされる() {
var meta = new Dictionary<string, object> {
{ "device_name", "DFK\n23\tUX" },
};
string json = ImageStats.BuildJson(meta, null);
Assert.That(json, Does.Contain("\"DFK\\n23\\tUX\""), "改行は \\n,タブは \\t にエスケープされること");
}
/// <summary>
/// BuildJson_string値中のその他の制御文字_uエスケープされる:
/// 短縮表記を持たない 0x20 未満の制御文字は '\u00XX' 形式にエスケープされること
/// </summary>
[Test]
public void BuildJson_string値中のその他の制御文字_uエスケープされる() {
var meta = new Dictionary<string, object> {
{ "serial_number", "A\u0001B\u001fC" },
};
string json = ImageStats.BuildJson(meta, null);
Assert.That(json, Does.Contain("\"A\\u0001B\\u001fC\""), "制御文字は \\u00XX にエスケープされること");
}
/// <summary>
/// BuildJson_string値中の非ASCII文字_そのまま出力される:
/// 制御文字以外(日本語など)はエスケープせずそのまま出力されること(特性化)
/// </summary>
[Test]
public void BuildJson_string値中の非ASCII文字_そのまま出力される() {
var meta = new Dictionary<string, object> {
{ "shading_correction", "未補正(LSC 保留)" },
};
string json = ImageStats.BuildJson(meta, null);
Assert.That(json, Does.Contain("\"未補正(LSC 保留)\""), "非 ASCII 文字はそのまま出力されること");
}
/// <summary>
/// BuildJson_statsリスト_frame_statsキーと各フレームのフィールドが生成される:
/// 1フレーム分の FrameStats を与えたとき、"frame_stats" 配列が生成され
/// "frame", "saturated_ratio", "p99_luminance", "mean_B/G/R", "max_B/G/R" が含まれること
/// </summary>
[Test]
public void BuildJson_1フレームのstats_frame_statsキーと全フィールドが生成される() {
// Arrange
var meta = new Dictionary<string, object> { { "device", "DFK23UX249" } };
var stats = new List<FrameStats> {
new FrameStats {
SaturatedRatio = 0.0,
P99 = 181.0,
MeanB = 120.5,
MeanG = 130.0,
MeanR = 140.0,
MaxB = 200.0,
MaxG = 210.0,
MaxR = 220.0,
}
};
// Act
string json = ImageStats.BuildJson(meta, stats);
// Assert: "frame_stats" 配列が存在する
Assert.That(json, Does.Contain("\"frame_stats\""), "frame_stats キーが存在すること");
// Assert: フレーム番号・各フィールドが含まれる
Assert.That(json, Does.Contain("\"frame\": 1"), "frame 番号 1 が含まれること");
Assert.That(json, Does.Contain("\"saturated_ratio\""), "saturated_ratio キーが含まれること");
Assert.That(json, Does.Contain("\"p99_luminance\""), "p99_luminance キーが含まれること");
Assert.That(json, Does.Contain("\"mean_B\""), "mean_B キーが含まれること");
Assert.That(json, Does.Contain("\"mean_G\""), "mean_G キーが含まれること");
Assert.That(json, Does.Contain("\"mean_R\""), "mean_R キーが含まれること");
Assert.That(json, Does.Contain("\"max_B\""), "max_B キーが含まれること");
Assert.That(json, Does.Contain("\"max_G\""), "max_G キーが含まれること");
Assert.That(json, Does.Contain("\"max_R\""), "max_R キーが含まれること");
}
/// <summary>
/// BuildJson_2フレームのstats_frame番号が1から始まり連番になる
/// </summary>
[Test]
public void BuildJson_2フレームのstats_frame番号が1から連番() {
var meta = new Dictionary<string, object> { { "n", 2 } };
var stats = new List<FrameStats> {
new FrameStats { SaturatedRatio = 0.0, P99 = 100.0 },
new FrameStats { SaturatedRatio = 0.0, P99 = 110.0 },
};
string json = ImageStats.BuildJson(meta, stats);
Assert.That(json, Does.Contain("\"frame\": 1"), "1枚目のフレーム番号は 1");
Assert.That(json, Does.Contain("\"frame\": 2"), "2枚目のフレーム番号は 2");
}
/// <summary>
/// BuildJson_statsがnull_frame_statsキーが生成されない
/// </summary>
[Test]
public void BuildJson_statsがnull_frame_statsキーが生成されない() {
var meta = new Dictionary<string, object> { { "key", "value" } };
string json = ImageStats.BuildJson(meta, null);
Assert.That(json.Contains("frame_stats"), Is.False,
"stats が null のとき frame_stats キーは生成されない");
}
/// <summary>
/// BuildJson_statsが空リスト_frame_statsキーが生成されない
/// </summary>
[Test]
public void BuildJson_statsが空リスト_frame_statsキーが生成されない() {
var meta = new Dictionary<string, object> { { "key", "value" } };
string json = ImageStats.BuildJson(meta, new List<FrameStats>());
Assert.That(json.Contains("frame_stats"), Is.False,
"stats が空リストのとき frame_stats キーは生成されない");
}
/// <summary>
/// BuildJson_SaturatedRatioが0_saturated_ratioが0にシリアライズされる:
/// SaturatedRatio = 0.0 → "saturated_ratio": 0
/// </summary>
[Test]
public void BuildJson_SaturatedRatioが0_JSON内で0が出力される() {
var meta = new Dictionary<string, object>();
var stats = new List<FrameStats> {
new FrameStats { SaturatedRatio = 0.0, P99 = 181.0 }
};
string json = ImageStats.BuildJson(meta, stats);
// G6 で 0.0 → "0"
Assert.That(json, Does.Contain("\"saturated_ratio\": 0"),
"飽和率 0 は JSON に 0 としてシリアライズされること");
}
// ---------------------------------------------------------------
// 異常系 — Calc の引数検証
// ---------------------------------------------------------------
/// <summary>
/// Calc_nullを渡す_ArgumentNullExceptionをスローする
/// </summary>
[Test]
public void Calc_null_ArgumentNullExceptionをスローする() {
Assert.Throws<ArgumentNullException>(
(Action)(() => ImageStats.Calc(null)),
"null Mat → ArgumentNullException");
}
/// <summary>
/// Calc_CV_8UC1のMatを渡す_ArgumentExceptionをスローする:
/// CV_8UC3 以外の Mat は受け付けない
/// </summary>
[Test]
public void Calc_CV8UC1Mat_ArgumentExceptionをスローする() {
using (var gray = new Mat(2, 2, MatType.CV_8U, new Scalar(100))) {
Assert.Throws<ArgumentException>(
(Action)(() => ImageStats.Calc(gray)),
"CV_8UC1 → ArgumentException(CV_8UC3 以外は不可)");
}
}
/// <summary>
/// Calc_CV_64FC3のMatを渡す_ArgumentExceptionをスローする:
/// 64 bit 浮動小数点型のカラー Mat も拒否される
/// </summary>
[Test]
public void Calc_CV64FC3Mat_ArgumentExceptionをスローする() {
using (var mat = new Mat(2, 2, MatType.CV_64FC3)) {
Assert.Throws<ArgumentException>(
(Action)(() => ImageStats.Calc(mat)),
"CV_64FC3 → ArgumentException(CV_8UC3 以外は不可)");
}
}
// ---------------------------------------------------------------
// 異常系 — CalcPercentile の引数検証
// ---------------------------------------------------------------
/// <summary>
/// CalcPercentile_nullを渡す_ArgumentNullExceptionをスローする
/// </summary>
[Test]
public void CalcPercentile_null_ArgumentNullExceptionをスローする() {
Assert.Throws<ArgumentNullException>(
(Action)(() => ImageStats.CalcPercentile(null, 99.0)),
"null Mat → ArgumentNullException");
}
/// <summary>
/// CalcPercentile_CV_8UC3のMatを渡す_ArgumentExceptionをスローする:
/// CalcPercentile は CV_8U(グレースケール)専用
/// </summary>
[Test]
public void CalcPercentile_CV8UC3Mat_ArgumentExceptionをスローする() {
using (var bgr = new Mat(2, 2, MatType.CV_8UC3, new Scalar(100, 100, 100))) {
Assert.Throws<ArgumentException>(
(Action)(() => ImageStats.CalcPercentile(bgr, 99.0)),
"CV_8UC3 → ArgumentException(CV_8U 以外は不可)");
}
}
}
}