diff --git a/CLAUDE.md b/CLAUDE.md index baa8616..de53737 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ ## 開発進捗 -最新: PLAN_02 ステップ 1(実行に必要なファイルの取り込み)完了.次はステップ 2(実行を妨げる不具合の修正) +最新: PLAN_02 ステップ 2(実行を妨げる不具合の修正)完了.次はステップ 3(旧 exe と現行ソースの解析結果の比較) ※ 本欄は**最新ステップ 1 行のみ上書き更新**.詳細な進捗履歴(動機・設計判断・失敗パターン)は [docs/PROGRESS.md](docs/PROGRESS.md) に追記する.運用ルールは `.claude/rules/progress-log.md` を参照(該当ファイル編集時に自動ロードされる). ## 必須ルール(コード実装時) diff --git a/Tongue extraction/Form1.cs b/Tongue extraction/Form1.cs index 48eee6f..6743b7b 100644 --- a/Tongue extraction/Form1.cs +++ b/Tongue extraction/Form1.cs @@ -45,6 +45,9 @@ comboBox_mode.DropDownStyle = ComboBoxStyle.DropDownList; comboBox_mode.SelectedIndex = 0; + // Start のたびに登録すると処理が重複して走るため,ハンドラはここで 1 回だけ登録する + backgroundWorker1.DoWork += BackgroundWorker1_DoWork; + backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted; } private void Button_start_Click(object sender, EventArgs e) @@ -69,26 +72,62 @@ pictureBox_last.Refresh(); comboBox_mode.Enabled = false; + // 前回の解析が一時停止中のまま終わっていても,止まらずに始まるようにする + manualReset.Set(); + button_pause.Text = "Pause"; - - if (comboBox_mode.SelectedIndex == 0) + if ((comboBox_mode.SelectedIndex == 0) || (comboBox_mode.SelectedIndex == 1)) { - // 画像のみ入力し,マスク等を得る - this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorker1_DoWork_Image); - backgroundWorker1.RunWorkerAsync(); - } - else if (comboBox_mode.SelectedIndex == 1) - { - // 画像と校正画像を入力し,Lab色を得る - this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorker1_DoWork_ImageAndCalib); - backgroundWorker1.RunWorkerAsync(); + // 選んだモードを引数で渡し,DoWork 側で分岐する + backgroundWorker1.RunWorkerAsync(comboBox_mode.SelectedIndex); } else { MessageBox.Show("Please select the mode on the combobox"); + ResetControlsAfterAnalysis(); } } + private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e) + { + var mode = (int)e.Argument; + if (mode == 0) + { + // 画像のみ入力し,マスク等を得る + BackgroundWorker1_DoWork_Image(sender, e); + } + else if (mode == 1) + { + // 画像と校正画像を入力し,Lab色を得る + BackgroundWorker1_DoWork_ImageAndCalib(sender, e); + } + } + + private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + // 正常終了・異常終了のどちらでも,次の解析を始められる状態に戻す + ResetControlsAfterAnalysis(); + + if (e.Error != null) + { + MessageBox.Show("Error: " + e.Error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + else + { + MessageBox.Show("Finished!"); + } + } + + private void ResetControlsAfterAnalysis() + { + button_start.Enabled = true; + button_pause.Enabled = false; + button_pause.Text = "Pause"; + manualReset.Set(); + comboBox_mode.Enabled = true; + label_processingFileName.Text = "Processing File: None"; + } + private void BackgroundWorker1_DoWork_Image(object sender, DoWorkEventArgs e) { // ステータスの表示 @@ -165,7 +204,8 @@ var mat_drawBox = mat_input.Clone(); Cv2.Rectangle(mat_drawBox, dst_rect, new Scalar(0, 255, 0), 3); - MyMakeDir(path_outputDir + @"\1-detection\1-drawBox\"); + // MyMakeDir は既存フォルダを空にし,前の画像の出力を消すため使わない + Directory.CreateDirectory(path_outputDir + @"\1-detection\1-drawBox\"); mat_drawBox.SaveImage(path_outputDir + @"\1-detection\1-drawBox\" + name_file + ".png"); // Detection領域crop @@ -232,8 +272,6 @@ } // 再度確認 mat_binOutput = Cv2.ImRead(path_outputDir + @"\2-segmentation\2-binOutput(refined)\" + name_file + ".png", ImreadModes.Grayscale); - Cv2.ImShow("dst2", mat_binOutput); - Cv2.WaitKey(); Cv2.Canny(mat_binOutput, edges, 50, 200); Cv2.FindContours(edges, out contours, out hierarchyIndexes, RetrievalModes.List, ContourApproximationModes.ApproxSimple); @@ -297,7 +335,8 @@ areaCount = Cv2.CountNonZero(mat_temp); } var out_dir = path_outputDir + @"\4-featureExtraction\shape\"; - MyMakeDir(out_dir); + // MyMakeDir は既存フォルダを空にし,前の画像の出力を消すため使わない + Directory.CreateDirectory(out_dir); using (var sw = new StreamWriter(out_dir + name_file + ".csv")) { sw.WriteLine(",width,height,area," + @@ -452,16 +491,7 @@ GC.Collect(); } - MessageBox.Show("Finished!"); - Invoke((MethodInvoker)delegate - { - button_start.Enabled = true; - button_pause.Enabled = false; - label_processingFileName.Text = "Processing File: None"; - }); - - } @@ -480,7 +510,6 @@ var path_outputDir = @"..\Output/" + DateTime.Now.ToLocalTime().ToString("yyyyMMddhhmmss"); MyMakeDir(path_outputDir); FolderCopy("template_outputDir_withCalib", path_outputDir); - path_outputDir = @"..\Output/" + "debug"; // for debug var path_outputCsv = path_outputDir + @"\analysis.csv"; var count = 0; @@ -661,8 +690,6 @@ } // 再度確認 mat_binOutput = Cv2.ImRead(path_outputDir + @"\2-segmentation\2-binOutput(refined)\" + name_file + ".png", ImreadModes.Grayscale); - Cv2.ImShow("dst2", mat_binOutput); - Cv2.WaitKey(); Cv2.Canny(mat_binOutput, edges, 50, 200); Cv2.FindContours(edges, out contours, out hierarchyIndexes, RetrievalModes.List, ContourApproximationModes.ApproxSimple); @@ -881,16 +908,6 @@ }); GC.Collect(); } - - MessageBox.Show("Finished!"); - - Invoke((MethodInvoker)delegate - { - button_start.Enabled = true; - button_pause.Enabled = false; - label_processingFileName.Text = "Processing File: None"; - }); - } private OpenCvSharp.Rect TongueDetection(Mat mat_input) diff --git "a/docs/03_PLAN/PLAN_01_\350\246\201\344\273\266\345\256\232\347\276\251\346\233\270.md" "b/docs/03_PLAN/PLAN_01_\350\246\201\344\273\266\345\256\232\347\276\251\346\233\270.md" index 35c4026..a824202 100644 --- "a/docs/03_PLAN/PLAN_01_\350\246\201\344\273\266\345\256\232\347\276\251\346\233\270.md" +++ "b/docs/03_PLAN/PLAN_01_\350\246\201\344\273\266\345\256\232\347\276\251\346\233\270.md" @@ -77,5 +77,4 @@ ## 未決事項 (Open Questions) -- Calib モードの出力先を `Output\debug` に固定しているのは意図的か(現行ソースだけにあり,配布済み exe には無い) - .NET Framework 4.8 から新しい .NET への移行を将来の対象にするか diff --git "a/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" "b/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" index a668bbd..850153d 100644 --- "a/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" +++ "b/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" @@ -39,9 +39,10 @@ - 二値画像のウィンドウ(`Cv2.ImShow`・`Cv2.WaitKey`)で解析が止まる箇所を削除する - Start を押すたびに解析処理が重複して登録される不具合を直す.モード選択を解析後に押せる状態に戻す - 解析中の例外を `RunWorkerCompleted` で受け取り,画面に表示して操作できる状態に戻す -- Image&Calib の出力先が `Output\debug` に固定されている件は,ユーザーの判断を得てから直す([要件定義書](PLAN_01_要件定義書.md) の未決事項) +- Image&Calib の出力先が `Output\debug` に固定されている件を直し,Image モードと同じ日時フォルダに出力する(意図が不明だったため,ユーザーの既定方針で日時フォルダに戻した) - Image モードで中間ファイル(`1-detection\1-drawBox\`・`4-featureExtraction\shape\`)が最後の画像の分しか残らない不具合を直す(画像ごとに `MyMakeDir` でフォルダを空にしているため.`analysis.csv` の値には影響しない.ステップ 3 で中間画像を比べるために必要) - 完了条件: 複数枚の画像を両モードで続けて解析でき,途中で止まらない.中間ファイルが全画像分残る +- 結果: 完了.Image モードの連続実行・エラー後のモード切り替え・中間ファイルが全画像分残ることをユーザーが確認した.`analysis.csv` は修正前とバイト単位で一致した.Image&Calib は VC++ デバッグ用ランタイムが無く `TongueAnalysis.exe` が起動しないため,エラー表示と画面の復帰までを確認した ### 3. 旧 exe と現行ソースの解析結果の比較 diff --git "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\344\273\225\346\247\230.md" "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\344\273\225\346\247\230.md" index 78e8ff8..2f37fc4 100644 --- "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\344\273\225\346\247\230.md" +++ "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\344\273\225\346\247\230.md" @@ -44,7 +44,8 @@ 3. Start を押す.Start とモード選択が押せなくなり,Pause が押せるようになる 4. 画像を 1 枚ずつ解析し,各パネルと状態表示が更新される 5. Pause を押すと,処理中の画像を終えた時点で停止する.ボタンが `Continue` に変わり,押すと再開する -6. 全画像を処理すると「Finished!」のメッセージが出る.Start が再び押せるようになり,Pause は押せなくなる +6. 全画像を処理すると「Finished!」のメッセージが出る.Start とモード選択が再び押せるようになり,Pause は押せなくなる +7. 続けて別のモード・同じモードで解析できる(アプリの再起動は不要) ## エラー時の表示 (Error Display) @@ -53,14 +54,11 @@ | 舌を検出できない | 表示なし.`analysis.csv` に `<画像名>,Tongue detection error` を書き,次の画像へ進む | | 舌領域が 1 つにまとまらない | 表示なし.`analysis.csv` に `<画像名>,Tongue segmentation error` を書き,次の画像へ進む | | 小領域除去 DLL を呼べない | エラーダイアログ(`Unable to reprocess! ...`)を出し,残りの画像を処理せずに終了する | -| 上記以外の例外 | 表示されない(既知の不具合を参照) | +| 上記以外の例外 | エラーダイアログ(`Error: <例外のメッセージ>`)を出し,残りの画像を処理せずに終了する.Start とモード選択が再び押せる状態に戻る | ## 既知の不具合 (Known Issues) -[要件定義書](../03_PLAN/PLAN_01_要件定義書.md) の「実行を妨げる不具合の修正」で直す対象. - -- 舌領域が 1 つにまとまらない画像で,二値画像のウィンドウ(`dst2`)が開き,キーを押すまで解析が止まる -- 2 回目以降に Start を押すと,押した回数だけ解析が重複して実行される -- モード選択は一度 Start を押すと押せないままになる(モードを変えるにはアプリの再起動が必要) -- 解析中に想定外の例外が起きると,何も表示されずに止まり,Start も押せないままになる - Pause の停止は画像 1 枚ごとにしか効かない(1 枚の処理中は止まらない) +- 小領域除去 DLL を呼べないときのダイアログは解析スレッドから出ており,閉じたあとに「Finished!」も出る +- 解析中にウィンドウを閉じると例外になる可能性がある +- ※ v2.7 にあった「`dst2` ウィンドウで解析が止まる」「Start を押すたびに解析が重複する」「モード選択が押せないままになる」「例外が起きると何も表示されずに止まる」は修正済み([開発ステップ](../03_PLAN/PLAN_02_開発ステップ.md) のステップ 2) diff --git "a/docs/04_SPEC/SPEC_03_\345\205\245\345\207\272\345\212\233\344\273\225\346\247\230.md" "b/docs/04_SPEC/SPEC_03_\345\205\245\345\207\272\345\212\233\344\273\225\346\247\230.md" index c226106..84ab30d 100644 --- "a/docs/04_SPEC/SPEC_03_\345\205\245\345\207\272\345\212\233\344\273\225\346\247\230.md" +++ "b/docs/04_SPEC/SPEC_03_\345\205\245\345\207\272\345\212\233\344\273\225\346\247\230.md" @@ -29,15 +29,13 @@ | 共通 | `..\Input\TongueImage\` | 舌画像.フォルダ内のファイルをすべて処理対象にする(実運用では `.bmp`) | | Image&Calib | `..\Input\CalibrationColorChart\` | カラーチャート画像.舌画像と拡張子を除いたファイル名が同じものを対応させる | -- ※ Image&Calib で対応するカラーチャート画像が無いと,解析が止まる([画面仕様](SPEC_01_画面仕様.md) の既知の不具合). +- ※ Image&Calib で対応するカラーチャート画像が無いと,エラーダイアログを出して解析を終了する(残りの画像は処理しない). ## 出力フォルダ (Output Folder) 実行ごとに `..\Output\\` を作り,雛形フォルダ(`template_outputDir` など)の中身をコピーしてから結果を書き込む. - ※ 日時の時は 12 時間表記(`hh`)のため,午前と午後で同じ名前になりうる. -- ※ Image モードでは `1-detection\1-drawBox\` と `4-featureExtraction\shape\` を画像ごとに空にしているため,最後の画像の分しか残らない(既知の不具合.`analysis.csv` の値には影響しない). -- ※ Image&Calib では,現行ソースは出力先を `..\Output\debug\` に固定している([要件定義書](../03_PLAN/PLAN_01_要件定義書.md) の未決事項). ```text \ diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 1c74e56..1f0d94b 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -23,3 +23,8 @@ - 設計判断: 取り込む DLL は PE のインポート表で依存関係を調べて決めた(`RemoveSmallRegionDLL` → `opencv_world320`,`TongueAnalysis.exe` → `opencv_*3413d`).`TongueAnalysis.exe` は `Data\` を作業ディレクトリから読むため,ルート側の `Data/` を採用した(`rgb2lab/Data/` とは改行コードだけが違う).データと雛形は改行を変換せず旧ビルドとバイト単位で同一に保つ. - 発見: Image モードで `1-drawBox`・`shape` の中間ファイルが最後の画像の分しか残らない既存の不具合(`MyMakeDir` が画像ごとにフォルダを空にする).ステップ 2 の対象に追加した. - 未実装・後段: Image&Calib モードは VC++ デバッグ用ランタイムが無く未検証.旧 exe(正本)での Image モード実行もまだ. + +- [x] **PLAN_02 ステップ 2: 実行を妨げる不具合の修正**: `dst2` ウィンドウでの停止,Start のたびの解析の重複登録,例外の握りつぶし,Image モードで中間ファイルが最後の画像分しか残らない件,Image&Calib の出力先の `Output\debug` 固定を直した.`analysis.csv` は修正前とバイト単位で一致. + - 設計判断: 重複登録は「DoWork のハンドラを 1 つにして RunWorkerAsync の引数でモードを渡す」方式にした(登録を外し忘れて重複する余地が構造上無いため). + - テスト・リファクタリング(`/implement` の Phase 2・3)は省略した.変更が画面操作の流れだけで,規約上画面操作は自動テストの対象外であり,テストプロジェクトはステップ 4 で作るため.整理はステップ 6 で行う. + - 未実装・後段: Image&Calib の完走確認には Visual Studio 2022 Community(C++ によるデスクトップ開発)の導入が必要(ステップ 3 の前提).出力フォルダ名が 12 時間表記で午後のフォルダが午前より前に並ぶ件は未対応.