|
| 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 |
0 commit comments