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

Last change on this file was 1114, checked in by srkline, 4 years ago

bug fixes for:

RealTime? updating at VSANS
Mask loading at SANS

added highResGain field for VSANS R/W

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