2017-04-05 43 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)