The source to the main section of the program is contained in this appendix. The complete program also has a routine to load a LumiscanTM image into memory (see appendix B) and the bitmaps files containing the graphics used for button labels (see appendix C).
<sandra.pro>
;======================================================================
;
; Sandra -- a portal image processing system written in IDL
;
; Written by Jonathan Buzzard. (last update 12/08/96)
;
; S - System for
; A - Analysing
; N - Normal
; D - Displacements in
; R - Radiotherapy
; A - Alignment
;
;======================================================================
;
; The next three routines take care of the events being dispatched by
; XMANAGER, performing all the necessary actions.
;
;
; Handle the events generated by the main application
;
PRO SandraEventHdlr, event
WIDGET_CONTROL, GET_UVALUE=control, event.id
IF (STRPOS(control, "LEFT") EQ 0) THEN BEGIN
iWhich = 1
sAction = STRMID(control, 4, STRLEN(control)-4)
control = "ACTION"
ENDIF
IF (STRPOS(control, "RIGHT") EQ 0) THEN BEGIN
iWhich = 2
sAction = STRMID(control, 5, STRLEN(control)-5)
control = "ACTION"
ENDIF
CASE control OF
"ABOUT": BEGIN
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
WIDGET_CONTROL, State.InfoText, SET_VALUE="System for Analysing"$
+" Normal Displacements in Radiotherapy Alignment"$
+" Version 1.1 (09/08/1996)"
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
ENDCASE
"EXIT": WIDGET_CONTROL, event.top, /DESTROY
"ACTION" : CALL_PROCEDURE, sAction+"_EVENT", event.top, iWhich
"IMAGE_LEFT": BEGIN
; Marking control points on the images
IF event.press NE 0 THEN RETURN
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
; if mouse press in the correct image add a control point
IF (NOT (State.ControlPtCount AND 1)) THEN BEGIN
WSET, State.LeftImgHdl
PLOTS, event.x, event.y, /DEVICE, COLOR=!D.TABLE_SIZE-1, PSYM=6
InfoTxt = STRING(FORMAT='("Fiducial Pair:",I2," Left Point:",I4,",",I4)',$
1+State.ControlPtCount/2, event.x, event.y)
WIDGET_CONTROL, State.InfoText, SET_VALUE=InfoTxt
WIDGET_CONTROL, State.LeftCtrlPts, GET_UVALUE=leftpts
leftpts = [leftpts, event.x, event.y]
State.ControlPtCount = State.ControlPtCount+1
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=leftpts
ENDIF ELSE BEGIN
WIDGET_CONTROL, State.InfoText, SET_VALUE="Click in the right-hand"$
+" image to select the corresponding point"
ENDELSE
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
ENDCASE
"IMAGE_RIGHT": BEGIN
; Marking control points on the images
IF event.press NE 0 THEN RETURN
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
; if mouse press in the correct image add a control point
IF (State.ControlPtCount AND 1) THEN BEGIN
WSET, State.RightImgHdl
PLOTS, event.x, event.y, /DEVICE, COLOR=!D.TABLE_SIZE-1, PSYM=6
InfoTxt = STRING(FORMAT='("Fiducial Pair:",I2," Right Point:",I4,",",I4)',$
1+State.ControlPtCount/2, event.x, event.y)
WIDGET_CONTROL, State.InfoText, SET_VALUE=InfoTxt
WIDGET_CONTROL, State.RightCtrlPts, GET_UVALUE=rightpts
rightpts = [rightpts, event.x, event.y]
State.ControlPtCount = State.ControlPtCount+1
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=rightpts
ENDIF ELSE BEGIN
WIDGET_CONTROL, State.InfoText, SET_VALUE="Click in the left-hand"$
+" image to select a primary fiducial point"
ENDELSE
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
ENDCASE
ELSE: CALL_PROCEDURE, control+"_EVENT", event.top
ENDCASE
END
;
; Handle the events generated by the histogram dialog
;
PRO HistEventHdlr, event
WIDGET_CONTROL, GET_UVALUE=control, event.id
CASE control OF
"CANCEL": WIDGET_CONTROL, event.top, /DESTROY
"EQUALIZE" : BEGIN
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
WIDGET_CONTROL, State.ImageStore, GET_UVALUE=iImage
; do the actual histogram equalization
iImage = HIST_EQUAL(TEMPORARY(iImage), MINV=State.LowCut,$
MAXV=State.HighCut)
WSET, State.ImageDraw
TVSCL, iImage
WIDGET_CONTROL, State.ImageStore, SET_UVALUE=iImage
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
WIDGET_CONTROL, event.top, /DESTROY
ENDCASE
"SLIDE_LOW": BEGIN
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
iNewCut = event.value
; stop low cutoff from becoming greater than high cutoff
IF (iNewCut GE State.HighCut) THEN BEGIN
iNewCut=State.HighCut-1
WIDGET_CONTROL, event.id, SET_VALUE=State.HighCut-1
ENDIF
bTemp = State.HistImage(iNewCut,*)
State.HistImage(iNewCut,0:127) = 200
State.HistImage(State.LowCut,*)=State.LowCutStore
State.LowCut = iNewCut
State.LowCutStore = bTemp
WSET, State.HistDraw
TV, State.HistImage
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
ENDCASE
"SLIDE_HIGH": BEGIN
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
iNewCut = event.value
; stop high cutoff from becoming less than low cutoff
IF (iNewCut LE State.LowCut) THEN BEGIN
iNewCut=State.LowCut+1
WIDGET_CONTROL, event.id, SET_VALUE=State.LowCut+1
ENDIF
bTemp = State.HistImage(iNewCut,*)
State.HistImage(iNewCut,0:127) = 200
State.HistImage(State.HighCut,*)=State.HighCutStore
State.HighCut = iNewCut
State.HighCutStore = bTemp
WSET, State.HistDraw
TV, State.HistImage
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
ENDCASE
ENDCASE
END
;
; Handle the events generated by the Fader dialog
;
PRO FaderEventHdlr, event
WIDGET_CONTROL, GET_UVALUE=control, event.id
CASE control OF
"DISMISS": WIDGET_CONTROL, event.top, /DESTROY
"SLIDE_FADE" : BEGIN
WIDGET_CONTROL, event.top, /HOURGLASS
WIDGET_CONTROL, event.top, GET_UVALUE=State, /NO_COPY
; get the two images
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iRightImage, /NO_COPY
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iLeftImage, /NO_COPY
; combine the two images at the appropriate fading
WSET, State.ImageDraw
TVSCL, event.value*FIX(iRightImage(0:state.XSize-1,0:State.YSize-1))+$
(8-event.value)*FIX(iLeftImage(0:state.XSize-1,0:State.YSize-1))
; store the two images back in there user values
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iRightImage, /NO_COPY
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iLeftImage, /NO_COPY
WIDGET_CONTROL, event.top, SET_UVALUE=State, /NO_COPY
ENDCASE
ENDCASE
END
;
; Clean up when Sandra exits; ie. restore colour table
;
PRO CleanUpSandra, wSandraWindow
; Get the color table saved in the window's user value
WIDGET_CONTROL, wSandraWindow, GET_UVALUE=SandraState
TVLCT, SandraState.ColourTable
END
;
; calculate the affine transformation and apply it
;
PRO affine_event, child
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
; need same number of points in each window and at least 2 pairs
IF (State.ControlPtCount LT 4) THEN BEGIN
WIDGET_CONTROL, State.InfoText,$
SET_VALUE="Need at least two fiducial points to calculate tranformation"
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
RETURN
ENDIF
IF (State.ControlPtCount AND 1) THEN BEGIN
WIDGET_CONTROL, State.InfoText,$
SET_VALUE="Need same number of fiducial points in each window"
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
RETURN
ENDIF
WIDGET_CONTROL, State.LeftCtrlPts, GET_UVALUE=leftpts, /NO_COPY
WIDGET_CONTROL, State.RightCtrlPts, GET_UVALUE=rightpts, /NO_COPY
; the transformation calculated is the one to bring the
; right hand image into alignment with the left and image.
; IT DOES NOT represent the changes to be made on the treatment or
; simulator machines to bring the patient into the correct position.
A = DBLARR(4,State.ControlPtCount)
FOR iLoop=0,State.ControlPtCount-1,2 DO BEGIN
a(0,iLoop) = DOUBLE(leftpts(2+iLoop))
a(1,iLoop) = -1.0D*DOUBLE(leftpts(3+iLoop))
a(2,iLoop) = 1.0D
a(3,iLoop) = 0.0D
a(0,iLoop+1) = DOUBLE(leftpts(3+iLoop))
a(1,iLoop+1) = DOUBLE(leftpts(2+iLoop))
a(2,iLoop+1) = 0.0D
a(3,iLoop+1) = 1.0D
ENDFOR
B = DBLARR(State.ControlPtCount)
FOR iLoop=0, State.ControlPtCount-1 DO BEGIN
b(iLoop) = DOUBLE(rightpts(2+iLoop))
ENDFOR
SVDC, A, w, u, v, /DOUBLE
n = N_ELEMENTS(w)
wp = DBLARR(n, n)
FOR iLoop=0,n-1 DO $
IF (ABS(w(iLoop)) GE 1.0D-7) THEN wp(iloop, iloop) = 1.0D/w(iloop)
x = v ## wp ## TRANSPOSE(u) ## b
; decompose the transformation into its seperate components.
; ONLY the rotation represents the change needed to patient
; setup. If you know the magnification of the two images you
; can work out the vertical movement needed to the table.
; If you know the coordinates of the beam centre (isocentre),
; and the pixel size, you can workout the lateral and
; longditude movements of the table to correct patient setup.
radeg = -57.29577951D
theta = ATAN(x(1),x(0))
AffText1 = STRING(FORMAT='("Rotation: ",F8.4," Scale: ",F8.4)',$
radeg*theta,SIN(theta)/x(1))
AffText2 = STRING(FORMAT='(" Translation: x=",F6.2," y=",F6.2)',$
x(2), x(3))
WIDGET_CONTROL, State.InfoText, SET_VALUE=AffText1+Afftext2
; generate the matrix holding the transformation polynomial
p = [x(2), -x(1), x(0), 0.0D]
q = [x(3), x(0), x(1), 0.0D]
; transform the image using cubic interpolation
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
iImage = POLY_2D(TEMPORARY(iImage), p, q, 2, MISSING=0)
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
; transform the control points (first need to invert matrix)
invtran = INVERT([[x(0),-x(1)], [x(1),x(0)]], /DOUBLE)
FOR iLoop=0,State.ControlPtCount-1,2 DO BEGIN
xx = rightpts(2+iLoop)
yy = rightpts(3+iLoop)
rightpts(2+iLoop) = xx*invtran(0)+yy*invtran(1)-x(2)
rightpts(3+iLoop) = xx*invtran(2)+yy*invtran(3)-x(3)
ENDFOR
; redraw the control points (add left points for comparison)
FOR iLoop=0,State.ControlPtCount-1,2 DO BEGIN
PLOTS, rightpts(2+iLoop), rightpts(3+iLoop), /DEVICE,$
COLOR=!D.TABLE_SIZE-1, PSYM=6
PLOTS, leftpts(2+iLoop), leftpts(3+iLoop), /DEVICE,$
COLOR=!D.TABLE_SIZE-1, PSYM=6
ENDFOR
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=rightpts, /NO_COPY
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=leftpts, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Histogram equalise an image
;
PRO histequ_event, child, iWhich
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
IF (iWhich EQ 1) THEN BEGIN
hImageDraw = State.LeftImgHdl
hImageStore = State.LeftImgStore
ENDIF ELSE BEGIN
hImageDraw = State.RightImgHdl
hImageStore = State.RightImgStore
ENDELSE
WIDGET_CONTROL, hImageStore, GET_UVALUE=iImage, /NO_COPY
; generate the histogram as an image
iHist = HISTOGRAM(iImage, BINSIZE=1)
fFact = 127.0/FLOAT(MAX(iHist(1:254)))
iHistImg = BYTARR(256,128)
FOR iLoop=1,254 DO BEGIN
iHistImg(iLoop,0:iHist(iLoop)*fFact)=255
ENDFOR
; put the information stored in user values back
WIDGET_CONTROL, hImageStore, SET_UVALUE=iImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
; create dialog box for histogram equalization
wHistWindow = WIDGET_BASE(TITLE="Histogram Equalization")
wHistBase = WIDGET_BASE(wHistWindow, /COLUMN)
wHistDraw = WIDGET_DRAW(wHistBase, XSIZE=256, YSIZE=128, RETAIN=2)
wHistLowLabel = WIDGET_LABEL(wHistBase, VALUE="Low cut-off point",$
/ALIGN_LEFT)
wHistLow = WIDGET_SLIDER(wHistBase, /SUPPRESS_VALUE, MINIMUM=0,$
MAXIMUM=255, VALUE=0, UVALUE="SLIDE_LOW")
wHistHighLabel = WIDGET_LABEL(wHistBase, VALUE="High cut-off point",$
/ALIGN_LEFT)
wHistHigh = WIDGET_SLIDER(wHistBase, /SUPPRESS_VALUE, MINIMUM=0,$
MAXIMUM=255, VALUE=255, UVALUE="SLIDE_HIGH")
wHistButtonBase = WIDGET_BASE(wHistBase, /ROW)
wHistEqual = WIDGET_BUTTON(wHistButtonBase, VALUE="Equalize",$
UVALUE="EQUALIZE")
wHistCancel = WIDGET_BUTTON(wHistButtonBase, VALUE="Cancel",$
UVALUE="CANCEL")
; realize the histogram dialog box
WIDGET_CONTROL, /REALIZE, wHistWindow
; display the histogram
WIDGET_CONTROL, wHistDraw, GET_VALUE=hHistDraw
WSET, hHistDraw
TV, iHistImg
; store the histogram state inforamtion in user value of the dialog
HistState = CREATE_STRUCT('LowCut', 0)
HistState = CREATE_STRUCT(HistState, 'LowCutStore', BYTARR(128))
HistState = CREATE_STRUCT(HistState, 'HighCut', 255)
HistState = CREATE_STRUCT(HistState, 'HighCutStore', BYTARR(128))
HistState = CREATE_STRUCT(HistState, 'HistImage', iHistImg)
HistState = CREATE_STRUCT(HistState, 'HistDraw', hHistDraw)
HistState = CREATE_STRUCT(HistState, 'ImageStore', hImageStore)
HistState = CREATE_STRUCT(HistState, 'ImageDraw', hImageDraw)
WIDGET_CONTROL, wHistWindow, SET_UVALUE=HistState, /NO_COPY
; register dialog box with XMANAGER as modal
XMANAGER, "Histogram", wHistWindow, EVENT_HANDLER="HistEventHdlr",$
/MODAL
END
;
; Pop up a dialog box to fade between the two images
;
PRO fade_event, child
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iRightImage, /NO_COPY
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iLeftImage, /NO_COPY
; find the sizes of the two images
rightSize=SIZE(iRightImage)
leftSize=SIZE(iLeftImage)
; select the smaller x and y sizes for image addition
IF (rightsize(1) LE leftsize(1)) THEN xx=rightsize(1) ELSE xx=leftsize(1)
IF (rightsize(2) LE leftsize(2)) THEN yy=rightsize(2) ELSE yy=leftsize(2)
; create dialog box for fading between two images
wFadeWindow = WIDGET_BASE(TITLE="Fader")
wFadeBase = WIDGET_BASE(wFadeWindow, /COLUMN)
wFadeDraw = WIDGET_DRAW(wFadeBase, XSIZE=xx, YSIZE=yy,$
X_SCROLL_SIZE=400, Y_SCROLL_SIZE=400,$
RETAIN=2, /SCROLL)
wFadeControlBase = WIDGET_BASE(wFadeBase, /ROW)
wFadeSlide = WIDGET_SLIDER(wFadeControlBase, MINIMUM=0,$
MAXIMUM=8, VALUE=4, UVALUE="SLIDE_FADE")
wFadeDismiss = WIDGET_BUTTON(wFadeControlBase, VALUE="Dismiss",$
UVALUE="DISMISS")
; realize the fader dialog box
WIDGET_CONTROL, /REALIZE, wFadeWindow
; display the two images equally mixed
WIDGET_CONTROL, wFadeDraw, GET_VALUE=hFadeDraw
WSET, hFadeDraw
TVSCL, FIX(iRightImage)+FIX(iLeftImage(0:xx-1,0:yy-1))
; store the fader state inforamtion in user value of the dialog
FadeState = CREATE_STRUCT('XSize', xx)
FadeState = CREATE_STRUCT(FadeState, 'YSize', yy)
FadeState = CREATE_STRUCT(FadeState, 'LeftImgStore', State.LeftImgStore)
FadeState = CREATE_STRUCT(FadeState, 'RightImgStore', State.RightImgStore)
FadeState = CREATE_STRUCT(FadeState, 'ImageDraw', hFadeDraw)
WIDGET_CONTROL, wFadeWindow, SET_UVALUE=FadeState, /NO_COPY
; restore the state inforamtion and images in their widget user values
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iRightImage, /NO_COPY
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iLeftImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
; register dialog box with XMANAGER as modal
XMANAGER, "Fader", wFadeWindow, EVENT_HANDLER="FaderEventHdlr",$
/MODAL
END
;
; Clear the control points from screen and memory
;
PRO clear_pts_event, child
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
; redraw the left image
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.LeftImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iImage, /NO_COPY
; redraw the right image
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
; reset the control points
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=[-1, -1]
State.ControlPtCount = 0
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Load an image, resetting control points
;
PRO load_event, child, iWhich
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
file = PICKFILE(FILE="", GROUP=child, FILTER="*.img", /READ)
IF (file EQ "") THEN BEGIN
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
RETURN
ENDIF
WIDGET_CONTROL, child, /HOURGLASS
; redraw the other image as necessary
IF (iWhich EQ 1) THEN BEGIN
IF (State.ControlPtCount GT 0) THEN BEGIN
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
lImgWdg = State.LeftImgWdg
hImageDraw =State.LeftImgHdl
hImageStore = State.LeftImgStore
ENDIF ELSE BEGIN
IF (State.ControlPtCount GT 0) THEN BEGIN
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.LeftImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
lImgWdg = State.RightImgWdg
hImageDraw =State.RightImgHdl
hImageStore = State.RightImgStore
ENDELSE
; reset the control points
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=[-1, -1]
State.ControlPtCount = 0
read_lum, file, iImage
iImage(WHERE(iImage GT 4094)) = 4094
iImage = BYTSCL(TEMPORARY(iImage))
iDim = SIZE(iImage)
WIDGET_CONTROL, lImgWdg, XSIZE=iDim(1), YSIZE=iDim(2)
; store the image size
IF (iWhich EQ 1) THEN BEGIN
State.LeftImgX = iDim(1)
State.LeftImgY = iDim(2)
ENDIF ELSE BEGIN
State.RightImgX = iDim(1)
State.RightImgY = iDim(2)
ENDELSE
WSET, hImageDraw
TVSCL, iImage
WIDGET_CONTROL, hImageStore, SET_UVALUE=iImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Routine to rotate an image counter clockwise 90 degrees
;
PRO rotccw_event, child, iWhich
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
IF (iWhich EQ 1) THEN BEGIN
lImgWdg = State.LeftImgWdg
hImageDraw = State.LeftImgHdl
hImageStore = State.LeftImgStore
iTemp = State.LeftImgX
State.LeftImgX = State.LeftImgY
State.LeftImgY = iTemp
WIDGET_CONTROL, lImgWdg, XSIZE=State.LeftImgX, YSIZE=State.LeftImgY
IF (State.ControlPtCount GT 1) THEN BEGIN
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDIF ELSE BEGIN
lImgWdg = State.RightImgWdg
hImageDraw = State.RightImgHdl
hImageStore = State.RightImgStore
iTemp = State.RightImgX
State.RightImgX = State.RightImgY
State.RightImgY = iTemp
WIDGET_CONTROL, lImgWdg, XSIZE=State.RightImgX, YSIZE=State.RightImgY
IF (State.ControlPtCount GT 0) THEN BEGIN
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.LeftImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDELSE
WIDGET_CONTROL, hImageStore, GET_UVALUE=iImage, /NO_COPY
iImage = ROTATE(TEMPORARY(iImage), 1)
WSET, hImageDraw
TVSCL, iImage
; reset the control points
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=[-1, -1]
State.ControlPtCount = 0
WIDGET_CONTROL, hImageStore, SET_UVALUE=iImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Routine to rotate an image clockwise 90 degrees
;
PRO rotcw_event, child, iWhich
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
IF (iWhich EQ 1) THEN BEGIN
lImgWdg = State.LeftImgWdg
hImageDraw = State.LeftImgHdl
hImageStore = State.LeftImgStore
iTemp = State.LeftImgX
State.LeftImgX = State.LeftImgY
State.LeftImgY = iTemp
WIDGET_CONTROL, lImgWdg, XSIZE=State.LeftImgX, YSIZE=State.LeftImgY
IF (State.ControlPtCount GT 1) THEN BEGIN
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDIF ELSE BEGIN
lImgWdg = State.RightImgWdg
hImageDraw = State.RightImgHdl
hImageStore = State.RightImgStore
iTemp = State.RightImgX
State.RightImgX = State.RightImgY
State.RightImgY = iTemp
WIDGET_CONTROL, lImgWdg, XSIZE=State.RightImgX, YSIZE=State.RightImgY
IF (State.ControlPtCount GT 0) THEN BEGIN
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.LeftImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDELSE
WIDGET_CONTROL, hImageStore, GET_UVALUE=iImage, /NO_COPY
iImage = ROTATE(TEMPORARY(iImage), 3)
Dim = SIZE(iImage)
WIDGET_CONTROL, lImgWdg, XSIZE=Dim(1), YSIZE=Dim(2)
WSET, hImageDraw
TVSCL, iImage
; reset the control points
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=[-1, -1]
State.ControlPtCount = 0
WIDGET_CONTROL, hImageStore, SET_UVALUE=iImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Routine to flip the portal image horizontally
;
PRO fliph_event, child, iWhich
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
IF (iWhich EQ 1) THEN BEGIN
hImageDraw = State.LeftImgHdl
hImageStore = State.LeftImgStore
IF (State.ControlPtCount GT 1) THEN BEGIN
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDIF ELSE BEGIN
hImageDraw = State.RightImgHdl
hImageStore = State.RightImgStore
IF (State.ControlPtCount GT 0) THEN BEGIN
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.LeftImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDELSE
WIDGET_CONTROL, hImageStore, GET_UVALUE=iImage, /NO_COPY
iImage = REVERSE(TEMPORARY(iImage),1)
WSET, hImageDraw
TVSCL, iImage
; reset the control points
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=[-1, -1]
State.ControlPtCount = 0
WIDGET_CONTROL, hImageStore, SET_UVALUE=iImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Routine to flip the portal image vertically
;
PRO flipv_event, child, iWhich
WIDGET_CONTROL, child, /HOURGLASS
WIDGET_CONTROL, child, GET_UVALUE=State, /NO_COPY
IF (iWhich EQ 1) THEN BEGIN
hImageDraw = State.LeftImgHdl
hImageStore = State.LeftImgStore
IF (State.ControlPtCount GT 1) THEN BEGIN
WIDGET_CONTROL, State.RightImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.RightImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.RightImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDIF ELSE BEGIN
hImageDraw = State.RightImgHdl
hImageStore = State.RightImgStore
IF (State.ControlPtCount GT 0) THEN BEGIN
WIDGET_CONTROL, State.LeftImgStore, GET_UVALUE=iImage, /NO_COPY
WSET, State.LeftImgHdl
TVSCL, iImage
WIDGET_CONTROL, State.LeftImgStore, SET_UVALUE=iImage, /NO_COPY
ENDIF
ENDELSE
WIDGET_CONTROL, hImageStore, GET_UVALUE=iImage, /NO_COPY
iImage = REVERSE(TEMPORARY(iImage),2)
WSET, hImageDraw
TVSCL, iImage
; reset the control points
WIDGET_CONTROL, State.LeftCtrlPts, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, State.RightCtrlPts, SET_UVALUE=[-1, -1]
State.ControlPtCount = 0
WIDGET_CONTROL, hImageStore, SET_UVALUE=iImage, /NO_COPY
WIDGET_CONTROL, child, SET_UVALUE=State, /NO_COPY
END
;
; Create the widgets that make up the point and click interface to SANDRA,
; and then register them with XMANAGER
;
PRO sandra
; get the current colour vectors to restore when application is exited.
TVLCT, savedR, savedG, savedB, /GET
; build colour table from colour vectors
colourTable = [[savedR],[savedG],[savedB]]
; create the top level window for the application
wSandraWindow = WIDGET_BASE(TITLE="Sandra", MBAR=wMenuBar)
wFileMenu = WIDGET_BUTTON(wMenuBar, VALUE="File", /MENU)
wOpen1Item = WIDGET_BUTTON(wFileMenu, VALUE="Open Left Image...",$
UVALUE="LEFTLOAD")
wOpen2Item = WIDGET_BUTTON(wFileMenu, VALUE="Open Right Image...",$
UVALUE="RIGHTLOAD")
wExitItem = WIDGET_BUTTON(wFileMenu, VALUE="Exit", UVALUE="EXIT")
wEditMenu = WIDGET_BUTTON(wMenuBar, VALUE="Edit", /MENU)
wClearPtItem = WIDGET_BUTTON(wEditMenu, VALUE="Clear Points",$
UVALUE="CLEAR_PTS")
wAffineItem = WIDGET_BUTTON(wEditMenu, VALUE="Affine Transform",$
UVALUE="AFFINE")
wFadeItem = WIDGET_BUTTON(wEditMenu, VALUE="Fade Images...",$
UVALUE="FADE")
wHelpMenu = WIDGET_BUTTON(wMenuBar, VALUE="Help", /HELP, /MENU)
wHelpItem = WIDGET_BUTTON(wHelpMenu, VALUE="About", UVALUE="ABOUT")
wSandraBase = WIDGET_BASE(wSandraWindow, /COLUMN)
wDrawBase = WIDGET_BASE(wSandraBase, /ROW)
wLeftBase = WIDGET_BASE(wDrawBase, /COLUMN)
wRightBase = WIDGET_BASE(wDrawBase, /COLUMN)
; Determine hardware display size.
DEVICE, GET_SCREEN_SIZE = screenSize
IF (screenSize(0) GT 640) THEN iScroll = 370 ELSE iScroll = 285
; create the two draw widgets to hold the images
wLeftDraw = WIDGET_DRAW(wLeftBase, XSIZE=512, YSIZE=512,$
X_SCROLL_SIZE=iScroll, Y_SCROLL_SIZE=iScroll,$
RETAIN=2, /BUTTON_EVENTS, /SCROLL,$
UVALUE = "IMAGE_LEFT")
wRightDraw = WIDGET_DRAW(wRightBase, XSIZE=512, YSIZE=512,$
X_SCROLL_SIZE=iScroll, Y_SCROLL_SIZE=iScroll,$
RETAIN=2, /BUTTON_EVENTS, /SCROLL,$
UVALUE= "IMAGE_RIGHT")
; load the bitmaps for the buttons
READ_X11_BITMAP, "rotccw.xbm", bRotateCCW
READ_X11_BITMAP, "rotcw.xbm", bRotateCW
READ_X11_BITMAP, "fliph.xbm", bFlipH
READ_X11_BITMAP, "flipv.xbm", bFlipV
READ_X11_BITMAP, "histoequ.xbm", bHistoEqu
; create the left image buttons
wLeftButtonBase = WIDGET_BASE(wLeftBase, /ROW)
wLeftRotateCCW = WIDGET_BUTTON(wLeftButtonBase, VALUE=bRotateCCW,$
UVALUE="LEFTROTCCW")
wLeftRotateCW = WIDGET_BUTTON(wLeftButtonBase, VALUE=bRotateCW,$
UVALUE="LEFTROTCW")
wLeftFlipH = WIDGET_BUTTON(wLeftButtonBase, VALUE=bFlipH,$
UVALUE="LEFTFLIPH")
wLeftFlipV = WIDGET_BUTTON(wLeftButtonBase, VALUE=bFlipV,$
UVALUE="LEFTFLIPV")
wLeftHistEqu = WIDGET_BUTTON(wLeftButtonBase, VALUE=bHistoEqu,$
UVALUE="LEFTHISTEQU")
; now create the right image buttons
wRightButtonBase = WIDGET_BASE(wRightBase, /ROW)
wRightRotateCCW = WIDGET_BUTTON(wRightButtonBase, VALUE=bRotateCCW,$
UVALUE="RIGHTROTCCW")
wRightRotateCW = WIDGET_BUTTON(wRightButtonBase, VALUE=bRotateCW,$
UVALUE="RIGHTROTCW")
wRightFlipH = WIDGET_BUTTON(wRightButtonBase, VALUE=bFlipH,$
UVALUE="RIGHTFLIPH")
wRightFlipV = WIDGET_BUTTON(wRightButtonBase, VALUE=bFlipV,$
UVALUE="RIGHTFLIPV")
wRightHistEqu = WIDGET_BUTTON(wRightButtonBase, VALUE=bHistoEqu,$
UVALUE="RIGHTHISTEQU")
; Create a text widget to display information
wInfoText = WIDGET_TEXT(wSandraBase, XSIZE=32, YSIZE = 1,$
VALUE=STRING(REPLICATE(32B,32)) )
; realize the window
WIDGET_CONTROL, /REALIZE, wSandraWindow
WIDGET_CONTROL, wLeftDraw, GET_VALUE=hLeftDraw
WIDGET_CONTROL, wRightDraw, GET_VALUE=hRightDraw
; save the previous colour table in the user value to restore on exit
SandraState = CREATE_STRUCT('ColourTable', colourTable)
SandraState = CREATE_STRUCT(SandraState, 'LeftImgHdl', hLeftDraw)
SandraState = CREATE_STRUCT(SandraState, 'LeftImgWdg', wLeftDraw)
SandraState = CREATE_STRUCT(SandraState, 'LeftImgStore', wLeftBase)
SandraState = CREATE_STRUCT(SandraState, 'LeftImgX',512)
SandraState = CREATE_STRUCT(SandraState, 'LeftImgY',512)
SandraState = CREATE_STRUCT(SandraState, 'RightImgHdl', hRightDraw)
SandraState = CREATE_STRUCT(SandraState, 'RightImgWdg', wRightDraw)
SandraState = CREATE_STRUCT(SandraState, 'RightImgStore', wRightBase)
SandraState = CREATE_STRUCT(SandraState, 'RightImgX',512)
SandraState = CREATE_STRUCT(SandraState, 'RightImgY',512)
SandraState = CREATE_STRUCT(SandraState, 'RightCtrlPts', wRightButtonBase)
SandraState = CREATE_STRUCT(SandraState, 'LeftCtrlPts', wLeftButtonBase)
SandraState = CREATE_STRUCT(SandraState, 'ControlPtCount', 0)
SandraState = CREATE_STRUCT(SandraState, 'InfoText', wInfoText)
WIDGET_CONTROL, wLeftButtonBase, SET_UVALUE=[-1, -1]
WIDGET_CONTROL, wRightButtonBase, SET_UVALUE=[-1, -1]
LOADCT, 1 ; 0-greyscale 1-blue/white 3-hotbody
; load two blank 512x512 images into draw widgets this means we
; don't have to track whether images are loaded.
WIDGET_CONTROL, wLeftBase, SET_UVALUE=BYTARR(512,512)
WIDGET_CONTROL, wRightBase, SET_UVALUE=BYTARR(512,512)
WIDGET_CONTROL, wSandraWindow, SET_UVALUE=SandraState, /NO_COPY
; register the application with XMANAGER
XMANAGER, "Sandra", wSandraWindow, EVENT_HANDLER="SandraEventHdlr",$
CLEANUP="CleanUpSandra"
END