def fit_circle_slow(self, beta_i, ring_scan_width, center_scan_width, resolution=1): """ Description =========== Here we want to find where the forward x-ray beam intersects the detector, (x_center, y_center). The radial profile of the diffraction pattern should exhibit scattering peaks, and the magnitude of the radial profile will depend on the chosen point for the center. Therefore, by calculating many radial profiles for a range of centers, and choosing the radial profile with the maximum magnitude across a specific radial range( e.g. across a Bragg ring), then we can optimize the center. Parameters ========== `beta_i` tuple of (x_center, y_center, ring_position) all in pixel units `ring_scan_width` how many pixels to scan for ring maxima `center_scan_width` how many pixels to scan in horizontal and vertical diredtions for ring maxima Returns ======= tuple of optimized center parameters (x_center, y_center, max_radius) """ self.resolution = resolution self.ring_radius_guess = round(beta_i[2]) self.center_x_guess, self.center_y_guess = round(beta_i[0]), \ round( beta_i[1] ) self.RadPro = RadialProfile( center=(self.center_x_guess, \ self.center_y_guess), img_shape=self.img.shape, mask=None, minlength=self.img.shape[0] ) self._set_radial_scan_range(ring_scan_width) self._define_possible_center_coordinates(center_scan_width) self._store_profiles_for_each_center() self._store_maxima_of_each_profile() self._find_center_with_maximum_ring_profile() self._set_max_ring_profile() return self._get_fit_parameters()
def rad_profiles(img_gen, cent, img_mask, rad_range): """ Makes radial profiles for each image in a generator of images... img_gen, generator of 2D numpy image arrays cent, where forward beam hits detector, in pixel units (fast_scan coordinate, slow_scan coorinate) img_mask, 2D boolean numpy array, same shape as the images 0 is masked, 1 is not masked rad_range, tuple of (min radius, max radius) in pixel units, (radius range of the radial profile) """ print("Computing radial profiles.. ") rp = RadialProfile(cent, mask=img_mask) rad_pros = [rp.calculate(i)[rad_range[0]:rad_range[1]] for i in img_gen] return rad_pros
def rad_profiles( img_gen, cent, img_mask, rad_range ): """ Makes radial profiles for each image in a generator of images... img_gen, generator of 2D numpy image arrays cent, where forward beam hits detector, in pixel units (fast_scan coordinate, slow_scan coorinate) img_mask, 2D boolean numpy array, same shape as the images 0 is masked, 1 is not masked rad_range, tuple of (min radius, max radius) in pixel units, (radius range of the radial profile) """ print("Computing radial profiles.. ") rp = RadialProfile( cent, mask=img_mask ) rad_pros = [rp.calculate(i)[rad_range[0]:rad_range[1]] for i in img_gen ] return rad_pros
def fit_circle_slow(self, beta_i, ring_scan_width, center_scan_width, resolution=1 ): """ Description =========== Here we want to find where the forward x-ray beam intersects the detector, (x_center, y_center). The radial profile of the diffraction pattern should exhibit scattering peaks, and the magnitude of the radial profile will depend on the chosen point for the center. Therefore, by calculating many radial profiles for a range of centers, and choosing the radial profile with the maximum magnitude across a specific radial range( e.g. across a Bragg ring), then we can optimize the center. Parameters ========== `beta_i` tuple of (x_center, y_center, ring_position) all in pixel units `ring_scan_width` how many pixels to scan for ring maxima `center_scan_width` how many pixels to scan in horizontal and vertical diredtions for ring maxima Returns ======= tuple of optimized center parameters (x_center, y_center, max_radius) """ self.resolution = resolution self.ring_radius_guess = round(beta_i[2]) self.center_x_guess, self.center_y_guess = round(beta_i[0]), \ round( beta_i[1] ) self.RadPro = RadialProfile( center=(self.center_x_guess, \ self.center_y_guess), img_shape=self.img.shape, mask=None, minlength=self.img.shape[0] ) self._set_radial_scan_range(ring_scan_width) self._define_possible_center_coordinates(center_scan_width) self._store_profiles_for_each_center() self._store_maxima_of_each_profile() self._find_center_with_maximum_ring_profile() self._set_max_ring_profile() return self._get_fit_parameters()
#~~~~~analysis parameters BEGIN img_sh = (1734, 1731) # point where forward beam intersects detector cent_fname = '/reg/d/psdm/cxi/cxilp6715/results/shared_files/center.npy' mask_fname = args.mask_file cent = np.load( cent_fname) mask = np.load( mask_fname) #~~~~~ WAXS parameters # minimium and maximum radii for calculating radial profile waxs_rmin = 100 # pixel units waxs_rmax = 1110 rp = RadialProfile( cent, img_sh, mask=mask ) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~ hit findin' parameters... beta = 50 # smoothing factor window_size = 200 # pixel units # maxima detection order = 250 # defines minimum neighborhood for local maxima (in radial pixel units) # paraemters for peak validation pk_range = (800, 1045) # radial pixel units, relative to the range of the radial profiles # e.g. will ensure the detected peak lies on rad_pofile[ pk_range[0] : pk_range[1]] #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def calibrate(img_gen, center, wavescan, detscan, q1_sim=2.48, q2_sim=2.99, wavelen_guess=1.41, detdist_guess=0.051, speed='slow', mask=None, index_query_fname=None, scan_width=50, beta=20, window_size=40, factor=0.007, pixsize=0.00005): """Use this function to calibrate the detector distance and the wavelength for a particular group of images (e.g. from a run)""" rad1_guess = int( round( np.tan( 2 * np.arcsin( q1_sim * wavelen_guess / 4. / np.pi)) * detdist_guess / pixsize)) rad2_guess = int( round( np.tan( 2 * np.arcsin( q2_sim * wavelen_guess / 4. / np.pi)) * detdist_guess / pixsize)) rad1_scan = np.arange(rad1_guess - scan_width, rad1_guess + scan_width) rad2_scan = np.arange(rad2_guess - scan_width, rad2_guess + scan_width) radpro = RadialProfile(center, img_shape=mask.shape, mask=mask) radpro.set_params( wavelen=wavelen_guess, detdist=detdist_guess, pixsize=pixsize, factor=factor) detdists, wavelens, scores = [], [], [] for img in img_gen: print ("\nCalibrating a new image...") if speed == 'slow': rp1 = radpro.calculate_using_fetch( img, rad1_scan, index_query_fname=index_query_fname) rp2 = radpro.calculate_using_fetch( img, rad2_scan, index_query_fname=index_query_fname) else: rp1 = radpro.calculate(img)[rad1_scan] rp2 = radpro.calculate(img)[rad2_scan] sm_rp1 = smooth(rp1, beta, window_size) sm_rp2 = smooth(rp2, beta, window_size) r1 = np.argmax(sm_rp1) + rad1_scan[0] r2 = np.argmax(sm_rp2) + rad2_scan[0] print(" Found ring1 at %d compared to %d." % (r1, rad1_guess)) print(" Found ring2 at %d compared to %d." % (r2, rad2_guess)) print(" Calibrating...") detdist, wavelen, score = calibrate_parameters( r1, r2, q1_sim, q2_sim, detscan, wavescan, pixsize) print( " New detector distance calibrated from %.4f --> %.4f" % (detdist_guess, detdist)) print( " New wavelength calibrated from %.4f --> %.4f" % (wavelen_guess, wavelen)) rad1 = int( round( np.tan( 2 * np.arcsin( q1_sim * wavelen / 4. / np.pi)) * detdist / pixsize)) rad2 = int( round( np.tan( 2 * np.arcsin( q2_sim * wavelen / 4. / np.pi)) * detdist / pixsize)) detdists.append(detdist) wavelens.append(wavelen) scores.append(score) return detdists, wavelens, scores
cent = np.load(cent_fname) # f_mask= h5py.File(mask_fname,'r') ##### ###Also have a common mask for calibration purpose basic_mask = np.load( '/reg/d/psdm/cxi/cxilr6716/results/masks/basic_psana_mask.npy') # use basic mask for now mask = np.load('/reg/d/psdm/cxi/cxilr6716/results/masks/basic_psana_mask.npy') # mask = f_mask['mask'].value #### #~~~~~ WAXS parameters # minimium and maximum radii for calculating radial profile waxs_rmin = 100 # pixel units waxs_rmax = 1110 rp = RadialProfile(cent, img_sh, mask=mask) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~ hit findin' parameters... waterpeak_filter = args.waterpeak_filter > 0 beta = 50 # smoothing factor window_size = 200 # pixel units # maxima detection order = 250 # defines minimum neighborhood for local maxima (in radial pixel units) # paraemters for peak validation pk_range = ( 604, 804 ) # radial pixel units, relative to the range of the radial profiles # e.g. will ensure the detected peak lies on rad_pofile[ pk_range[0] : pk_range[1]]
import sys sys.append("../.asu_tools/lib/python") sys.path.append("../.asu_tools/lib/python") from loki.RingData import RingFit rf = RingFit(img) #xi,yi = ( img.shape xi,yi = (1440,1440) get_ipython().run_line_magic('pinfo', 'rf.fit_circle_fast') #rf.fit_circle_fast( (xi,yi, ) xi-1137 ri = 303 rf.fit_circle_fast( (xi,yi,ri ), num_fitting_pts=5000, num_high_pix=100, ring_width=100) x,y,r = array([ 1450.10619788, 1430.22695615, 316.51128542]) from loki.RingData import RadialProfile rp = RadialProfile( (xi,yi), img.shape) figure(2);plot( rp.calculate(img)) rp = RadialProfile( (x,y), img.shape) figure(2);plot( rp.calculate(img)) #rf.fit_circle_fast( (xi,yi,ri ), num_fitting_pts=5000, num_high_pix=100, ring_width=100) circ = Circle(xy=(x,y), radius=r, fc='none', ec='r') figure(1);gca().add_patch(circ);draw() rf.fit_circle_fast( (xi,yi,ri ), num_fitting_pts=10000, num_high_pix=100, ring_width=150) xmymr x,y rf.fit_circle_slow?#( (xi,yi,ri ),) get_ipython().run_line_magic('pinfo', 'rf.fit_circle_slow') get_ipython().run_line_magic('pinfo', 'rf.fit_circle_slow') rf.fit_circle_slow( (x,y,r ), 50,30,2) get_ipython().run_line_magic('pinfo', 'rf.fit_circle_slow') rf.fit_circle_slow( (x,y,r ), 50.,30.,2.)
class RingFit: def __init__( self, img ): ''' Initialize a class for fitting shapes to images with shapes on them ==================================================================== img - 2d float np.array ''' self._set_circle_model() self._set_circle_model_fast() self._set_ellipse_model() self.img = np.copy(img) self._store_1D_image_index_arrays() def _set_circle_model_fast( self): f_circle = lambda beta,x : ( x[0] - beta[0] )**2 \ + ( x[1] - beta[1])**2 - beta[2]**2 self.circle_model_fast = odr.Model(f_circle, implicit=True, estimate=self.calc_estimate, fjacd=self.circle_jacobian_data, fjacb=self.circle_jacobian_beta) def _set_circle_model(self): # Equation for a circle f_circle = lambda beta,x : ( x[0] - beta[0] )**2 \ + ( x[1] - beta[1])**2 - beta[2]**2 self.circle_model = odr.Model( f_circle, implicit=True) def _set_ellipse_model(self): # Equation for an ellipse f_ellipse = lambda beta,x : (1/beta[2]/beta[2])*( x[0] -beta[0])**2 \ + (1/beta[3]/beta[3])*( x[1] -beta[1])**2-1 self.ellipse_model = odr.Model( f_ellipse, implicit=True) def _store_1D_image_index_arrays(self): # make 2d and 1d index arrays self.y, self.x = np.indices( self.img.shape ) self.x1D = self.x.ravel() self.y1D = self.y.ravel() def fit_circle_fast( self, beta_i, num_fitting_pts=5000, ring_width=20 , num_high_pix=20, return_mean = False): ''' Fit a circle to a ring image ============================== beta_i - float tuple , ( x_center, y_center,radius ) num_fitting_pts - int, number of pixels to include in fit ring_width - int, width ring-like region on image that contains ring of interest (pixels units) num_high_pix - int, remove this many pixels before running fit (removes artificial high pixels that might bias the fit) ''' self.ring_width = ring_width self._prepare_fitting_framework( beta_i, num_high_pix, num_fitting_pts) self._fit_a_model_fast(self.circle_model_fast, param_guess=beta_i) x,y = self._get_xy() a,b,r = self.beta_fit Ri = np.sqrt( (x-a)**2 + (y-b)**2) self.residual = np.sum((Ri-r)**2) mean_intens = self.img1D[ self.fit_indices].mean() return self.beta_fit, self.residual, mean_intens def fit_circle( self, beta_i, num_fitting_pts=5000, ring_width=20 , num_high_pix=20, return_mean = False): ''' Fit a circle to a ring image ============================== beta_i - float tuple , ( x_center, y_center,radius ) num_fitting_pts - int, number of pixels to include in fit ring_width - int, width ring-like region on image that contains ring of interest (pixels units) num_high_pix - int, remove this many pixels before running fit (removes artificial high pixels that might bias the fit) ''' self.ring_width = ring_width self._prepare_fitting_framework( beta_i, num_high_pix, num_fitting_pts) self._fit_a_model(self.circle_model, param_guess=beta_i) x,y = self._get_xy() a,b,r = self.beta_fit Ri = np.sqrt( (x-a)**2 + (y-b)**2) self.residual = np.sum((Ri-r)**2) mean_intens = self.img1D[ self.fit_indices].mean() return self.beta_fit, self.residual, mean_intens def _get_xy(self): """returns the x and y points used in _fit_a_model""" return self.pts[0], self.pts[1] def fit_ellipse(self, beta_i ,num_fitting_pts=5000, ring_width=40,num_high_pix = 20): ''' Fit an ellipse to a ring image ============================== beta_i - float tuple , (x_center, y_center, x_radius,y_radius) num_fitting_pts - int, number of pixels to include in fit ring_width - int, width ring-like region on image that contains ring of interest (pixels units) num_high_pix - int, remove this many pixels before running fit (removes artificial high pixels that might bias the fit) ''' self.ring_width = ring_width self._prepare_fitting_framework( beta_i, num_high_pix, num_fitting_pts) self._fit_a_model( self.ellipse_model, param_guess=beta_i) return self.beta_fit def _prepare_fitting_framework(self, ring_guess, num_high_pix, num_fitting_pts): # set the 1D image self.img1D = np.copy(self.img.ravel()) self._mask_1D_image(ring_guess) self._remove_highest(num_high_pix) num_fitting_pts = self._check_num_fitting_points(num_fitting_pts) # find indices of fit pixels in order of decreasing intensity self.fit_indices = np.argsort(self.img1D)[::-1][: num_fitting_pts] def _mask_1D_image(self, beta_i): # radius of each pixel radius_value = np.sqrt((self.x1D - beta_i[0])**2 + \ (self.y1D - beta_i[1])**2 ) self.img1D[ radius_value > beta_i[2] + self.ring_width/2 ] = 0 self.img1D[ radius_value < beta_i[2] - self.ring_width/2 ] = 0 def _remove_highest( self, num_high_pix): # removes the highest pixels so they don't corrupt the fit # (e.g. bad pixels) high_inds = np.argsort( self.img1D )[:num_high_pix] self.img1D[ high_inds ] = 0 def _check_num_fitting_points(self, num_fitting_pts): num_pts = np.where( self.img1D > 0 )[0].shape[0] if num_fitting_pts >= num_pts : num_fitting_pts = num_pts / 2 return num_fitting_pts @staticmethod def circle_jacobian_beta( beta, x): xc,yc,r = beta xi,yi = x df_db = np.empty(( len(beta), x.shape[1])) df_db[0] = 2*(xc-xi) # d_f/dxc df_db[1] = 2*(yc-yi) # d_f/dyc df_db[2] = -2*r # d_f/dr return df_db @staticmethod def circle_jacobian_data(beta, x): xc,yc,r = beta xi,yi = x df_dx = np.empty_like( x ) df_dx[0] = 2*(xi-xc) # d_f/dxi df_dx[1] = 2*(yi-yc) # d_f/dyi return df_dx @staticmethod def calc_estimate(data): xc0, yc0 = data.x.mean(axis=1) r0 = np.sqrt((data.x[0]-xc0)**2 +(data.x[1] -yc0)**2).mean() return xc0, yc0, r0 def _fit_a_model_fast(self, model, param_guess): # use odr module to fit data to model self.pts = np.row_stack( [self.x1D[ self.fit_indices], self.y1D[ self.fit_indices]]) lsc_data = odr.Data( self.pts , y=1) lsc_odr = odr.ODR(lsc_data, model)# param_guess) lsc_odr.set_job(deriv=3) lsc_odr.set_iprint(iter=1, iter_step=1) lsc_out = lsc_odr.run() self.beta_fit = lsc_out.beta def _fit_a_model(self, model, param_guess): # use odr module to fit data to model self.pts = np.row_stack( [self.x1D[ self.fit_indices], self.y1D[ self.fit_indices]]) lsc_data = odr.Data( self.pts , y=1) lsc_odr = odr.ODR( lsc_data, model, param_guess) lsc_out = lsc_odr.run() self.beta_fit = lsc_out.beta def fit_circle_slow(self, beta_i, ring_scan_width, center_scan_width, resolution=1 ): """ Description =========== Here we want to find where the forward x-ray beam intersects the detector, (x_center, y_center). The radial profile of the diffraction pattern should exhibit scattering peaks, and the magnitude of the radial profile will depend on the chosen point for the center. Therefore, by calculating many radial profiles for a range of centers, and choosing the radial profile with the maximum magnitude across a specific radial range( e.g. across a Bragg ring), then we can optimize the center. Parameters ========== `beta_i` tuple of (x_center, y_center, ring_position) all in pixel units `ring_scan_width` how many pixels to scan for ring maxima `center_scan_width` how many pixels to scan in horizontal and vertical diredtions for ring maxima Returns ======= tuple of optimized center parameters (x_center, y_center, max_radius) """ self.resolution = resolution self.ring_radius_guess = round(beta_i[2]) self.center_x_guess, self.center_y_guess = round(beta_i[0]), \ round( beta_i[1] ) self.RadPro = RadialProfile( center=(self.center_x_guess, \ self.center_y_guess), img_shape=self.img.shape, mask=None, minlength=self.img.shape[0] ) self._set_radial_scan_range(ring_scan_width) self._define_possible_center_coordinates(center_scan_width) self._store_profiles_for_each_center() self._store_maxima_of_each_profile() self._find_center_with_maximum_ring_profile() self._set_max_ring_profile() return self._get_fit_parameters() def _set_radial_scan_range(self, ring_scan_width): self.ring_scan_start = self.ring_radius_guess - int( ring_scan_width / 2 ) self.ring_scan_stop = self.ring_radius_guess + int( ring_scan_width / 2 ) def _define_possible_center_coordinates(self, center_scan_width): num_center_scan_points = int( center_scan_width / self.resolution) scan_range_x = np.linspace(self.center_x_guess- \ center_scan_width / 2. , self.center_x_guess + \ center_scan_width / 2. , num_center_scan_points ) scan_range_y = np.linspace(self.center_y_guess- \ center_scan_width / 2. , self.center_y_guess + \ center_scan_width / 2. , num_center_scan_points ) self.possible_centers = [ (x,y) for x in scan_range_x for y in scan_range_y] def _store_profiles_for_each_center(self): self.possible_ring_profiles = [] for center in self.possible_centers: self.RadPro.update_center( center) radial_profile = self.RadPro.calculate(self.img) ring_profile = radial_profile[ self.ring_scan_start:\ self.ring_scan_stop] self.possible_ring_profiles.append( ring_profile) def _store_maxima_of_each_profile(self): self.ring_profile_maxima = [ max( ring_profile) for ring_profile in self.possible_ring_profiles ] def _find_center_with_maximum_ring_profile(self): self.fit_center = self.possible_centers[ np.argmax(self.ring_profile_maxima) ] def _set_max_ring_profile(self): self.max_profile = self.possible_ring_profiles[ \ np.argmax(self.ring_profile_maxima) ] def _get_fit_parameters(self): fit_radius = np.argmax( self.max_profile ) + self.ring_scan_start return ( self.fit_center[0], self.fit_center[1], fit_radius )
def calibrate(img_gen, center, wavescan, detscan, q1_sim=2.48, q2_sim=2.99, wavelen_guess=1.41, detdist_guess=0.051, speed='slow', mask=None, index_query_fname=None, scan_width=50, beta=20, window_size=40, factor=0.007, pixsize=0.00005): """Use this function to calibrate the detector distance and the wavelength for a particular group of images (e.g. from a run)""" rad1_guess = int( round( np.tan(2 * np.arcsin(q1_sim * wavelen_guess / 4. / np.pi)) * detdist_guess / pixsize)) rad2_guess = int( round( np.tan(2 * np.arcsin(q2_sim * wavelen_guess / 4. / np.pi)) * detdist_guess / pixsize)) rad1_scan = np.arange(rad1_guess - scan_width, rad1_guess + scan_width) rad2_scan = np.arange(rad2_guess - scan_width, rad2_guess + scan_width) radpro = RadialProfile(center, img_shape=mask.shape, mask=mask) radpro.set_params(wavelen=wavelen_guess, detdist=detdist_guess, pixsize=pixsize, factor=factor) detdists, wavelens, scores = [], [], [] for img in img_gen: print("\nCalibrating a new image...") if speed == 'slow': rp1 = radpro.calculate_using_fetch( img, rad1_scan, index_query_fname=index_query_fname) rp2 = radpro.calculate_using_fetch( img, rad2_scan, index_query_fname=index_query_fname) else: rp1 = radpro.calculate(img)[rad1_scan] rp2 = radpro.calculate(img)[rad2_scan] sm_rp1 = smooth(rp1, beta, window_size) sm_rp2 = smooth(rp2, beta, window_size) r1 = np.argmax(sm_rp1) + rad1_scan[0] r2 = np.argmax(sm_rp2) + rad2_scan[0] print(" Found ring1 at %d compared to %d." % (r1, rad1_guess)) print(" Found ring2 at %d compared to %d." % (r2, rad2_guess)) print(" Calibrating...") detdist, wavelen, score = calibrate_parameters(r1, r2, q1_sim, q2_sim, detscan, wavescan, pixsize) print(" New detector distance calibrated from %.4f --> %.4f" % (detdist_guess, detdist)) print(" New wavelength calibrated from %.4f --> %.4f" % (wavelen_guess, wavelen)) rad1 = int( round( np.tan(2 * np.arcsin(q1_sim * wavelen / 4. / np.pi)) * detdist / pixsize)) rad2 = int( round( np.tan(2 * np.arcsin(q2_sim * wavelen / 4. / np.pi)) * detdist / pixsize)) detdists.append(detdist) wavelens.append(wavelen) scores.append(score) return detdists, wavelens, scores
data_fname = '/reg/d/psdm/cxi/cxilp6715/scratch/analysis/run%(key)s/small_data-%(key)s.hdf5' % { 'key': run, } cent_fname = '/reg/data/ana14/cxi/cxilp6715/scratch/bin/center.npy' mask_fname = '/reg/data/ana14/cxi/cxilp6715/scratch/bin/mask_rough.npy' # point where forward beam intersects detector cent = np.load(cent_fname) mask = np.load(mask_fname) img_sh = mask.shape # minimium and maximum radii for calculating radial profile rmin = 150 #50 # pixel units rmax = 800 #1110 rp = RadialProfile(cent, img_sh, mask) print("Loading data") f = h5py.File(data_fname, 'r') dat = f['assembled'] print("Computing the radial profiles") radprof = np.array([rp.calculate(img)[rmin:rmax] for img in dat]) #plot radial profile #for i in range( 0,len(radprof),10): # plt.plot(radprof[i], 'b') #plt.show() print("About to cluster...") # CLUSTERING NOW #