简单显示DICOM格式文件图像

时间:2017-04-05 17:52:26

标签: image scheme racket dicom

我正在尝试阅读一个简单的dicom文件名为" CR-MONO1-10-chest"来自http://www.barre.nom.fr/medical/samples/,这是一张440x440大小的图像。

http://www.dclunie.com/medical-image-faq/html/part1.html所示,图像数据位于文件的末尾:

  

换句话说,如果图像是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))

这根本不显示任何图像。

问题在哪里,我该如何解决?

2 个答案:

答案 0 :(得分:3)

您正在正确计算图像数据偏移,此数据似乎是原始和未压缩的。问题似乎只是Racket不支持这种图像数据。这是16位单通道强度数据。每两个字节代表一个灰度级的像素(实际上,对于该图像,只使用10位,其他6位应该被忽略)。

make-object make-bitmap函数似乎仅支持颜色(24或32)或单色(1)位深度。这就是你的例子中跳出来的东西:当你创建位图时,你没有说明像素是16位。还缺少有关字节顺序的任何内容。并且在Racket文档中没有任何地方显示它允许您指定其中任何一个。

在获取深度函数documentation中似乎缺乏对16位灰度数据的支持:

  

(发送位图get-depth)→exact-nonnegative-integer?   获取位图的颜色深度,为单色位图为1   和32为彩色位图。另见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标头的许多部分以正确解码图像数据。

答案 1 :(得分:3)

问题是Racket绘图库无法识别图像编码。我建议将其转换为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)

此特定程序仅适用于以小端顺序存储在2个字节中的10位/像素,但只需稍加努力就可以将其参数化以用于其他编码。

如果你有一个包含该格式的多个图像的文件,最后,该程序应该能够将它们作为位图列表提取出来。

(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)