2026年2月10日 星期二

FHIR R4 DiagnosticReport:深入解析資源、欄位與 Patient 關聯

FHIR R4 DiagnosticReport:深入解析資源、欄位與 Patient 關聯

在 HL7 FHIR 中,DiagnosticReport(診斷報告資源) 是用來表示由診斷服務提供者出具之報告,包括實驗室結果、影像解讀、病理報告等資訊。與單一的 Observation(觀察結果)相比,它能彙整更多臨床語意與文檔層級的結果,是診斷流程中重要的事件資源。

📌 DiagnosticReport 的核心定位與價值

DiagnosticReport 是 FHIR Workflow 中的一種事件型資源,其主要用途為:

  • 彙整和表達完整的診斷服務成果,涵蓋文字敘述、編碼結果、影像或可附加完整報告(如 PDF)。
  • 結合診斷程序上下游關係,例如檢驗請求、觀察結果、醫療就診事件等。
  • 與 Observation 形成一對多的關係:報告可能引用多個 Observation 來表示原子值(例如血液檢查各項指標)。
  • 能支援多樣診斷類別:實驗室、影像、病理、心臟檢查等。

從臨床流程來看,DiagnosticReport 是“檢查完成後的產品”,可連結到請求來源(例如 ServiceRequest)、檢體(Specimen)、受檢者(Patient)、醫療事件(Encounter)、解讀者(Practitioner)等多種資源。


🧱 DiagnosticReport 的欄位解構:名稱、意義與實際用法

以下依照官方定義逐一解析 DiagnosticReport 項目(含基本資源層級與臨床需求):

identifier

報告的商業識別碼,可為多個(例如各系統內部 ID、實驗室編號等)。此欄位利於跨系統辨識同一報告。

basedOn

表示這份 DiagnosticReport 是基於何種請求資源所產生,如 CarePlan、MedicationRequest、NutritionOrder 或 ServiceRequest。這是一種 workflow 追蹤,用於回溯診斷產出的起源。

status

報告的狀態,例如 registered、partial、preliminary、final 等,影響臨床使用時機(是否為最終結果)。

category

代表報告所屬的服務類型,例如實驗室(lab)、影像(imaging)等,用以檢索與分類。

code

為必填欄位,用來標示這份報告的具體診斷性質(如一般實驗室報告、特定掃描診斷等),典型使用 LOINC 等編碼系統。

subject

**核心欄位,這是報告的主體(subject),通常會 reference Patient。 FHIR 的設計允許 subject 為 Patient、Group、Device 或 Location,但在大部分臨床上下文中的常見用法是指向 Patient(受檢者)。 這個 reference 是 DiagnosticReport 與 Patient 之間最直接的連結點。

encounter

連結到 Encounter 資源,用於表明這份報告關聯到何種就診事件(例如門診、住院事件等),有助於維持臨床事件的時間與上下文關係。

effective[x]

表示報告結果與檢查執行的時間相關資訊,可以是單一時間(effectiveDateTime)或時間區間(effectivePeriod),用於時間序列與診療路徑分析。

issued

報告何時正式發布或可供查閱。這在流程控制與資料版本管理中非常重要。

performer

這是執行該報告之診斷服務的實體參考,可能是 Practitioner、PractitionerRole、Organization 或 CareTeam。可用於責任區分與品質評估。

resultsInterpreter

引用那些負責最終結果解讀之專業角色(例如主治醫師或解讀者 team)。

specimen

如果診斷基於檢體(例如血液、組織切片),會 Reference Specimen 資源。這能讓前後端系統將報告結果與原始檢體檔案串連。

result

DiagnosticReport 與 Observation 資源最重要的關聯之一:這表示此報告對“各別觀察值”的引用(例如血中紅血球濃度這種原子值),通常是 0..* 多對多。

imagingStudy

在影像報告中,直接引用 ImagingStudy,使接收端能查詢完整的影像內容(例如 DICOM Study)以供瀏覽與視覺化。

media

若診斷報告涉及附加的影像(例如拍攝圖片、病理切片圖像),透過 media 子元素設定圖像相關的 meta 與連結。

conclusion / conclusionCode

以文字或可編碼方式提供測試的臨床結論或總結,用於提高理解或自動化決策支援(如結論標籤)。

presentedForm

提供可下載的完整報告形式(例如 PDF、Word、影像檔等),適合直接呈現在使用者介面或儲存作為法律/醫療文件。


🔗 Resource 之間的 Reference 與 Patient 的關連鏈

在 DiagnosticReport 中,與 Patient 相關或可能有跨資源 Reference 的欄位包括:

  • subject → Patient(最直接)
  • encounter → Encounter(此欄位中 Encounter 通常也含有對 Patient 的 reference)
  • specimen → Specimen(Specimen 通常會 reference Patient/Encounter)
  • result → Observation(Observation 通常 reference Patient)
  • basedOn → 可能 reference ServiceRequest 或其他 request,這些 request 也會 reference Patient
  • performer / resultsInterpreter → 可能 reference Practitioner / Organization,但這些角色通常也有關聯 Patient 通常不直接 reference
  • imagingStudy → ImagingStudy 內部也 reference Patient 以表明影像結果是針對誰執行的

因此,從 DiagnosticReport → subject → Patient → Observation / Encounter / Specimen / ImagingStudy,可以建立一條臨床事件詳細鏈結的資料關係圖。


🧠 Profiles 與 Extensions 的角色

除了基本資源外,FHIR 的 Profiles 與 Extensions 扮演著重要角色:

  • Profiles 是針對某一特定需求或領域的約束/延伸版本,可限制欄位 cardinality、value set、必填性等,讓資料交換更嚴謹且符合領域規範。例如許多 Implementation Guide 提供了 “Laboratory Report Profile” 或 “DiagnosticReport Patient Profile”。([HL7 Europe][4])
  • Extensions 是在標準欄位不夠表達時的擴充機制,用於補充特定場景資訊。

Profiles 是否強制使用、或有哪些特定 extension 需要加入,通常依照業務需求或 IG(Implementation Guide)規範決定。


📍 與 Patient 的 Reference Network(經由 DiagnosticReport)

在實際 FHIR 生態中,DiagnosticReport 可能與 Patient 產生以下重要關聯:

  • 直接的 subject Reference:表明報告主體
  • 經由 Encounter 間接 Reference:診斷報告可能屬於某次就診
  • Result Observation 間接 Reference:Observation 資源的 subject 通常 reference Patient
  • 請求來源資源 Reference(ServiceRequest 等)之間的 Patient 參考鏈

這些參考可以支援完整的臨床資料串接與審計流程,讓查詢和分析 Patient 的診斷流程更加完整。


✅ 結語

FHIR 的 DiagnosticReport 是一個高度連結性與臨床語意豐富的資源,能夠承接一份診斷在一個病歷生命週期中的全貌——從請求、執行、結果至最終發佈與結論。

透過理解這些欄位與可能的 reference paths,我們可以設計更完善的 FHIR 醫療互操作系統,讓資料在系統間流動時能夠完整、可識別並可重用,尤其是與 Patient 關聯的上下游資訊。 




將 DICOM 轉換成為一個 Image

將 DICOM 轉換成為一個 Image

2025 年,我大多在進行醫療相關應用軟體開發,在進行 AI 臨床試驗平台開發的過程中,經常需要處理 DICOM 影像檔案。這個系統需要讓使用者上傳一個 L3 DICOM 影像檔案,然後將其轉換為 PNG 格式,以便在網頁上顯示。這個過程涉及到 DICOM 影像的解析、處理和轉換,並且需要確保轉換後的圖像質量和準確性。

對於 DICOM(Digital Imaging and Communications in Medicine)是一種用於儲存和傳輸醫療影像的標準格式。將 DICOM 影像轉換為一般的圖像格式(如 PNG 或 JPEG)是非常常見的需求,這樣可以方便地在各種應用程式中顯示和處理這些影像。

為了要完成這樣的需求,我們可以使用 C# 語言來開發一個簡單的應用程式,利用一些現有的庫來處理 DICOM 影像的解析和轉換。這裡選擇的是 fo-dicom 這個開源的 DICOM 庫,它提供了豐富的功能來處理 DICOM 影像,並且支援多種圖像格式的轉換。

底下是我的測試過程,這裡會將一個 DICOM 檔案轉換成為 PNG 格式的圖像,並且在過程中會顯示一些相關的資訊。

建立 主控台應用程式 專案

  • 開啟 Visual Studio 2026
  • 選擇「建立新專案」
  • 在 [建立新專案] 視窗中,在右方清單內,找到並選擇 [主控台應用程式] 項目
  • 然後點擊右下方「下一步」按鈕
  • 此時將會看到 [設定新的專案] 對話窗
  • 在該對話窗的 [專案名稱] 欄位中,輸入專案名稱,例如 "csDicomToImage"
  • 然後點擊右下方「下一步」按鈕
  • 接著會看到 [其他資訊] 對話窗
  • 在這個對話窗內,確認使用底下的選項
    • 架構:.NET 10.0 (或更新版本)
    • 勾選 不要使用最上層陳述式 (這是我的個人習慣)
  • 然後點擊右下方「建立」按鈕
  • 現在,已經完成了這個 主控台應用程式 專案的建立

安裝 fo-dicom.Imaging.ImageSharp 套件

套件 fo-dicom.Imaging.ImageSharp 是一個用於處理 DICOM 影像的套件,它提供了將 DICOM 影像轉換為 ImageSharp 影像的功能。這裡是該套件提供功能的主要清單:

  • DICOM 影像轉換:將 DICOM 影像轉換為 ImageSharp 影像格式,方便進行後續的圖像處理和顯示。
  • 支援多種 DICOM 影像格式:該套件支援多種 DICOM 影像格式,包括單幀和多幀的 DICOM 影像。
  • 圖像處理功能:提供了一些基本的圖像處理功能,例如調整亮度、對比度、旋轉等,方便用戶對 DICOM 影像進行處理。

使用底下方式進行安裝此套件

  • 在 Visual Studio 的「方案總管」視窗中,右鍵點擊專案名稱
  • 從右鍵選單中,選擇「管理 NuGet 套件」
  • 在 NuGet 套件管理器視窗中,切換到「瀏覽」標籤頁
  • 在搜尋框中,輸入 "fo-dicom.Imaging.ImageSharp" 並按下 Enter 鍵
  • 從搜尋結果中,找到 "fo-dicom.Imaging.ImageSharp" 套件 並點擊它
  • 在這裡的範例中,使用該套件的版本為 5.2.2
  • 在右側的詳細資訊面板中,點擊「安裝」按鈕

安裝 fo-dicom.Codecs 套件

套件 fo-dicom.Codecs 是一個用於處理 DICOM 影像編解碼的套件,它提供了對 DICOM 影像進行編碼和解碼的功能。這裡是該套件提供功能的主要清單:

  • DICOM 影像編碼:將圖像數據編碼為 DICOM 影像格式,方便儲存和傳輸。
  • DICOM 影像解碼:將 DICOM 影像解碼為圖像數據,方便進行後續的圖像處理和顯示。

使用底下方式進行安裝此套件

  • 在 Visual Studio 的「方案總管」視窗中,右鍵點擊專案名稱
  • 從右鍵選單中,選擇「管理 NuGet 套件」
  • 在 NuGet 套件管理器視窗中,切換到「瀏覽」標籤頁
  • 在搜尋框中,輸入 "fo-dicom.Codecs" 並按下 Enter 鍵
  • 從搜尋結果中,找到 "fo-dicom.Codecs" 套件 並點擊它
  • 在這裡的範例中,使用該套件的版本為 5.2.2
  • 在右側的詳細資訊面板中,點擊「安裝」按鈕

撰寫程式碼

  • 打開 Program.cs 檔案,並將其內容替換為以下程式碼:
using FellowOakDicom;
using FellowOakDicom.Imaging;
using SixLabors.ImageSharp.Formats.Png;

namespace csDicomToImage;

internal class Program
{
    static async Task Main(string[] args)
    {
        // 初始化 DICOM 設定
        InitializeDicom();

        string dicomFile = "image-00000.dcm";
        string pngFile = "out.png";
        string rootPath = Directory.GetCurrentDirectory();
        string dicomFilePath = Path.Combine(rootPath, dicomFile);
        string pngFilePath = Path.Combine(rootPath, pngFile);

        // 轉換單個檔案
        ConvertSingleFile(dicomFilePath, pngFilePath);

        // 批次轉換資料夾中的所有 DICOM 檔案
        // ConvertDirectory("input_folder", "output_folder");
    }

    /// <summary>
    /// 初始化 DICOM 設定,註冊 ImageSharp 影像管理器
    /// </summary>
    private static void InitializeDicom()
    {
        try
        {
            new DicomSetupBuilder()
                .RegisterServices(s =>
                s.AddFellowOakDicom()
                 .AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
                 .AddImageManager<ImageSharpImageManager>())
          .SkipValidation()
          .Build();

            Console.WriteLine("DICOM 初始化成功");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"DICOM 初始化失敗: {ex.Message}");
            throw;
        }
    }

    public static void ConvertSingleFile(string dicomPath, string pngPath)
    {
        try
        {
            Console.WriteLine($"開始轉換: {dicomPath}");

            // 開啟 DICOM 檔案
            var dicomFile = DicomFile.Open(dicomPath);
            var dicomImage = new DicomImage(dicomFile.Dataset);

            // 渲染影像
            //var foo = dicomImage.RenderImage().As<Bitmap>();
            var renderedImage = dicomImage.RenderImage();

            // 將 renderedImage 轉換為 ImageSharp Image 並儲存為 PNG
            var sharpImage = renderedImage.AsSharpImage();

            // 儲存為 PNG
            using (var fileStream = new FileStream(pngPath, FileMode.Create))
            {
                sharpImage.Save(fileStream, new PngEncoder());
            }

            Console.WriteLine($"轉換完成: {pngPath}");

            // 顯示影像資訊
            //DisplayImageInfo(dicomFile.Dataset, sharpImage);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"轉換失敗: {ex.Message}");
            throw;
        }
    }
}

這個 Console 主控台應用程式,一開始將會呼叫 InitializeDicom() 方法來初始化 DICOM 的設定,這個方法內使用了 DicomSetupBuilder() 來註冊 DICOM 的服務,並且指定使用 ImageSharpImageManager 來處理影像的管理。這個 AddFellowOakDicom() 方法是用來註冊 DICOM 的核心功能,而 AddTranscoderManager() 方法則是用來註冊 DICOM 影像的編解碼器,這裡使用的是 NativeTranscoderManager,這個編解碼器可以支援多種 DICOM 影像格式的轉換。

在第一次接觸這個套件,我對於這個註冊過程感到有些困惑,因為它涉及到一些較為底層的設定,但在閱讀了相關的文件和範例程式碼後,我逐漸理解了這個過程的意義。這個註冊過程是必要的,因為它告訴 DICOM 庫如何處理影像的管理和編解碼,這樣在後續的轉換過程中,DICOM 庫就能夠正確地處理影像數據。

接下來,在 Main() 方法中,我們定義了要轉換的 DICOM 檔案路徑和輸出的 PNG 檔案路徑,然後呼叫 ConvertSingleFile() 方法來進行轉換。

這個方法內部首先會使用 DicomFile.Open 方法來開啟指定的 DICOM 檔案,然後用剛剛開啟的 Dicom 檔案來建立一個 [DicomImage] 物件,使用 DicomImage 類別有一個方法 [RenderImage()] 可以用來渲染影像,該方法會回傳一個 IImage 物件,使用該物件的 [AsSharpImage()] 方法來取得一個 ImageSharp 的圖像格式,最後將其儲存為 PNG 格式的檔案。

如此,便可以透過上述過程將 DICOM 影像成功轉換為 PNG 格式,並且在過程中顯示相關的資訊。

執行程式

首先先來看這個專案的執行結果:

  • 按下 F5 鍵或點擊「開始」按鈕來執行程式
  • 底下為實際操作過程的輸出文字
DICOM 初始化成功
開始轉換: C:\Vulcan\Github\CSharp2025\csDicomToImage\csDicomToImage\bin\Debug\net9.0\image-00000.dcm 

轉換完成: C:\Vulcan\Github\CSharp2025\csDicomToImage\csDicomToImage\bin\Debug\net9.0\out.png