專案

一般

配置概況

動作

Feature #183

進行中
SC SC

CT 模式全面支援(DICOM 解析、W/L 預設、測量工具、多 Series 瀏覽)與考試介面重構

Feature #183: CT 模式全面支援(DICOM 解析、W/L 預設、測量工具、多 Series 瀏覽)與考試介面重構

是由 Sashiba Chou10 天 前加入.

狀態:
New
優先權:
Normal
被分派者:
開始日期:
2026-03-24
完成日期:
2026-03-24 (逾期 10 天)
完成比例:

100%

預估工時:
10:00 小時

概述

一、新增檔案

  1. server/dicom.js(356 行)— 全新 DICOM 解析模組
    - scanCTFolder(folderPath):遞迴掃描 DICOM 資料夾,解析
    header(SeriesInstanceUID、InstanceNumber、SliceLocation、Rows、Cols、BitsAllocated、PixelRepresen
    tation、RescaleSlope/Intercept、WindowCenter/Width、PixelSpacing),按 Series 分組並排序
    - getSlicePng(ctId, seriesId, sliceIndex, { wc, ww }):DICOM→PNG 轉換(HU windowing),支援自訂
    Window/Level 參數,磁碟快取(server/cache/),快取 key 含 _wc{}_ww{}
    - parseDicomPixels(ctId, seriesId, sliceIndex):共用 helper,解析 DICOM raw pixel
    data(Int16/Uint16/Uint8)
    - getSliceHU(ctId, seriesId, sliceIndex, { x, y, rx, ry }):橢圓區域 HU 統計(mean, std, min,
    max, count),座標為百分比
    - getCTInfo(ctId):回傳 series metadata 含 pixelSpacing [rowSpacing, colSpacing]、rows、cols
    - registerCTFromQuestions(questions):Server 啟動時從 questions.json 重建 ctRegistry
    - PixelSpacing 從 DICOM tag x00280030 擷取("rowSpacing\colSpacing" 格式)
  2. client/src/components/CTContextMenu.jsx(95 行)— 共用右鍵選單元件
    - Window Presets:Brain(40/80)、Chest/Mediastinum(40/400)、Lung(-600/1500)、Abdomen(40/350)、Bon
    e(400/1800)、Soft Tissue(50/400)、Default(DICOM)
    - Measurement Tools:HU Measurement (Ellipse)、Length Measurement、Deselect Tool
    - Viewport 邊界修正(避免選單超出螢幕)
    - ESC / 點擊外部關閉、暗色主題
  3. restart.ps1(116 行)— PowerShell 重啟server腳本
    - 自動 kill port 3001/5173 佔用的 process
    - 等待 port 釋放(最多 10 秒,含 force kill 重試)
    - 依序啟動 server → client,輪詢確認 port listening
    - 顯示 Local + Network URL

二、Server 修改(server/index.js)

  1. 新增 import { scanCTFolder, getSlicePng, getCTInfo, getSliceHU, registerCTFromQuestions } from
    dicom.js
  2. 新增 import { mkdirSync, renameSync } from fs
  3. Migration 擴充:舊 CT 格式 seriesId → seriesIds[] + primarySeriesId 自動遷移
  4. 啟動時呼叫 registerCTFromQuestions() 重建 ctRegistry
  5. 新增 CT API 端點:
    - POST /api/ct-scan:掃描本機 DICOM 資料夾
    - POST /api/ct-upload:瀏覽器上傳 DICOM 檔案(multer, 最多 2000 檔 / 50MB per file),上傳至
    server/uploads/ct_{timestamp}/,自動還原目錄結構
    - GET /api/ct-slice/:ctId/:seriesId/:sliceIndex:加入 ?wc=&ww= query params 支援自訂 W/L 渲染
    - GET /api/ct-hu/:ctId/:seriesId/:sliceIndex?x=&y=&rx=&ry=:HU 橢圓區域量測
    - GET /api/ct-info/:ctId:回傳 series metadata(含 pixelSpacing、rows、cols)
  6. clientQuestions 建構(practice-start、join-room、start-game 三處):CT 題目回傳 { type:'ct',
    ctId, seriesIds, primarySeriesId, totalSlices }
  7. isPointInROI 加入第 4 參數 flagSliceIndex,支援 CT Z 軸(sliceIndex)匹配
  8. scoreAnswer 多 series CT 計分:只計算 primary series 上的 flag
  9. player.currentQuestion 改用 playerAnswers.length 追蹤(修正完成判定)
  10. 新增 dicom-parser、sharp 依賴(package.json + package-lock.json)

三、GameClient.jsx 修改

CT 支援:

  1. 新增 CT state:ctSliceIndex、activeSeriesId、seriesMetadata、interactionMode(flag/zoom/pan)
  2. 新增 Space+drag pan(spaceHeld + panDragRef)、中鍵拖曳 zoom(zoomDragRef)
  3. Scroll 行為改為:Ctrl+wheel = zoom,plain wheel = CT slice 切換(X-Ray 模式無動作)
  4. 切 slice 時清除 measurements + drawingMeasurement
  5. Fetch /api/ct-info 取得 series metadata、pixelSpacing、image dims
  6. imageSrc 加入 ?wc=&ww= W/L 查詢參數
  7. Flag 放置加入 sliceIndex + seriesId
  8. 新增 switchSeries(seriesId) 切換 CT series
  9. PACS-style series sidebar(左側,w-16,CT multi-series 時顯示)
  10. Slice info badge(左上角,Slice X / Y)
  11. W/L preset badge(可點 × 回復 DICOM 預設)
  12. Tool badge(HU Measurement / Length,可點 × 取消)
  13. Interaction mode 按鈕列(Flag / Zoom / Pan)

右鍵選單 + 測量工具:
14. 右鍵短按(< 5px 移動)= 開啟 CTContextMenu,拖曳 = 亮度/對比(rightClickRef 追蹤)
15. HU 橢圓量測:drag 繪製橢圓 → mouseUp 呼叫 /api/ct-hu → 顯示 Mean/Std HU(cyan 色)
16. 長度量測:兩次 click 定端點 → 用 pixelSpacing 計算 mm 距離(黃色),無 pixelSpacing 時顯示 px
17. drawCanvas 繪製 measurements(實線=完成,虛線=繪製中)
18. Cursor 依 ctTool 顯示 crosshair
19. HU fetch 加入 r.ok 檢查 + stats.error 檢查 + console.warn 錯誤訊息

回顧 canvas (Review):
20. Review 支援 CT slice 滾動(reviewCtSlice)
21. Review 支援 W/L 預設(reviewCtWindowPreset + reviewCtContextMenu)
22. Review image URL 含 W/L query params + slice index
23. Review 右鍵短按開啟 CTContextMenu
24. Review 支援中鍵 zoom + Space+drag pan(reviewZoomDragRef, reviewPanDragRef)
25. Review 提示文字區分 CT vs X-Ray
26. Review truthROIs 依 sliceIndex 過濾

題號導航 + 答案管理:
27. answersRef 陣列追蹤每題狀態(classification、flags、ctSliceIndex、submitted、feedback、marked
、questionStartTime、elapsedMs)
28. saveDraft() 離開題目時儲存草稿並累計 elapsedMs
29. navigateTo(idx) 切題前先 saveDraft
30. toggleMark() 標記題目(黃色圓點)
31. findNextIncomplete() / findPrevIncomplete() 跳轉未完成題
32. 載入題目時還原草稿/已提交狀態,重置 questionStartTime(累計時間已存於 elapsedMs)
33. submitAnswer() 使用累計時間 elapsedMs + (Date.now() - questionStartTime)
34. rejoin 時重建 answersRef 已提交記錄
35. score-update 回傳時存入 answersRef.feedback

布局修改:
36. Leaderboard 從頂層 absolute 移至 Answer Controls flex-col 內(Normal/Abnormal 按鈕下方)
37. 底部控制列新增:題號導航列(w-8 h-8 按鈕)、Prev/Next 按鈕、Mark 按鈕、Submit 按鈕
38. submitAndNext() 拆分為 submitAnswer()(不自動跳題)

bug fixes:
39. drawCanvas flags 過濾加入 flag.seriesId !== currentSeriesId(修正跨 series flag 顯示)
40. drawCanvas 依賴加入 activeSeriesId
41. Review flags 過濾加入 flag.seriesId !== reviewSeriesId

enhancements:
42. 已提交題號:bg-gray-800 text-gray-600 cursor-not-allowed,disabled 且不可點擊
43. 有草稿未提交:bg-blue-600 text-white(亮藍色)
44. 尚未作答:bg-gray-500 text-white(亮灰色)
45. 新增 elapsedDisplay state + 每秒更新的 setInterval 計時器
46. 左下角顯示 Time: mm:ss(未提交時顯示,提交後隱藏)

四、Leaderboard.jsx 修改

  1. 移除 absolute top-4 left-4 z-20 定位 + min-w-[200px],改為 flow child + min-w-[180px]
    max-w-[220px]
  2. 新增 useEffect, useRef, useState import
  3. prevEntriesRef 追蹤前一次 entries
  4. 偵測 score 或 rank 異動 → flashIds Set
  5. 異動的 row 套用 CSS animation leaderboard-flash(黃色閃爍 0.3s × 2 次)
  6. 1200ms 後清除 flashIds
  7. 內嵌 <style> 定義 @keyframes leaderboard-flash

五、PracticeGame.jsx 修改

(與 GameClient 相同模式,無回顧 canvas、無 Leaderboard)

  1. CT state、Space+drag、中鍵 zoom、右鍵選單、W/L 預設、測量工具
  2. 題號導航 + answersRef + saveDraft/navigateTo/toggleMark
  3. drawCanvas 加入 flag sliceIndex + seriesId 過濾
  4. drawCanvas 加入 measurements 繪製
  5. truthROIs sliceIndex 過濾
  6. Scroll = CT slice, Ctrl+scroll = zoom
  7. HU fetch 改善錯誤處理
  8. Series sidebar + slice info + W/L/tool badges
  9. drawCanvas 依賴加入 activeSeriesId

六、TeacherTool.jsx 修改

  1. CT 匯入:handleCTFolderSelect 瀏覽器上傳 DICOM → /api/ct-upload
  2. CT state:ctMode、ctUploading、ctScanResult、ctSelectedSeries、ctSelectedSeriesIds(多選)、ctP
    rimarySeriesId、ctSliceIndex、ctTotalSlices
  3. CT series 選擇 UI、multi-select checkbox、primary series 指定
  4. CT slice 載入:useEffect 依據 ctSliceIndex + ctWindowPreset 更新 currentImageUrl
  5. ROI 繪製加入 sliceIndex(CT 模式)、drawCanvas 過濾 ROI by sliceIndex
  6. 右鍵選單 + 測量工具(使用 ctMeasureTool 避免與 activeTool 衝突)
  7. HU fetch 改善錯誤處理
  8. 儲存題目時包含 CT 欄位(type、folderPath、ctId、seriesIds、primarySeriesId、totalSlices)
  9. 編輯既有 CT 題目時載入 CT 資料 + 呼叫 /api/ct-scan 重建 registry
  10. cancelEdit 和 handleFileSelect 重置所有 CT state
  11. Space 鍵改為 { capture: true } 避免按鈕觸發
  12. Scroll 改為 Ctrl+wheel = zoom、plain wheel = CT slice
  13. Slice info overlay 顯示於 canvas 左上角
  14. CT pixelSpacing 透過 /api/ct-info 取得

七、依賴變更(server/package.json)

  • 新增 dicom-parser: ^1.8.21
  • 新增 sharp: ^0.34.5

沒有任何資料可供顯示

動作

匯出至 PDF Atom