2017-04-05 187 views
-1

的簡單顯示我試圖讀取http://www.barre.nom.fr/medical/samples/名爲「CR-MONO1-10胸」一個簡單的DICOM文件,該文件是一個440x440大小的圖像。DICOM格式的文​​件圖像

作爲上http://www.dclunie.com/medical-image-faq/html/part1.html expained,圖像數據是在文件的結尾:

換句話說,如果圖像是由256 256,未壓縮的,和12-16位 深(並因此通常,但並非總是存儲爲兩個字節 每像素),那麼我們都知道,該文件將包含 256 * 256 * 2 = 131072字節像素數據在文件的結尾。如果 文件是說145408個字節長,因爲所有的GE Signa的3X/4X文件是 例如,那麼你需要跳過14336個字節的報頭的你到 數據之前。假設逐行開始左上角光柵 順序,嘗試字節順序兩種選擇,處理16至8位 窗口的問題,很快你有 工作站的屏幕上的圖像。

http://people.cas.sc.edu/rorden/dicom/index.html上的數字還表明圖像數據位於文件的末尾。

我使用下面的代碼在這個文件中讀取並顯示圖像:

(define in (open-input-file "CR-MONO1-10-chest" #:mode 'binary)) 
(define size (* 2 440 440))      ; width and ht are 440 
(define ignore (read-bytes (- 387976 size) in)) ; size of file is 387976 
(define imgdata (read-bytes size in)) 
(close-input-port in) 

(define img (make-object bitmap% imgdata 440 440)) 
img 

但是,它只能顯示黑白像素的隨機搭配:

enter image description here

使用440 * 440而不是2 * 440 * 440也不起作用。

下面的代碼也不會讀取圖像:

(define img (make-object bitmap% in 'unknown)) 

這不顯示任何圖像的。

問題在哪裏,我該如何解決?

+1

我對你使用的編碼語言並不熟悉,但你確定你是以16位顯示圖像嗎?也就是說,這是一個單通道16位圖像。還要注意,在這個圖像的頭部,它表示只有10位用於存儲數據 - 所以你應該掩蓋掉每個像素的其他6位 - 如果你忽略它,它可能仍然看起來很好,因爲額外的位可能(但不是保證)爲零。 – whiskeyspider

+1

球拍本身不支持DICOM圖像格式,因此您必須對其進行解碼。編寫整個解碼器不在Stack Overflow的範圍內,所以這個問題太廣泛了。 –

+0

我不想寫整個解碼器。我只是試圖顯示文件末尾的圖像數據。 – rnso

回答

3

您計算圖像數據正確地偏移,這數據似乎是原始和壓縮。問題似乎只是說球拍不支持這種圖像數據。這是16位單通道強度數據。每兩個字節表示一個灰度像素(實際上對於這個圖像只使用了10位,其他6位應該被忽略)。

make-objectmake-bitmap函數似乎只支持顏色(24或32)或單色(1)位深度。在你的例子中,這就是你跳出來的地方:你創建位圖時你沒有任何地方說明你的像素是16位的。也沒有關於字節順序的任何事情。在Racket文檔中沒有任何地方顯示它允許您指定其中任何一個。

的16位灰度數據支持缺乏在獲取深度函數documentation似乎很明顯:

(派位圖得到深度)→精確 - 非負整數? 獲取位圖,這對於一個單色位圖 和32的彩色位圖爲1的顏色深度。另見is-color ?.

下面是一個解決方案,循環並將每個16位像素轉換爲ARGB像素。

#lang racket/gui 
(define in (open-input-file "CR-MONO1-10-chest" #:mode 'binary)) 
(define size (* 2 440 440))      ; width and ht are 440 
(define ignore (read-bytes (- 387976 size) in)) ; size of file is 387976 
(define imgdata (read-bytes size in)) 
(close-input-port in) 

(define rgbdata (make-bytes (* 4 440 440))) 
(define img (make-object bitmap% 440 440)) 

(define max 1024.0) ; 10 bits valid 

(for ([y 440]) 
    (for ([x 440]) 
     (define index (+ (* y 440) x)) 
     (define b1 (bytes-ref imgdata (+ (* index 2) 0))) ; first byte 
     (define b2 (arithmetic-shift (bytes-ref imgdata (+ (* index 2) 1)) 8)) ; second byte 
     (define val (bitwise-xor b1 b2)) ; combine bytes 
     (define screenval (exact-floor (* 255 (/ val max)))) ; convert to 8-bit screen value 

     ; create ARGB pixel 
     (define b (bytes (bytes-ref (make-bytes 1 255) 0) (bytes-ref (make-bytes 1 screenval) 0) (bytes-ref (make-bytes 1 screenval) 0) (bytes-ref (make-bytes 1 screenval) 0)))  
     (send img set-argb-pixels x y 1 1 b) 
)) 

img 

生產:

enter image description here

一個值得注意的問題,但:要知道,有很多,很多方面的圖像數據可以存儲在DICOM文件(在頁面上你鏈接一些很好的例子)。並且DICOM頭部的許多部分需要注意正確解碼圖像數據。

+0

是的,它的工作原理。謝謝。我從這個文件中轉儲了初始字節,發現序列「DICM」不存在於128字節的偏移處。我認爲這是dicom文件的常見表現,但顯然這不是必需的。 (在初始128字節之後的「DICM」出現在該網頁上的其他文件中)。 – rnso

+0

@rnso字節128處的DICM是識別DICOM文件的方式。該文件是一個離羣值。它缺少序言和元文件信息塊。在字節128處找不到DICM非常罕見。 – whiskeyspider

3

問題是,球拍繪圖庫無法識別該圖像編碼。我建議將其轉換成32位ARGB:

(require racket/draw) 

(define (dicom->bitmap path x y) 
    (let* ([bmp   (make-object bitmap% x y)] 
     [dc    (send bmp make-dc)] 
     [dicom   (file->bytes path #:mode 'binary)] 
     [header-size (- (file-size path) (* 2 x y))] 
     [dicom-img/raw (subbytes dicom header-size)] 
     [dicom-img/argb (dicom-img->argb dicom-img/raw)]) 
    (send dc set-argb-pixels 0 0 x y dicom-img/argb) 
    (send dc get-bitmap))) 

(define (dicom-img->argb bytes) 
    (let* ([len   (bytes-length bytes)] 
     [pixel-count (/ len 2)] 
     [argb  (make-bytes (* 4 pixel-count))]) 
    (define (set-pixel! value ix) 
     (let ([offset (* 4 ix)]) 
     (bytes-set! argb offset 0) 
     (bytes-set! argb (+ 1 offset) value) 
     (bytes-set! argb (+ 2 offset) value) 
     (bytes-set! argb (+ 3 offset) value))) 
    (for ([ix (in-range pixel-count)]) 
     (let* ([offset  (* 2 ix)] 
      [pixel-value (+ (bytes-ref bytes offset) 
           (arithmetic-shift (bytes-ref bytes (+ 1 offset)) 8))] 
      [scaled-value (arithmetic-shift pixel-value -2)]) 
     (set-pixel! scaled-value ix))) 
    argb)) 

然後就可以調用它像這樣:

(dicom->bitmap "CR-MONO1-10-chest" 440 440) 

該特定節目僅適用於存儲在每個little- 2個字節10位/象素endian順序,但只要適度努力,您可以將其參數化爲其他編碼。

如果你有一個帶有多格式圖像的文件,所有這些文件最後都應該能夠將它們提取爲位圖列表。

(require racket/draw) 

(define (dicom->bitmap* path x y z) 
    (let* ([dicom   (file->bytes path #:mode 'binary)] 
     [img-size  (* 2 x y z)] 
     [header-size  (- (file-size path) img-size)] 
     [dicom-img/raw* (for/list ([z^ (in-range z)]) 
          (let* ([offset (+ header-size (* z^ img-size))] 
            [img-bytes (subbytes dicom offset (+ offset img-size))]) 
           img-bytes))]) 
    (map (λ (raw) (raw->bitmap raw x y)) dicom-img/raw*))) 

(define (raw->bitmap bytes x y) 
    (let* ([bmp    (make-object bitmap% x y)] 
     [drawing-context (send bmp make-dc)] 
     [dicom-img/argb (raw->argb bytes)]) 
    (send drawing-context set-argb-pixels 0 0 x y dicom-img/argb) 
    (send drawing-context get-bitmap))) 

(define (raw->argb bytes) 
    (let* ([len   (bytes-length bytes)] 
     [pixel-count (/ len 2)] 
     [argb  (make-bytes (* 4 pixel-count))]) 
    (define (set-pixel! value ix) 
     (let ([offset (* 4 ix)]) 
     (bytes-set! argb offset 0) 
     (bytes-set! argb (+ 1 offset) value) 
     (bytes-set! argb (+ 2 offset) value) 
     (bytes-set! argb (+ 3 offset) value))) 
    (for ([ix (in-range pixel-count)]) 
     (let* ([offset  (* 2 ix)] 
      [pixel-value (+ (bytes-ref bytes offset) 
           (arithmetic-shift (bytes-ref bytes (+ 1 offset)) 8))] 
      [scaled-value (arithmetic-shift pixel-value -2)]) 
     (set-pixel! scaled-value ix))) 
    argb)) 

其中z是圖像的數量。我只能用z = 1來測試它:(dicom->bitmap* "CR-MONO1-10-chest" 440 440 1)