# CLAUDE.md This file provides guidance to Claude Code when working with code in this repository. ## 概要 **ImgTestSystem** は yfoxlib(`yfoximg` / `yfoxbase`)を使う **ノーコード画像検査アプリ**(純MFC)。 パラダイムは隣の `C:\debug\ImgChkSystem`(ブロックをワイヤで繋ぐノードエディタ)とは **意図的に別物**: > **検査レシピ = 上から下へ順に実行する「ステップ」の縦リスト**(配線なし)。 > 料理のレシピ / 検査チェックリストのメタファ。ステップは2種: > - **処理**(画像を加工): グレー化 / メディアン / 二値化(帯) / 大津 / ガンマ / コントラスト > - **検査**(測って範囲判定→PASS/FAIL): 平均輝度 / 白画素率 UI: 左=ステップ縦リスト(追加/削除/↑↓)・右上=選択ステップのパラメータ入力・右下=作業画像プレビュー・ 上部=[画像を開く]/[実行]と総合判定バナー。ノーコード=ドロップダウンと数値入力のみ、コードもワイヤも書かない。 ## ビルド VS2022 (v143 / MFC Dynamic / Release|x64 / /utf-8 / stdcpp17)。 ```powershell $msb="C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\amd64\MSBuild.exe" & $msb ImgTestSystem.sln /p:Configuration=Release /p:Platform=x64 ``` - リンク: `yfoximg.lib;yfoxbase.lib;gdiplus.lib`(libdir `C:\debug\yfoxlib\x64\$(Configuration)`)。 - include: `C:\debug\yfoxlib\yfoximg;...\yfoxbase;...\yfoxbase\include`。 - PostBuild で `yfoximg.dll`/`yfoxbase.dll` を出力先へコピー。出力=`bin\x64\\`。 ## アーキテクチャ UI非依存の **core/** と MFC UI層(ルート)に分離。 ### core/(ウィンドウ非依存) - `Recipe.h` — `StepKind`(処理/検査)、`StepParam`(数値パラメータ)、`Step`、`Recipe`(ステップの並び)。 - `StepCatalog.h` — 種別→表示名 `StepName` / 検査判定 `IsInspect` / 既定パラメータ生成 `MakeStep` / パレット `AllStepKinds`。 **新ステップ追加は `StepKind` に足し、この2関数(`StepName`/`MakeStep`)と `InspectEngine` の `RunStep` に1ケース足すだけ。** - `InspectEngine.h/.cpp` — **唯一 yfoximg に依存する層**。`LoadImage`→`Run(recipe, upTo)` で作業画像を原画像から作り直し、 各ステップを `CImgProcessing`(`RGB2BW`/`Threshold`/`Threshold_Otsu`/`Avg`/`Gamma`/`Contrast`/`Median`)で適用。 `upTo>=0` でその番号までプレビュー実行、`-1` で全実行し総合 PASS/FAIL を返す。`DrawWork` でプレビュー描画。 ### UI層(ルート) - `App.h/.cpp` — `CImgTestApp:CWinApp`。GDI+ 起動、`/selftest` 分岐、`CMainFrame` 生成。 - `MainFrame.h/.cpp` — `CMainFrame:CFrameWnd`。子コントロールは **リソース不使用で OnCreate で動的生成**(.rc なし=RC の cp932 罠を回避)。 リスト選択→パラメータ欄ロード+`PreviewUpTo`、[追加/削除/↑↓]でレシピ編集、[実行]で全実行+バナー更新。 - `SelfTest.cpp` — `/selftest`(ヘッドレス)。合成画像→レシピ実行→`selftest_result.txt`(UTF-8)。 ## yfoximg 作法(重要) - アンブレラ `yfoximg.h`(OpenGL 引き込み)は使わず **`ImgProcessing.h` を直接 include**。 - yfoximg は **NOMINMAX を定義しない**(裸の `min`/`max` マクロ前提)。GDI+ も min/max マクロを要求するため、 `ImgProcessing.h` を含む TU(`InspectEngine.cpp`/`SelfTest.cpp`)では **`std::min/std::max` を使わない**(手書きクランプ)。 - 8bit グレー画像は `SetGrayPalette()` してから `Draw`/`Save`。処理系は R1(入力不変)・R2(出力は新規 `CDiBitmap`)。 - 画像は 8/24/32bit を受理。`Ensure8` が 32→24(手動)→`RGB2BW` で 8bit へ正規化してから濃淡系処理を行う。 ## 検証 - `ImgTestSystem.exe /selftest` → 合成画像(一様輝度128)で グレー化→平均輝度→白画素率 を実行し PASS を確認、 `selftest_result.txt` を出力(終了コード 0=PASS)。 ## わかりやすさ強化(3方式を統合) - **ビジュアル・パイプライン**(`PipelineView.*`, `CPipelineView:CWnd`): 各ステップをカード化し、 「そのステップ適用後のサムネイル」を縦に並べる=画像が上から下へ変化する様子が見える。 検査カードは大きな緑✓/赤✗バッジ+測定値。左ストライプ 青=処理/橙=検査。ダブルバッファ描画・ホイール/スクロール対応。 **罠**: `CDiBitmap::Draw`(サムネ)は DC の `BkMode` を OPAQUE/暗色へ戻すので、直後の文字描画前に `SetBkMode(TRANSPARENT)` を必ず再設定する(でないと暗地に明字で反転)。 - **テンプレート**(`core/Templates.h` + `wizard::ChooseTemplate`): 目的(キズ/欠け/明るさ/文字)を選ぶと完成レシピが入る。 - **対話式ウィザード**(`Wizard.*` + `CTaskDialog`): Q&A でレシピ生成。.rc 不要のため `CTaskDialog`(要 Common-Controls v6 マニフェスト=stdafx.h の pragma)。 - 右パネル: 選択ステップのパラメータを **CSliderCtrl でライブ調整**(`OnHScroll`→即再実行→サムネ更新)。整数域は実値、 ガンマ等の実数域は 0..1000 マッピング。合否バナーは `OnCtlColor` で緑/赤/灰に色分け。 - 検査種別に **塊(キズ/穴)の数**(`InspectBlobCount`)を追加(`LabelingInit`/`Labeling`/`m_nLabelCount`)。 ## 検証(追加) - `ImgTestSystem.exe /demo` → デモ画像(明るい地に暗い欠陥3点)+ キズ検査テンプレを読み込み実行(画面確認用)。 スクリーンショットは `PrintWindow(PW_RENDERFULLCONTENT=2)` で捕捉可(Windows PowerShell 5.1 + System.Drawing)。 ## レシピ保存/ROI/並べ替え/検査ログ(追加実装) - **レシピ保存/読込**(`core/RecipeStore.*`): 人が読める UTF-8 テキスト `*.itr`(`STEP ` / `PARAM ` / `ROI ...`)。 `StepKindId`/`StepKindFromId`(`StepCatalog.h`)で enum を安定文字列 ID として永続化。読込は `MakeStep` 既定へキー一致で上書き(前方互換)。メニュー ファイル。 - **検査範囲 ROI**(`Recipe::roiEnabled/roi`): 大プレビュー上をドラッグして矩形指定(`CMainFrame::OnLButtonDown/MouseMove/Up`)。 描画メソッド `DrawShot/DrawSource` は**実際に描いた矩形**(アスペクト維持=`AspectFit`)を返し、client↔画像座標の相互変換に使う。 検査は ROI 内で計測: 明るさ=`RectAvg(roi)` / 白画素率=`Threshold(...,roi)`÷ROI面積 / 塊数=`Labeling(...,roi)`。処理ステップは寸法不変なので全ステップ ROI 同座標系。ROI は大プレビュー+カードサムネに黄枠表示。メニュー 検査範囲。 - **カードのドラッグ並べ替え**(`CPipelineView`): 押下→しきい値超で `m_dragging`、青い挿入インジケータ、離すと `WM_PV_MOVESTEP(from,slot)`→`CMainFrame::OnPipeMoveStep` が並べ替え+再実行。 - **判定結果画像の保存 / 検査ログ**: `InspectEngine::SaveResultImage`=最終画像を24bit化し ROI枠+PASS/FAIL バナー+検査要約(縁取り文字)を焼き込み保存(拡張子で png/bmp/jpg)。 検査ログ=記録フォルダ(`SHBrowseForFolder`)へ `result_.png` 保存+`inspect_log.csv`(UTF-8 BOM, 日時/総合/画像/詳細)追記、フォルダを開く。メニュー 検査ログ。 - **⚠ 罠**: Windows ヘッダの `#define small char`(rpcndr.h)のため、ローカル変数名に `small` を使うと C2628。`fSmall` 等にする。 ## 複数ROI / MRU / CSV拡張(追加実装) - **複数ROI**(`Recipe::rois` = `vector`。空=画像全体): 大プレビューを**ドラッグで範囲追加**、**右クリックでその範囲を削除**、 メニュー 検査範囲=「最後を取り消す」「すべてリセット」。検査は全ROIを合算(明るさ=面積重み付き平均 / 白画素率=合算/合計面積 / 塊数=全ROIマスクを結合→union bbox でラベリング)。 大プレビュー+カード+結果画像に黄枠+番号を描画。`.itr` は `ROI l t r b` を範囲ごとに1行(旧 `ROI enabled l t r b` 5値も読める)。 - **⚠ 重要バグ修正**: yfoximg `CImgProcessing::Threshold` を**同一バッファへ sub-rect で複数回呼ぶとマスクが壊れる**(2個ROIで塊数0になった)。 `MakeBinary` は Threshold をやめ、**画素を直接走査してROI内だけ白く塗る**方式に変更(8bit前提=Ensure8済)。`/selftest` に複数ROI塊カウント検証(全体3/大ROI2/小2個ROI2)を常設。 - **最近使ったレシピ(MRU)**: 最大5件を `HKCU\Software\YFox\ImgTestSystem\Recent\File1..5` に保存(`SetRegistryKey(L"YFox")`)。 ファイルメニューの「最近使ったレシピ」サブメニュー(`m_hRecentMenu` を保持し `UpdateRecentMenu` で再構築、`ON_COMMAND_RANGE`)。開く/保存時に `AddRecent`。存在しない項目はクリック時に一覧から除去提案。 - **検査ログCSV 拡張**: 列= 日時,総合判定,元画像,レシピ,ステップ数,検査数,合格数,不合格数,検査範囲数,結果画像,詳細(UTF-8 BOM)。記録フォルダも `HKCU\...\Log\Folder` に永続化。 ## 現状 処理6種+検査3種、ビジュアル・パイプライン、テンプレート、ウィザード、スライダー調整、 レシピ保存/読込(+MRU)、複数ROI 指定、カードのドラッグ並べ替え、判定結果画像保存/検査ログ(CSV拡張)、/selftest・/demo。 `/selftest` は 実行 + レシピ往復一致 + 結果画像保存 + 複数ROI塊カウント を検証(RESULT: PASS)。