C#開發PACS醫學影像處理系統(十五):Dicom影像交叉定位線演算法

喬克灬叔叔發表於2020-09-17

1.定位線概念:某個方位的影像在另一個方向的影像上的投影相交線,例如橫斷面(從頭到腳的方向)在矢狀面(從左手到右手)上的影像投影面交線。

舉個例子:右邊的是MR(核磁共振)的某一幀切片,這是從頭開始掃描,掃描到眼睛這個位置,

而左邊影像是從左手到右手的掃描切片,那麼右邊影像的位置就恰好在左邊影像的眼睛的位置,用紅線標出的位置則為定位線,一般用於醫生參考病灶在矢狀面、冠狀面和橫斷面的具體方位。

 

2.檢查裝置示意圖

在笛卡爾空間直角座標系中Y 右肩膀到左肩膀,X 後背到前胸,Z 足到頭

 

 

 

3.演算法流程

 

 

 4.程式碼實現:

建立影像結構

public FrameGeometry(DicomDataset image)
            : this(image.GetString(DicomTag.FrameOfReferenceUID),
                  image.GetValues<double>(DicomTag.ImagePositionPatient),
                  image.GetValues<double>(DicomTag.ImageOrientationPatient),
                  image.GetValues<double>(DicomTag.PixelSpacing),
                  image.GetSingleValue<int>(DicomTag.Columns),
                  image.GetSingleValue<int>(DicomTag.Rows))
        {
            // TODO: 
           FrameOfReferenceUID:圖片UID
           ImagePositionPatient:病人方向
           ImageOrientationPatient:圖片方向
           PixelSpacing:畫素間距
           Columns:寬度
           Rows:高度
}

判斷是否符合定位條件:

     /// <summary>/// 判斷是否可以畫定位線
        /// </summary>
        /// <param name="sourceFrame">源影像結構</param>
        /// <param name="destinationFrame">目標影像結構</param>
        /// <returns></returns>
        public static bool CanDrawLocalizer(FrameGeometry sourceFrame, FrameGeometry destinationFrame)
        {
            // 檢查影像結構
            if (sourceFrame == null) return false;
            if (destinationFrame == null) return false;

            // 如果兩個幀中的任何一個不是空間座標系,則無法定位
            if (sourceFrame.Orientation == FrameOrientation.None || destinationFrame.Orientation == FrameOrientation.None) return false;
            // 只有正交影像才能繪製,方向相同則退出
            if (sourceFrame.Orientation == destinationFrame.Orientation) return false;

            // 檢查FrameOfReferenceUid
            if (string.IsNullOrEmpty(sourceFrame.FrameOfReferenceUid) || string.IsNullOrEmpty(destinationFrame.FrameOfReferenceUid)) return false;
            if (sourceFrame.FrameOfReferenceUid != destinationFrame.FrameOfReferenceUid) return false;

            return true;
        }

計算交線點:

     /// <summary>
        ///回兩個影像相交處公共畫素線
        /// </summary>
        /// <param name="sourceFrame">源影像結構</param>
/// <param name="destinationFrame">目標影像結構</param>
/// <param name="startPoint">起點輸出</param> /// <param name="endPoint">重點輸出</param> /// <returns></returns> public static bool CalcualteIntersectionLocalizer(FrameGeometry sourceFrame, FrameGeometry destinationFrame, out Point2 startPoint, out Point2 endPoint) { double t; // 平面方程係數 double nA, nB, nC, nD, nP; var lstProj = new List<Point3D>(); // 初始化 startPoint = Point2.Origin; endPoint = Point2.Origin; // 驗證 if (destinationFrame.DirectionNormal.IsZero) return false; nP = destinationFrame.DirectionNormal * destinationFrame.PointTopLeft; nA = destinationFrame.DirectionNormal * sourceFrame.PointTopLeft; nB = destinationFrame.DirectionNormal * sourceFrame.PointTopRight; nC = destinationFrame.DirectionNormal * sourceFrame.PointBottomRight; nD = destinationFrame.DirectionNormal * sourceFrame.PointBottomLeft; // AB if (Math.Abs(nB - nA) > Constants.Epsilon) { t = (nP - nA) / (nB - nA); if (t > 0 && t <= 1) lstProj.Add(sourceFrame.PointTopLeft + t * (sourceFrame.PointTopRight - sourceFrame.PointTopLeft)); } // BC if (Math.Abs(nC - nB) > Constants.Epsilon) { t = (nP - nB) / (nC - nB); if (t > 0 && t <= 1) lstProj.Add(sourceFrame.PointTopRight + t * (sourceFrame.PointBottomRight - sourceFrame.PointTopRight)); } // CD if (Math.Abs(nD - nC) > Constants.Epsilon) { t = (nP - nC) / (nD - nC); if (t > 0 && t <= 1) lstProj.Add(sourceFrame.PointBottomRight + t * (sourceFrame.PointBottomLeft - sourceFrame.PointBottomRight)); } // DA if (Math.Abs(nA - nD) > Constants.Epsilon) { t = (nP - nD) / (nA - nD); if (t > 0 && t <= 1) lstProj.Add(sourceFrame.PointBottomLeft + t * (sourceFrame.PointTopLeft - sourceFrame.PointBottomLeft)); } if (lstProj.Count != 2) return false; // 從空間座標系返回平面座標系 startPoint = destinationFrame.TransformPatientPointToImage(lstProj[0]); endPoint = destinationFrame.TransformPatientPointToImage(lstProj[1]); return true; }

得到座標之後就可以利用繪圖操作類(參考本系列教程之圖形標記)來自己繪製定位線。

看效果:

 

 

對於較複雜的身體部位,也可以同時繪製定位線範圍,來確定當前序列的定位範圍,思路是先計算第一幀和最後一幀,用黃色虛線標出,再計算當前幀。

看效果:

 

C#開發PACS、RIS醫學影像處理系統

目錄整理:

(一)PACS客戶端:

C#開發PACS醫學影像處理系統(一):開發背景和功能預覽

C#開發PACS醫學影像處理系統(二):介面佈局之選單欄

C#開發PACS醫學影像處理系統(三):介面佈局之工具欄

C#開發PACS醫學影像處理系統(四):介面佈局之狀態列

C#開發PACS醫學影像處理系統(五):查詢病人資訊列表

C#開發PACS醫學影像處理系統(六):載入Dicom影像

C#開發PACS醫學影像處理系統(七):讀取影像Dicom資訊

C#開發PACS醫學影像處理系統(八):單元格變換

C#開發PACS醫學影像處理系統(九):序列控制元件與拖拽

C#開發PACS醫學影像處理系統(十):Dicom影像下載策略與演算法

C#開發PACS醫學影像處理系統(十一):Dicom影像掛片協議

C#開發PACS醫學影像處理系統(十二):繪圖處理之圖形標記

C#開發PACS醫學影像處理系統(十三):繪圖處理之病灶測量

C#開發PACS醫學影像處理系統(十四):處理Dicom影像窗寬窗位

C#開發PACS醫學影像處理系統(十五):Dicom影像交叉定位線演算法

C#開發PACS醫學影像處理系統(十六):2D處理之平移和縮放

C#開發PACS醫學影像處理系統(十七):2D處理之任意角度旋轉與映象翻轉

C#開發PACS醫學影像處理系統(十八):Dicom影像色彩增強(偽彩)

C#開發PACS醫學影像處理系統(十九):Dicom影像反色處理(負片)

C#開發PACS醫學影像處理系統(二十):Dicom影像放大鏡功能

 

(二)PACS三維:MRP、MIP、VR

C#開發PACS醫學影像三維重建(一):使用VTK三維重建Dicom影像

 

(三)PACS網頁端:開發Web版本的PACS

C#開發Web端PACS(一):基於PACS客戶端思想重寫Web端

 

(四)PACS移動端:開發基於HTML5移動端版本的PACS

C#開發移動端PACS(一):使用HTML5和CSS3開發PACS手機端頁面

C#開發移動端PACS(二):使用 .Net MVC 開發手機端PACS服務端

 

(五)PACS服務端:

C#開發PACS醫學影像處理系統服務端(一):醫療裝置的連線與收圖

C#開發PACS醫學影像處理系統服務端(二):高併發架構

 

(六)PACS與RIS系統的通訊與整合

在RIS系統中調起PACS並開啟Dicom影像

 

(七)雲PACS與遠端會診

C#開發PACS醫學影像處理系統之雲PACS(區域PACS)(一):架構概述

C#開發PACS醫學影像處理系統之雲PACS(區域PACS)(二):遠端會診與雙向轉診

 

(八)科幻級視訊特效:使用Adobe After Effects 製作PACS影像處理系統宣傳視訊

 

QQ:1850969244

近10年開發經驗,主攻C#、ASP MVC,HTML5,

B/S C/S 皆可,目前研究醫療領域醫學影像相關技術,

任何技術問題歡迎加QQ交流。

相關文章