; $Id: warp.pro,v 1.5 1995/04/25 00:04:48 billo Exp $ ; ; Copyright (c) 1995, Research Systems, Inc. All rights reserved. ; Unauthorized reproduction prohibited. ;+ ; NAME: Warp ; ; PURPOSE: This example demonstrates the image processing capabilities of IDL. ; ; MAJOR TOPICS: Image Porcessing. ; ; CALLING SEQUENCE: Warp ; ; PROCEDURE: Warping ... ; ; MAJOR FUNCTIONS and PROCEDURES: ; ; COMMON BLOCKS and STRUCTURES: ; ; MODIFICATION HISTORY: Written by: DLD, RSI, March 1995 ; Based on work by: WSO, RSI ;- PRO ReadIndexOfPeople, names, offsets, USE_CURRENT=use_current filename = "people.idx" IF (KEYWORD_SET(use_current) EQ 0) THEN $ filename = FILEPATH(filename, SUBDIRECTORY=['examples','data']) ; Openr the file with the JPEG photos and get the logical unit no. OPENR, lun, filename, /GET_LUN ; Create a variable for the total number of people to be returned in imageCount = 0L ; Read in the JPEG photos count READF, lun, imageCount ; Create a structure to read indexStruct = REPLICATE({ position: 0L, name: ""}, imageCount+2) ; Read in the index structure - position and name of each photo READF, lun, indexStruct ; Remove trailing and leading blanks from the names names = STRTRIM(indexStruct(0:imageCount-1).name, 2) ; return offsets array with position of each image offsets = indexStruct.position ; free the logical unit number FREE_LUN, lun END FUNCTION ReadImageOfPeople, index, lun, offsets, MAXVALUE=maxValue, $ WINFOTEXT=wInfoText IF KEYWORD_SET(wInfoText) THEN $ WIDGET_CONTROL, wInfoText, SET_VALUE="Reading JPEG compressed image" ; Set the file position to point to the desired image POINT_LUN, lun, offsets(index) ; Read the JPEG photo READ_JPEG, UNIT = lun, image ; If the keyword MAXVALUE is not defined - create it IF (N_ELEMENTS(maxValue) LE 0) THEN $ maxValue=230 ; Rescale the image values between zero and maxValue to ; zero and !D.N_TABLE_SIZE-2 - last color used for control vectors image = BYTSCL(image, MIN=0, MAX=maxValue, TOP=!D.TABLE_SIZE-2) ; Erase the info if the wInfoText widget was updated above IF KEYWORD_SET(wInfoText) THEN $ WIDGET_CONTROL, wInfoText, SET_VALUE=" " RETURN, image END PRO DisplayPeopleGroup, imageLun, imageOffsets, WINFOTEXT=infoText COMMON warpCommon, cImageWindow, cControlVectorCount, $ cNames, cGroupCount, cSrcPointsX, cDestPointsX, csrcPointsY, cDestPointsY,$ cwInfoText, cImageAreaSize, cImageIconSize, cwClearButton, cwInfoButton, $ cImageLun, cImageOffsets, cGroupAreaWidth, cGroupAreaHeight, $ cImage, cwWarpButton, cButtonDown, cwQuinticButton, cQuinticValue, $ cwCornerButton, cCornerValue, cColorTable, cwImageArea, cwGroupArea ; Read Group image groupImage = ReadImageOfPeople(cGroupCount, imageLun, imageOffsets, $ WINFOTEXT=infoText) ; Trim group image of 378x378 from 384x384 image groupImage = groupImage(0:377, 6:383) ; The group shot is stored as an NxN array of individual images ; Get the dimensions of the group shot dimension = CEIL(SQRT(cGroupCount)) ; Reform the image in order to pluck the individuals from the group shot ; Each individual is a 54x54pixel image within the group shot groupImage = reform(groupImage, 54, dimension, 54, dimension) ; Initialize indexes in order to spin through the NxN group shot row = dimension - 1 column = 0 ; Save the current draw window groupDrawWindow = !D.WINDOW ; Create an off-screen pixmap to blast the individual images into WINDOW, /FREE, /PIXMAP, XSIZE=cGroupAreaWidth, YSIZE=cGroupAreaHeight groupPixmap = !D.WINDOW WSET, groupPixmap ; Set the pixmap to draw group shot FOR index = 0, cGroupCount-1 DO BEGIN ; Grab each individual from the group shot individualImage = reform(groupImage(*, column, *, row), 54, 54) ; Compress the images to iconic bitmap images individualImage = REBIN(individualImage, cImageIconSize, cImageIconSize) ; Display the image in the group window TV, individualImage, index ; Increment to point to next individual in group shot column = column + 1 ; If last individual in row - goto next row IF column EQ dimension THEN BEGIN row = row - 1 column = 0 ENDIF ENDFOR ; Make the group shot display window the current graph port WSET, groupDrawWindow ; Copy pixmap of reconfigured group to the display window DEVICE, COPY=[0, 0, cGroupAreaWidth, cGroupAreaHeight, 0, 0, groupPixmap] ; Free the off-screen pixmap. WDELETE, groupPixmap END PRO DisplayWarpedIndividual, imageIndex COMMON warpCommon IF imageIndex LT cGroupCount THEN BEGIN ; Reset the control vectors when new image selected ClearTiePts ; Set the correct graphics window WSET, cImageWindow ; image window anImage = ReadImageOfPeople(imageIndex(0), $ cImageLun, cImageOffsets, WINFOTEXT=cwInfoText) ; Compress or expand the image to fit into cImageAreaSize anImageSize = (size(anImage))(1) if anImageSize ne cImageAreaSize then $ anImage = CONGRID(anImage, cImageAreaSize, cImageAreaSize) ; Display the image TV, anImage ; Update image area cImage = anImage ; Set name label ; WIDGET_CONTROL, cwInfoText, SET_VALUE=cNames(imageIndex) ENDIF END PRO ClearTiePts COMMON warpCommon ; Empty the control points lists cSrcPointsX = 0 cDestPointsX = 0 cSrcPointsY = 0 cDestPointsY = 0 ; Reset the count of control vectors cControlVectorCount = 0 ; If cImage contains a valid image (2 dimensions)... IF (SIZE(cImage))(0) EQ 2 THEN BEGIN ; Set image window to be the current graphics window WSET, cImageWindow ; Redisplay the image without control vectors TV, cImage ENDIF ; Disable the buttons until a control vector has been added WIDGET_CONTROL, cwClearButton, SENSITIVE=0 WIDGET_CONTROL, cwWarpButton, SENSITIVE=0 END PRO CleanUpWarp, wWarpWindow COMMON warpCommon ; Reset these common block variables as scalar zeros to free memory cImage = 0 cSrcPointsX = 0 cDestPointsX = 0 cSrcPointsY = 0 cDestPointsY = 0 cNames = 0 ; Restore the previous color table TVLCT, cColorTable ; Close the images file and free the logical unit number FREE_LUN, cImageLun END PRO WarpImage, image, srcX, destX, srcY, destY, QUINTIC=quintic,$ CORNERS=corners, GROUP_LEADER=group ; This procedure actually does the warping..... IF (N_ELEMENTS(quintic) EQ 0) THEN $ quintic=0 IF (N_ELEMENTS(corners) EQ 0) THEN $ corners=0 IF (N_ELEMENTS(group) EQ 0) THEN $ group=0 totalControlPts = N_ELEMENTS(srcX) IF totalControlPts NE N_ELEMENTS(destX) OR $ totalControlPts NE N_ELEMENTS(srcY) OR $ totalControlPts NE N_ELEMENTS(destY) THEN BEGIN buttonPushed = WIDGET_MESSAGE(/ERROR, $ "The number of control points doesn't match.") RETURN ENDIF imageSize = SIZE(image) imageWidth = imageSize(1) ; Image size in "x" direction imageHeight = imageSize(2) ; Image size in "y" direction ; Check if corners should be anchored. IF (corners NE 0b) THEN BEGIN x0 = [destX,0,imageWidth-1,imageWidth-1,0] x1 = [srcX,0,imageWidth-1,imageWidth-1,0] y0 = [destY,0,0,imageHeight-1,imageHeight-1] y1 = [srcY,0,0,imageHeight-1,imageHeight-1] ENDIF ELSE BEGIN x0 = destX x1 = srcX y0 = destY y1 = srcY ENDELSE ; Warp the image imagew = WARP_TRI(x0,y0,x1,y1,image,QUINTIC=quintic) ; Create a top level base for the warped image. warpImgWindow = WIDGET_BASE(TITLE='Warped Image', TLB_FRAME_ATTR=1,$ GROUP_LEADER=group) ; Create a draw widget to display the warped image. warpImgArea = WIDGET_DRAW(warpImgWindow, XSIZE=imageWidth, $ YSIZE=imageHeight, RETAIN=2) ; Make the window visible WIDGET_CONTROL,warpImgWindow,/REALIZE ; Get the graphics window numbers for use with WSET WIDGET_CONTROL, warpImgArea, GET_VALUE=wwin ; Set the current window WSET, wwin ; Display the warped image TV, imagew END PRO BeginArrow, x, y, imageXSize, imageYSize, imageWindow COMMON warpCommon ; Set the current graphics port WSET, imageWindow ; User XOR drawing mode. DEVICE, SET_GRAPHICS=6 ; Get starting point. x0 = x y0 = y ; Set initial ending point. x1 = x y1 = y colorIndex = !D.TABLE_SIZE-2 ; Plot initial vector PLOTS, [x0,x1], [y0,y1], /DEVICE, COLOR=colorIndex ; Keep track of control points. IF (KEYWORD_SET(cSrcPointsX) EQ 0) THEN $ cSrcPointsX = [x0] $ ELSE $ cSrcPointsX = [cSrcPointsX, x0] IF (KEYWORD_SET(cDestPointsX) EQ 0) THEN $ cDestPointsX = [x1] $ ELSE $ cDestPointsX = [cDestPointsX, x1] IF (KEYWORD_SET(cSrcPointsY) EQ 0) THEN $ cSrcPointsY = [y0] $ ELSE $ cSrcPointsY = [cSrcPointsY, y0] IF (KEYWORD_SET(cDestPointsY) EQ 0) THEN $ cDestPointsY = [y1] $ ELSE $ cDestPointsY = [cDestPointsY, y1] ; Increment the total of control vectors cControlVectorCount = cControlVectorCount + 1 TVCRS, 1 END PRO UpdtArrow, x, y, imageXSize, imageYSize, imageWindow COMMON warpCommon ; Set the current graphics port WSET, imageWindow colorIndex = !D.TABLE_SIZE-2 ; Erase old vector. x0 = cSrcPointsX(cControlVectorCount-1) x1 = cDestPointsX(cControlVectorCount-1) y0 = cSrcPointsY(cControlVectorCount-1) y1 = cDestPointsY(cControlVectorCount-1) PLOTS, [x0,x1], [y0,y1], /DEVICE, COLOR=colorIndex ; Get new ending point. x1 = x > 0 < (!D.X_SIZE-1) y1 = y > 0 < (!D.Y_SIZE-1) ; Plot new vector. PLOTS, [x0,x1], [y0,y1], /DEVICE, COLOR=colorIndex ; Keep track of new destination point. cDestPointsX(cControlVectorCount-1) = x1 cDestPointsY(cControlVectorCount-1) = y1 END PRO EndArrow, x, y, imageXSize, imageYSize, imageWindow COMMON warpCommon ; Set the current graphics port WSET, imageWindow colorIndex = !D.TABLE_SIZE-2 ; Erase old vector. x0 = cSrcPointsX(cControlVectorCount-1) x1 = cDestPointsX(cControlVectorCount-1) y0 = cSrcPointsY(cControlVectorCount-1) y1 = cDestPointsY(cControlVectorCount-1) PLOTS, [x0,x1], [y0,y1], /DEVICE, COLOR=colorIndex ; Restore drawing mode. DEVICE, SET_GRAPHICS=3 colorIndex = !D.TABLE_SIZE-1 ; Draw final arrow. ARROW, x0, y0, x1, y1, COLOR=colorIndex, HSIZE=(!D.X_SIZE/32), /SOLID ; Enable the buttons when a control vector has been added WIDGET_CONTROL, cwClearButton, SENSITIVE=1 IF (cCornerValue EQ 0) THEN BEGIN IF (cControlVectorCount LT 3) THEN $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=0 $ ELSE $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=1 ENDIF ELSE BEGIN IF (cControlVectorCount LT 1) THEN $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=0 $ ELSE $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=1 ENDELSE TVCRS, 1 END PRO DisplayWarpInfo, parent infoText = [ $ " We have included the pictures of the employees of Research "+ $ "Systems so that you can see the people who develop and support IDL. "+ $ "This example also illustrates image warping (the distortion of "+ $ "images), and image compression using IDL.", $ "", $ " The pictures are stored in JPEG compressed image format, which is "+ $ "fully supported by IDL. Each 384 by 384 image, containing 144K "+ $ "bytes, is stored using JPEG in less than 9K bytes, resulting in "+ $ "approximately 16 to 1 compression.", $ "", $ " To experiment with image warping, first select a person from "+ $ "the group shot by clicking on that individual. The selected "+ $ "individual photograph will appear in the image location at the "+ $ "bottom of the window. ", $ "", $ " Next you will need to add control, or 'tie', vectors. Control "+ $ "vectors are used to specify "+ $ "how the image is to be warped. Each vector origin "+ $ "is transformed to the vector endpoint in the 'warped' output "+ $ "image.", $ "",$ " To make a control vector, click and drag within the image "+ $ "window. An arrow will be drawn anchored at the position of the "+ $ "mouse click (representing the origin point); the head of "+ $ "the arrow will be drawn at the position of the mouse release "+ $ "(representing the position to which the origin point is to "+ $ "be warped). ", $ "",$ " To actually warp the image (using the current control vectors), "+ $ "click the Warp button."] ShowInfo, TITLE="Warping Example Information", GROUP=parent, WIDTH=80, HEIGHT=24, $ INFOTEXT=infoText END PRO WarpMouseHelp, widgetID, ENTERING=entering COMMON warpCommon ; Initialize the mouse help line to be empty helpText = "" CASE widgetID OF cwGroupArea: BEGIN helpText = "Click the mouse to select an individual to warp" ENDCASE cwClearButton: BEGIN IF KEYWORD_SET(entering) THEN BEGIN IF cControlVectorCount GT 0 THEN $ helpText = "Click this button to remove all control vectors" $ ELSE $ helpText = "This button removes control vectors. Currently none are set." ENDIF ELSE $ helpText = "All control vectors are removed" ENDCASE cwQuinticButton: BEGIN IF cQuinticValue THEN $ helpText = "Reset this button to use linear interpolation" $ ELSE $ helpText = "Set this button to use quintic interpolation" ENDCASE cwCornerButton: BEGIN IF cCornerValue THEN $ helpText = "Reset this button to remove corner anchors" $ ELSE $ helpText = "Set this button to use corner anchors" ENDCASE cwWarpButton: BEGIN IF (cCornerValue EQ 1) THEN BEGIN IF (cControlVectorCount LT 1) THEN $ helpText = "Drag a control vector in the image before trying to warp" $ ELSE $ helpText = "Click this button to display the warped image" ENDIF ELSE BEGIN CASE cControlVectorCount OF 0: helpText = "Drag three control vectors in the image before trying to warp" 1: helpText = "Drag two more control vectors in the image before trying to warp" 2: helpText = "Drag one more control vector in the image before trying to warp" ELSE: helpText = "Click this button to display the warped image" ENDCASE ENDELSE ENDCASE cwInfoButton: helpText = 'Click this button to display "How To" information about warping' cwImageArea: BEGIN ; Update the text on an enter event. helpText = "Drag in this area to set a control vector" ENDCASE cwInfoText: helpText = "Move the mouse over different widgets to get information" ELSE: helpText = "" ENDCASE ; Set the text in the help line if the event corresponds to ; a help line event id. WIDGET_CONTROL, cwInfoText, SET_VALUE=helpText END PRO WarpEvents, event COMMON warpCommon eventType = TAG_NAMES(event, /STRUCTURE_NAME) IF eventType EQ "WIDGET_TRACKING" THEN BEGIN IF event.enter EQ 1 THEN $ WarpMouseHelp, event.id, /ENTERING $ ELSE $ WIDGET_CONTROL, cwInfoText, SET_VALUE="" RETURN ENDIF WIDGET_CONTROL, event.id, GET_UVALUE=theControl ;Get the event's value CASE theControl OF "GROUP_AREA": $ ; If mouse pressed... IF (event.press NE 0) THEN BEGIN ; Calculate the which person in the group was clicked on imageIndex = (event.x / cImageIconSize) + $ (cGroupAreaWidth/cImageIconSize) * $ ((cGroupAreaHeight-event.y) / cImageIconSize) ; If the user clicked on a face... DisplayWarpedIndividual, imageIndex ENDIF "IMAGE": BEGIN ; Marking control vectors on the images CASE event.type OF 0: BEGIN ; Button press. cButtonDown = 1b BeginArrow, event.x, event.y, (SIZE(cImage))(1), $ (SIZE(cImage))(2), cImageWindow END 1: BEGIN ; Button Release. cButtonDown = 0b EndArrow, event.x, event.y, (SIZE(cImage))(1), $ (SIZE(cImage))(2), cImageWindow END 2: BEGIN ; Motion. IF (cButtonDown NE 0b) THEN $ UpdtArrow, event.x, event.y, (SIZE(cImage))(1), $ (SIZE(cImage))(2), cImageWindow END ENDCASE ENDCASE "QUINTIC": BEGIN ; Set the quintic flag cQuinticValue = event.select WarpMouseHelp, event.id ENDCASE "CORNERS": BEGIN ; Set the corners flag cCornerValue = event.select IF (cCornerValue EQ 0) THEN BEGIN IF (cControlVectorCount LT 3) THEN $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=0 $ ELSE $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=1 ENDIF ELSE BEGIN IF (cControlVectorCount LT 1) THEN $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=0 $ ELSE $ WIDGET_CONTROL, cwWarpButton, SENSITIVE=1 ENDELSE WarpMouseHelp, event.id ENDCASE "CLEAR": BEGIN ; Reset the count of control vectors ClearTiePts WarpMouseHelp, event.id ENDCASE "QUIT": BEGIN ;Done? ; Close the window WIDGET_CONTROL, event.top, /DESTROY ENDCASE "INFO" : BEGIN DisplayWarpInfo, event.top ENDCASE "WARP": BEGIN IF (cCornerValue EQ 0) AND (cControlVectorCount LT 3) THEN $ buttonPushed = WIDGET_MESSAGE(/ERROR, $ "There must be a minimum of three control vectors.") $ ELSE BEGIN ; Display wait cursor. WIDGET_CONTROL, event.top, /HOURGLASS WarpImage, cImage, cSrcPointsX, cDestPointsX, $ cSrcPointsY, cDestPointsY, QUINTIC=cQuinticValue, $ CORNERS=cCornerValue, GROUP_LEADER=event.top ENDELSE ENDCASE ELSE: HELP, /structure, event ;Dunno... ENDCASE ;String value RETURN END PRO Warp, GROUP = group, USE_CURRENT=use_current ;Main people procedure ;+ ; USE_CURRENT = use files in current dir. ;- COMMON warpCommon ; If this example is already running - return. Only one copy of this ; example can run at any one time IF XREGISTERED("WarpDemo") THEN $ RETURN ; Get the current color vectors to restore when this application is exited. TVLCT, savedR, savedG, savedB, /GET cColorTable = [[savedR],[savedG],[savedB]] ; Determine hardware display size. DEVICE, GET_SCREEN_SIZE = screenSize ; Determine the maximum group shot size to be 80% of the horizontal ; screen size maxGroupSize = FLOOR(screenSize(0) * .8) ; On larger monitors use a larger group shot IF maxGroupSize GT 648 THEN BEGIN cImageIconSize = 54 ; iconic images of individuals 54x54 pixels groupRows = 4 ; Number of rows in the group shot groupColumns = 12 ; Number of columns in the group shot if screenSize(1) gt 600 then cImageAreaSize = 384 $ ;Enough scrn height? else cImageAreaSize = 256 ENDIF ELSE BEGIN cImageIconSize = 27 ; Iconic images of individuals 27x27 pixels groupRows = 3 ; Number of rows in the group shot groupColumns = 16 ; Number of columns in the group shot cImageAreaSize = 192 ENDELSE ; Initialize the total number of warping control vectors cControlVectorCount = 0 ; Drawable area width (number columns times image size) for group shot cGroupAreaWidth = groupColumns * cImageIconSize ; Drawable area height (number rows times image size) for group shot cGroupAreaHeight = groupRows * cImageIconSize ; Initialize button values. cButtonDown = 0b cQuinticValue = 0 cCornerValue = 1 ; Read the index file "people.idx" to get the structure ; for the image file "people.jpg" ReadIndexOfPeople, cNames, cImageOffsets, USE_CURRENT=use_current ; This is the file where all the jpeg'ged photos reside filename = "people.jpg" ; If not using the current directory for the image file ; - use the "examples/data" directory IF KEYWORD_SET(use_current) EQ 0 THEN $ filename = FILEPATH(filename, SUBDIRECTORY=['examples','data']) ; Open the images file for reading and get its logical unit number OPENR, cImageLun, filename, /STREAM, /GET ;For VMS... ; Count how many individuals are in the images file cGroupCount = N_ELEMENTS(cNames) cImage = 0 ; Create the main window wWarpWindow = WIDGET_BASE(title="Warping", /COLUMN) groupLabel1 = WIDGET_LABEL(wWarpWindow, /ALIGN_LEFT, $ VALUE="Select an individual by clicking on them.") ; Create the draw to display the group shot cwGroupArea = WIDGET_DRAW(wWarpWindow, XSIZE=cGroupAreaWidth, $ YSIZE=cGroupAreaHeight, RETAIN=2, /BUTTON_EVENTS, $ UVALUE = "GROUP_AREA", /TRACKING_EVENTS) ; Create the base to display the two images to warp wWarpArea = WIDGET_BASE(wWarpWindow, /ROW) wControlPanel = WIDGET_BASE(wWarpArea, /COLUMN) ; Create a toggle button for quintic warping. wNonExclBase = WIDGET_BASE(wControlPanel,/COLUMN,/NONEXCLUSIVE) cwQuinticButton = WIDGET_BUTTON(wNonExclBase,VALUE='Quintic Warping',$ UVALUE="QUINTIC", /TRACKING_EVENTS) ; Set the buttons initial value WIDGET_CONTROL, cwQuinticButton, SET_BUTTON=cQuinticValue ; Create a toggle button for anchoring the corner points. cwCornerButton = WIDGET_BUTTON(wNonExclBase,VALUE='Anchor Corners',$ UVALUE="CORNERS", /TRACKING_EVENTS) ; Set the buttons initial value WIDGET_CONTROL, cwCornerButton, SET_BUTTON=cCornerValue ; Create button to clear control vectors. cwClearButton = WIDGET_BUTTON(wControlPanel, VALUE="Clear Vectors", $ UVALUE="CLEAR", /TRACKING_EVENTS) ; Disable the button until control vectors have been added WIDGET_CONTROL, cwClearButton, SENSITIVE=0 ; Create button to start warp cwWarpButton = WIDGET_BUTTON(wControlPanel, VALUE="Warp", $ UVALUE="WARP", /TRACKING_EVENTS) ; Disable the button until image is ready to be warped WIDGET_CONTROL, cwWarpButton, SENSITIVE=0 ; Create button to display information about warping cwInfoButton = WIDGET_BUTTON(wControlPanel, VALUE="Info...", $ UVALUE="INFO", /TRACKING_EVENTS) ; Create the image area cwImageArea = WIDGET_DRAW(wWarpArea, XSIZE=cImageAreaSize, $ YSIZE=cImageAreaSize, RETAIN=2, /BUTTON_EVENTS, $ /MOTION_EVENTS, UVALUE = "IMAGE", /TRACKING_EVENTS) ; Create a text widget to display this example's information cwInfoText = WIDGET_TEXT(wWarpWindow, XSIZE=24, YSIZE = 1, $ VALUE=STRING(REPLICATE(32B,24)), /TRACKING_EVENTS) ; Make the window visible WIDGET_CONTROL, /REALIZE, wWarpWindow ; Default the draw widget cursors to an arrow, operate in pseudo color DEVICE, /CURSOR_ORIGINAL, DECOMPOSED=0 ; Get the graphics window numbers for use with WSET WIDGET_CONTROL, cwImageArea, GET_VALUE = cImageWindow WIDGET_CONTROL, cwGroupArea, GET_VALUE = cGroupWindow ; Display photos in shades of gray (load grayscale color table) LOADCT, 0, /SILENT ; Set the last color index to green to make the control vectors stand out TVLCT, 0, 255, 0, !D.TABLE_SIZE-1 ; Set which draw widget to display into WSET, cGroupWindow ; Display group shot of everyone in the group shot draw widget DisplayPeopleGroup, cImageLun, cImageOffsets, WINFOTEXT=cwInfoText ; Generate initial seed - try to use the lesser significant digits for ; the actual seeds index1 = RANDOMU(seed) * 10000 index1 = index1 - FLOOR(index1) ; Display initial random individual for warping DisplayWarpedIndividual, FLOOR(index1 * cGroupCount) ; Register this widget application with XManager and tell it to pass ; any events to the "WarpEvents" eventhandler XManager, "WarpDemo", wWarpWindow, EVENT_HANDLER = "WarpEvents", $ GROUP = group, CLEANUP="CleanUpWarp" END