xrs_rois.py 32 KB
Newer Older
1
2
3
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
4
5
import six
from six.moves import range
christoph's avatar
christoph committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/python
# Filename: xrs_rois.py

#/*##########################################################################
#
# The XRStools software package for XRS spectroscopy
#
# Copyright (c) 2013-2014 European Synchrotron Radiation Facility
#
# This file is part of the XRStools XRS spectroscopy package developed at
# the ESRF by the DEC and Software group.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#############################################################################*/
__author__ = "Christoph J. Sahle - ESRF"
__contact__ = "christoph.sahle@esrf.fr"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"

import numpy as np
43
import copy
christoph's avatar
christoph committed
44
import h5py
45
import os
christoph's avatar
christoph committed
46
import matplotlib.pyplot as plt
47
from collections import Iterable
christoph's avatar
christoph committed
48

49
50
51
# commented the *import because otherwise sphinx documents all the symbol of other packages 
# from xrs_utilities import *
# from math_functions import *
52
53
from . import xrs_utilities
from . import math_functions
54
55
56
###########################################


christoph's avatar
christoph committed
57
58
59
60
from matplotlib.widgets import Cursor, Button
from scipy.ndimage import measurements
from scipy import signal

61
62
63
64
65
66

def h5_assign_force(h5group, name, item):
    if name in h5group:
        del h5group[name]
    h5group[name] = item

christoph's avatar
christoph committed
67
class container:
68
69
70
71
72
    """
    Random container class to hold values
    """
    def __init__(self):
        pass
christoph's avatar
christoph committed
73

Alessandro MIRONE's avatar
Alessandro MIRONE committed
74
75
76
77
78
79
80



sl={}
sl[0  ] = slice(0  ,256)
sl[256] = slice(256,512)
sl[512] = slice(512,768)
Alessandro Mirone's avatar
Alessandro Mirone committed
81
82
83
sl[3*256] = slice(3*256    ,3*256 + 256)
sl[4*256] = slice(4*256    ,4*256 + 256)

Alessandro MIRONE's avatar
Alessandro MIRONE committed
84
85
86
87
88

V147 = [1,4,7,10,2,5,8,11,3,6,9,12]
V1296 = [12,9,6,3,11,8,5,2,10,7,4,1]
V1074 = [10,7,4,1,11,8,5,2,12,9,6,3     ]
V369  = [3,6,9,12,2,5,8,11,1,4,7,10             ]
89
V1234 =  [1,2,3,4]
operator for beamline's avatar
operator for beamline committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103


def order_generator_ascending(a,b,c,d):
    return [ a,b,c,d,
             a+1,b+1,c+1,d+1,
             a+2,b+2,c+2,d+2]
def order_generator_descending(a,b,c,d):
    return [ a,b,c,d,
             a-1,b-1,c-1,d-1,
             a-2,b-2,c-2,d-2]

OG_inc = order_generator_ascending
OG_dec = order_generator_descending

Alessandro Mirone's avatar
Alessandro Mirone committed
104
geo_informations = {(256,768,None): { "DET_PIXEL_NUM":256, "geo":[256,768], "nofrois":36,
Alessandro MIRONE's avatar
Alessandro MIRONE committed
105
106
107
108
                                 "subnames": ["RD" ,"LU","B"],
                                 "subgeos" : [(sl[0] ,sl[0]),
                                              (sl[0],sl[256]),
                                              (sl[0],sl[512])]   ,
operator for beamline's avatar
operator for beamline committed
109
110
111
                                 "analyser_nIDs": {"LU":{"3x4":OG_inc(10,7,4,1),"Vertical": OG_inc(1,4,7,10)},  
                                                   "RD":{"3x4":OG_inc(1,4,7,10),"Vertical": OG_inc(1,4,7,10)},
                                                   "B": {"3x4":OG_inc(1,4,7,10),"Vertical": OG_inc(1,4,7,10)}  
Alessandro MIRONE's avatar
Alessandro MIRONE committed
112
113
                                                   }
                                 },
Alessandro Mirone's avatar
Alessandro Mirone committed
114
                    (512,768,None) : { "DET_PIXEL_NUM":256, "geo":[512,768],"nofrois":72,
115
                                  "subnames":["VD" , "VU","VB","HR" ,"HL","HB", ] ,
Alessandro MIRONE's avatar
Alessandro MIRONE committed
116
117
118
119
120
121
                                  "subgeos"  :[(sl[0],sl[0]     ),
                                               (sl[0],sl[256]   ),
                                               (sl[0],sl[512]   ),
                                               (sl[256],sl[0]   ),
                                               (sl[256],sl[256] ),
                                               (sl[256],sl[512] )],
operator for beamline's avatar
operator for beamline committed
122
123
124
125
126
127
                                  "analyser_nIDs": {"VD":{"3x4": OG_dec(12,9,6,3)  ,"Vertical":   OG_dec(12,9,6,3)  },
                                                    "VU":{"3x4": OG_dec(3,6,9,12)  ,"Vertical":   OG_dec(12,9,6,3)  },
                                                    "VB":{"3x4": OG_dec(3,6,9,12)  ,"Vertical":   OG_dec(12,9,6,3) },
                                                    "HR":{"3x4": OG_inc(1,4,7,10)  ,"Vertical":   OG_inc(1,4,7,10) },
                                                    "HL":{"3x4": OG_inc(10,7,4,1)  ,"Vertical":   OG_inc(1,4,7,10) },
                                                    "HB":{"3x4": OG_inc(10,7,4,1)  ,"Vertical":   OG_inc(1,4,7,10) },
Alessandro MIRONE's avatar
Alessandro MIRONE committed
128
129
                                                    }
                                  },
Alessandro Mirone's avatar
Alessandro Mirone committed
130
                    (256,256,None):{"DET_PIXEL_NUM":256, "geo":[256,256],"nofrois":1,"subnames":["DETECTOR"],"subgeos" : [(sl[0] ,sl[0])],
Alessandro Mirone's avatar
Alessandro Mirone committed
131
132
                               "analyser_nIDs": {"DETECTOR":{"3x4":V147,"Vertical": V147}}
                                },
133
                    (256,256,"1X1-4"):{"DET_PIXEL_NUM":256, "geo":[256,256],"nofrois":4,"subnames":["DETECTOR"],"subgeos" : [(sl[0] ,sl[0])],
Alessandro Mirone's avatar
Alessandro Mirone committed
134
                               "analyser_nIDs": {"DETECTOR":{ "Vertical": V1234} }
135
                                }
Alessandro Mirone's avatar
Alessandro Mirone committed
136
                }
Alessandro MIRONE's avatar
Alessandro MIRONE committed
137

Alessandro Mirone's avatar
Alessandro Mirone committed
138
139

geo_informations[(256,768,"1X3-12")]=geo_informations[(256,768,None)]
Alessandro Mirone's avatar
Alessandro Mirone committed
140
geo_informations[(512,768,"2X3-12")]=geo_informations[(512,768,None)]
Alessandro Mirone's avatar
Alessandro Mirone committed
141
142
143
geo_informations[(256,256,"1X1-12")]=geo_informations[(256,256,None)]


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
for NBCU in [255,256]:
        geo_informations[(NBCU,5*259,"1X5-1")] = { "DET_PIXEL_NUM":NBCU, "geo":[NBCU,5*259],"nofrois":5,
                                                   "subnames":["A" ,"B","C","D" , "E" ] ,
                                                   "subgeos"  :[(slice(0,NBCU),slice(259*0,259*1)  ),
                                                                (slice(0,NBCU),slice(259*1,259*2)  ),
                                                                (slice(0,NBCU),slice(259*2,259*3)  ),
                                                                (slice(0,NBCU),slice(259*3,259*4)   ),
                                                                (slice(0,NBCU),slice(259*4,259*5)   ),
                                                        ],
                                                   "analyser_nIDs": {"A":{"Vertical":[1]},
                                                                     "B":{"Vertical":[1]},
                                                                     "C":{"Vertical":[1]},
                                                                     "D":{"Vertical":[1]},
                                                                     "E":{"Vertical":[1]},
                                                             }
                                           }
        
        
        geo_informations[(NBCU,5*259+1,"1X5-1")]  = geo_informations[(NBCU,5*259,"1X5-1")] 
Alessandro Mirone's avatar
Alessandro Mirone committed
163

Alessandro MIRONE's avatar
Alessandro MIRONE committed
164
165
166
167
def get_geo_informations(shape):
    return geo_informations[shape]


christoph's avatar
christoph committed
168
class roi_object:
169
170
171
    """
    Container class to hold all relevant information about given ROIs.
    """
172
173
174
175
176
177
178
179
180
181
182
183
    def __init__( self ):
        self.roi_matrix     = np.array([]) # single matrix of zeros, ones, twos, ... ,
                                           # n's (where n is the number of ROIs defined)
        self.red_rois       = {}           # dictionary, one entry for each ROI, each ROI
                                           # has an origin and a rectangular box of ones
                                           # and zeros defining the ROI
        self.indices        = []           # list of list of tuples (one list of tuples
                                           # for each ROI)
        self.number_of_rois = 0            # number of ROIs defined
        self.kind           = []           # keyword (e.g. 'zoom', 'line', 'auto', etc.),
                                           # certain features (esp. in imaging) are only
                                           # available for certain kinds of ROIs
184
185
        self.x_indices      = [] # list of numpy arrays of x-indices (for each ROI)
        self.y_indices      = [] # list of numpy arrays of y-indices (for each ROI)
186
187
        self.masks          = [] # 3D numpy array with slices of zeros and ones (same
                                 # size as detector image) for each roi
188
        self.input_image    = [] # 2D imput image that was used to define the ROIs
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

    def __add__( self, roi_obj ):
        """ **__add__**

        Allows appending two ROI objects by using the + operator.
        """
        assert ( type(roi_obj) == type(self) )

        # create a new instance
        new_obj = roi_object()

        # copy the ROIs
        new_obj.red_rois = copy.deepcopy( self.red_rois )

        # append the other ROIs
        self_len = len( new_obj.red_rois )
        for ii,key in enumerate( sorted( roi_obj.red_rois ) ):
            new_key = 'ROI%02d'%( ii+self_len )
            if not new_key in list( new_obj.red_rois.keys() ):
                new_obj.red_rois[new_key] = roi_obj.red_rois[key]
                new_obj.red_rois[new_key][1][ new_obj.red_rois[new_key][1]>0 ] += self_len
            else:
                'something fishy happened, skipping %s.'%(key)
                return self

        # add the input images
        new_obj.input_image = self.input_image + roi_obj.input_image

        # convert summed ROIs to other ROI formats
        new_obj.roi_matrix     = convert_redmatrix_to_matrix( new_obj.red_rois,
                                np.zeros_like( new_obj.input_image ), offsetX=0,
                                                                  offsetY=0 )
        new_obj.masks          = convert_roi_matrix_to_masks( new_obj.roi_matrix)
        new_obj.indices        = convert_matrix_rois_to_inds( new_obj.roi_matrix)
        new_obj.number_of_rois = int( np.amax( new_obj.roi_matrix ) )
        new_obj.x_indices      = convert_inds_to_xinds( new_obj.indices )
        new_obj.y_indices      = convert_inds_to_yinds( new_obj.indices )
226
        
227
228
229
230
231
232
        return new_obj

    def load_rois_fromMasksDict( self, masksDict, newshape=None, kind="zoom" ):
        """ **load_rois_fromMasksDict**

        """
233
234
235
236
        self.kind=kind
        self.red_rois = masksDict
        if newshape is not None:
            self.roi_matrix = np.zeros(newshape)
237
238
        self.roi_matrix = convert_redmatrix_to_matrix( masksDict, self.roi_matrix,
                                                           offsetX=0, offsetY=0)
239
240
241
242
243
244
        self.masks          = convert_roi_matrix_to_masks(self.roi_matrix)
        self.indices        = convert_matrix_rois_to_inds(self.roi_matrix)
        self.number_of_rois = int(np.amax(self.roi_matrix))
        self.x_indices      = convert_inds_to_xinds(self.indices)
        self.y_indices      = convert_inds_to_yinds(self.indices)

245
    def writeH5( self, fname ):
246
        """ **writeH5**
247

248
249
250
        Creates an HDF5 file and writes the ROIs into it.

        Args:
myron's avatar
myron committed
251
252
          * fname (str) : Full path and filename for the HDF5 file to be created.

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
        """
        if self.indices:
            # check if file already exists
            if os.path.isfile(fname):
                os.remove(fname)
            f = h5py.File(fname, "w")
            f.require_group("rois_definition")
            f["rois_definition"]["image"] = self.input_image
            f["rois_definition"].require_group("rois_dict")
            write_rois_toh5(f["rois_definition"]["rois_dict"],self.red_rois)
            f.close()
        else:
            print('There are no ROIs to save.')

    def loadH5(self,fname):
        """ **loadH5**
269

270
271
272
        Loads ROIs from an HDF5 file written by the self.writeH5() method.

        Args:
myron's avatar
myron committed
273
          * fname (str) : Full path and filename for the HDF5 file to be read.
274
        """
myron's avatar
myron committed
275
276
277
278
279
        groupname=""
        if ":" in fname:
            fname, groupname = xrs_utilities.split_hdf5_address(fname)
            groupname = groupname +"/"

280
        f   = h5py.File(fname, "r")
myron's avatar
myron committed
281
        self.input_image = f[groupname + "rois_definition"]["image"][:]
282
        self.red_rois    = {}
myron's avatar
myron committed
283
284
285
286
        if groupname == "" : 
            shape = load_rois_fromh5(f,self.red_rois)
        else:
            shape = load_rois_fromh5(f[groupname],self.red_rois)
287
288
289
290
291

        if 1:
            self.load_rois_fromMasksDict(self.red_rois ,  newshape = shape, kind="zoom")
        else:
    
292
293
            self.roi_matrix     = convert_redmatrix_to_matrix( self.red_rois,
                                    np.zeros_like(self.input_image), offsetX=0, offsetY=0)
294
295
296
297
298
            self.indices        = convert_matrix_rois_to_inds(self.roi_matrix)
            self.number_of_rois = int(np.amax(self.roi_matrix))
            self.x_indices      = convert_inds_to_xinds(self.indices)
            self.y_indices      = convert_inds_to_yinds(self.indices)
            self.masks          = convert_roi_matrix_to_masks(self.roi_matrix)
christoph's avatar
christoph committed
299

300
301
302
303
304
305
    def load_shadok_h5( self, fname, group_name1, group_name2='ROI_AS_SELECTED' ):
        """ **load_shadok_h5**

        Load ROIs from a HDF5-file created by the Shadok/XRS_Swissknife.
        """

christoph's avatar
christoph committed
306
307
308
309
        f   = h5py.File(fname, "r")
        self.input_image = f[group_name1][group_name2]["rois_definition"]["image"][:]
        self.red_rois    = {}
        load_rois_fromh5(f[group_name1][group_name2],self.red_rois)
310
311
        self.roi_matrix     = convert_redmatrix_to_matrix( self.red_rois,
                                    np.zeros_like(self.input_image), offsetX=0, offsetY=0)
christoph's avatar
christoph committed
312
313
314
315
316
        self.indices        = convert_matrix_rois_to_inds(self.roi_matrix)
        self.number_of_rois = int(np.amax(self.roi_matrix))
        self.x_indices      = convert_inds_to_xinds(self.indices)
        self.y_indices      = convert_inds_to_yinds(self.indices)
        self.masks          = convert_roi_matrix_to_masks(self.roi_matrix)
317
                
318
319
320
321
322
323
324
    def append( self, roi_object ):
        """ **append**

        Append other ROI definitions.

        Args:
          * roi_object (roi_obj) : Instance of the roi_object class.
325
        
326
327
328
329
330
        """
        assert ( type(roi_object) == type(self) )
        
        orig_length = len( self.red_rois )
                
331
332
333
334
        for ii,key in enumerate(sorted(roi_object.red_rois)):
            new_key = 'ROI%02d'%(ii+orig_length)
            self.red_rois[new_key] = roi_object.red_rois[key]
            self.red_rois[new_key][1][ self.red_rois[new_key][1]>0 ] += orig_length
335

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
        # convert summed ROIs to other ROI formats
        self.roi_matrix     = convert_redmatrix_to_matrix( self.red_rois,
                                np.zeros_like( self.input_image ), offsetX=0,
                                                                  offsetY=0 )
        self.masks          = convert_roi_matrix_to_masks( self.roi_matrix)
        self.indices        = convert_matrix_rois_to_inds( self.roi_matrix)
        self.number_of_rois = int( np.amax( self.roi_matrix ) )
        self.x_indices      = convert_inds_to_xinds( self.indices )
        self.y_indices      = convert_inds_to_yinds( self.indices )

    def get_number_of_rois( self ):
        """ **get_number_of_rois**
        Returns the number of currently defined ROIs.

        """
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
        return self.number_of_rois

    def get_indices(self):
        return self.indices

    def get_x_indices(self):
        return self.x_indices

    def get_y_indices(self):
        return self.y_indices

    def get_bounding_boxes(self):
        return self.bounding_boxes

    def get_masks(self):
        return self.masks

    def get_copy(self):
        """
        **get_copy**
        Returns a deep copy of self.
        """
        return copy.deepcopy(self)

375
376
377
    def strip( self ):
        """ **strip**
        Strips extra zeros from border of the ROIs.
378
379

        """
380
381
382
383
384
385
386
387
388
389
390
391
        for key in self.red_rois:
            num = int("".join([c for c in key if c.isdigit()]))
            origin = self.red_rois[key][0]
            data   = self.red_rois[key][1]

            inds1, inds2 = np.where(data>0)
            new_data = np.zeros((inds1.max()-inds1.min()+1, inds2.max()-inds2.min()+1))
            new_data = data[inds1, inds2].reshape(new_data)
            new_origin = (origin[0]+inds1.min(), origin[1]+inds2.min())

            self.red_rois[key][0] = new_origin
            self.red_rois[key][1] = new_data
392

393
    def delete_empty_rois( self ):
394
        """ **delete_empty_rois**
395
396

        Deletes ROI entries that are completely empty.
397
        """
398
399
400
401

        for key in self.red_rois:
            if not np.any(self.red_rois[key][1]) > 0:
                self.pop(key)
402
    
403
404
405
    def shift( self, shiftVal, direction='horiz', roi_inds=None ):
        """ **shift**

406
407
408
        Displaces the defined ROIs by the provided value.

        Args
409
410
411
412
413
414
415
416
417
418
          * shiftVal (int) : Value by which the ROIs should be shifted.

          * direction (str) : Description of which direction to shift 
                              by (can be 'horiz' or 'vert'), default 
                              is 'horiz'.

          * roi_inds (int) or (sequence) : Index or Sequence (iterable) 
                                           for which ROIs should be shifted. 
                                           If None, all ROIs defined are 
                                           shifted (default.)
419
        """
420
        
421

422
423
        if not roi_inds:
            inds = list(range(len(self.red_rois)))
424
        else:
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
            inds = roi_inds

        if not isinstance( inds, Iterable ):
            inds = list([inds])

        for ind in inds:
            key = 'ROI%02d'%ind
            if direction == 'horiz':
                self.red_rois[key][0][1] += shiftVal

            elif direction == 'vert':
                self.red_rois[key][0][0] += shiftVal

        # convert summed ROIs to other ROI formats
        self.roi_matrix     = convert_redmatrix_to_matrix( self.red_rois,
                                np.zeros_like( self.input_image ), offsetX=0,
                                                                  offsetY=0 )
        self.masks          = convert_roi_matrix_to_masks( self.roi_matrix)
        self.indices        = convert_matrix_rois_to_inds( self.roi_matrix)
        self.number_of_rois = int( np.amax( self.roi_matrix ) )
        self.x_indices      = convert_inds_to_xinds( self.indices )
        self.y_indices      = convert_inds_to_yinds( self.indices )

    def pop( self, roi_key=None ):
        """ **pop**
        
        Discards a ROI.
452

453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
        Args
          * roi_key (str) : Dict key for ROI to delete. If None, the ROI with 
                            highest index (defined last) will be discarded (defalt).
        """

        # delete last ROI if no key is specified
        if not roi_key:
            roi_key = sorted(list( self.red_rois.keys()))[-1]

        # make sure the ROI exists
        assert(roi_key in list(self.red_rois.keys()) )

        # delete the ROI from the maskDict
        self.red_rois.pop( roi_key )

        # convert summed ROIs to other ROI formats
        self.roi_matrix     = convert_redmatrix_to_matrix( self.red_rois,
                                np.zeros_like( self.input_image ), offsetX=0,
                                                                  offsetY=0 )
        self.masks          = convert_roi_matrix_to_masks( self.roi_matrix)
        self.indices        = convert_matrix_rois_to_inds( self.roi_matrix)
        self.number_of_rois = int( np.amax( self.roi_matrix ) )
        self.x_indices      = convert_inds_to_xinds( self.indices )
        self.y_indices      = convert_inds_to_yinds( self.indices )
        
christoph's avatar
christoph committed
478
        
479
480
481
    def show(self, cmap='Blues', interpolation='nearest', logscaling=True):
        """ **show**

christoph's avatar
christoph committed
482
483
484
485
        Creates a figure showing the existing ROIs.

        Args:
          * colormap (str): Image colormape (matplotlib.colors.Colormap).
486

christoph's avatar
christoph committed
487
          * interpolation (str): see matplotlib.pyplot.imshow()
488

christoph's avatar
christoph committed
489
490
491
492
493
494
495
496
497
498
499
500
          * logscaling (bool): Use logarithmic scaling for image, default is True.
        
        """
        # make sure ROIs are defined, image exists
        assert len(self.red_rois)>0, "Please select some rois first."
        assert np.any(self.input_image), "No 2D image found."

        if logscaling:
            inds = self.input_image == 0.0
            image = copy.deepcopy(self.input_image)
            image[inds] = 1.0
            image = np.log( image )
501
        else:
christoph's avatar
christoph committed
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
            image = self.input_image
        
        # prepare figure
        plt.ioff() # unset interactive mode
        fig, ax = plt.subplots()
        ax.imshow( image, interpolation=interpolation, cmap=cmap )
        #plt.subplots_adjust(bottom=0.2)

        # ticks and labels
        ax.set_xlabel( 'x [pixels]' )
        ax.set_ylabel( 'y [pixels]' )
        ax.set_xlim( [0, image.shape[1]] )
        ax.set_ylim( [image.shape[0], 0] )
        ax.tick_params( left=False,
                     labelleft=True,
                     right=False,
                     labelright=False,
                     bottom=False,
                     top=False,
                     labelbottom=True )

        # draw ROIs and labels
        for ii, key in enumerate( sorted(self.red_rois) ):
            # plot the ROI as frame
            corner = self.red_rois[key][0]
            inset  = self.red_rois[key][1]
            shape  = self.red_rois[key][1].shape
            contour_line_x = [corner[1], corner[1]+shape[1], corner[1]+shape[1], corner[1], corner[1] ]
            contour_line_y = [corner[0], corner[0], corner[0]+shape[0], corner[0]+shape[0], corner[0] ]
            ax.plot( contour_line_x, contour_line_y, '-k', lw=0.8 )
            # find center of the ROI and write the label
            xcenter = corner[1] + int( inset.shape[1]/2)
            ycenter = corner[0] + int( inset.shape[0]/2)
            string  = '%02d' % (ii+1)
            plt.text( xcenter, ycenter, string )

        plt.show()
christoph's avatar
christoph committed
539

540
541

def convert_redmatrix_to_matrix( masksDict, mask, offsetX=0, offsetY=0 ):
542
    for key, (pos,M)  in six.iteritems(masksDict):
543
544
        num=int("".join([c for c in key if c.isdigit()]))
        S = M.shape
545

546
        inset =    (slice(offsetY+pos[0]  , offsetY+pos[0]+S[0]   ), slice(  offsetX+pos[1]  , offsetX+pos[1]+S[1] ) )
Alessandro Mirone's avatar
Alessandro Mirone committed
547
        mask[  inset   ][M>0] =  num+1
548

549
550
    return mask

christoph's avatar
christoph committed
551
def convert_redmatrix_to_matrix_my( masksDict, mask, offsetX=0, offsetY=0):
552
553
554
555
556
557
558
559
560
    for key in masksDict:
        num = int("".join([c for c in key if c.isdigit()]))
        origin = masksDict[key][0]
        data   = masksDict[key][1]
        for xx in range(len(data[:,0])):
            for yy in range(len(data[0,:])):
                if data[xx,yy] >= 1.0:
                    mask[origin[0]+xx,origin[1]+yy] = num+1
    return mask
561
562


christoph's avatar
christoph committed
563
564
565
566
567
568
569
570
571
572
573
574
def convert_inds_to_matrix(ind_rois,image_shape):
    """
    Converts a ROI defined by a list of lists of tuples into a ROI
    that is defined by an array containing zeros, ones, twos, ..., n's, 
    where n is the number of ROIs.
    ind_rois    = list of lists with pairs of pixel indices
    image_shape = touple defining the shape of the matrix for which the ROIs are valid 
    """
    roi_matrix = np.zeros(image_shape)
    counter = 1
    for pixel in ind_rois:
        for xyind in pixel:
575
            roi_matrix[int(xyind[0]),int(xyind[1])] = counter
christoph's avatar
christoph committed
576
577
578
        counter += 1
    return roi_matrix

579
def convert_matrix_to_redmatrix(matrix_rois, labelformat= 'ROI%02d'):
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
    """
    Converts a ROI defined by an array containing zeros, ones, twos, ..., n's, 
    where n is the number of ROIs, into a dictionary with keys 'ROI00',
    'ROI01', ..., 'ROInn'. Each entry of the dictionary is a list containing a
    tuple with the origin and the reduced ROI.
    matrix_roi = numpy array
    """
    redmatrix = {}
    for ind in range(int(np.amax(matrix_rois))):
        try:
            indices   = np.where(matrix_rois == ind+1)
            origin    = (np.amin(indices[0]), np.amin(indices[1]))
            bound_box = matrix_rois[np.amin(indices[0]):np.amax(indices[0])+1,np.amin(indices[1]):np.amax(indices[1])+1]
            thekey    =labelformat % ind
            redmatrix[thekey] = [origin,bound_box]
        except: # handle empty ROIs
            indices   = np.where(matrix_rois == ind+1)
            origin    = (0, 0)
            bound_box = matrix_rois[0:0,0:0]
            thekey    =labelformat % ind
            redmatrix[thekey] = [origin,bound_box]
    return redmatrix
christoph's avatar
christoph committed
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

def convert_inds_to_xinds(roi_inds):
    """
    Converts ROIs defined in lists of lists of x-y-coordinate tuples into
    a list of x-coordinates only.
    """
    xind_rois = []
    for roi in roi_inds:
        xinds = []
        for pair in roi:
            xinds.append(pair[0])
        xind_rois.append(xinds)
    return xind_rois

def convert_inds_to_yinds(roi_inds):
    """
    Converts ROIs defined in lists of lists of x-y-coordinate tuples into
    a list of y-coordinates only.
    """
    yind_rois = []
    for roi in roi_inds:
        yinds = []
        for pair in roi:
            yinds.append(pair[1])
        yind_rois.append(yinds)
    return yind_rois

def convert_roi_matrix_to_masks(roi_matrix):
    """
    Converts a 2D ROI matrix with zeros, ones, twos, ..., n's (where n is the number of ROIs) to
    a 3D matrix with one slice of zeros and ones per ROI.
    """
    # get the shape
635

Alessandro Mirone's avatar
Alessandro Mirone committed
636
    roi_masks = np.zeros((int(np.amax(roi_matrix)),roi_matrix.shape[0],roi_matrix.shape[1]))
637

christoph's avatar
christoph committed
638
639
    for ii in range(int(np.amax(roi_matrix))):
        inds = np.where(roi_matrix[:,:] == ii+1)
640
641
        for jj in range(len(inds[0])):
            roi_masks[ii,inds[0][jj],inds[1][jj]] = ii+1
christoph's avatar
christoph committed
642
643
644
    return roi_masks

def convert_matrix_rois_to_inds(roi_matrix):
645
646
647
648
649
650
651
652
653
654
655
656
657
    """
    Converts a 2D ROI matrix with zeros, ones, twos, ..., n's (where n is the number of ROIs) to
    a list of lists each of which has tuples with coordinates for each pixel in each roi.
    """
    rois = []
    number_of_rois = int(np.amax(roi_matrix))
    for ii in range(int(number_of_rois)):
        inds = np.where(roi_matrix[:,:] == ii+1)
        oneroi = []
        for i in range(len(inds[0])):
            oneroi.append( (inds[0][i],inds[1][i]) )        
        rois.append(oneroi)
    return rois
christoph's avatar
christoph committed
658
659

def break_down_det_image(image,pixel_num):
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
    """
    Desomposes a Detector image into subimages. Returns a 3D matrix. 
    """
    # check that there are integer multiples of pixel_num in the big image
    if not image.shape[0] % pixel_num == 0 or not image.shape[1] % pixel_num == 0:
        print( 'There must be an integer number of \'pixel_num\' in the large image.')
        return

    detnum_row  = image.shape[0]//pixel_num
    detnum_col  = image.shape[1]//pixel_num
    num_of_dets = detnum_row * detnum_col
    det_images  = np.zeros((num_of_dets,pixel_num,pixel_num))
    x_ranges = []
    for ii in range(detnum_col):
        x_ranges.append((ii*pixel_num, (ii+1)*pixel_num))

    y_ranges = []
    for ii in range(detnum_row):
        y_ranges.append((ii*pixel_num, (ii+1)*pixel_num))

    counter = 0
    offsets = []
    for i in x_ranges:
        for j in y_ranges:
            det_images[counter,:,:]=image[j[0]:j[1],i[0]:i[1]]
            offsets.append((j[0],i[0]))
            counter += 1

    return det_images, offsets
christoph's avatar
christoph committed
689
690

def shift_roi_indices(indices,shift):
691
692
693
694
695
696
697
698
699
    """
    Applies a given shift (xshift,yshift) to given indices. \
    indices = list of (x,y)-tuples
    shift   = (xshift,yshift) tuple
    """
    for ind in indices:
        ind[0] += shift[0]
        ind[1] += shift[1]
    return indices
christoph's avatar
christoph committed
700
701

def merge_roi_objects_by_matrix(list_of_roi_objects,large_image_shape,offsets,pixel_num):
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
    """
    Merges several roi_objects into one big one using the roi_matrices.
    """
    # prepare a big roi matrix
    roi_matrix = np.zeros(large_image_shape)
    counter = 0
    for roi_obj in list_of_roi_objects:
        max_number = int(np.amax(roi_matrix))
        single_matrix = roi_obj.roi_obj.roi_matrix
        inds = single_matrix != 0
        single_matrix[inds] += max_number
        roi_matrix[offsets[counter][0]:offsets[counter][0]+pixel_num,offsets[counter][1]:offsets[counter][1]+pixel_num] = single_matrix
        #roi_obj.roi_obj.roi_matrix + max_number
        counter += 1

    # make a new object to return
    merged_obj = roi_object()
    merged_obj.roi_matrix     = roi_matrix
    merged_obj.indices        = convert_matrix_rois_to_inds(roi_matrix)
    merged_obj.red_rois       = convert_matrix_to_redmatrix(roi_matrix)
    merged_obj.number_of_rois = int(np.amax(roi_matrix))
    merged_obj.kind           = list_of_roi_objects[0].roi_obj.kind
    merged_obj.x_indices      = convert_inds_to_xinds(merged_obj.indices)
    merged_obj.y_indices      = convert_inds_to_yinds(merged_obj.indices)
    merged_obj.masks          = convert_roi_matrix_to_masks(roi_matrix)

    return merged_obj
christoph's avatar
christoph committed
729
730

def swap_indices_old_rois(old_indices):
731
732
733
734
735
736
737
738
739
740
    """
    Swappes x- and y-indices from indices ROIs.
    """
    new_indices = []
    for roi in old_indices:
        one_new_roi = []
        for point in roi:
            one_new_roi.append((point[1],point[0]))
        new_indices.append(one_new_roi)
    return new_indices
christoph's avatar
christoph committed
741

742

Alessandro Mirone's avatar
Alessandro Mirone committed
743
744

def  load_rois_fromh5_address(address):
myron's avatar
myron committed
745
    filename, groupname = xrs_utilities.split_hdf5_address(address)
Alessandro Mirone's avatar
Alessandro Mirone committed
746
747
748
    h5file  = h5py.File(filename, "r")
    h5group = h5file[groupname]
    masks={}
myron's avatar
myron committed
749
750
    newshape, imagesum = load_rois_fromh5(h5group,masks, retrieveImage=True)
    myroi = roi_object()
Alessandro Mirone's avatar
Alessandro Mirone committed
751
752
753
754
    myroi.load_rois_fromMasksDict(masks, newshape=newshape)
    return myroi


755
def load_rois_fromh5(h5group_tot,md, retrieveImage=False, metadata = None):
Alessandro Mirone's avatar
Alessandro Mirone committed
756
757
758
759
760
761
    h5group = h5group_tot["rois_definition/rois_dict"]
    for key in h5group.keys():
        md[key]=[]
        md[key].append(h5group[key]["origin"][:])
        md[key].append(h5group[key]["mask"][:])

762
763
        if metadata is not None:
            newmeta = {}
myron's avatar
myron committed
764
765
766
767
            print("metadata", h5group[key], key)
            if "metadata" in h5group[key]:
                for kk in h5group[key]["metadata"]:
                    newmeta[kk] = h5group[key]["metadata"][kk].value
768
769
            metadata[key]=newmeta

Alessandro Mirone's avatar
Alessandro Mirone committed
770
771
    h5data = h5group_tot["rois_definition/image"]
    shape = h5data.shape
772
773
774
775
    if retrieveImage:
        return shape, np.array(h5data[:])
    else:
        return shape
Alessandro Mirone's avatar
Alessandro Mirone committed
776

777
def write_rois_toh5(h5group,md, filterMask=None, metadata=None):
Alessandro Mirone's avatar
Alessandro Mirone committed
778
    for key in md.keys():
779
780
781
        if key in h5group:
                del h5group[key]
                
Alessandro Mirone's avatar
Alessandro Mirone committed
782
        h5group.require_group(key)
783
784
785
786
787
788
789
790
791

        README="""origin : the Y and X coordinates of the bottom left corner of the mask (putting the origin at the bottom left)
mask : the mask , 1 for considered pixel, zero for discarded ones.
"""
            
        h5_assign_force(h5group[key], "README" , README   )


        
Alessandro Mirone's avatar
Alessandro Mirone committed
792
        h5group[key]["origin"]=md[key][0]
793
        if filterMask is None:
794
795
796
797
                h5group[key]["mask"]=md[key][1]
        else:
                ori = md[key][0]
                sh  = md[key][1].shape
798
                Mfilter =  filterMask[ori[0]:ori[0]+sh[0], ori[1]:ori[1]+sh[1]    ]
799
800
                h5group[key]["mask"]=md[key][1]*Mfilter
                
801
802
803
804
        if metadata is not None:
            if key in metadata:
                metagroup = h5group[key].require_group("metadata")

myron's avatar
API ok    
myron committed
805
806
                for kk,mdata in metadata[key].items():
                    metagroup[kk] = mdata
Alessandro Mirone's avatar
Alessandro Mirone committed
807

808

809

810

myron's avatar
myron committed
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
def  write_rois_toh5_for_resynth(h5group,md, filterMask=None, metadata=None):
    for original_key in md.keys():

        # key = str(int(original_key[len("ROI"):]))
        key = original_key

        
        h5group.require_group(key)

        
        h5group[key]["origin"]=md[original_key][0]
        if filterMask is None:
                h5group[key]["mask"]=md[original_key][1]
        else:
                ori = md[original_key][0]
                sh  = md[original_key][1].shape
                Mfilter =  filterMask[ori[0]:ori[0]+sh[0], ori[1]:ori[1]+sh[1]    ]
                h5group[key]["mask"]=md[key][1]*Mfilter
                



833
834


835