def QPHIMapStream(bins=(800, 360)): ''' Transform the scattering image into a q, phi map. Parameters ---------- bins : 2 tuple, optional the number of bins to divide into **Stream Inputs** img : 2d np.ndarray the image mask : 2d np.ndarray, optional the mask origin : 2 tuple the beam center in the image qmap : 2d np.ndarray the qmap of the image **Stream Outputs** sqphi : 2d np.ndarray the sqphi map qs : 1d np.ndarray the q values phis : 1d np.ndarray the phi values Returns ------- sin : Stream instance the input stream (see Stream Inputs) sout : Stream instance the output stream (see Stream Outputs) Examples -------- >>> bins = (3, 4) >>> sin, sout = QPHIMapStream(bins=bins) >>> L = sout.sink_to_list() >>> mask = None >>> img = np.random.random((10, 10)) >>> origin = (3, 3) >>> sdoc = StreamDoc(kwargs=dict(image=img, ... origin=origin, ... mask=mask)) >>> sin.emit(sdoc) ''' sin = sc.Stream(stream_name="QPHI map Stream") sout = scs.map(qphiavg, sin, bins=bins) sout = scs.add_attributes(sout, stream_name="qphiavg") return sin, sout
def ThumbStream(blur=None, crop=None, resize=None): ''' Thumbnail stream **Stream Inputs** image : 2d np.ndarray the image **Stream Outputs** sin : Stream instance the stream input sout : Stream instance the stream output Parameters ---------- blur : float, optional the sigma of the Gaussian kernel to convolve image with for smoothing default is None, no smoothing crop : 4 tuple of int, optional the boundaries to crop by default is None, no cropping resize : int, optional the factor to resize by for example resize=2 performs 2x2 binning of the image Stream Inputs ------------- image : 2d np.ndarray the image Returns ------- sin : the stream input sout : the stream output ''' # TODO add flags to actually process into thumbs sin = sc.Stream(stream_name="Thumbnail Stream") sout = scs.add_attributes(sin, stream_name="thumb") # s1 = sin.add_attributes(stream_name="ThumbStream") sout = scs.map(_blur, sout, sigma=blur, remote=True) sout = scs.map(_crop, sout, crop=crop, remote=True) sout = scs.map(_resize, sout, resize=resize, remote=True) # change the key from image to thumb sout = scs.select(sout, ('image', 'thumb')) return sin, sout
def PeakFindingStream(name='peakfind'): ''' Find peaks in 1d line data. **Stream Inputs** sqx : 1d np.ndarray the x domain of the curve sqy : 1d np.ndarray the y domain of the curve **Stream Outputs** model: lmfit.Model instance The model for the fit y_origin: inds_peak: list the peak indices xdata: ratio: ydata: wdata: bkgd: variance: variance_mean: peaksx: peaksy: Parameters ---------- name : str, optional name of stream ''' sin = sc.Stream(stream_name="Peak Finder") # pkfind stream sout = scs.map(call_peak, scs.select(sin, 'sqy', 'sqx')) sout = scs.add_attributes(sout, stream_name=name) return sin, sout
def ImageTaggingStream(): ''' Creates an image tagging stream. This stream will take in an image and output a tage as to what the image is. **Stream Inputs** image : 2d np.ndarray the image to be tagged **Stream Outputs** tag_name : str the name of the tag for the image ''' sin = sc.Stream(stream_name="Image Tagger") sout = scs.map(infer, scs.select(sin, 'image')) sout = scs.add_attributes(sout, stream_name="image-tag") return sin, sout
def AngularCorrelatorStream(bins=(800, 360)): ''' Stream to run angular correlations. **Stream Inputs** image : 2d np.ndarray the image to run the angular correltions on mask : 2d np.ndarray the mask origin : 2 tuple the beam center of the image bins : tuple the number of bins in q and phi method : string, optional the method to use for the angular correlations defaults to 'bgest' q_map : the q_map to be used **Stream Outputs** sin : Stream instance the stream input sout : Stream instance the stream output ''' # TODO : Allow optional kwargs in streams sin = sc.Stream(stream_name="Angular Correlation") sout = scs.select(sin, ('image', 'image'), ('mask', 'mask'), ('origin', 'origin'), ('q_map', 'r_map')) sout = scs.map(angular_corr, sout, bins=bins) sout = scs.add_attributes(sout, stream_name="angular-corr") return sin, sout
# L_attributes = sout_attributes.sink_to_list() sout_primary.connect(sin_attributes) sout_filled = scs.merge(sc.zip(sout_filled, scs.to_attributes(sout_attributes))) sin_calib, sout_calib = CalibrationStream() sout_attributes.connect(sin_calib) # L_calib = sout_calib.sink_to_list() # sout_calib2 = sout_calib # sout_calib = sc.Stream() # the PrimaryFilteringStream already split the detectors # s_image = sc.Stream() s_image = scs.add_attributes(sout_filled, stream_name="image") # L_image = s_image.sink_to_list() # TODO : fix and remove this is for pilatus300 should be in mask gen s_mask = scs.map(generate_mask, sout_attributes) # L_mask = s_mask.sink_to_list() # #s_zipped = # #L_zipped= s_zipped.sink_to_list() s_imgmaskcalib = scs.merge(sc.zip(s_image, sout_calib, s_mask)) # L_imgmaskcalib = s_imgmaskcalib.sink_to_list() # some small streams def get_origin(**kwargs):
def ImageStitchingStream(return_intermediate=False): ''' Image stitching **Stream Inputs** image : 2d np.ndarray the image for the stitching mask : 2d np.ndarray the mask origin : 2 tuple the beam center stitchback : bool whether or not to stitchback to previous image **Stream Outputs** image : 2d np.ndarray the stitched image mask : 2d np.ndarray the mask from the stitch origin : 2 tuple the beam center stitchback : bool whether or not to stitchback to previous image Returns ------- sin : Stream instance the input stream sout : Stream instance the output stream Parameters ---------- return_intermediate : bool, optional decide whether to return intermediate results or not defaults to False Notes ----- Any normalization of images (for ex: by exposure time) should be done before inputting to this stream. Examples -------- >>> sin, sout = ImageStitchingStream() >>> L = sout.sink_to_list() >>> mask = np.ones((10, 10), dtype=np.int64) >>> img1 = np.ones_like(mask, dtype=float) >>> # 3 rows are higher >>> img1[2:4] = 2 >>> # some arb value >>> origin1 = [2, 3] >>> # roll along zero axis >>> img2 = np.roll(img1, 2, axis=0) >>> # rolled by two >>> origin2 = [2+2, 3] >>> # first image, stitchback can be anything >>> sdoc1 = StreamDoc(kwargs=dict(mask=mask, image=img1, ... origin=origin1, ... stitchback=False)) >>> sin.emit(sdoc1) >>> # emit a second image and it will be stitched >>> sdoc2 = StreamDoc(kwargs=dict(mask=mask, image=img2, ... origin=origin2, ... stitchback=True)) >>> sin.emit(sdoc2) >>> # A new image with False stitchback will have output >>> # stream output a result >>> img3 = np.random.random((10,10)) >>> origin3 = (0,0) >>> sdoc3 = StreamDoc(kwargs=dict(mask=mask, image=img3, ... origin=origin3, ... stitchback=False)) >>> sin.emit(sdoc3) >>> len(L) == 1 True >>> # the stitched image is here: >>> img = L[0]['kwargs']['image'] ''' # TODO : add state. When False returned, need a reason why def validate(x): if not hasattr(x, 'kwargs'): raise ValueError("No kwargs") kwargs = x['kwargs'] expected = ['mask', 'origin', 'stitchback', 'image'] for key in expected: if key not in kwargs: message = "{} not in kwargs".format(key) raise ValueError(message) if not isinstance(kwargs['mask'], np.ndarray): message = "mask is not array" raise ValueError(message) if not isinstance(kwargs['image'], np.ndarray): message = "image is not array" raise ValueError(message) if len(kwargs['origin']) != 2: message = "origin not length 2" raise ValueError(message) return x # TODO : remove the add_attributes part and just keep stream_name sin = sc.Stream(stream_name="Image Stitching Stream") # sout = sc.map(sin, validate) # sin.map(lambda x : print("Beginning of stream data\n\n\n")) # TODO : remove compute requirement # TODO : incomplete sout = scs.add_attributes(sin, stream_name="stitch") sout = scs.select(sout, ('image', None), ('mask', None), ('origin', None), ('stitchback', None)) # put all args into a tuple def pack(*args): return args sout = scs.map(pack, sout) # sout = scs.map(s3.map(psdm(pack)) sout = scs.accumulate(_xystitch_accumulate, sout) sout = scs.map(scs.star(_xystitch_result), sout) def stitchbackcomplete(xtuple): ''' only plot images whose stitch is complete, and only involved more than one image NOTE : *Only* the bool "True" will activate a stitch. "1" does not count. This is handled by checking 'is True' and 'is not True' ''' # previous must have been true for stitch to have involved more than # one image prev = xtuple[0]['kwargs']['stitchback'] # next must be False (or just not True) to be complete next = xtuple[1]['kwargs']['stitchback'] return next is not True and prev is True # swin.map(lambda x : print("result : {}".format(x)), raw=True) # only get results where stitch is stopped # NOTE : need to compute before filtering here # now emit some dummy value to swin, before connecting more to stream # swin.emit(dict(attributes=dict(stitchback=0))) # TODO : figure out how to filter if not return_intermediate: # keep previous two results sout = sout.sliding_window(2) sout = sout.filter(stitchbackcomplete) sout = sout.map(lambda x: x[0]) return sin, sout
def LineCutStream(axis=0, name=None): ''' Obtain line cuts from a 2D image. Just simple slicing. It's a stream mainly to make this more standard. Parameters ---------- axis : int, optional the axis to obtain linecuts from. Default is 0 (so we index rows A[i]) If 1, the index cols (A[:,i]) name : str, optional the name of the stream **Stream Inputs** image : 2d np.ndarray the image to obtain line cuts from y : 1d np.ndarray The y (row) values per pixel x : The x (column) values per pixel vals : list the values to obtain the linecuts from **Stream Outputs** linecuts : list a list of line cuts linecuts_domain : 1d np.ndarray the domain of the line cuts linecuts_vals : 1d np.ndarray the corresponding value for each line cut Returns ------- sin : Stream instance the input stream (see Stream Inputs) sout : Stream instance the output stream (see Stream Outputs) ''' def linecuts(image, y, x, vals, axis=0): ''' Can potentially return an empty list of linecuts.''' linecuts = list() linecuts_vals = list() if axis == 1: # swap x y and transpose tmp = y y = x x = tmp linecuts_domain = x for val in vals: ind = np.argmin(np.abs(y - val)) linecuts.append(image[ind]) linecuts_vals.append(y[ind]) return dict(linecuts=linecuts, linecuts_domain=linecuts_domain, linecuts_vals=linecuts_vals) # the string for the axis axisstr = ['y', 'x'][axis] if name is None: stream_name = 'linecuts-axis{}'.format(axisstr) else: stream_name = name + "-axis{}".format(axisstr) sin = sc.Stream(stream_name=stream_name) sout = scs.map(linecuts, sin, axis=axis) sout = scs.add_attributes(sout, stream_name=stream_name) return sin, sout
def CircularAverageStream(): ''' Circular average stream. **Stream Inputs** image : 2d np.ndarray the image to run circular average on calibration : 2D np.ndarray the calibration object, with members: q_map : 2d np.ndarray the magnite of the wave vectors r_map : 2d np.ndarray the pixel positions from center mask : 2d np.ndarray, optional the mask bins : int or tuple, optional if an int, the number of bins to divide into if a list, the bins to use **Stream Outputs** sqx : 1D np.ndarray the q values sqxerr : 1D np.ndarray the error q values sqy : 1D np.ndarray the intensities sqyerr : 1D np.ndarray the error in intensities (approximate) Notes ----- Assumes square pixels Assumes variance comes from shot noise only (by taking average along ring/Npixels) If bins is None, it does its best to estimate pixel sizes and make the bins a pixel in size. Note, for Ewald curvature this is not straightforward. You need both a r_map in pixels from the center and the q_map for the actual q values. Returns ------- sin : Stream instance the source stream (see Stream Inputs) sout : Stream instance the output stream (see Stream Outputs) Examples -------- >>> from streamz import Stream >>> from SciStreams import StreamDoc >>> import numpy as np >>> s = Stream() >>> from SciStreams.streams.XS_Streams import CircularAverageStream >>> sin, sout = CircularAverageStream() >>> s.connect(sin) >>> mask = None >>> bins = 3 >>> img = np.random.random((10, 10)) >>> x = np.linspace(-5, 5, 10) >>> X, Y = np.meshgrid(x, x) >>> r_map = np.sqrt(X**2 + Y**2) >>> q_map = r_map*.12 >>> class Calib: ... def __init__(self, qmap, rmap): ... self.q_map = qmap ... self.r_map = rmap >>> calibration = Calib(q_map, r_map) >>> sdoc = StreamDoc(kwargs=dict(image=img, ... calibration=calibration, ... mask=mask, ... bins=bins)) >>> # emit data as usual >>> sin.emit(sdoc) ''' # TODO : extend file to mltiple writers? def validate(x): kwargs = x['kwargs'] if 'image' not in kwargs or 'calibration' not in kwargs: message = "expected two kwargs: " message += "(image, calibration), " message += "got {} instead".format(list(kwargs.keys())) raise ValueError(message) # kwargs are optional so don't validate them return x sin = sc.Stream(stream_name="Circular Average") sout = scs.add_attributes(sin, stream_name="circavg") # No validation for now # validation should not be necessary, should just throw an error # sout = sout.map(validate) sout = scs.map(circavg_from_calibration, sout) return sin, sout
def PrimaryFilteringStream(): ''' Filter the stream for just primary results. **Stream Inputs** md : dict No requirements data : dict must have a 2D np.ndarray with one of accepted detector keys **Stream Outputs** Two streams are outputted, sout and serr sout : the stream with valid data Outputs from zero to any number streams (depends on how many detectors were found) md : detector_key : the detector key (string) data : data with only one image as detector key if there was more than one, it selects one of them Note this has unspecified behaviour. serr : the stream with bad data. This can be sinked to an error stream Examples -------- >>> # A typical workflow is as follows: >>> # instantiate the main stream input >>> from streamz import Stream >>> s = Stream() >>> # create the filtering stream >>> from SciStreams.streams.XS_Streams import PrimaryFilteringStream >>> sin, sout = PrimaryFilteringStream() >>> s.connect(sin) >>> import numpy as np >>> # create dummy detector image, from pilatus300 >>> img = np.random.random((619, 487)) >>> from SciStreams.core.StreamDoc import StreamDoc >>> sdoc = StreamDoc(kwargs=dict(pilatus300_image=img)) >>> # save result in a list L that you can review later >>> L = sout.sink_to_list() >>> # emit the data >>> s.emit(sdoc) Returns ------- sin : Stream instance the source stream (see Stream Inputs) sout : Stream instance the output stream (see Stream Outputs) ''' sin = sc.Stream(stream_name="Primary Filter") # a primary filter, data will not go through if does not match attributes sout = sin.filter(filter_attributes) # get the error streams attributes (to output to some log) serr = sin.filter(lambda x: not filter_attributes) serr = scs.get_attributes(serr) serr = scs.add_attributes(serr, error="primary_filter") sout = sc.map(sout, pick_allowed_detectors) # turn list into individual streams # (if empty list, emits nothing, this is sort of like filter) sout = sout.concat() # just some checks to see if it's good data, else ignore return sin, sout, serr