source: sans/Dev/trunk/NCNR_User_Procedures/Reduction/SANS/MaskUtils.ipf @ 683

Last change on this file since 683 was 665, checked in by srkline, 13 years ago

Made preferences a common panel (moved to PlotUtilsMacro?.ipf and globals to root:Packages:NIST:) and added menu items for all packages. Many files had to be modified so that the preferences could be properly accessed

File Open dialog now is set to "All files" so that XML can be selected. I think that all open access that doesn't already have the full path go through this common function.

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