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