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

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

fixed two bugs in mask reading where the 128x128=16384 data length was hard-wired. Now uses pixX*pixY to be generic. (found by JaeHie? when reading 192x192 HFIR data.

File size: 16.3 KB
Line 
1#pragma rtGlobals=1             // Use modern global access method.
2#pragma version=5.0
3#pragma IgorVersion=6.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: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 data as")             //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.