Initial commit: ノーコード画像検査アプリ (MVP + 追加機能一式)

検査レシピ=処理/検査ステップの縦リスト(配線なし)。yfoxlib(yfoximg/yfoxbase)使用。

- ビジュアル・パイプライン(カード+適用後サムネイル+緑/赤 合否)
- テンプレート/対話式ウィザードでレシピ生成(CTaskDialog)
- パラメータのスライダー・ライブ調整
- レシピ保存/読込(*.itr) + 最近使ったファイル(MRU)
- 検査範囲(ROI)を複数マウス指定(ドラッグ追加/右クリック削除)
- カードのドラッグ並べ替え
- 判定結果画像の保存 + 検査ログCSV(項目拡張)
- 処理6種+検査3種(明るさ/白画素率/塊の数)
- /selftest(実行+レシピ往復+結果画像+複数ROI塊カウント検証)、/demo

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 09:26:10 +09:00
co-authored by Claude Opus 4.8
commit e4eca6b303
24 changed files with 3120 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
#pragma once
//
// StepCatalog.h - ステップ種別のメタデータ(表示名/アイコン/平易な説明/種類/既定パラメータ)。
// 新しいステップ種別を足すときは Recipe.h の StepKind と、この各関数、
// および InspectEngine::RunStep に1ケース足すだけ。
//
#include "Recipe.h"
namespace core {
// 種別 → 画面表示名(平易な日本語)。
inline CString StepName(StepKind k)
{
switch (k)
{
case StepKind::Grayscale: return L"白黒にする";
case StepKind::Median: return L"ノイズを消す";
case StepKind::Binarize: return L"明るさで白黒に分ける";
case StepKind::Otsu: return L"自動で白黒に分ける";
case StepKind::Gamma: return L"明るさをそろえる";
case StepKind::Contrast: return L"コントラストを強める";
case StepKind::InspectBrightness: return L"検査:全体の明るさ";
case StepKind::InspectWhiteRatio: return L"検査:白い部分の割合";
case StepKind::InspectBlobCount: return L"検査:塊(キズ/穴)の数";
}
return L"?";
}
// 種別 → アイコン(絵文字)。
inline CString StepIcon(StepKind k)
{
switch (k)
{
case StepKind::Grayscale: return L"⬜";
case StepKind::Median: return L"🧹";
case StepKind::Binarize: return L"◐";
case StepKind::Otsu: return L"🎚";
case StepKind::Gamma: return L"💡";
case StepKind::Contrast: return L"🌗";
case StepKind::InspectBrightness: return L"🔎";
case StepKind::InspectWhiteRatio: return L"🔎";
case StepKind::InspectBlobCount: return L"🔎";
}
return L"•";
}
// 種別 → 1行の平易な説明(このステップは何をするか)。
inline CString StepHint(StepKind k)
{
switch (k)
{
case StepKind::Grayscale: return L"カラー画像を白黒(グレー)にします。検査の前準備。";
case StepKind::Median: return L"細かいザラつき(ノイズ)をならして消します。";
case StepKind::Binarize: return L"指定した明るさの範囲だけを白にし、他を黒にします。";
case StepKind::Otsu: return L"しきい値を自動で決めて白黒に分けます。";
case StepKind::Gamma: return L"暗い所/明るい所の見え方を調整します。";
case StepKind::Contrast: return L"明暗の差をはっきりさせます。";
case StepKind::InspectBrightness: return L"画像全体の平均の明るさが範囲内なら合格。";
case StepKind::InspectWhiteRatio: return L"白い部分の割合(%)が範囲内なら合格。";
case StepKind::InspectBlobCount: return L"白い塊(キズや穴)の数が範囲内なら合格。";
}
return L"";
}
// 種別 → 保存用の安定した文字列ID(enum値は将来変わり得るため文字列で永続化)。
inline CString StepKindId(StepKind k)
{
switch (k)
{
case StepKind::Grayscale: return L"grayscale";
case StepKind::Median: return L"median";
case StepKind::Binarize: return L"binarize";
case StepKind::Otsu: return L"otsu";
case StepKind::Gamma: return L"gamma";
case StepKind::Contrast: return L"contrast";
case StepKind::InspectBrightness: return L"inspect_brightness";
case StepKind::InspectWhiteRatio: return L"inspect_whiteratio";
case StepKind::InspectBlobCount: return L"inspect_blobcount";
}
return L"?";
}
// 文字列ID → 種別。見つかれば true。
inline bool StepKindFromId(const CString& id, StepKind& out)
{
static const StepKind all[] = {
StepKind::Grayscale, StepKind::Median, StepKind::Binarize, StepKind::Otsu,
StepKind::Gamma, StepKind::Contrast, StepKind::InspectBrightness,
StepKind::InspectWhiteRatio, StepKind::InspectBlobCount,
};
for (StepKind k : all)
if (StepKindId(k) == id) { out = k; return true; }
return false;
}
// 検査ステップか(合否に寄与するか)。
inline bool IsInspect(StepKind k)
{
return k == StepKind::InspectBrightness
|| k == StepKind::InspectWhiteRatio
|| k == StepKind::InspectBlobCount;
}
// 種別ごとの既定パラメータを持つ Step を生成。
inline Step MakeStep(StepKind k)
{
Step s;
s.kind = k;
switch (k)
{
case StepKind::Grayscale:
case StepKind::Median:
break;
case StepKind::Otsu:
s.params.push_back({ L"invert", L"白黒を反転", 0, 0, 1 });
break;
case StepKind::Binarize:
s.params.push_back({ L"lo", L"この明るさ以上", 128, 0, 255 });
s.params.push_back({ L"hi", L"この明るさ以下", 255, 0, 255 });
s.params.push_back({ L"invert", L"白黒を反転", 0, 0, 1 });
break;
case StepKind::Gamma:
s.params.push_back({ L"gamma", L"明るさ調整(1=そのまま)", 1.0, 0.2, 3.0 });
break;
case StepKind::Contrast:
s.params.push_back({ L"inMin", L"暗side基準", 0, 0, 255 });
s.params.push_back({ L"inMax", L"明side基準", 255, 0, 255 });
s.params.push_back({ L"outMin", L"出力min", 0, 0, 255 });
s.params.push_back({ L"outMax", L"出力max", 255, 0, 255 });
break;
case StepKind::InspectBrightness:
s.params.push_back({ L"min", L"合格の最小(暗い側)", 50, 0, 255 });
s.params.push_back({ L"max", L"合格の最大(明るい側)", 200, 0, 255 });
break;
case StepKind::InspectWhiteRatio:
s.params.push_back({ L"thresh", L"白とみなす明るさ", 128, 0, 255 });
s.params.push_back({ L"min", L"合格の最小(%)", 0, 0, 100 });
s.params.push_back({ L"max", L"合格の最大(%)", 100, 0, 100 });
break;
case StepKind::InspectBlobCount:
s.params.push_back({ L"thresh", L"白とみなす明るさ", 128, 0, 255 });
s.params.push_back({ L"invert", L"暗い塊を数える", 0, 0, 1 });
s.params.push_back({ L"min", L"合格の最小(個)", 0, 0, 100 });
s.params.push_back({ L"max", L"合格の最大(個)", 0, 0, 100 });
break;
}
return s;
}
// パレット(追加できる種別の一覧・並び順)。
inline const StepKind* AllStepKinds(int& n)
{
static const StepKind kinds[] = {
StepKind::Grayscale,
StepKind::Median,
StepKind::Binarize,
StepKind::Otsu,
StepKind::Gamma,
StepKind::Contrast,
StepKind::InspectBrightness,
StepKind::InspectWhiteRatio,
StepKind::InspectBlobCount,
};
n = (int)(sizeof(kinds) / sizeof(kinds[0]));
return kinds;
}
} // namespace core