Skip to content

Commit 619f47d

Browse files
committed
UPD: move all current code
1 parent 4a76984 commit 619f47d

File tree

68 files changed

+80098
-70
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+80098
-70
lines changed

patches/chx_outlier_detection.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
def is_outlier(points,thresh=3.5,verbose=False):
2+
"""MAD test
3+
"""
4+
points.tolist()
5+
if len(points) ==1:
6+
points=points[:,None]
7+
if verbose:
8+
print('input to is_outlier is a single point...')
9+
median = np.median(points)*np.ones(np.shape(points))#, axis=0)
10+
11+
diff = (points-median)**2
12+
diff=np.sqrt(diff)
13+
med_abs_deviation= np.median(diff)
14+
modified_z_score = .6745*diff/med_abs_deviation
15+
return modified_z_score > thresh
16+
17+
def outlier_mask(avg_img,mask,roi_mask,outlier_threshold = 7.5,maximum_outlier_fraction = .1,verbose=False,plot=False):
18+
"""
19+
outlier_mask(avg_img,mask,roi_mask,outlier_threshold = 7.5,maximum_outlier_fraction = .1,verbose=False,plot=False)
20+
avg_img: average image data (2D)
21+
mask: 2D array, same size as avg_img with pixels that are already masked
22+
roi_mask: 2D array, same size as avg_img, ROI labels 'encoded' as mask values (i.e. all pixels belonging to ROI 5 have the value 5)
23+
outlier_threshold: threshold for MAD test
24+
maximum_outlier_fraction: maximum fraction of pixels in an ROI that can be classifed as outliers. If the detected fraction is higher, no outliers will be masked for that ROI.
25+
verbose: 'True' enables message output
26+
plot: 'True' enables visualization of outliers
27+
returns: mask (dtype=float): 0 for pixels that have been classified as outliers, 1 else
28+
dependency: is_outlier()
29+
30+
function does outlier detection for each ROI separately based on pixel intensity in avg_img*mask and ROI specified by roi_mask, using the median-absolute-deviation (MAD) method
31+
32+
by LW 06/21/2023
33+
"""
34+
hhmask = np.ones(np.shape(roi_mask))
35+
pc=1
36+
37+
for rn in np.arange(1,np.max(roi_mask)+1,1):
38+
rm=np.zeros(np.shape(roi_mask));rm=rm-1;rm[np.where( roi_mask == rn)]=1
39+
pixel = roi.roi_pixel_values(avg_img*rm, roi_mask, [rn] )
40+
out_l = is_outlier((avg_img*mask*rm)[rm>-1], thresh=outlier_threshold)
41+
if np.nanmax(out_l)>0: # Did detect at least one outlier
42+
ave_roi_int = np.nanmean((pixel[0][0])[out_l<1])
43+
if verbose: print('ROI #%s\naverage ROI intensity: %s'%(rn,ave_roi_int))
44+
try:
45+
upper_outlier_threshold = np.nanmin((out_l*pixel[0][0])[out_l*pixel[0][0]>ave_roi_int])
46+
if verbose: print('upper outlier threshold: %s'%upper_outlier_threshold)
47+
except:
48+
upper_outlier_threshold = False
49+
if verbose: print('no upper outlier threshold found')
50+
ind1 = (out_l*pixel[0][0])>0; ind2 = (out_l*pixel[0][0])< ave_roi_int
51+
try:
52+
lower_outlier_threshold = np.nanmax((out_l*pixel[0][0])[ind1*ind2])
53+
except:
54+
lower_outlier_threshold = False
55+
if verbose: print('no lower outlier threshold found')
56+
else:
57+
if verbose: print('ROI #%s: no outliers detected'%rn)
58+
59+
### MAKE SURE we don't REMOVE more than x percent of the pixels in the roi
60+
outlier_fraction = np.sum(out_l)/len(pixel[0][0])
61+
if verbose: print('fraction of pixel values detected as outliers: %s'%np.round(outlier_fraction,2))
62+
if outlier_fraction > maximum_outlier_fraction:
63+
if verbose: print('fraction of pixel values detected as outliers > than maximum fraction %s allowed -> NOT masking outliers...check threshold for MAD and maximum fraction of outliers allowed'%maximum_outlier_fraction)
64+
upper_outlier_threshold = False; lower_outlier_threshold = False
65+
66+
if upper_outlier_threshold:
67+
hhmask[avg_img*rm > upper_outlier_threshold] = 0
68+
if lower_outlier_threshold:
69+
hhmask[avg_img*rm < lower_outlier_threshold] = 0
70+
71+
if plot:
72+
if pc == 1: fig,ax = plt.subplots(1,5,figsize=(24,4))
73+
plt.subplot(1,5,pc);pc+=1;
74+
if pc>5: pc=1
75+
pixel = roi.roi_pixel_values(avg_img*rm*mask, roi_mask, [rn] )
76+
plt.plot( pixel[0][0] ,'bo',markersize=1.5 )
77+
if upper_outlier_threshold or lower_outlier_threshold:
78+
x=np.arange(len(out_l))
79+
plt.plot([x[0],x[-1]],[ave_roi_int,ave_roi_int],'g--',label='ROI average: %s'%np.round(ave_roi_int,4))
80+
if upper_outlier_threshold:
81+
ind=(out_l*pixel[0][0])> upper_outlier_threshold
82+
plt.plot(x[ind],(out_l*pixel[0][0])[ind],'r+')
83+
plt.plot([x[0],x[-1]],[upper_outlier_threshold,upper_outlier_threshold],'r--',label='upper thresh.: %s'%np.round(upper_outlier_threshold,4))
84+
if lower_outlier_threshold:
85+
ind=(out_l*pixel[0][0])< lower_outlier_threshold
86+
plt.plot(x[ind],(out_l*pixel[0][0])[ind],'r+')
87+
plt.plot([x[0],x[-1]],[lower_outlier_threshold,lower_outlier_threshold],'r--',label='lower thresh.: %s'%np.round(upper_outlier_threshold,4))
88+
plt.ylabel('Intensity') ;plt.xlabel('pixel');plt.title('ROI #: %s'%rn);plt.legend(loc='best',fontsize=8)
89+
90+
if plot:
91+
fig,ax = plt.subplots()
92+
plt.imshow(hhmask)
93+
hot_dark=np.nonzero(hhmask<1)
94+
cmap = plt.cm.get_cmap('viridis')
95+
plt.plot(hot_dark[1],hot_dark[0],'+',color=cmap(0))
96+
plt.xlabel('pixel');plt.ylabel('pixel');plt.title('masked pixels with outlier threshold: %s'%outlier_threshold)
97+
98+
return hhmask

patches/fix_get_sid_filenames.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
def get_sid_filenames(hdr,verbose=False):
2+
import glob
3+
from time import strftime, localtime
4+
start_doc = hdr.start
5+
stop_doc = hdr.stop
6+
success = False
7+
8+
ret = (start_doc["scan_id"], start_doc["uid"], glob.glob(f"{start_doc['data path']}*_{start_doc['sequence id']}_master.h5")) # looking for (eiger) datafile at the path specified in metadata
9+
if len(ret[2])==0:
10+
if verbose: print('could not find detector filename from "data_path" in metadata: %s'%start_doc['data path'])
11+
else:
12+
if verbose: print('Found detector filename from "data_path" in metadata!');success=True
13+
14+
if not success: # looking at path in metadata, but taking the date from the run start document
15+
data_path=start_doc['data path'][:-11]+strftime("%Y/%m/%d/",localtime(start_doc['time']))
16+
ret = (start_doc["scan_id"], start_doc["uid"], glob.glob(f"{data_path}*_{start_doc['sequence id']}_master.h5"))
17+
if len(ret[2])==0:
18+
if verbose: print('could not find detector filename in %s'%data_path)
19+
else:
20+
if verbose: print('Found detector filename in %s'%data_path);success=True
21+
22+
if not success: # looking at path in metadata, but taking the date from the run stop document (in case the date rolled over between creating the start doc and staging the detector)
23+
data_path=start_doc['data path'][:-11]+strftime("%Y/%m/%d/",localtime(stop_doc['time']))
24+
ret = (start_doc["scan_id"], start_doc["uid"], glob.glob(f"{data_path}*_{start_doc['sequence id']}_master.h5"))
25+
if len(ret[2])==0:
26+
if verbose: print('Sorry, could not find detector filename....')
27+
else:
28+
if verbose: print('Found detector filename in %s'%data_path);success=True
29+
return ret

patches/polygonmask_fix.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# fix for newer environments on jupyter hub: polygon( y,x, shape = image.shape) -> need to be specific about shape
2+
# Not needed for older, local environments on srv1,2,3. Fix above will probably also work there, BUT there would be a problem with disk <-> circle that has been renamed in skimage at some point
3+
# -> older, local environments on srv1,2,3 work without this fix, only apply when running on jupyter hub
4+
def create_multi_rotated_rectangle_mask( image, center=None, length=100, width=50, angles=[0] ):
5+
''' Developed at July 10, 2017 by Y.G.@CHX, NSLS2
6+
Create multi rectangle-shaped mask by rotating a rectangle with a list of angles
7+
The original rectangle is defined by four corners, i.e.,
8+
[ (center[1] - width//2, center[0]),
9+
(center[1] + width//2, center[0]),
10+
(center[1] + width//2, center[0] + length),
11+
(center[1] - width//2, center[0] + length)
12+
]
13+
14+
Parameters:
15+
image: 2D numpy array, to give mask shape
16+
center: integer list, if None, will be the center of the image
17+
length: integer, the length of the non-ratoted rectangle
18+
width: integer, the width of the non-ratoted rectangle
19+
angles: integer list, a list of rotated angles
20+
21+
Return:
22+
mask: 2D bool-type numpy array
23+
'''
24+
25+
from skimage.draw import polygon
26+
from skimage.transform import rotate
27+
cx,cy = center
28+
imy, imx = image.shape
29+
mask = np.zeros( image.shape, dtype = bool)
30+
wy = length
31+
wx = width
32+
x = np.array( [ max(0, cx - wx//2), min(imx, cx+wx//2), min(imx, cx+wx//2), max(0,cx-wx//2 ) ])
33+
y = np.array( [ cy, cy, min( imy, cy + wy) , min(imy, cy + wy) ])
34+
rr, cc = polygon( y,x, shape = image.shape)
35+
mask[rr,cc] =1
36+
mask_rot= np.zeros( image.shape, dtype = bool)
37+
for angle in angles:
38+
mask_rot += np.array( rotate( mask, angle, center= center ), dtype=bool) #, preserve_range=True)
39+
return ~mask_rot
40+
41+
def create_cross_mask( image, center, wy_left=4, wy_right=4, wx_up=4, wx_down=4,
42+
center_disk = True, center_radius=10
43+
):
44+
'''
45+
Give image and the beam center to create a cross-shaped mask
46+
wy_left: the width of left h-line
47+
wy_right: the width of rigth h-line
48+
wx_up: the width of up v-line
49+
wx_down: the width of down v-line
50+
center_disk: if True, create a disk with center and center_radius
51+
52+
Return:
53+
the cross mask
54+
'''
55+
from skimage.draw import line_aa, line, polygon, disk
56+
57+
imy, imx = image.shape
58+
cx,cy = center
59+
bst_mask = np.zeros_like( image , dtype = bool)
60+
###
61+
#for right part
62+
wy = wy_right
63+
x = np.array( [ cx, imx, imx, cx ])
64+
y = np.array( [ cy-wy, cy-wy, cy + wy, cy + wy])
65+
rr, cc = polygon( y,x,shape=image.shape)
66+
bst_mask[rr,cc] =1
67+
68+
###
69+
#for left part
70+
wy = wy_left
71+
x = np.array( [0, cx, cx,0 ])
72+
y = np.array( [ cy-wy, cy-wy, cy + wy, cy + wy])
73+
rr, cc = polygon( y,x,shape=image.shape)
74+
bst_mask[rr,cc] =1
75+
76+
###
77+
#for up part
78+
wx = wx_up
79+
x = np.array( [ cx-wx, cx + wx, cx+wx, cx-wx ])
80+
y = np.array( [ cy, cy, imy, imy])
81+
rr, cc = polygon( y,x,shape=image.shape)
82+
bst_mask[rr,cc] =1
83+
84+
###
85+
#for low part
86+
wx = wx_down
87+
x = np.array( [ cx-wx, cx + wx, cx+wx, cx-wx ])
88+
y = np.array( [ 0,0, cy, cy])
89+
rr, cc = polygon( y,x,shape=image.shape)
90+
bst_mask[rr,cc] =1
91+
92+
if center_radius!=0:
93+
rr, cc = disk((cy, cx), center_radius, shape = bst_mask.shape)
94+
bst_mask[rr,cc] =1
95+
96+
97+
full_mask= ~bst_mask
98+
99+
return full_mask

patches/roi_nr_2019_3_0_1.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
def get_roi_nr(qdict,q,phi,q_nr=True,phi_nr=False,q_thresh=0, p_thresh=0, silent=True, qprecision=5):
2+
"""
3+
function to return roi number from qval_dict, corresponding Q and phi, lists (sets) of all available Qs and phis
4+
[roi_nr,Q,phi,Q_list,phi_list]=get_roi_nr(..)
5+
calling sequence: get_roi_nr(qdict,q,phi,q_nr=True,phi_nr=False, verbose=True)
6+
qdict: qval_dict from analysis pipeline/hdf5 result file
7+
q: q of interest, can be either value (q_nr=False) or q-number (q_nr=True)
8+
q_thresh: threshold for comparing Q-values, set to 0 for exact comparison
9+
phi: phi of interest, can be either value (phi_nr=False) or q-number (phi_nr=True)
10+
p_thresh: threshold for comparing phi values, set to 0 for exact comparison
11+
silent=True/False: Don't/Do print lists of available qs and phis, q and phi of interest
12+
by LW 10/21/2017
13+
update by LW 08/22/2018: introduced thresholds for comparison of Q and phi values (before: exact match required)
14+
update 2019/09/28 add qprecision to get unique Q
15+
update 2020/3/12 explicitly order input dictionary to fix problem with environment after 2019-3.0.1
16+
"""
17+
import collections
18+
from collections import OrderedDict
19+
qdict = collections.OrderedDict(sorted(qdict.items()))
20+
qs=[]
21+
phis=[]
22+
for i in qdict.keys():
23+
qs.append(qdict[i][0])
24+
phis.append(qdict[i][1])
25+
qslist=list(OrderedDict.fromkeys(qs))
26+
qslist = np.unique( np.round(qslist, qprecision ) )
27+
phislist=list(OrderedDict.fromkeys(phis))
28+
qslist=list(np.sort(qslist))
29+
phislist=list(np.sort(phislist))
30+
if q_nr:
31+
qinterest=qslist[q]
32+
qindices = [i for i,x in enumerate(qs) if np.abs(x-qinterest) < q_thresh]
33+
else:
34+
qinterest=q
35+
qindices = [i for i,x in enumerate(qs) if np.abs(x-qinterest) < q_thresh] # new
36+
if phi_nr:
37+
phiinterest=phislist[phi]
38+
phiindices = [i for i,x in enumerate(phis) if x == phiinterest]
39+
else:
40+
phiinterest=phi
41+
phiindices = [i for i,x in enumerate(phis) if np.abs(x-phiinterest) < p_thresh] # new
42+
ret_list=[list(set(qindices).intersection(phiindices))[0],qinterest,phiinterest,qslist,phislist] #-> this is the original
43+
if silent == False:
44+
print('list of available Qs:')
45+
print(qslist)
46+
print('list of available phis:')
47+
print(phislist)
48+
print('Roi number for Q= '+str(ret_list[1])+' and phi= '+str(ret_list[2])+': '+str(ret_list[0]))
49+
return ret_list

pyCHX/XPCS_GiSAXS.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
This module is for the GiSAXS XPCS analysis
55
"""
66

7+
8+
79
from skbeam.core.accumulators.binned_statistic import BinnedStatistic1D, BinnedStatistic2D
810

911
from pyCHX.chx_compress import (
@@ -771,7 +773,8 @@ def show_label_array_on_image(
771773
"""
772774
ax.set_aspect("equal")
773775
if log_img:
774-
im = ax.imshow(image, cmap=imshow_cmap, interpolation="none", norm=LogNorm(norm), **kwargs) # norm=norm,
776+
norm=LogNorm(vmin=vmin, vmax=vmax)
777+
im = ax.imshow(image, cmap=imshow_cmap, interpolation="none", norm=norm) # norm=norm,
775778
else:
776779
im = ax.imshow(image, cmap=imshow_cmap, interpolation="none", norm=norm, **kwargs) # norm=norm,
777780

0 commit comments

Comments
 (0)