📄 dicom_viewer.pro
字号:
; Replace existing tree with new tree
widget_control, (*s).wtree, /destroy
(*s).wtree = widget_tree((*s).tbase, $
xsize = geom.xsize, ysize = geom.ysize, $
event_pro = 'dicom_viewer_tree_event', uvalue = files)
; INC : Increment at which data array size is increased
inc = 100
ind = 0
sz = inc
; DICOM_INFO:
; 0 : Patient ID
; 1 : Study Instance UID
; 2 : Series Instance UID
; 3 : UID
; 4 : Patient Name
; 5 : Study
; 6 : Series
; 7 : Image
; 8 : Filename
dicom_info = strarr(9,sz)
for i = 0, n_elements(files)-1 do begin
catch, err
if err ne 0 then begin
catch, /cancel
continue
endif
odicom = obj_new('idlffdicomex', files[i], _extra = {no_pixel_data:1})
if ind eq (sz-1) then begin
; Need to increase DICOM_INFO size to accomidate more data
widget_control, (*s).dirlabel, get_value = tempstr
widget_control, (*s).dirlabel, set_value = tempstr + '.'
dicom_info = [[temporary(dicom_info)],[strarr(9,inc)]]
sz = sz + inc
endif
; Collect information:
dicom_info[0,ind] = odicom -> getvaluedefault('0010,0020', def) ; Patient ID
dicom_info[1,ind] = odicom -> getvaluedefault('0020,000D', def) ; Study Instance UID
dicom_info[2,ind] = odicom -> getvaluedefault('0020,000E', def) ; Series Instance UID
dicom_info[3,ind] = dicom_info[1,ind] + dicom_info[2,ind] + dicom_info[3,ind] ; UID
dicom_info[4,ind] = odicom -> getvaluedefault('0010,0010', def) ; Patient Name
dicom_info[5,ind] = odicom -> getvaluedefault('0020,0010', '') + $ ; Study
' ' + odicom -> getvaluedefault('0008,1030', '')
dicom_info[6,ind] = odicom -> getvaluedefault('0020,0011', '') + $ ; Series
' ' + odicom -> getvaluedefault('0008,103E', '')
dicom_info[7,ind] = odicom -> getvaluedefault('0020,0013', ''); Image
dicom_info[8,ind] = files[i]
ind++
obj_destroy, odicom
endfor
if ind eq 0 then begin
; No DICOM files found...
widget_control, (*s).dirlabel, set_value = $
'No Dicom files found in ' + file_dirname(files[0])
return, 0
endif
; Remove extra array elements and sort.
dicom_info = dicom_info[*,0:(ind-1)]
dicom_info = dicom_info[*,sort(dicom_info[3,*])]
seriesdata = lonarr(2,ind)
pat_name = (pat_id = (sty = (ser = '')))
for i = 0, ind-1 do begin
if (pat_id ne dicom_info[0,i]) || (pat_name ne dicom_info[4,i]) then begin
; New Patient
pat_id = dicom_info[0,i]
; pat_name = dicom_info[4,i]
wpat = widget_tree((*s).wtree, /folder, $
value = pat_id + ' : ' + dicom_info[4,i], $
uvalue = 'PATIENT')
endif
if (sty ne dicom_info[1,i]) || (pat_name ne dicom_info[4,i]) then begin
; New Study
sty = dicom_info[1,i]
wstudy = widget_tree(wpat, /folder, $
value = 'Study ' + dicom_info[5,i], $
uvalue = 'STUDY', /expanded)
endif
if (ser ne dicom_info[2,i]) || (pat_name ne dicom_info[4,i]) then begin
; New Series
ser = dicom_info[2,i]
wseries = widget_tree(wstudy, /folder, $
value = 'Series ' + dicom_info[6,i], $
uvalue = 'SERIES', /expanded)
endif
wimage = widget_tree(wseries, uvalue = dicom_info[8,i], $
value = 'Image ' + dicom_info[7,i] + ' ( ' + $
file_basename(dicom_info[8,i]) + ' ) ', $
uname = dicom_info[8,i])
seriesdata[*,i] = [wseries,wimage]
pat_name = dicom_info[4,i]
endfor
if file ne '' then begin
; Expand tree to select FILE in the tree widget.
wimage = widget_info((*s).wtree, find_by_uname = file)
if wimage gt 0 then $
widget_control, wimage, /set_tree_select, $
/set_tree_visible
endif
widget_control, (*s).dirlabel, set_value = file_dirname(files[0])
if ptr_valid((*s).seriesdata) then $
ptr_free, (*s).seriesdata
(*s).seriesdata = ptr_new(seriesdata, /no_copy)
widget_control, hourglass = 0
return, 1
end
;----------------------------------------------------------------------
; dicom_viewer_display_series
;+
; @returns Empty string if no error, otherwise returned string is
; error message.
;
; @param s {in}{type=pointer} Pointer to application state.
;-
function dicom_viewer_display_series, s
compile_opt idl2
WIDGET_CONTROL, /HOURGLASS
; Get the images associated with this series
wSeries = WIDGET_INFO((*s).wTree, /TREE_SELECT)
seriesData = *(*s).seriesData
idx = where(seriesData[0,*] eq wSeries, c)
if (c eq 0) then return, 'the series has no images' ; no images
wImages = reform(seriesData[1,idx])
if (n_elements(wImages) lt 3) then return, 'the series must contain at least 3 images' ; too few images
; Get DICOM objects for each image in the series
objs = objarr(n_elements(wImages))
for i=0, n_elements(wImages)-1 do begin
WIDGET_CONTROL, wImages[i], GET_UVALUE=fname
objs[i] = obj_new('IDLffDicomEx', fname[0])
endfor
; Check that all images have the same orientation
orient = double(objs[0]->GetValueDefault('0020,0037', ''))
if (orient[0] eq '') then begin
obj_destroy, objs ; image orientation patient tag not filled in
return, 'the image orientation patient tag is missing or empty'
endif
for i=1, n_elements(wImages)-1 do begin
t = double(objs[i]->GetValueDefault('0020,0037', ''))
if ~array_equal(orient,t) then begin
obj_destroy, objs ; at least one image doesn't fit
return, 'the images must all have the same orientation'
endif
endfor
; Check that all images have the same attributes
nframes = objs[0]->GetValueDefault('0028,0008',1)
rows = objs[0]->GetValueDefault('0028,0010',0)
cols = objs[0]->GetValueDefault('0028,0011',0)
bitsalloc = objs[0]->GetValueDefault('0028,0100',8)
pixelrep = objs[0]->GetValueDefault('0028,0103',0)
if (nframes ne 1) then begin
obj_destroy, objs
return, 'cannot make a volume from multiframe data' ; don't handle multiframe images
endif
attr = [nframes, rows, cols, bitsalloc, pixelrep]
for i=1, n_elements(wImages)-1 do begin
t = [objs[i]->GetValueDefault('0028,0008',1), $
objs[i]->GetValueDefault('0028,0010',0), $
objs[i]->GetValueDefault('0028,0011',0), $
objs[i]->GetValueDefault('0028,0100',8), $
objs[i]->GetValueDefault('0028,0103',0)]
if ~array_equal(attr, t) then begin
obj_destroy, objs
return, 'image attributes are not consistent within the series' ; attributes do not match
endif
endfor
; Extract all the image positions
ipp = dblarr(3, n_elements(wImages))
for i=0, n_elements(wImages)-1 do begin
ipp[*,i] = double(objs[i]->GetValueDefault('0020,0032', ''))
endfor
; Build the normal to the plane
norm = [orient[1]*orient[5] - orient[2]*orient[4], $
orient[2]*orient[3] - orient[0]*orient[5], $
orient[0]*orient[4] - orient[1]*orient[3]]
; Calculate the distance along the normal for each image and sort
d = dblarr(n_elements(wImages))
for i=0, n_elements(wImages)-1 do begin
d[i] = total(norm*ipp[*,i])
endfor
idx = sort(d)
d = d[idx] ; distances
wImages = wImages[idx] ; widget references
objs = objs[idx] ; DICOM objects
; Calculate the z spacing, this must be the same (within 10%) for all slices
dz = abs(d[0]-d[1]) ; mm
for i=1, n_elements(wImages)-1 do begin
diff = abs(dz - abs(d[i] - d[i-1]))
if (diff gt dz/10.0) then begin
obj_destroy, objs
return, 'image slices are not evenly spaced' ; slices are not evenly spaced (to within 10%)
endif
endfor
; Extract the image data now that the images are in order
vol = make_array(cols, rows, n_elements(wImages), $
TYPE=size(objs[0]->GetPixelData(),/TYPE))
for i=0, n_elements(wImages)-1 do begin
pix = objs[i]->GetPixelData(/ORDER)
if n_elements(size(pix,/DIM)) ne 2 then begin
obj_destroy, objs
return, 'unable to process non-monochrome images' ; only processing simple grayscale images
endif
vol[*,*,i] = pix
endfor
vol = bytscl(temporary(vol))
;
; Determine the actual volume size taking the in-plane
; pixel spacing and z spacing into account. This assumes
; square pixels (the most common case).
;
d = double(objs[0]->GetValueDefault('0028,0030','')) ; in-plane resolution (mm)
if (d[0] eq 0.0) then begin
obj_destroy, objs
return, 'image in-plane resolution tag missing or empty' ; this really should never happen, bad DICOM file
endif
dx = d[0]
dy = d[1]
;
; Rescale so that the largest dimension is n in length
;
n = 256
x = dx*cols
y = dy*rows
z = dz*n_elements(wImages)
order = sort([x,y,z])
case order[2] of
0: begin
newx = n
newy = long(n*y/x)
newz = long(n*z/x)
end
1: begin
newy = n
newx = long(n*x/y)
newz = long(n*z/y)
end
2: begin
newz = n
newx = long(n*x/z)
newy = long(n*y/z)
end
endcase
vol = congrid(temporary(vol), newx, newy, newz)
; Clean up since we no longer need the DICOM objects
obj_destroy, objs
; Pass the volume to iVolume to display
iVolume, vol, HINTS=2
return, ''
end
;----------------------------------------------------------------------
; dicom_viewer_menu
;+
; Handle menu choices.
;
; @param ev {in}{type=structure} Event structure.
;-
pro dicom_viewer_menu, ev
compile_opt logical_predicate, HIDDEN
; Get the uvalue identifying the menu choice.
WIDGET_CONTROL, ev.id, GET_UVALUE=uval
; Get the application state and current file.
WIDGET_CONTROL, ev.top, GET_UVALUE=s
WIDGET_CONTROL, (*s).wtable, GET_UVALUE=file
case uval of
'open': BEGIN
;
; Open a file
;
if size(file,/type) then path=file_dirname(file) else path=!dir
file=dialog_pickfile(TITLE='Please select file to open this directory', path=path, $
DIALOG_PARENT=ev.top)
if (file[0] eq '') then return
if n_elements(file) eq 1 then begin
file = file[0]
if file_test(file,/DIRECTORY) then dir = file $
else dir=file_dirname(file,/MARK_DIR)
files=file_search(dir+'*',/TEST_REGULAR)
; Update the list with all the files in the directory
endif else begin
files = file
file = file[0]
endelse
; Open and display the selected file
if dicom_viewer_open_file(file, s) then begin
WIDGET_CONTROL, (*s).wTree, SET_UVALUE=files
void = dicom_viewer_populate_tree(s, file)
endif
dicom_viewer_update_sensitivity, s
END
'opendir': begin
if size(file,/type) then path=file_dirname(file) else path=!dir
dir = dialog_pickfile(title = 'Please select a directory to open.', $
dialog_parent = ev.top, /directory, path = path)
if dir ne '' then begin
files = file_search(dir + '*')
widget_control, (*s).wtree, set_uvalue = files
widget_control, (*s).wtable, set_value=strarr(6,6)
(*s).image->idlgrimage::setproperty, data=bytarr(2,2)
(*s).view->setproperty, viewplane_rect=[0,0,2,2]
(*s).image -> setproperty, nframes = 0
(*s).win->draw
widget_control, (*s).wexport, sensitive=0
widget_control, ev.top, base_set_title = 'IDL Dicom Viewer'
dicom_viewer_update_sensitivity, s
if dicom_viewer_populate_tree(s, '') then $
widget_control, (*s).wtable, set_uvalue = files[0]
endif
end
'export': BEGIN
catch, err
if (err eq 0) then begin
outFile = dialog_pickfile(TITLE='Write DICOM tag info to text file', /WRITE, DIALOG_PARENT=ev.top)
if outFile eq '' then return
o = obj_new('IDLffDICOMEx',file,/non)
void = o->EnumerateTags(FILENAME=outFile, /QUIET)
catch, /CANCEL
endif else begin
catch, /CANCEL
WIDGET_CONTROL, (*s).wtable, SET_VALUE=strarr(6,6)
(*s).image->IDLgrImage::SetProperty, DATA=bytarr(2,2)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -