source: sans/SANSReduction/trunk/Put in User Procedures/SANS_Reduction_v5.00/MaskUtils.ipf @ 47

Last change on this file since 47 was 47, checked in by srkline, 16 years ago

bug fixes and performance tweaks

File size: 15.3 KB
Line 
1#pragma rtGlobals=1             // Use modern global access method.
2#pragma version=5.0
3#pragma IgorVersion=4.0
4
5//*********************
6// Vers. 1.2 092101
7//
8//entry procedure for reading a mask file
9//mask files are currently only acceptable in MCID format, as written out by NIH
10//Image, available on the Macs (haven't tested the output of the Java version
11//(Image/J)
12//
13// also contains new drawing routines for drawing a mask within IGOR
14// and saving it in the required format
15// - uses simpler drawing tools (fill objects, no lines) for speed
16//
17//*************************
18
19
20
21//reads the data (1=mask, 0 = no mask)
22//and plots a quickie image to make sure it's ok
23//data is always read into root:MSK folder
24//
25Proc ReadMASK()
26       
27        //SetDataFolder root:MSK
28        String fname = PromptForPath("Select Mask file")
29        if(strlen(fname)==0)
30                return
31        endif
32        ReadMCID_MASK(fname)
33       
34        //SetDataFolder root:MSK
35////    SRK SEP06 disable plot of mask data, just show the overlay
36////    String waveStr = "root:MSK:data"
37////    NewImage/F/S=2/K=1 $waveStr
38////    ModifyImage '' ctab= {*,*,YellowHot,0}
39        maskButtonProc("maskButton")
40//      OverlayMask(1)
41
42
43//      Display/W=(10,50,400,400); AppendImage $waveStr
44
45        //back to root folder (redundant)
46        SetDataFolder root:
47End
48
49
50//reads the mask data into the root:MSK folder
51//setDataFolder is required here
52//y-values must be flipped to get proper array assignment of the mask
53//
54Function ReadMCID_MASK(fname)
55        String fname
56        // Reads MCID-format mask files written out by SANS IMAGE
57        // put data into MSK data folder
58        // flip the y-values to correspond to the work file
59        NVAR pixelsX = root:myGlobals:gNPixelsX
60        NVAR pixelsY = root:myGlobals:gNPixelsY
61        SetDataFolder root:MSK
62        Killwaves/Z data,data0          //kill the old data, if it exists
63        String cmd = "GBLoadWave/N=data/T={72,72}/O/S=4/W=1/U=16384 /Q  \"" + fname +"\""
64        Execute cmd
65        SetDataFolder root:MSK                                          //make sure correct data folder is set
66        WAVE data0 = $"root:MSK:data0"
67        Redimension/N=(pixelsX,pixelsY) data0
68        Flip_Y(data0)
69       
70        SetDataFolder root:MSK
71        Rename data0,data
72       
73        Variable/G root:MSK:gIsLogScale = 0
74        String/G root:MSK:fileList = GetFileNameFromPathNoSemi(fname)
75        //back to root folder
76        SetDataFolder root:
77        return(0)
78End
79
80//
81//flips the y-data of the MCID format array that was read in
82//the input array (pixelsX x pixelsY) is overwritten as output
83//
84// AND MUST BE SQUARE!
85//
86Function Flip_Y(mat)
87        Wave mat
88       
89        //reverses the order of y-indices in an MCID-style matrix output from SANS IMAGE,
90        //so that a mask file will be displayed properly on screen -
91        //"IMAGE" format is (0,0) in top left
92        //the (WORK)_data matches the detector, with (0,0) in the bottom left corner
93        NVAR pixelsX = root:myGlobals:gNPixelsX
94        NVAR pixelsY = root:myGlobals:gNPixelsY
95       
96        Variable ii,jj,kk
97        Make/O/N=(pixelsX) temp_y
98       
99        ii=0
100        do
101                jj=0
102                do
103                        temp_y[jj]=mat[ii][jj]
104                        jj+=1
105                while(jj<pixelsY)
106                kk=0
107                do
108                        mat[ii][pixelsX-1-kk]=temp_y[kk]
109                        kk+=1
110                while(kk<pixelsX)
111                ii+=1
112        while(ii<pixelsX)
113       
114        KillWaves temp_y
115        Return (0)
116End
117
118
119//**********************
120// for drawing a mask, see GraphWaveDraw (and Edit)
121//and for Image progessing demo - look in the examples forder in the
122//IGOR Pro folder....possible slider bar, constrast adjustment
123//
124//the following are macros and functions to overlay a mask on an image
125//
126//ResetLoop sets all of the zeros in the mask to NaN's so that they are
127//not plotted
128Function ResetLoop(tempStr)
129        String tempstr
130       
131        NVAR pixelsX = root:myGlobals:gNPixelsX
132        NVAR pixelsY = root:myGlobals:gNPixelsY
133        Variable ii=0,jj=0
134        Wave junk = $tempStr
135       
136        do
137                jj=0
138                do
139                        if(junk[ii][jj] == 0)
140                                junk[ii][jj] = NaN
141                        else
142                                junk[ii][jj] = 1
143                        endif
144                        jj+=1
145                while(jj<pixelsY)
146                ii+=1
147        while(ii<pixelsX)
148End
149
150//
151//toggles a mask on/off of the SANS_Data window
152// points directly to window, doesn't need current display type
153//
154// if state==1, show the mask, if ==0, hide the mask
155Function OverlayMask(state)
156        Variable state
157       
158        String maskPath = "root:MSK:data"
159        if(WaveExists($maskPath) == 1)
160                //duplicate the mask, which is named "data"
161                Duplicate/O root:MSK:data root:MSK:overlay
162                Redimension/D root:MSK:overlay
163       
164                String tempStr = "root:MSK:overlay"
165                ResetLoop(tempStr)              //keeps 1's and sets 0's to NaN
166       
167                //check to see if mask overlay is currently displayed
168                DoWindow SANS_Data
169                if(V_flag==0)
170                        return(0)
171                endif
172               
173                CheckDisplayed/W=SANS_Data root:MSK:overlay
174                //Print "V_flag = ",V_flag
175       
176                If(V_Flag == 1)         //overlay is present
177                        if(state==0)
178                                RemoveImage overlay
179                        endif           //don't need to do anything if we want to keep the mask
180                Else            //overlay is not present
181                        if(state==1)
182                                //append the new overlay
183                                AppendImage/L=left/B=bottom root:MSK:overlay
184                                //set the color table to vary from 0 to * (=max data = 1), with blue maximum
185                                //Nan's will appear transparent (just a general feature of images)
186                                ModifyImage/W=SANS_Data overlay ctab={0,*,BlueRedGreen,0}
187                        endif           //don't do anything if we don't want the overlay
188                Endif
189        Endif
190End
191
192//checkbox control procedure to toggle to "erase" mode
193//where the filled regions will be set to 0=no mask
194//
195Function EraseCheckProc(ctrlName,checked) : CheckBoxControl
196        String ctrlName
197        Variable checked
198
199        //SetDrawEnv fillpat=-1         //set the fill to erase
200        SetDrawEnv fillpat=1                    //keep a solid fill, use DrawMode to decide y/n mask state
201        if(checked)
202                CheckBox DrawCheck value=0
203        Endif
204End
205
206//checkbox control procedure to toggle to "draw" mode
207//where the filled regions will be set to 1=yes mask
208//
209Function DrawCheckProc(ctrlName,checked) : CheckBoxControl
210        String ctrlName
211        Variable checked
212
213        SetDrawEnv fillPat=1            //solid fill
214        if(checked)
215                CheckBox EraseCheck value=0
216        Endif
217End
218
219//function that polls the checkboxes to determine whether to add the
220//fill regions to the mask or to erase the fill regions from the mask
221//
222Function DrawMode()             //returns 1 if in "Draw" mode, 0 if "Erase" mode
223        ControlInfo DrawCheck
224        Return(V_Value)
225End
226
227// function that works on an individual pixel (sel) that is either part of the fill region
228// or outside it (= not selected). returns either the on state (=1) or the off state (=0)
229// or the current mask state if no change
230//** note that the acual numeric values for the on/off state are passed in and back out - so
231// the calling routine must send the correct 0/1/curr state
232// **UNUSED******
233Function MakeMask(sel,off,on,mask)
234        Variable sel,off,on,mask
235       
236        variable isDrawMode
237        isDrawMode = drawmode()
238       
239        if( sel )
240                if(isDrawMode)
241                        return on               //add point
242                else
243                        return off              //erase
244                endif
245        else
246                return mask             //don't change it
247        endif
248end
249
250
251//tempMask is already a byte wave of 0/1 values
252//does the save of the tempMask, which is the current state of the mask
253//
254Function SaveMaskButtonProc(ctrlName) : ButtonControl
255        String ctrlName
256       
257        WriteMask(root:myGlobals:drawMask:tempMask)
258       
259End
260
261//closes the mask drawing window, first asking the user if they have saved their
262//mask creation. Although lying is a bad thing, you will have to lie and say that
263//you saved your mask if you ever want to close the window
264//
265Function DoneMaskButtonProc(ctrlName) : ButtonControl
266        String ctrlName
267
268        DoAlert 1,"Have you saved your mask?"
269        if(V_flag==1) //yes selected
270                DoWindow/K drawMaskWin
271                KillDataFolder root:myGlobals:drawMask
272                KillWaves/Z M_ROIMask
273        Endif
274End
275
276//clears the entire drawing by setting it to NaN, so there is nothing displayed
277//
278Function ClearMaskButtonProc(ctrlName) : ButtonControl
279        String ctrlName
280       
281        SetDrawLayer/K ProgFront
282        WAVE tempOverlay=root:myGlobals:drawMask:tempOverlay
283        KillWaves/Z M_ROIMask,root:myGlobals:drawMask:tempMask
284        if(WaveExists(tempOverlay))
285                tempOverlay=NaN
286        endif
287End
288
289//Macro DrawMaskMacro()
290//      DrawMask()
291//End
292
293//main entry procedure for drawing a mask
294//needs to have a dataset in curDispType folder to use as the background image
295// for the user to draw on top of. Does not need data in the RAW folder anymore
296// - initializes the necessary folder and globals, and draws the graph/control bar
297//
298Function DrawMask()             //main entry procedure
299        //there must be data in root:curDispType:data FIRST
300        SVAR curType=root:myGlobals:gDataDisplayType
301        if(WaveExists($("root:"+curType+":data") ))
302                DoWindow/F drawMaskWin
303                If(V_flag == 0)
304                        InitializeDrawMask(curType)
305                        //draw panel
306                        Execute "DrawMaskWin()"
307                Endif
308        else
309                //no data
310                DoAlert 0,"Please display a representative data file using the main control panel"
311        Endif
312End
313
314//initialization of the draw window, creating the necessary data folder and global
315//variables if necessary
316//
317Function InitializeDrawMask(type)
318        String type
319        //create the global variables needed to run the draw window
320        //all are kept in root:myGlobals:drawMask
321        If( ! (DataFolderExists("root:myGlobals:drawMask"))  )
322                //create the data folder and the globals
323                                NewDataFolder/O root:myGlobals:drawMask
324                                Duplicate/O $("root:"+type+":data") root:myGlobals:drawMask:data                //copy of the data
325                Endif
326                //if the data folder's there , then the globals must also be there so don't do anything
327End
328
329//the macro for the graph window and control bar
330//
331Proc DrawMaskWin()
332        PauseUpdate; Silent 1           // building window...
333        Display /W=(178,84,605,513) /K=2 as "Draw A Mask"
334        DoWindow/C drawMaskWin
335        AppendImage root:myGlobals:drawMask:data
336        ModifyImage data cindex= :myGlobals:NIHColors
337        ModifyGraph width={Aspect,1},height={Aspect,1},cbRGB=(32768,54615,65535)
338        ModifyGraph mirror=2
339        ShowTools rect
340        ControlBar 70
341        CheckBox drawCheck,pos={40,24},size={44,14},proc=DrawCheckProc,title="Draw"
342        CheckBox drawCheck,help={"Check to add drawn regions to the mask"}
343        CheckBox drawCheck,value= 1,mode=1
344        CheckBox EraseCheck,pos={40,43},size={45,14},proc=EraseCheckProc,title="Erase"
345        CheckBox EraseCheck,help={"Check to erase drawn regions from the mask"}
346        CheckBox EraseCheck,value= 0,mode=1
347        Button button1,pos={146,3},size={90,20},title="Load MASK",help={"Loads an old mask in to the draw layer"}
348        Button button1,proc=LoadOldMaskButtonProc
349        Button button4,pos={146,25},size={90,20},proc=ClearMaskButtonProc,title="Clear MASK"
350        Button button4,help={"Clears the entire mask"}
351        Button button5,pos={290,7},size={50,20},proc=SaveMaskButtonProc,title="Save"
352        Button button5,help={"Saves the currently drawn mask to disk. The new mask MUST be re-read into the experiment for it to apply to data"}
353        Button button6,pos={290,40},size={50,20},proc=DoneMaskButtonProc,title="Done"
354        Button button6,help={"Closes the window. Reminds you to save your mask before quitting"}
355        Button button0,pos={130,47},size={120,20},proc=toMASKButtonProc,title="Convert to MASK"
356        Button button0,help={"Converts drawing objects to the mask layer (green)\rDoes not save the mask"}
357        Button button7,pos={360,25},size={25,20},proc=ShowMaskHelp,title="?"
358        Button button7,help={"Show the help file for drawing a mask"}
359        GroupBox drMode,pos={26,5},size={85,61},title="Draw Mode"
360       
361        SetDrawLayer ProgFront
362        SetDrawEnv xcoord= bottom,ycoord= left,save     //be sure to use axis coordinate mode
363EndMacro
364
365Function ShowMaskHelp(ctrlName) : ButtonControl
366        String ctrlName
367        DisplayHelpTopic/K=1 "SANS Data Reduction Tutorial[Draw a Mask]"
368End
369
370//loads a previously saved mask in the the draw layer
371// - does not affect the state of the current mask used for data reduction
372//
373Function LoadOldMaskButtonProc(ctrlName) : ButtonControl
374        String ctrlName
375       
376        //load into temp--- root:myGlobals:drawMask:tempMask
377        String fname = PromptForPath("Select Mask file")
378//      ReadMCID_MASK(fname)
379
380        // Reads MCID-format mask files written out by SANS IMAGE
381        // put data into MSK data folder
382        // flip the y-values to correspond to the work file
383        NVAR pixelsX = root:myGlobals:gNPixelsX
384        NVAR pixelsY = root:myGlobals:gNPixelsY
385       
386        SetDataFolder root:myGlobals:DrawMask
387        Killwaves/Z data,data0,tempMask         //kill the old data, if it exists
388        String cmd = "GBLoadWave/N=data/T={72,72}/O/S=4/W=1/U=16384 /Q  \"" + fname +"\""
389        Execute cmd
390        SetDataFolder root:myGlobals:DrawMask                                   //make sure correct data folder is set
391        WAVE data0 = $"root:myGlobals:DrawMask:data0"
392        Redimension/B/N=(pixelsX,pixelsY) data0
393        Flip_Y(data0)
394       
395        SetDataFolder root:myGlobals:DrawMask
396        Rename data0,tempMask           //tempMask can be killed and re-named, since it's not on a graph
397       
398        SetDataFolder root:
399       
400        OverlayTempMask()               //put the new mask on the image
401End
402
403//button control that commits the drawn objects to the current mask drawing
404// and refreshes the drawing
405//
406Function toMASKButtonProc(ctrlName) : ButtonControl
407        String ctrlName
408       
409        ImageGenerateROIMask data               //M_ROIMask is in the root: folder
410       
411        CumulativeMask()                        //update the new mask (cumulative)
412        OverlayTempMask()               //put the new mask on the image
413End
414
415//update the current mask - either adding to the drawing or erasing it
416//
417//current mask is "tempMask", and is byte
418//overlay is "tempOverlay" and is SP
419//
420Function CumulativeMask()
421
422        //if M_ROIMask does not exist, make a quick exit
423        if( ! (WaveExists(M_ROIMask)) )
424                return(0)
425        endif
426        if(!waveExists(root:myGlobals:drawMask:tempMask))
427                //make new waves
428                Duplicate/O M_ROIMask root:myGlobals:drawMask:tempMask
429                Wave tempM=root:myGlobals:drawMask:tempMask
430                tempM=0
431        else
432                Wave tempM=root:myGlobals:drawMask:tempMask
433        endif
434        //toggle(M_ROIMask,root:myGlobals:drawMask:tempMask)
435       
436        WAVE M_ROIMask=M_ROIMask
437        Variable isDrawMode
438        isDrawMode = drawmode()         //=1 if draw, 0 if erase
439        if(isDrawMode)
440                tempM = (tempM || M_ROIMask)            //0=0, any 1 =1
441        else
442                // set all 1's in ROI to 0's in temp
443                tempM = M_ROIMask ? 0 : tempM
444        endif
445End
446
447//overlays the current mask (as drawn) on the base image of the drawMaskWin
448// mask is drawn in the typical green, not part of the NIHColorIndex
449//
450// same overlay technique as for the SANS_Data window, NaN does not plot
451// on an image, and is "transparent"
452//
453Function OverlayTempMask()
454
455        //if tempMask does not exist, make a quick exit
456        if( ! (WaveExists(root:myGlobals:drawMask:tempMask)) )
457                return(0)
458        endif
459        //clear the draw layer
460        SetDrawLayer/K ProgFront
461        //append the overlay if necessary, otherwise the mask is already updated
462        Duplicate/O root:myGlobals:drawMask:tempMask root:myGlobals:drawMask:tempOverlay
463        WAVE tempOverlay=root:myGlobals:drawMask:tempOverlay
464        Redimension/S tempOverlay
465        tempOverlay=tempOverlay/tempOverlay*tempOverlay
466       
467        CheckDisplayed/W=DrawMaskWin tempOverlay
468        //Print "V_flag = ",V_flag
469        If(V_Flag == 1)
470                //do nothing, already on graph
471        else
472                //append the new overlay
473                AppendImage tempOverlay
474                ModifyImage tempOverlay ctab= {0,*,BlueRedGreen,0}
475        Endif
476End
477
478
479//********************
480//writes an MCID-style MASK file, exactly as it would be output from NIH Image
481//file is:
482//      4 bytes
483// 128x128=16384 bytes (mask)   (in the general case, pixelsX x pixelsX = SQUARE)
484// 508 bytes
485// = 16896 bytes
486// incoming data is a 2-D wave of any precision data, 0's and 1's
487//
488Function WriteMask(data)
489        Wave data
490       
491        NVAR pixelsX = root:myGlobals:gNPixelsX
492        NVAR pixelsY = root:myGlobals:gNPixelsY
493       
494        Variable refnum,ii=0,jj=0,dummy,num=pixelsX
495        String fullpath=""
496        Open/D refnum as fullpath               //won't actually open the file
497        If(cmpstr(S_filename,"")==0)
498                //user cancel, don't write out a file
499                Close/A
500                Abort "no data file was written"
501        Endif
502        fullpath = S_filename
503       
504        Make /B/O/N=(pixelsX) byteWave
505       
506        //actually open the file
507        Open/C="SANS"/T="MASK" refNum as fullpath
508        FSetPos refNum, 0
509        //write 4 bytes (to be skipped when reading the file)
510        FBinWrite /F=1 refnum,ii
511        FBinWrite /F=1 refnum,ii
512        FBinWrite /F=1 refnum,ii
513        FBinWrite /F=1 refnum,ii
514       
515        ii=num-1
516        jj=0
517        do
518                byteWave=data[p][ii]
519                FBinWrite /F=1 refnum,byteWave
520                ii-=1
521        while(ii>=0)
522       
523        //pad the rest of the file
524        ii=0
525        jj=0
526        do
527                FBinWrite /F=1 refnum,jj
528                ii+=1
529        while(ii<508)
530       
531        //close the file
532        Close refnum
533        ReadMCID_MASK(fullpath)
534        Killwaves/Z byteWave
535End
536
Note: See TracBrowser for help on using the repository browser.