這個本來沒啥 不是什麼演算法 絕技。 都不值得一提。
其實這個是醫學影像膠片曝光時排版的一個邏輯。
dicom標準第三部分 主要是講IOD定義 在第166頁有這樣的描述:
表C.13.5-1圖象盒象素描述組件
屬性名稱 標記 說明
圖象位置 (2020,0010) 基於圖象顯示格式(2010,0010)的膠片的圖象位置。
參閱C.13.5.1的規範。
這個所謂的“基於圖象顯示格式(2010,0010)的膠片的圖象位置”到底是啥意思呢 ?還是像往常一樣拿個執行個體瞧瞧:
20 20 10 00 ............ ..
00000010 02 00 00 00 02 00
20 20 10 00 ............ ..
00000010 02 00 00 00 03 00
dicom資料元素的結構就不多說了哈 詳情請查看dicom標準。只看最後兩位,可見這個所謂的“基於圖象顯示格式(2010,0010)的膠片的圖象位置” 就是一個個的從前到後的排序。從裝置工作站發送膠片曝光作業的時候 根據dicom協議 映像傳輸 那麼它就是以這種 “序列”的方式發送的。
再看dicom標準另外一段 第三部分167頁 :
C.13.5.1圖象位置
膠片中圖象的位置;圖象位置序列的編碼是基於選擇的圖象顯示格式
(2010,0010)。圖象位置序列以值1遞增。
圖象位置(2020,0010)定義如下:
- 標準顯示格式:圖象盒序列是主要行順序(從左至右或從上到下);左象位置等於1。
- 行顯示格式:圖象盒序列是主要行順序(從左至右或從上到下);左象位置等於1。
- 列顯示格式:圖象盒序列是主要列順序(從左至右或從上到下);左象位置等於1。
- SLIDE顯示格式:圖象盒序列是主要行順序(從左至右或從上到下);左象位置等於1。
- SUPERSLIDE顯示格式:圖象盒序列是主要行順序(從左至右或從上到下);左象位置等於1。
關於ImgBox 的幾種方式的細節我們暫時不去管 只管第一種 ,因為就我遇到的 90%以上都是 “標準顯示格式”在膠片曝光dimse的過程中 createFilmBox 的時候 會收到一個參數 command元素0x20100010, 它的值是類似這樣STANDARD\1,1 這個standard 就是表示 這是一個“標準顯示格式” 逗號分隔開的兩個數字 第一個是列數 第二個是行數。標準顯示格式就是: (在一個指定行數列數的網格裡把映像按從左至右從上到下的方式排列)
說到底我們要實現的就是根據dicom標準 把這種依次編號的映像拼接成 一個整幅的網格映像 讓它列印出來看上去跟工作站上閱片時的所見即所得 (WYSIWYG),可以根據編號擷取指定映像 可以根據幾行幾列或許指定映像 可以根據編號獲知他是第幾行第幾列 可以根據第幾行第幾列獲知他的編號。就這樣簡單。
來簡單分析下,可以通過STANDARD\colNum,rowNum 來確定行數跟列數。這就容易了
他是一個“持續堆疊的過程”
第一行堆滿了堆第二行 從左往右的不斷堆。
以x表示列 y表示行 從1開始,num表示編號 從1開始 , 可以得出這樣的結論:
for(1 to num)
{
if(num%colNum!=0)//不能除盡
{
y=num/colNun+1;
x=num%colNum;
else
{
y=num/colNun;
x=colNum;
}
}
你能理解這個過程麼 ?什麼, 能用麼 你可以驗證下:
num=7
7%3!=0
{
y=7/3+1=>3
x=7%3=>1
}
num=6
6%3==0
{
y=6/3=>2
x=3=>3
}
num=5
5%3!=0
{
y=5/3+1=>3
x=5%3=>2
}
為什麼要確定x跟y呢,因為輸出的時候要給單幅映像定位 就是左上方 有了x跟y 才能夠實現。可以有一個image的數組 用來儲存接收到的映像 它們按照編號從前到後排列。
解決一個問題了
還有另外一個問題 映像的縮放。
如果我假設輸出地區是寬度=297 高度=420。膠片的分格是4行3列 。那麼單幅映像的尺寸是 寬度=297/3 高度=420/4 但是每個分格的映像尺寸通常是不固定的,就像普通的看圖軟體都有個顯示比例叫“縮放到顯示地區” 我們要做的就是這個 這一過程可以用這個圖說明:
預先就設定映像都有兩種等比例縮放尺寸 高度對齊(讓高度跟可顯示地區相等) 寬度對齊(讓寬度跟可顯示地區相等)。
這裡有一個簡單的等比例公式 縮放前後
寬度對齊情況下:
顯示地區寬(縮放後的寬度)/原圖寬=縮放後的高度/原圖高
高度對齊情況下:
顯示地區高(縮放後的高度)/原圖高=縮放後的寬度/原圖寬
分別得出兩種情況另一邊的長度
顯示地區寬(縮放後的寬度)*原圖高/原圖寬=縮放後的高度
顯示地區高(縮放後的高度)*原圖寬/原圖高=縮放後的寬度
然後對兩種情況進行判斷,如果哪種情況縮放後的映像地區超出了顯示範圍 則被否決。
如 藍色框代表映像 ,黑色框代表顯示地區:
就這樣就達到了適應顯示範圍的等比例縮放。簡單吧。
怎麼把上述思路整合 然後用代碼實現,首先我們得定義一個類 這個類叫Paper 相當於一張膠片 他是待列印範圍 。
然後相應的要有 行數列數 顯示地區高度寬度 等變數, 還要有一個image數組用來儲存順序編號排列的映像。
1 public class Paper 2 { 3 int row, col;//行數 與 列數 4 int width, height;//膠片寬度 高度 5 IList<Image> images;//順序編號的映像 6 7 public Paper(int _width, int _height, int _row, int _col) 8 { 9 width = _width; height = _height; row = _row; col = _col;10 images = new List<Image>(row * height);11 }12 //初始化顯示地區13 public Paper(int _row, int _col)14 {15 //width = 345 * 9; height = 420 * 9; row = _row; col = _col;//14INx17IN16 width = 297 * 9; height = 420 * 9; row = _row; col = _col;//A317 images = new List<Image>(row * height);18 }19 //新增映像20 public void addImg(Image img)21 {22 if (images.Count >= row * col)23 return;24 else25 {26 images.Add(img);27 }28 }29 }
這相當於又是定義了一個資料模型的架構 初始化一個執行個體代表輸出一張新的膠片。物件導向的分析設計是多麼的好 哇哈哈。
材料有了 。下面這個函數才是重頭戲,用於實現第一部分 分析的所有邏輯[膠片排版 跟 映像縮放],請對照第一部分的說明來看:
1 //排版後的輸出 2 //排版方式為從上到下從左至右 3 public Image layout() 4 { 5 Image layouted = new Bitmap(width, height); 6 7 Graphics g = Graphics.FromImage(layouted); 8 g.Clear(Color.Green); 9 for (int i = 0; i < images.Count; i++)10 {11 int _row, _col;12 if ((i + 1) % col != 0)13 {14 _row = (i + 1) / col + 1 - 1; // _row = (i + 1) / col + 1 - 1;15 _col = (i + 1) % col - 1; // _col = (i + 1) / col - 1;16 }17 else18 {19 _row = (i + 1) / col - 1;20 _col = col - 1;21 }22 if (_col < 0)23 {24 _col = 0;25 }26 if (_row < 0)27 {28 _row = 0;29 }30 GraphicsUnit u = GraphicsUnit.Pixel;31 RectangleF recSrc = images[i].GetBounds(ref u);//原映像大小32 RectangleF recDst;//縮放後大小並調整位移位置33 34 //寬比寬 =長比長 超出的那一邊需要固定長度 縮小以調整到視野內35 float recH = ((width / col) * recSrc.Height) / recSrc.Width,36 recW = ((height / row) * recSrc.Width) / recSrc.Height;37 38 if (recW > (width / col))//寬度超出 固定寬度調整高度39 recDst = new RectangleF((width / col) * _col + 0,40 (height / row) * _row + ((height / row) - recH) / 2,41 (width / col), recH);42 else//高度超出 固定高度調整寬度43 recDst = new RectangleF((width / col) * _col + ((width / col) - recW) / 2,44 (height / row) * _row + 0,45 recW, (height / row));46 47 g.DrawImage(images[i], recDst, images[i].GetBounds(ref u), u);48 //if (i == 4)49 images[i].Save(i + "out.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);50 }51 52 //layouted.Save("layout.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);53 g.Dispose();54 55 return layouted;56 }
調用:
1 Paper film = new Paper(3, 2);2 film.addImg(Image.FromFile("1.jpg"));3 film.addImg(Image.FromFile("2.jpg"));4 film.addImg(Image.FromFile("3.jpg"));5 film.addImg(Image.FromFile("4.jpg"));6 film.addImg(Image.FromFile("5.jpg"));7 film.addImg(Image.FromFile("6.jpg"));8 9 film.layout();
一直說想要講下dicom協議的通訊 ,看來下次吧。
源碼及測試檔案下載猛擊此處