def lambda_values(evt,pulse_energy,sum_over_bkg_frames,fit_bkg,sample_params,outkey=""): frame_expected_phc = numpy.dot(sample_params,numpy.array([pulse_energy**3,pulse_energy**2,pulse_energy,1])) lambdav = sum_over_bkg_frames*frame_expected_phc/fit_bkg.sum() lambdav[lambdav<=0] = 1e-30 v = evt["analysis"] add_record(v, "analysis", outkey+"lambda_values", lambdav) add_record(v, "analysis", outkey+"expected_phc", frame_expected_phc)
def hitrate(evt, hit, history=100, unit='percent', outkey="hitrate"): """Counts hits and adds current hit rate to ``evt["analysis"][outkey]``. Args: :evt: The event variable :hit: A boolean (True for hit, False for miss) Kwargs: :history(int): Buffer length, default = 100 :outkey(str): Data key of resulting ``Record``, default is "hitrate" :unit(str): Unit of hitrate, 'fraction' or 'percent', default is 'fraction' :Authors: Benedikt J. Daurer ([email protected]) Tomas Ekeberg """ global hitrate_counters if outkey not in hitrate_counters or hitrate_counters[outkey].maxlen != history: hitrate_counters[outkey] = collections.deque([], history) hitrate_counters[outkey].append(bool(hit)) hitcount = np.array(hitrate_counters[outkey].count(True)) ipc.mpi.sum("hitcount - " + outkey, hitcount) v = evt["analysis"] if (ipc.mpi.is_main_worker()): hitrate = hitcount[()] / (ipc.mpi.nr_workers() * float(len(hitrate_counters[outkey]))) if unit == 'fraction': add_record(v, "analysis", outkey, hitrate) elif unit == 'percent': add_record(v, "analysis", outkey, 100.*hitrate)
def cropAndCenter(evt, data_rec, cx=None, cy=None, w=None, h=None, outkey='cropped'): data = data_rec.data name = data_rec.name Ny, Nx = data.shape if cx is None: cx = (Nx - 1) / 2. if cy is None: cy = (Ny - 1) / 2. # Round to .0 / .5 cx = np.round(cx * 2) / 2. cy = np.round(cy * 2) / 2. if w is None: w = Nx if h is None: h = Ny data_cropped = data[int(round(cy - h / 2)):int(round(cy + h / 2)), int(round(cx - w / 2)):int(round(cx + w / 2))] add_record(evt["analysis"], "analysis", outkey, data_cropped)
def radial(evt, type, key, mask=None, cx=None, cy=None): """Compute the radial average of a detector image given the center position (and a mask). Adds the records ``evt["analysis"]["radial average - " + key]`` and ``evt["analysis"]["radial distance - " + key]``. .. note:: This feature depends on the python package `libspimage <https://github.com/FilipeMaia/libspimage>`_. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :mask: Binary mask, pixels that are masked out are not counted into the radial average. :cx(float): X-coordinate of the center position. If None the center will be in the middle. :cy(float): Y-coordinate of the center position. If None the center will be in the middle. :Authors: Max F. Hantke ([email protected]) """ success, spimage = utils.io.load_spimage() if not success: print "Skipping analysis.pixel_detector.radial" return image = evt[type][key].data r, img_r = spimage.radialMeanImage(image, msk=mask, cx=cx, cy=cy, output_r=True) valid = np.isfinite(img_r) if valid.sum() > 0: r = r[valid] img_r = img_r[valid] add_record(evt["analysis"], "analysis", "radial distance - " + key, r) add_record(evt["analysis"], "analysis", "radial average - " + key, img_r)
def bin(evt, type, key, binning, mask=None): """Bin a detector image given a binning factor (and mask). Adds the records ``evt["analysis"]["binned image - " + key]`` and ``evt["analysis"]["binned mask - " + key]``. .. note:: This feature depends on the python package `libspimage <https://github.com/FilipeMaia/libspimage>`_. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) :binning(int): The linear binning factor Kwargs: :mask: Binary mask, pixels that are masked out are not counted into the binned value. :Authors: Max F. Hantke ([email protected]) """ success, spimage = utils.io.load_spimage() if not success: print "Skipping analysis.pixel_detector.bin" return image = evt[type][key].data binned_image, binned_mask = spimage.binImage(image, binning, msk=mask, output_binned_mask=True) add_record(evt["analysis"], "analysis", "binned image - "+key, binned_image) if binned_mask is not None: add_record(evt["analysis"], "analysis", "binned mask - "+key, binned_mask)
def photon_error(evt, type_data, key_data, type_fit, key_fit, adu_per_photon): import scipy.misc data = np.array(evt[type_data][key_data].data / (1. * adu_per_photon), dtype="float") fit = np.array(evt[type_fit][key_fit].data / (1. * adu_per_photon), dtype="float") data_best = fit.round() data = data.copy() M = fit != 0 M *= data > 0 M *= data_best > 0 K = data[M] W = fit[M] Ks = data_best[M] # Stirling lKf = K * np.log(K) - K tmp = K < 5 if tmp.sum(): lKf[tmp] = np.log(scipy.misc.factorial(K[tmp], exact=False)) # Stirling lKsf = Ks * np.log(Ks) - Ks tmp = Ks < 5 if tmp.sum(): lKsf[tmp] = np.log(scipy.misc.factorial(Ks[tmp], exact=False)) error = (Ks * np.log(W) - lKsf) - (K * np.log(W) - lKf) error = error.sum() add_record(evt["analysis"], "analysis", "photon error", error, unit='')
def commonModeCSPAD2x2(evt, type, key, mask=None): """Subtraction of common mode using median value of masked pixels (left and right half of detector are treated separately). Adds a record ``evt["analysis"]["cm_corrected - " + key]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :mask: Binary mask :Authors: Max F. Hantke ([email protected]) Benedikt J. Daurer ([email protected]) """ data = evt[type][key].data dataCorrected = np.copy(data) lData = data[:,:data.shape[1]/2] rData = data[:,data.shape[1]/2:] if mask is None: lMask = np.ones(shape=lData.shape, dtype="bool") rMask = np.ones(shape=rData.shape, dtype="bool") else: lMask = mask[:,:data.shape[1]/2] == False rMask = mask[:,data.shape[1]/2:] == False if lMask.sum() > 0: dataCorrected[:,:data.shape[1]/2] -= np.median(lData[lMask]) if rMask.sum() > 0: dataCorrected[:,data.shape[1]/2:] -= np.median(rData[rMask]) add_record(evt["analysis"], "analysis", "cm_corrected - " + key, dataCorrected)
def radial(evt, type, key, mask=None, cx=None, cy=None): """Compute the radial average of a detector image given the center position (and a mask) and saves it to ``evt["analysis"]["radial average - " + key]`` and the radial distances are saved to``evt["analysis"]["radial distance - " + key]`` Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :mask: Binary mask, pixels that are masked out are not counted into the radial average. :cx(float): X-coordinate of the center position. If None the center will be in the middle. :cy(float): Y-coordinate of the center position. If None the center will be in the middle. :Authors: Max F. Hantke ([email protected]) """ import spimage image = evt[type][key].data r, img_r = spimage.radialMeanImage(image, msk=mask, cx=cx, cy=cy, output_r=True) valid = np.isfinite(img_r) if valid.sum() > 0: r = r[valid] img_r = img_r[valid] add_record(evt["analysis"], "analysis", "radial distance - "+key, r) add_record(evt["analysis"], "analysis", "radial average - "+key, img_r)
def hitrate(evt, hit, history=100, unit='percent', outkey="hitrate"): """Counts hits and adds current hit rate to ``evt["analysis"][outkey]``. Args: :evt: The event variable :hit: A boolean (True for hit, False for miss) Kwargs: :history(int): Buffer length, default = 100 :outkey(str): Data key of resulting :func:`~backend.Record` object, default is "hitrate" :unit(str): Unit of hitrate, 'fraction' or 'percent', default is 'percent' :Authors: Benedikt J. Daurer ([email protected]), Tomas Ekeberg """ hit = np.atleast_1d(hit) global hitrate_counters if outkey not in hitrate_counters or hitrate_counters[ outkey].maxlen != history: hitrate_counters[outkey] = collections.deque([], history) for h in hit: hitrate_counters[outkey].append(bool(h)) hitcount = np.array(hitrate_counters[outkey].count(True)) ipc.mpi.sum("hitcount - " + outkey, hitcount) v = evt["analysis"] if (ipc.mpi.is_main_event_reader()): hitrate = hitcount[()] / (ipc.mpi.nr_event_readers() * float(len(hitrate_counters[outkey]))) if unit == 'fraction': add_record(v, "analysis", outkey, hitrate) elif unit == 'percent': add_record(v, "analysis", outkey, 100. * hitrate)
def pnccdGain(evt, record, gainmode): """Returns gain (Number of ADUs per photon) based on photon energy record and gain mode. Args: :evt: The event variable :record: A photon energy ``Record`` given in eV :gainmode: The gain mode of PNCCD (3,4,5,6) or 0 for no gain """ maximum_gain = 1250 # at photon energy of 1keV if gainmode == 6: # 1/1 gain = maximum_gain elif gainmode == 5 :# 1/4 gain = maximum_gain/4 elif gainmode == 4: # 1/16 gain = maximum_gain/16 elif gainmode == 3: # 1/64 gain = maximum_gain/64 elif gainmode == 2: # 1/128 gain = maximum_gain/128 elif gainmode == 1: # 1/256 gain = maximum_gain/256 elif gainmode == 0: gain = 1. gain = gain * (record.data / 1000.) # Rescale gain given a photon energy in eV add_record(evt['analysis'], 'analysis', 'gain', gain)
def absolute_error(evt, type_a, key_a, type_b, key_b, out_key=None): """Returning the absolute error between two records as a new record.""" a = evt[type_a][key_a] b = evt[type_b][key_b] if out_key is None: out_key = "abs(%s - %s)" %(a.name, b.name) add_record(evt["analysis"], "analysis", out_key, abs(a.data-b.data), unit='')
def assemble(evt, type, key, x, y, nx=None, ny=None, subset=None, outkey=None): """Asesembles a detector image given some geometry and adds assembled image to ``evt["analysis"]["assembled - " + key]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) :x(int ndarray): X coordinates :y(int ndarray): Y coordinates Kwargs: :nx(int): Total width of assembled image (zero padding) :ny(int): Total height of assembled image (zero padding) :Authors: Benedikt J. Daurer ([email protected]) """ if not key in initialized: if subset is not None: x_ss = [] y_ss = [] for i in subset: panel = i / 2 asic = i % 2 x_ss.append(x[panel,:,(asic*194):((asic+1)*194)]) y_ss.append(y[panel,:,(asic*194):((asic+1)*194)]) x_ss = np.hstack(x_ss) y_ss = np.hstack(y_ss) else: x_ss = x y_ss = y assembled, height, width, shape, y_ss, x_ss = utils.array.assembleImage(x_ss, y_ss ,nx=nx, ny=ny, return_indices=True) initialized[key] = { 'assembled':assembled, 'height':height, 'width':width, 'shape':shape, 'y':y_ss, 'x':x_ss } assembled = initialized[key]['assembled'] height = initialized[key]['height'] width = initialized[key]['width'] shape = initialized[key]['shape'] y = initialized[key]['y'] x = initialized[key]['x'] if subset is not None: data = [] for i in subset: panel = i / 2 asic = i % 2 data.append(evt[type][key].data[panel,:,(asic*194):((asic+1)*194)]) data = np.hstack(data) else: data = evt[type][key].data assembled[height-shape[0]:, :shape[1]][y,x] = data if outkey is None: add_record(evt["analysis"], "analysis", "assembled - "+key, assembled) else: add_record(evt["analysis"], "analysis", outkey, assembled)
def lambda_values(evt,pulse_energy,sum_over_bkg_frames,fit_bkg,sample_params,outkey=""): frame_expected_phc = np.dot(sample_params,np.array([pulse_energy**3,pulse_energy**2,pulse_energy,1])) lambdav = sum_over_bkg_frames*frame_expected_phc/fit_bkg.sum() lambdav[lambdav<=0] = 1e-30 v = evt["analysis"] add_record(v, "analysis", outkey+"lambda_values", lambdav) add_record(v, "analysis", outkey+"expected_phc", frame_expected_phc)
def commonModeCSPAD2x2(evt, type, key, mask=None): """Subtraction of common mode using median value of masked pixels (left and right half of detector are treated separately). Adds a record ``evt["analysis"]["cm_corrected - " + key]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :mask: Binary mask :Authors: Max F. Hantke ([email protected]) Benedikt J. Daurer ([email protected]) """ data = evt[type][key].data dataCorrected = np.copy(data) lData = data[:, :data.shape[1] / 2] rData = data[:, data.shape[1] / 2:] if mask is None: lMask = np.ones(shape=lData.shape, dtype="bool") rMask = np.ones(shape=rData.shape, dtype="bool") else: lMask = mask[:, :data.shape[1] / 2] == False rMask = mask[:, data.shape[1] / 2:] == False if lMask.sum() > 0: dataCorrected[:, :data.shape[1] / 2] -= np.median(lData[lMask]) if rMask.sum() > 0: dataCorrected[:, data.shape[1] / 2:] -= np.median(rData[rMask]) add_record(evt["analysis"], "analysis", "cm_corrected - " + key, dataCorrected)
def someAnalysis(evt, type, key, keyword=None): """An example for an analysis module. Please document here in the docstring: - what the module is doing - what arguments need to be passed - what the module returns (adds to the event variable) - who the authors are Args: :evt: The event variable :type(str): The event type :key(str): The event key Kwargs: :keyword(type): Kewyword description (default = None) :Authors: Name (email), Name (email) """ # ADD YOUR CODE HERE # # something = .... add_record(evt["analysis"], "analysis", "somethingNew" + key, something, unit=some_unit)
def bin(evt, type, key, binning, mask=None): """Bin a detector image given a binning factor (and mask). Adds the records ``evt["analysis"]["binned image - " + key]`` and ``evt["analysis"]["binned mask - " + key]``. .. note:: This feature depends on the python package `libspimage <https://github.com/FilipeMaia/libspimage>`_. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) :binning(int): The linear binning factor Kwargs: :mask: Binary mask, pixels that are masked out are not counted into the binned value. :Authors: Max F. Hantke ([email protected]) """ success, spimage = utils.io.load_spimage() if not success: print "Skipping analysis.pixel_detector.bin" return image = evt[type][key].data binned_image, binned_mask = spimage.binImage(image, binning, msk=mask, output_binned_mask=True) add_record(evt["analysis"], "analysis", "binned image - " + key, binned_image) if binned_mask is not None: add_record(evt["analysis"], "analysis", "binned mask - " + key, binned_mask)
def photon_error(evt, type_data, key_data, type_fit, key_fit, adu_per_photon): import scipy.misc data = np.array(evt[type_data][key_data].data / (1.*adu_per_photon), dtype="float") fit = np.array(evt[type_fit][key_fit].data / (1.*adu_per_photon), dtype="float") data_best = fit.round() data = data.copy() M = fit != 0 M *= data > 0 M *= data_best > 0 K = data[M] W = fit[M] Ks = data_best[M] # Stirling lKf = K*np.log(K)-K tmp = K < 5 if tmp.sum(): lKf[tmp] = np.log( scipy.misc.factorial(K[tmp], exact=False) ) # Stirling lKsf = Ks*np.log(Ks)-Ks tmp = Ks < 5 if tmp.sum(): lKsf[tmp] = np.log( scipy.misc.factorial(Ks[tmp], exact=False) ) error = ( Ks * np.log(W) - lKsf ) - ( K * np.log(W) - lKf ) error = error.sum() add_record(evt["analysis"], "analysis", "photon error", error, unit='')
def someAnalysis(evt, type, key, keyword=None): """An example for an analysis module. Please document here in the docstring: - what the module is doing - what arguments need to be passed - what the module returns (adds to the event variable) - who the authors are Args: :evt: The event variable :type(str): The event type :key(str): The event key Kwargs: :keyword(type): Kewyword description (default = None) :Authors: Name (email), Name (email) """ # ADD YOUR CODE HERE # # something = .... add_record(evt["analysis"], "analysis", "somethingNew"+key, something, unit=some_unit)
def countTof(evt, record, signalThreshold=1, minWindow=0, maxWindow=-1, hitscoreThreshold=2, outkey="tof: "): """A simple hitfinder that performs a peak counting test on a time-of-flight detector signal, in a specific subwindow and adds the result to ``evt["analysis"][outkey + "isHit"]``, and the hitscore to ``evt["analysis"][outkey + "hitscore"]``. Args: :evt: The event variable :record: A ToF detector :func:`~backend.Record` object Kwargs: :signalThreshold(str): The threshold of the signal, anything above this contributes to the score, default=1 :minWindow(int): Lower limit of window, default=0 :maxWindow(int): Upper limit of window, default=1 :hitscoreThreshold(int): events with hitscore (Nr. of photons) above this threshold are hits, default=2 :outkey(str): Prefix of data key of resulting :func:`~backend.Record` object, default is "tof: " :Authors: Carl Nettelblad ([email protected]) """ hitscore = record.data[minWindow:maxWindow] > signalThreshold hit = hitscore > hitscoreThreshold v = evt["analysis"] add_record(v, "analysis", outkey + "isHit", hit) add_record(v, "analysis", outkey + "hitscore", hitscore)
def bgsub(evt, type, key, bg): data = evt[type][key].data if bg is not None: dataCorrected = data - bg else: dataCorrected = data add_record(evt["analysis"], "analysis", "bgsub - " + key, dataCorrected)
def commonModePNCCD(evt, type, key, outkey=None, transpose=False, signal_threshold=None): """Common mode correction for PNCCDs. For each row its median value is subtracted (left and right half of detector are treated separately). Adds a record ``evt["analysis"][outkey]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :outkey(str): The event key for the corrected image, default is "corrected - " + key :transpose(bool): Apply procedure on transposed image :signal_threshold(float): Apply procedure by using only pixels below given value :Authors: Max F. Hantke ([email protected]) Benedikt J. Daurer ([email protected]) """ if outkey is None: outkey = "corrected - " + key data = evt[type][key].data if transpose: data = data.transpose() dataCorrected = np.copy(data) lData = np.copy(data[:, :data.shape[1] / 2]) rData = np.copy(data[:, data.shape[1] / 2:]) if signal_threshold is not None: # Set values above singal_threshold to nan np.putmask(lData, lData > signal_threshold, np.nan) np.putmask(rData, rData > signal_threshold, np.nan) # Calculate median from values that are not nan lCM = np.nanmedian(lData, axis=1).repeat(lData.shape[1]).reshape(lData.shape) rCM = np.nanmedian(rData, axis=1).repeat(rData.shape[1]).reshape(rData.shape) # If a whole row is above threshold the CM correction shall not be applied np.putmask(lCM, np.isnan(lCM), 0.) np.putmask(rCM, np.isnan(rCM), 0.) # Subtract common modes dataCorrected[:, :data.shape[1] / 2] -= lCM dataCorrected[:, data.shape[1] / 2:] -= rCM if transpose: dataCorrected = dataCorrected.transpose() add_record(evt["analysis"], "analysis", outkey, dataCorrected)
def getMaskedParticles(evt, type, key, output, thresh = 20, minX = 800, maxX = 1500, minY = 0, maxY = 1700, kw = 5): """Black-box method to create a masked version of a camera image where individual illuminated particles constitute a mask.""" outimg = np.zeros(evt[type][key].data.shape, np.dtype(np.uint8)) kernel = np.ones((kw*2+1,kw*2+1), np.uint8) outimg[minY:maxY, minX:maxX] = evt[type][key].data[minY:maxY,minX:maxX] > thresh outimg = cv2.dilate(outimg, kernel) # cv2.adaptiveThreshold(evt[type][key].data.astype(np.uint8), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) add_record(evt["analysis"], "analysis", output, outimg)
def photon_count_frame(evt, front_type_s, front_key_s, aduThreshold, outkey=""): photon_frame = (evt[front_type_s][front_key_s].data / aduThreshold).round() photon_frame[photon_frame <= 0] = 0 v = evt["analysis"] add_record(v, "analysis", outkey + "photon_count", photon_frame)
def countContours(evt, type, key, maskedKey, outimage, outvector): imageoutput = np.ndarray(evt[type][key].data.shape, np.uint8) (contours,_) = cv2.findContours(evt["analysis"][maskedKey].data, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) for i in xrange(len(contours)): cv2.drawContours(imageoutput, contours, i, i + 1, -1) needed_labels = np.arange(1, len(contours)) counts = scipy.ndimage.measurements.sum(evt[type][key].data, imageoutput, needed_labels) add_record(evt["analysis"], "analysis", outimage, imageoutput) add_record(evt["analysis"], "analysis", outvector, counts)
def cmc_pnccd(evt, type, key): try: data = evt[type][key].data except: print "NO DATA!" return if data is None: return dataCorrected = utils.array.cmc_pnccd(data) add_record(evt["analysis"], "analysis", "cmc_pnccd - " + key, dataCorrected)
def stat_hitfinder(evt, pulse_energy, thr_params, bag_bkg, outkey="bagscore: "): thr = thr_params[0] * pulse_energy + thr_params[1] + 2 * bag_bkg.std() hit = evt["analysis"]["baglivo_score"].data > thr v = evt["analysis"] add_record(v, "analysis", outkey + "isHit", hit) add_record(v, "analysis", outkey + "threshold", thr)
def sphereModel(evt, type, key_centerx, key_centery, key_diameter, key_intensity, shape, wavelength=1., pixelsize=110, distance=1000, adu_per_photon=1, quantum_efficiency=1, material='virus', poisson=False): """Return sphere model. .. note:: For this function, `libspimage <https://github.com/FilipeMaia/libspimage>`_ needs to be installed. Args: :evt: The event variable :type(str): The event type, e.g. analysis :key_centerx(str): The event key of the estimated off center shift in x :key_centery(str): The event key of the estimated off center shift in y :key_diameter(str): The event key of the estimated diameter :key_intensity(str): The event key of the estimated intensity :shape(tuple): The shape of the fit Kwargs: :wavelength(float): Photon wavelength [nm] (default = 1) :pixelsize(int): Side length of a pixel [um] (default=110) :distance(int): Distance from interaction to detector [mm] (default = 1000) :adu_per_photon(int): ADUs per photon (default = 1) :quantum_efficiency(float): Quantum efficiency of the detector (default = 1) :material(str): Material of particle, e.g. virus, protein, water, ... (default = virus) :poisson(bool): If True, apply poisson sampling (default = False) :Authors: Benedikt J. Daurer ([email protected]), Max Hantke, Filipe Maia """ success, spimage = utils.io.load_spimage() if not success: print "Skipping analysis.sizing.sphereModel" return centerx = evt[type][key_centerx].data centery = evt[type][key_centery].data diameter = evt[type][key_diameter].data * 1e-9 intensity = evt[type][key_intensity].data * 1e-3 / 1e-12 wavelength *= 1e-9 distance *= 1e-3 pixelsize *= 1e-6 size = spimage.sphere_model_convert_diameter_to_size(diameter, wavelength, pixelsize, distance) scaling = spimage.sphere_model_convert_intensity_to_scaling(intensity, diameter, wavelength, pixelsize, distance, quantum_efficiency, adu_per_photon, material) fit = spimage.I_sphere_diffraction(scaling, spimage.rgrid(shape, (centerx, centery)), size) if poisson: fit = np.random.poisson(fit) add_record(evt["analysis"], "analysis", "fit", fit, unit='ADU')
def countContours(evt, type, key, maskedKey, outimage, outvector): imageoutput = np.ndarray(evt[type][key].data.shape, np.uint8) (contours, _) = cv2.findContours(evt["analysis"][maskedKey].data, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) for i in xrange(len(contours)): cv2.drawContours(imageoutput, contours, i, i + 1, -1) needed_labels = np.arange(1, len(contours)) counts = scipy.ndimage.measurements.sum(evt[type][key].data, imageoutput, needed_labels) add_record(evt["analysis"], "analysis", outimage, imageoutput) add_record(evt["analysis"], "analysis", outvector, counts)
def commonModePNCCD(evt, type, key, outkey=None, transpose=False, signal_threshold=None, mask=None, min_nr_pixels_per_median=1): """Common mode correction for PNCCDs. For each row its median value is subtracted (left and right half of detector are treated separately). Adds a record ``evt["analysis"][outkey]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :outkey(str): The event key for the corrected image, default is "corrected - " + key :transpose(bool): Apply procedure on transposed image :signal_threshold(float): Apply procedure by using only pixels below given value :mask: You may provide a boolean mask with values that shall be excluded from common mode correction :min_nr_pixels_per_median(int): Common mode correction will be skipped for pixel lines that do not contain as much as this number of values :Authors: Max F. Hantke ([email protected]) Benedikt J. Daurer ([email protected]) """ if outkey is None: outkey = "corrected - " + key data = evt[type][key].data dataCorrected = np.copy(data) lData = dataCorrected[:, :data.shape[1] / 2] rData = dataCorrected[:, data.shape[1] / 2:] if mask is not None: lMask = mask[:, :data.shape[1] / 2] rMask = mask[:, data.shape[1] / 2:] else: lMask = None rMask = None _cmc(lData, msk=lMask, axis=1 if not transpose else 0, signal_threshold=signal_threshold, min_nr_pixels_per_median=min_nr_pixels_per_median) _cmc(rData, msk=rMask, axis=1 if not transpose else 0, signal_threshold=signal_threshold, min_nr_pixels_per_median=min_nr_pixels_per_median) add_record(evt["analysis"], "analysis", outkey, dataCorrected)
def absolute_error(evt, type_a, key_a, type_b, key_b, out_key=None): """Returning the absolute error between two records as a new record.""" a = evt[type_a][key_a] b = evt[type_b][key_b] if out_key is None: out_key = "abs(%s - %s)" % (a.name, b.name) add_record(evt["analysis"], "analysis", out_key, abs(a.data - b.data), unit='')
def baglivo_score(evt,poisson_mask,outkey=""): #poisson_mask = poisson_mask.astype(bool) N = evt["analysis"]["expected_phc"].data observed_phc = evt["analysis"]["photon_count"].data[poisson_mask] lambda_values = evt["analysis"]["lambda_values"].data[poisson_mask] normalized_lambdas = lambda_values/lambda_values.sum() partial_sum = observed_phc*(numpy.log(observed_phc) - numpy.log(normalized_lambdas) - numpy.log(N)) partial_sum[observed_phc==0] = 0 logval = partial_sum.sum() v = evt["analysis"] add_record(v, "analysis", outkey+"baglivo_score", logval)
def baglivo_score(evt,poisson_mask,outkey=""): #poisson_mask = poisson_mask.astype(bool) N = evt["analysis"]["expected_phc"].data observed_phc = evt["analysis"]["photon_count"].data[poisson_mask] lambda_values = evt["analysis"]["lambda_values"].data[poisson_mask] normalized_lambdas = lambda_values/lambda_values.sum() partial_sum = observed_phc*(np.log(observed_phc) - np.log(normalized_lambdas) - np.log(N)) partial_sum[observed_phc==0] = 0 logval = partial_sum.sum() v = evt["analysis"] add_record(v, "analysis", outkey+"baglivo_score", logval)
def commonModePNCCD(evt, type, key, outkey=None, transpose=False, signal_threshold=None): """Common mode correction for PNCCDs. For each row its median value is subtracted (left and right half of detector are treated separately). Adds a record ``evt["analysis"][outkey]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :outkey(str): The event key for the corrected image, default is "corrected - " + key :transpose(bool): Apply procedure on transposed image :signal_threshold(float): Apply procedure by using only pixels below given value :Authors: Max F. Hantke ([email protected]) Benedikt J. Daurer ([email protected]) """ if outkey is None: outkey = "corrected - " + key data = evt[type][key].data if transpose: data = data.transpose() dataCorrected = np.copy(data) lData = np.copy(data[:,:data.shape[1]/2]) rData = np.copy(data[:,data.shape[1]/2:]) if signal_threshold is not None: # Set values above singal_threshold to nan np.putmask(lData, lData > signal_threshold, np.nan) np.putmask(rData, rData > signal_threshold, np.nan) # Calculate median from values that are not nan lCM = np.nanmedian(lData,axis=1).repeat(lData.shape[1]).reshape(lData.shape) rCM = np.nanmedian(rData,axis=1).repeat(rData.shape[1]).reshape(rData.shape) # If a whole row is above threshold the CM correction shall not be applied np.putmask(lCM, np.isnan(lCM) , 0.) np.putmask(rCM, np.isnan(rCM) , 0.) # Subtract common modes dataCorrected[:,:data.shape[1]/2] -= lCM dataCorrected[:,data.shape[1]/2:] -= rCM if transpose: dataCorrected = dataCorrected.transpose() add_record(evt["analysis"], "analysis", outkey, dataCorrected)
def averagePulseEnergy(evt, type): """Averages pulse energies. Expects an event and an event type and adds the average pulse energy to ``evt["analysis"]["averagePulseEnergy"]``. :Authors: Filipe Maia """ pulseEnergies = evt[type] pulseEnergy = [] for pE in pulseEnergies.values(): if (pE.unit == ureg.mJ): pulseEnergy.append(pE.data) if pulseEnergy: add_record(evt["analysis"], "analysis", "averagePulseEnergy", np.mean(pulseEnergy), ureg.mJ)
def moveHalf(evt, record, vertical=0, horizontal=0, outkey='half-moved', transpose=False): data = record.data if transpose: data = np.transpose(data) ny, nx = data.shape data_moved = np.zeros((ny + abs(vertical), nx + horizontal), dtype=data.dtype) if horizontal < 0: horizontal = 0 if vertical < 0: data_moved[-vertical:, :nx / 2] = data[:, :nx / 2] data_moved[:vertical, nx / 2 + horizontal:] = data[:, nx / 2:] elif vertical > 0: data_moved[:-vertical, :nx / 2] = data[:, :nx / 2] data_moved[vertical:, nx / 2 + horizontal:] = data[:, nx / 2:] else: data_moved[:, :nx / 2] = data[:, :nx / 2] data_moved[:, nx / 2 + horizontal:] = data[:, nx / 2:] if transpose: data_moved = np.transpose(data_moved) return add_record(evt["analysis"], "analysis", outkey, data_moved)
def cmc(evt, type, key, mask=None): """ Common mode subtraction with median value from pixels within given mask. Add record ``evt["analysis"]["cmc - " + key]`` Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :mask: Binary mask :Authors: Max F. Hantke ([email protected]) """ data = evt[type][key].data dataCorrected = utils.array.cmc(data, mask=mask) add_record(evt["analysis"], "analysis", "cmc - " + key, dataCorrected)
def onEvent(evt): #analysis.event.printKeys(evt) analysis.event.printNativeKeys(evt) print(evt['photonPixelDetectors']['AGIPD'].data.shape) # Timestamp T = evt["eventID"]["Timestamp"] # Nr. of pulses per train npulses = len(T.timestamp) analysis.event.printProcessingRate(pulses_per_event=npulses) print("%d pulses per train" % (npulses)) print("Bad cells: ", T.badCells) # Read data/gain from AGIPD source agipd_data = evt['photonPixelDetectors']['AGIPD'].data[0] agipd_gain = evt['photonPixelDetectors']['AGIPD'].data[1] agipd_train = add_record(evt['analysis'], 'analysis', 'AGIPD/train', agipd_data) # Do hitfinding on a full train analysis.hitfinding.countLitPixels(evt, agipd_train, aduThreshold=0, hitscoreThreshold=0, stack=True) hittrain = evt['analysis']['litpixel: isHit'].data # Select pulses from the AGIPD train that are hits agipd_hits = agipd_train.data[..., hittrain] # Hitrate analysis.hitfinding.hitrate(evt, hittrain) if ipc.mpi.is_main_worker(): print("The current hit rate is %.2f %%" % evt['analysis']['hitrate'].data) # Iterate through the hits max_hits = 10 # The maximum number of hits to be selected from each train for i in range(len(agipd_hits[:max_hits])): agipd_pulse = add_record(evt['analysis'], 'analysis', 'AGIPD', agipd_hits[..., i]) plotting.image.plotImage(agipd_pulse) print("")
def getMaskedParticles(evt, type, key, output, thresh=20, minX=800, maxX=1500, minY=0, maxY=1700, kw=5): """Black-box method to create a masked version of a camera image where individual illuminated particles constitute a mask.""" outimg = np.zeros(evt[type][key].data.shape, np.dtype(np.uint8)) kernel = np.ones((kw * 2 + 1, kw * 2 + 1), np.uint8) outimg[minY:maxY, minX:maxX] = evt[type][key].data[minY:maxY, minX:maxX] > thresh outimg = cv2.dilate(outimg, kernel) add_record(evt["analysis"], "analysis", output, outimg)
def add_new(): data_keys = self.sort_data_dict() i, u, p, s = entry_item.get(), entry_unit.get(), entry_price.get(), entry_stock.get() if len(i) != 0 and len(u) != 0 and len(p) != 0 and len(s) != 0: key = f'{i}|{u}' if key not in data_keys: try: backend.add_record((i, u, float(p), float(s))) self.update_data_list() except ValueError: messagebox.showwarning( 'Warning', '"Price" and "Stock" should contain numbers not words') else: messagebox.showwarning( 'Warning', 'This item is already in database') search() else: messagebox.showwarning('Warning', "You can't add nothing")
def cropAndCenter(evt, data_rec, cx=None, cy=None, w=None, h=None, outkey='cropped'): data = data_rec.data name = data_rec.name Ny, Nx = data.shape if cx is None: cx = (Nx-1)/2. if cy is None: cy = (Ny-1)/2. # Round to .0 / .5 cx = np.round(cx * 2)/2. cy = np.round(cy * 2)/2. if w is None: w = Nx if h is None: h = Ny data_cropped = data[cy-h/2:cy+h/2, cx-w/2:cx+w/2] add_record(evt["analysis"], "analysis", outkey, data_cropped)
def totalNrPhotons(evt, type, key, aduPhoton=1, aduThreshold=0.5): """Estimates the total nr. of photons on the detector and adds it to ``evt["analysis"]["nrPhotons - " + key]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :aduPhoton(int): ADU count per photon, default = 1 :aduThreshold(int): only pixels above this threshold given in units of ADUs are valid, default = 0.5 :Authors: Benedikt J. Daurer ([email protected]) """ data = evt[type][key].data.flat valid = data > aduThreshold add_record(evt["analysis"], "analysis", "nrPhotons - " + key, sum(data[valid]) / float(aduPhoton))
def findCenter(evt, type, key, mask=None, x0=0, y0=0, maxshift=10, threshold=0.5, blur=4): """Estimating the center of diffraction based on pair-wise correlation enforcing friedel-symmetry and adding the estimated off center shifts cx and cy to ``evt['analysis']['offCenterX']`` and ``evt['analysis']['offCenterX']``. .. note:: For this function, `libspimage <https://github.com/FilipeMaia/libspimage>`_ needs to be installed. Args: :evt: The event variable :type(str): The event type of detectors, e.g. photonPixelDetectors :key(str): The event key of a detector, e.g. CCD Kwargs: :mask(bool or int): Only valid pixels (mask == True or 1) are used (default: all pixels are valid) :x0(int): Initial guess for off center shift in x given in pixels (default = 0) :y0(int): Initial guess for off center shift in y given in pixels (default = 0) :maxshift(int): Maximum shift (in both directions) in pixels that is used for searching optimum (default = 10) :threshold(float): Intensities below this threshold are set to zero (default = 0.5) :blur(int): Radius of the blurring kernel used to find the solution quickly (default = 4) :Authors: Benedikt J. Daurer ([email protected]), Filipe Maia, Tomas Ekeberg """ success, spimage = utils.io.load_spimage() if not success: print "Skipping analysis.sizing.findCenter" return img = evt[type][key].data if mask is None: mask = np.ones(shape=img.shape, dtype="bool") else: mask = np.array(mask, dtype="bool") cx, cy = spimage.find_center(img, mask, method='blurred', x0=x0, y0=y0, dmax=maxshift, threshold=threshold, blur_radius=blur) v = evt["analysis"] add_record(v, "analysis", "offCenterX", cx, unit='px') add_record(v, "analysis", "offCenterY", cy, unit='px') add_record(v, "analysis", "cx", (img.shape[1] - 1) / 2. + cx, unit='px') add_record(v, "analysis", "cy", (img.shape[0] - 1) / 2. + cy, unit='px')
def maxPhotonValue(evt, record, aduPhoton=1, outkey=None): """Estimates the maximum number of photons on one pixel on the detector and adds it to ``evt["analysis"][outkey]``. Args: :evt: The event variable :record: The data record (e.g. evt['photonPixelDetectors']['CCD']) Kwargs: :aduPhoton(int): ADU count per photon, default = 1 :outkey(str): Data key of resulting data record, default is 'maxPhotons' :Authors: Tomas Ekeberg ([email protected]) Benedikt J. Daurer ([email protected]) """ if outkey is None: outkey = 'maxPhotons' data = record.data.flat add_record(evt["analysis"], "analysis", outkey, max(data) / float(aduPhoton))
def commonModePNCCD2(evt, type, key, outkey=None, signal_threshold=None, mask=None, min_nr_pixels_per_median=1): """Common mode correction for PNCCDs. For each row its median value is subtracted (left and right half of detector are treated separately). Adds a record ``evt["analysis"][outkey]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :outkey(str): The event key for the corrected image, default is "corrected - " + key :transpose(bool): Apply procedure on transposed image :signal_threshold(float): Apply procedure by using only pixels below given value :mask: You may provide a boolean mask with values that shall be excluded from common mode correction :min_nr_pixels_per_median(int): Common mode correction will be skipped for pixel lines that do not contain as much as this number of values :Authors: Max F. Hantke ([email protected]) Benedikt J. Daurer ([email protected]) """ if outkey is None: outkey = "corrected - " + key data = evt[type][key].data dataCorrected = np.copy(data) quads = [dataCorrected[:data.shape[0]/2,:data.shape[1]/2], dataCorrected[data.shape[0]/2:,:data.shape[1]/2], dataCorrected[:data.shape[0]/2,data.shape[1]/2:], dataCorrected[data.shape[0]/2:,data.shape[1]/2:]] if mask is not None: mquads = [mask[:data.shape[0]/2,:data.shape[1]/2], mask[data.shape[0]/2:,:data.shape[1]/2], mask[:data.shape[0]/2,data.shape[1]/2:], mask[data.shape[0]/2:,data.shape[1]/2:]] else: mquads = [None, None, None, None] for quad,mquad in zip(quads,mquads): _cmc(quad, msk=mquad, axis=0, signal_threshold=signal_threshold, min_nr_pixels_per_median=min_nr_pixels_per_median) _cmc(quad, msk=mquad, axis=1, signal_threshold=signal_threshold, min_nr_pixels_per_median=min_nr_pixels_per_median) add_record(evt["analysis"], "analysis", outkey, dataCorrected)
def countPhotons(evt, type, key, hitscoreThreshold=200): """A simple hitfinder that performs a limit test against an already defined photon count for detector key. Adds a boolean to ``evt["analysis"]["isHit" + key]`` and the hitscore to ``evt["analysis"]["hitscore - " + key]``. Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) Kwargs: :hitscoreThreshold(int): events with hitscore (Nr. of photons) above this threshold are hits, default=200 :Authors: Carl Nettelblad ([email protected]) """ v = evt["analysis"] hitscore = v["nrPhotons - "+key] v["isHit - "+key] = hitscore > hitscoreThreshold add_record(v, "analysis", "hitscore - "+key, hitscore)
def averagePhotonEnergy(evt, records, outkey="averagePhotonEnergy"): """Averages across given photon energies and adds it to evt["analysis"][outkey]. Args: :evt: The event variable :records: A dictionary of photon energy ``Records`` Kwargs: :outkey(str): Data key of resulting ``Record``, default is 'averagePhotonEnergy' :Authors: Benedikt J. Daurer """ photonEnergy = [] for pE in records.values(): if (pE.unit == ureg.eV): photonEnergy.append(pE.data) if photonEnergy: add_record(evt["analysis"], "analysis", outkey, np.mean(photonEnergy), ureg.eV)
def stxm(evt, data_rec, pulse_energy=1., mode='bf', cx=None, cy=None, r=20, mask=None, badmask=None): data = data_rec.data if badmask is None: badmask = np.ones_like(data).astype(np.bool8) else: badmask = np.bool8(badmask) Ny, Nx = data.shape if cx is None: cx = (Nx-1)/2. if cy is None: cy = (Ny-1)/2. # Round to .0 / .5 cx = np.round(cx * 2)/2. cy = np.round(cy * 2)/2. xx, yy = np.meshgrid(np.arange(Nx)-cx, np.arange(Ny)-cy) rr = np.sqrt(xx**2 + yy**2) if mode == 'bf': if mask is None: mask = rr < r else: mask = np.bool8(mask) v = data[mask & badmask].sum() / pulse_energy elif mode == 'df': if mask is None: mask = rr > r else: mask = ~np.bool8(mask) v = data[mask & badmask].sum() / pulse_energy elif mode == 'sum': v = data[badmask].sum() / pulse_energy elif mode == 'diff': # Calc crop coordinates Nx_half = min([cx, Nx-1-cx]) Ny_half = min([cy, Ny-1-cy]) x1_min = cx - Nx_half y1_min = cy - Ny_half x2_max = cx + Nx_half + 1 y2_max = cy + Ny_half + 1 Nx_half = int(np.ceil(Nx_half)) Ny_half = int(np.ceil(Ny_half)) x1_max = x1_min + Nx_half y1_max = y1_min + Ny_half x2_min = x2_max - Nx_half y2_min = y2_max - Ny_half # Calc diff # Original type might be unsigned integer # # Casting is done to doubles for accumulation. tmp = data*badmask diffx = (tmp[y1_min:y2_max, x1_min:x1_max].sum(dtype=np.float64) - tmp[y1_min:y2_max, x2_min:x2_max].sum(dtype=np.float64)) / pulse_energy diffy = (tmp[y1_min:y1_max, x1_min:x2_max].sum(dtype=np.float64) - tmp[y2_min:y2_max, x1_min:x2_max].sum(dtype=np.float64)) / pulse_energy # Combine diff v = np.sqrt(diffx**2+diffy**2) rec = add_record(evt["analysis"], "analysis", "stxm %s" %mode, v) return rec
def totalNrPhotons(evt, record, aduPhoton=1, aduThreshold=0.5, outkey=None): """Estimates the total nr. of photons on the detector and adds it to ``evt["analysis"][outkey]``. Args: :evt: The event variable :record: The data record (e.g. evt['photonPixelDetectors']['CCD']) Kwargs: :aduPhoton(int): ADU count per photon, default = 1 :aduThreshold(int): only pixels above this threshold given in units of ADUs are valid, default = 0.5 :outkey(str): Data key of resulting data record, default is 'nrPhotons' :Authors: Benedikt J. Daurer ([email protected]) """ if outkey is None: outkey = 'nrPhotons' data = record.data.flat valid = data > aduThreshold add_record(evt["analysis"], "analysis", outkey, sum(data[valid]) / float(aduPhoton))
def countHitscore(evt, hitscore, hitscoreThreshold=200, outkey=""): """A simple hitfinder that performs a limit test against an already defined hitscore and adds the result to ``evt["analysis"][outkey + "isHit"]``, and the hitscore to ``evt["analysis"][outkey + "hitscore"]``. Args: :evt: The event variable :hitscore: A pre-defined hitscore Kwargs: :hitscoreThreshold(int): Events with hitscore above this threshold are hits, default=200 :Authors: Carl Nettelblad ([email protected]) Benedikt J. Daurer """ hit = hitscore > hitscoreThreshold v = evt["analysis"] add_record(v, "analysis", outkey + "isHit", hit) add_record(v, "analysis", outley + "hitscore", hitscore)
def commonModeLines(evt, record, outkey=None, direction='vertical'): """Common mode correction subtracting the median along lines. Args: :evt: The event variable :record: A pixel detector ``Record``` Kwargs: :outkey: The event key for the corrected detecor image, default is "corrected" :direction: The direction of the lines across which median is taken, default is vertical """ if outkey is None: outkey = "corrected" data = record.data dataCorrected = np.copy(data) if direction is 'vertical': dataCorrected -= np.transpose(np.median(data,axis=0).repeat(data.shape[0]).reshape(data.shape)) elif direction is 'horizontal': dataCorrected -= np.median(data,axis=1).repeat(data.shape[1]).reshape(data.shape) add_record(evt["analysis"], "analysis", outkey, dataCorrected)
def averagePulseEnergy(evt, records, outkey="averagePulseEnergy"): """Averages across given pulse energies and adds it to evt["analysis"][outkey]. Args: :evt: The event variable :records: A dictionary of pulse energy :func:`~Record` objects Kwargs: :outkey(str): Data key of resulting :func:`~backend.Record`, default is 'averagePulseEnergy' :Authors: Filipe Maia, Benedikt J. Daurer """ pulseEnergy = [] for pE in records.values(): if (pE.unit == ureg.mJ): pulseEnergy.append(pE.data) if pulseEnergy: add_record(evt["analysis"], "analysis", outkey, np.mean(pulseEnergy), ureg.mJ)
def stxmCenterOfMass(evt, data_rec): center_of_mass_y, center_of_mass_x = scipy.ndimage.measurements.center_of_mass(data_rec.data) # this assumes that the image is centered already. center_of_mass_y -= data_rec.data.shape[0]/2. - 0.5 center_of_mass_x -= data_rec.data.shape[1]/2. - 0.5 diff = np.sqrt(center_of_mass_x**2 + center_of_mass_x**2) # x_rec = add_record(evt["analysis"], "analysis", "stxm center of mass x", center_of_mass_x) # y_rec = add_record(evt["analysis"], "analysis", "stxm center of mass y", center_of_mass_y) # return y_rec, x_rec rec = add_record(evt["analysis"], "analysis", "stxm center of mass", diff) return rec
def bin(evt, type, key, binning, mask=None): """Bin a detector image given a binning factor (and mask) to ``evt["analysis"]["binned image - " + key]`` (``evt["analysis"]["binned mask - " + key]`` Args: :evt: The event variable :type(str): The event type (e.g. photonPixelDetectors) :key(str): The event key (e.g. CCD) :binning(int): The linear binning factor Kwargs: :mask: Binary mask, pixels that are masked out are not counted into the binned value. :Authors: Max F. Hantke ([email protected]) """ import spimage image = evt[type][key].data binned_image, binned_mask = spimage.binImage(image, binning, msk=mask, output_binned_mask=True) add_record(evt["analysis"], "analysis", "binned image - "+key, binned_image) if binned_mask is not None: add_record(evt["analysis"], "analysis", "binned mask - "+key, binned_mask)