Esempio n. 1
0
    def findMulti(self, M, threshold=None, num_iter=None):

        if threshold is None:
            threshold = self.threshold

        if num_iter is None:
            num_iter = self.num_iterations

        for trans in range(2):
            for i in range(num_iter):
                new_streak = self.findSingle(M,
                                             transpose=trans,
                                             threshold=threshold)
                if empty(new_streak):
                    break
                else:
                    new_streak.subtractStreak(M)
                    if self.use_subtract_mean: M -= np.nanmean(M)
                    # np.nan_to_num(M, copy=False)

                    if self.use_show:
                        new_streak.plotLines()
                        f = plt.gcf()
                        f.canvas.draw()
                        f.canvas.flush_events()
        return M
Esempio n. 2
0
    def getRadonVariance(self, transpose=0):
        """ Get the partial Radon transforms of the background noise for some transpose.
            Lazy Reloading: only delete old var-maps if input size changed (or 
            if we changed expansion mode) and then calculate the var-maps on demand.
            
        """

        # check if we need to recalculate the var map
        if not empty(self._size_var) and (
                not compare_size(self.im_size, self._size_var)
                or self.useExpand() != self._expanded_var):
            if self.debug_bit: print("Clearing the Radon var-maps")
            self._radon_var_map = [
            ]  # clear this to be lazy loaded with the right size
            self._radon_var_map_trans = []

        # if there is no var map, we need to lazy load it
        if empty(self._radon_var_map):

            # do we have a variance map or scalar??
            # if empty(self._input_var):
            #     self._input_var = self._default_var_scalar
            #
            # if scalar(self._input_var):
            #     self._var_scalar = self._input_var
            #     self._var_map = self._input_var * np.ones(self.im_size, dtype='float32')
            # else:
            #     self._var_scalar = np.median(self.input_var)
            #     self._var_map = self._input_var

            self._size_var = self.im_size
            self._expanded_var = self.useExpand()
            self._radon_var_map = FRT(self.var_map,
                                      partial=True,
                                      expand=self._expanded_var,
                                      transpose=False)
            self._radon_var_map_trans = FRT(self.var_map,
                                            partial=True,
                                            expand=self._expanded_var,
                                            transpose=True)

        if transpose:
            return self._radon_var_map_trans
        else:
            return self._radon_var_map
Esempio n. 3
0
 def write_to_disk(self, filename=None):
     if empty(filename):
         if empty(self.filename):
             return 
         else: 
             filename = os.path.splitext(self.filename)[0]+'.h5'
     
     with h5py.File(filename, 'a') as hf:
         numbers=[int(re.search(r'\d+$', k).group()) for k in hf.keys()]
         
         if empty(numbers):
             new_number = 1
         else:
             new_number = max(numbers) + 1
         
         ds = hf.create_dataset('streak_%03d' % (new_number), data=self.image_cutout)
         
         ds.attrs['corner'] = self.corner_cutout
Esempio n. 4
0
 def makeCutout(self, size=None):
     
     if empty(self.image):
         return; 
     
     if empty(size):
         size = self.cut_size;
     
     d1 = int(np.floor(size/2))
     d2 = int(np.ceil(size/2))
     
     x1 = int(round(self.mid_x_full))-d1
     x2 = int(round(self.mid_x_full))+d2
     y1 = int(round(self.mid_y_full))-d1
     y2 = int(round(self.mid_y_full))+d2
     
     self.image_cutout = self.image[y1:y2,x1:x2]
     
     self.size_cutout = (size, size)
     self.corner_cutout = (y1, x1)
Esempio n. 5
0
    def input_var(self, val):
        # if working with scalar variance, only need to update the scaling of var maps
        if scalar(val) and scalar(self._input_var):
            if not empty(self._radon_var_map):
                self._radon_var_map = [
                    m * val / self.input_var for m in self._radon_var_map
                ]
            if not empty(self._radon_var_map_trans):
                self._radon_var_map_trans = [
                    m * val / self.input_var for m in self._radon_var_map_trans
                ]

        else:  # new or old input_var is not scalar, must recalculate var maps
            self.clearVarMap()

        self._input_var = val
        if scalar(val):
            self._var_scalar = val
            self._var_map = None
        else:
            self._var_scalar = np.median(val)
            self._var_map = val
Esempio n. 6
0
    def update_from_finder(self, finder):
        if empty(finder): return

        for att in self.__dict__.keys():  # load all attributes in "self" that exist in "finder"
            if hasattr(finder, att) and not callable(getattr(finder,att)):
                setattr(self, att, getattr(finder, att))

        self.noise_var = finder.var_scalar
        self.psf_sigma = finder.sigma_psf

        self.corner_section = finder.current_section_corner

        self.was_convolved = bool(finder.use_conv)
        self.was_expanded = bool(finder.useExpand())
Esempio n. 7
0
    def __set_input_psf(self, val):

        if scalar(val):
            if empty(self.input_psf) or not scalar(
                    self.input_psf) or val != self.input_psf:
                self._psf = gaussian2D(val)
                self._sigma_psf = val

        elif isinstance(val, np.ndarray) and val.ndim == 2:
            if scalar(self.input_psf
                      ) or val.shape != self._input_psf.shape or np.any(
                          val != self.input_psf):
                self._psf = val
                a = fit_gaussian(
                    val)  # run a short minimization 2D fitter to gaussian
                self._sigma_psf = (a.x[1] +
                                   a.x[2]) / 2  # average the x and y sigma

        else:
            raise ValueError("input_psf must be a scalar or a numpy array")

        self._input_psf = val
        self._psf = self._psf / np.sqrt(np.sum(self._psf**2))  # normalized PSF
Esempio n. 8
0
    def sigma_psf(self):
        if empty(self._input_psf):
            self.input_psf = self._default_sigma_psf  # go through the setter for input_psf and calculate psf and sigma_psf

        return self._sigma_psf
Esempio n. 9
0
 def var_map(self):
     if empty(self._var_map) and not empty(self.im_size):
         return self.var_scalar * np.ones(self.im_size)
     else:
         return self._var_map
Esempio n. 10
0
 def var_scalar(self):
     if empty(self._var_scalar):
         return self._default_var_scalar
     else:
         return self._var_scalar
Esempio n. 11
0
 def x2c(self):
     if empty(self.corner_cutout):
         return None
     else:
         return self.x2 + self.corner_section[1] - self.corner_cutout[1]
Esempio n. 12
0
    def calculate(self):
        # these are the raw results from this subframe
        self.size_section = self.image_section_proc.shape
        if self.transposed:
            self.size_section_tr = tuple(reversed(self.size_section))
        else:
            self.size_section_tr = self.size_section

        if not empty(self.image):  # part of the orignal image that is defined as the cutout (without processing!)
            self.image_section_raw = self.image[self.corner_section[0]:self.size_section[0], self.corner_section[1]:self.size_section[1]]

        self.radon_y1 = (2**(self.foldings-1)) * self.radon_max_idx[1]  # size of each slice in y, times the slice number (index)
        self.radon_y2 = (2**(self.foldings-1)) * (self.radon_max_idx[1]+1)  # same thing, top index (should we include the last pixel?)
        offset = (self.subframe.shape[2] - self.size_section_tr[1])//2  # this is added on either side if was_expanded
        self.radon_x0 = self.radon_max_idx[2] - offset  # position of x0 in the subframe (removing the expanded pixels)
        self.radon_dx = self.radon_max_idx[0] - self.subframe.shape[0]//2  # the angle is in dim0 needs to offset for negative angles
        
        # signal to noise from the subframe maximum
        self.snr = self.subframe[self.radon_max_idx]
        self.is_short = self.subframe.ndim>2 and self.subframe.shape[1]>1
        
        # assume there is no transpose (then add it if needed)
        self.y1 = self.radon_y1
        self.y2 = self.radon_y2
        self.dy = self.y2-self.y1
        self.dx = self.radon_dx
        if self.radon_dx!=0:
            self.a = self.dy/self.dx
            self.x0 = self.radon_x0 - self.y1/self.a
            self.b = -self.a*self.x0
            self.th = math.degrees(math.atan(self.a))
            self.x1 = (self.y1-self.b)/self.a
            self.x2 = (self.y2-self.b)/self.a
        else:
            self.a = float('NaN')
            self.x0 = self.radon_x0
            self.b = float('NaN')
            self.th = 90
            self.x1 = self.radon_x0
            self.x2 = self.radon_x0
        
        self.L = abs(self.radon_dy/math.sin(math.radians(self.th)))
#        f = math.fabs(math.sin(math.radians(self.th)))
        self.I = self.snr*math.sqrt(self.noise_var*2*math.sqrt(math.pi)*self.psf_sigma/(self.L))
        self.snr_fwhm = self.I*0.81/math.sqrt(self.noise_var)
        
        if self.transposed:
            self.x1, self.y1 = self.y1, self.x1
            self.x2, self.y2 = self.y2, self.x2
            self.a = 1/self.a
            self.b, self.x0 = self.x0, self.b
            self.th = 90 - self.th
            
        # self.x1 = round(self.x1)
        # self.x2 = round(self.x2)
        # self.y1 = round(self.y1)
        # self.y2 = round(self.y2)
        # self.x0 = round(self.x0)

        ############ calculate the errors on the Radon parameters #############

        R = np.array(self.subframe[:,self.radon_max_idx[1],:]) # present this slice as 2D image
        xmax = self.radon_max_idx[2]
        ymax = self.radon_max_idx[0]
        
        S = round(self.psf_sigma*self.num_psfs_peak_region) # size of area of a few PSF widths around the peak
        x1 = xmax - S
        if x1<0: x1 = 0
        x2 = xmax + S
        if x2>=R.shape[1]: x2 = R.shape[1]-1
        y1 = ymax - S
        if y1<0: y1 = 0
        y2 = ymax + S
        if y2>=R.shape[0]: y2 = R.shape[0]-1
        x1 = int(x1)
        x2 = int(x2)
        y1 = int(y1)
        y2 = int(y2)
        C = np.array(R[y1:y2,x1:x2]) # make a copy of the array
        xgrid,ygrid = np.meshgrid(range(C.shape[1]),range(C.shape[0]))

        idx = np.unravel_index(np.nanargmax(C), C.shape)
        mx = C[idx]
        
        C[C<mx-self.num_snr_peak_region] = 0
        
        xgrid = xgrid - idx[1]
        ygrid = ygrid - idx[0]
        
        self.radon_x_var = np.sum(C*xgrid**2)/np.sum(C)
        self.radon_dx_var = np.sum(C*ygrid**2)/np.sum(C) 
        self.radon_xdx_cov = np.sum(C*xgrid*ygrid)/np.sum(C)
        
        self.peak_region = C

        self.makeCutout()
Esempio n. 13
0
 def mid_y_full(self): # this gives the middle y position from the full image
     if empty(self.y1f) or empty(self.y2f):
         return None
     else:
         return (self.y1f+self.y2f)/2
Esempio n. 14
0
 def mid_y(self):
     if empty(self.y1) or empty(self.y2):
         return None
     else:
         return (self.y1+self.y2)/2
Esempio n. 15
0
 def mid_x(self):
     if empty(self.x1) or empty(self.x2):
         return None
     else:
         return (self.x1+self.x2)/2
Esempio n. 16
0
 def y2c(self):
     if empty(self.corner_cutout):
         return None
     else:
         return self.y2 + self.corner_section[0] - self.corner_cutout[0]
Esempio n. 17
0
    def findSingle(self, M, transpose=False, threshold=None):

        if empty(M):
            return None

        self.im_size = imsize(M)

        if empty(threshold):
            threshold = self.threshold

        streak = None

        self.num_frt_calls += 1

        if self.use_short:

            R_partial = FRT(
                M, transpose=transpose, partial=True,
                expand=False)  # these are raw Radon partial transforms

            # divide by the variance map, geometric factor, and PSF norm for each level
            V = self.getRadonVariance(transpose)
            G = [
                self.getGeometricFactor(m)
                for m in range(2,
                               len(R_partial) + 2)
            ]  # m counts the number of foldings, partials start at 2
            P = self.getNormFactorPSF()

            R_partial = [
                R_partial[i] / np.sqrt(V[i] * G[i] * P)
                for i in range(len(R_partial))
            ]

            R = R_partial[-1][:, 0, :]  # get the final Radon image as 2D map

            snrs_idx = [np.nanargmax(r)
                        for r in R_partial]  # best index for each folding
            # snrs_max = np.array([r[i] for i,r in zip(snrs_idx,R_partial)]) # best SNR for each folding
            snrs_max = np.array([np.nanmax(r) for r in R_partial
                                 ])  # best SNR for each folding

            best_idx = np.nanargmax(snrs_max)  # which folding has the best SNR
            best_snr = snrs_max[
                best_idx]  # what is the best SNR of all foldings

            if best_snr >= threshold and 2**best_idx >= self.min_length:
                peak_coord = np.unravel_index(
                    snrs_idx[best_idx], R_partial[best_idx].shape
                )  # the x,y,z of the peak in that subframe

                streak = self.makeStreak(snr=best_snr,
                                         transpose=transpose,
                                         threshold=threshold,
                                         peak=peak_coord,
                                         foldings=best_idx + 2,
                                         subframe=R_partial[best_idx],
                                         section=M)

        else:

            R = FRT(M, transpose=transpose, partial=False, expand=True)

            V = self.getRadonVariance(transpose)
            foldings = len(
                V) + 1  # the length tells you how many foldings we need

            V = V[-1][:, 0, :]  # get the last folding and flatten it to 2D
            G = self.getGeometricFactor(foldings)
            P = self.getNormFactorPSF

            R = R / np.sqrt(V * G * P)

            R_partial = R[:, np.
                          newaxis, :]  # this is how it would look from a partial transpose output

            idx = np.argmax(R)
            best_snr = R[idx]

            peak_coord = np.unravel_index(idx, R.shape)
            peak_coord = (
                peak_coord[0], 0, peak_coord[1]
            )  # added zero for y start position that is often non-zero in the partial transforms

            streak = self.makeStreak(snr=best_snr,
                                     transpose=transpose,
                                     threshold=threshold,
                                     peak=peak_coord,
                                     foldings=foldings,
                                     subframe=R_partial,
                                     section=M)

        self.best_SNR = max(
            best_snr, self.best_SNR
        )  # this will always have the best S/N until "clear" is called

        if not empty(streak):
            self.streaks.append(streak)
            if self.use_write_cutouts:
                if empty(self.max_length_to_write
                         ) or streak.L < self.max_length_to_write:
                    streak.write_to_disk()

        # store the final FRT result (this is problematic once we start iterating over findSingle!)
        if transpose:
            self.radon_image_trans = R
        else:
            self.radon_image = R

        if self.debug_bit > 1:
            print(
                "Running FRT %d times, trans= %d, thresh= %f, found streak: %d"
                %
                (self.num_frt_calls, transpose, threshold, not empty(streak)))

        return streak
Esempio n. 18
0
def FRT(M_in,
        transpose=False,
        expand=False,
        padding=True,
        partial=False,
        output=None):
    """ Fast Radon Transform (FRT) of the input matrix M_in (must be 2D numpy array)
    Additional arguments: 
     -transpose (False): transpose M_in (replace x with y) to check all the other angles. 
     -expand (False): adds zero padding to the sides of the passive axis to allow for corner-crossing streaks
     -padding (True): adds zero padding to the active axis to fill up powers of 2. 
     -partial (False): use this to save second output, a list of Radon partial images (useful for calculating variance at different length scales)
     -output (None): give the a pointer to an array with the right size, for FRT to put the return value into it.
                     Note that if partial=True then output must be a list of arrays with the right dimensions. 
    """
    #    print("running FRT with: transpose= "+str(transpose)+", expand= "+str(expand)+", padding= "+str(padding)+", partial= "+str(partial)+", finder= "+str(finder))

    ############### CHECK INPUTS AND DEFAULTS #################################

    if empty(M_in):
        return

    if M_in.ndim > 2:
        raise Exception("FRT cannot handle more dimensions than 2D")

    ############## PREPARE THE MATRIX #########################################

    M = np.array(M_in)  # keep a copy of M_in to give to finalizeFRT

    np.nan_to_num(M, copy=False)  # get rid of NaNs (replace with zeros)

    if transpose:
        M = M.T

    if padding:
        M = padMatrix(M)

    if expand:
        M = expandMatrix(M)

    ############## PREPARE THE MATRIX #########################################

    Nfolds = getNumLogFoldings(M)
    (Nrows, Ncols) = M.shape

    M_out = []

    if not empty(output):  # will return the entire partial transform list
        if partial:
            for m in range(2, Nfolds + 1):
                if output[m - 1].shape != getPartialDims(M, m):
                    raise RuntimeError("Wrong dimensions of output array["+str(m-1)+"]: "\
                    +str(output[m-1].shape)+", should be "+str(getPartialDims(M,m)))

            M_out = output

        else:
            if output.shape[0] != 2 * M.shape[0] - 1:
                raise RuntimeError("Y dimension of output ("+str(output.shape[0])+\
                ") is inconsistent with (padded and doubled) input ("+str(M.shape[0]*2-1)+")")
            if output.shape[1] != M.shape[1]:
                raise RuntimeError("X dimension of output ("+str(output.shape[1])+\
                ") is inconsistent with (expanded?) input ("+str(M.shape[1])+")")

    dx = np.array([0], dtype='int64')

    M = M[np.newaxis, :, :]

    for m in range(1, Nfolds + 1):  # loop over logarithmic steps

        M_prev = M
        dx_prev = dx

        Nrows = M_prev.shape[1]

        max_dx = 2**(m) - 1
        dx = range(-max_dx, max_dx + 1)
        if partial and not empty(output):
            M = M_out[m -
                      1]  # we already have memory allocated for this result
        else:
            M = np.zeros((len(dx), Nrows // 2, Ncols),
                         dtype=M.dtype)  # make a new array each time

        counter = 0

        for i in range(Nrows //
                       2):  # loop over pairs of rows (number of rows in new M)

            for j in range(len(dx)):  # loop over different shifts

                # find the value and index of the previous shift
                dx_in_prev = int(float(dx[j]) / 2)
                j_in_prev = dx_in_prev + int(len(dx_prev) / 2)
                # print "dx[%d]= %d | dx_prev[%d]= %d | dx_in_prev= %d" % (j, dx[j], j_in_prev, dx_prev[j_in_prev], dx_in_prev)
                gap_x = dx[j] - dx_in_prev  # additional shift needed

                M1 = M_prev[j_in_prev, counter, :]
                M2 = M_prev[j_in_prev, counter + 1, :]

                M[j, i, :] = shift_add(M1, M2, -gap_x)

            counter += 2

        if partial and empty(
                output
        ):  # only append to the list if it hasn't been given from the start using "output"
            M_out.append(M)

#     end of loop on m

    if not partial:  # we don't care about partial transforms, we were not given an array to fill
        #        M_out = np.transpose(M, (0,2,1))[:,:,0] # lose the empty dimension
        M_out = M[:, 0, :]  # lose the empty dim

        if not empty(output):  # do us a favor and also copy it into the array
            np.copyto(output, M_out)
            # this can be made more efficient if we use the "output" array as
            # target for assignment at the last iteration on m.
            # this will save an allocation and a copy of the array.
            # however, this is probably not very expensive and not worth
            # the added complexity of the code

    return M_out
Esempio n. 19
0
    def input(self,
              image,
              variance=None,
              psf=None,
              filename=None,
              batch_num=None):
        """
        Input an image and search for streaks in it.
        Inputs: -images (expect numpy array, can be 3D)
                -variance (can be scalar or map of noise variance)
                -psf: point spread function of the image (scalar gaussian sigma or map)
                -filename: for tracking the source of discovered streaks
                -batch_num: if we are running many batches in this run
        """

        if empty(image):
            raise Exception("Cannot do streak finding without an image!")

        self.clear()

        if self.use_crop:
            image = crop2size(image, self.crop_size)

        self.im_size = imsize(image)
        self.image = image

        self.filename = filename

        # input the variance, if given!
        if not empty(variance):
            if not scalar(variance) and self.use_crop:
                self.input_var = crop2size(variance, self.crop_size)
            else:
                self.input_var = variance

        # input the PSF if given!
        if not empty(psf):
            self.input_psf = psf

        # housekeeping
        self.filename = filename
        self.batch_num = batch_num

        corners = []

        if self.use_sections:
            sections = jigsaw(image,
                              self.size_sections,
                              output_corners=corners)
        else:
            sections = image[np.newaxis, ...]

        for i in range(sections.shape[0]):

            if not empty(corners):
                self.current_section_corner = corners[i]

            sec = sections[i, :, :]
            # (m,v) = image_stats(sec)
            sec = self.preprocess(sec)
            self.scanThresholds(sec)

        if self.use_show:
            plt.clf()
            h = plt.imshow(self.image)
            h.set_clim(0, 5 * np.sqrt(self.var_scalar))
            [streak.plotLines(im_type='full') for streak in self.streaks]
            plt.title("full frame image")
            plt.xlabel(self.filename)
            f = plt.gcf()
            f.canvas.draw()
            f.canvas.flush_events()
Esempio n. 20
0
def frt(M_in,
        transpose=False,
        expand=False,
        padding=True,
        partial=False,
        finder=None,
        output=None):
    """ Fast Radon Transform (FRT) of the input matrix M_in (must be 2D numpy array)
    Additional arguments: 
     -transpose (False): transpose M_in (replace x with y) to check all the other angles. 
     -expand (False): adds zero padding to the sides of the passive axis to allow for corner-crossing streaks
     -padding (True): adds zero padding to the active axis to fill up powers of 2. 
     -partial (False): use this to save second output, a list of Radon partial images (useful for calculating variance at different length scales)
     -finder (None): give a "finder" object that is used to scan for streaks. Must have a "Scan" method. 
     -output (None): give the right size array for FRT to put the return value into it.
    """
    #    print "running FRT with: transpose= "+str(transpose)+", expand= "+str(expand)+", padding= "+str(padding)+", partial= "+str(partial)+", finder= "+str(finder)

    ############### CHECK INPUTS AND DEFAULTS #################################

    if empty(M_in):
        return

    if M_in.ndim > 2:
        raise Exception("FRT cannot handle more dimensions than 2D")

    if not empty(finder):
        if not isinstance(finder, pyradon.finder.Finder):
            raise Exception(
                "must input a pyradon.finder.Finder object as finder...")

        scan_method = getattr(finder, 'scan', None)
        if not scan_method or not callable(scan_method):
            raise Exception("finder given to FRT doesn't have a scan method")

        finder.last_streak = []

    ############## PREPARE THE MATRIX #########################################

    M = np.array(M_in)  # keep a copy of M_in to give to finalizeFRT

    if transpose:
        M = M.T

    if not empty(finder):
        finder._im_size_tr = M.shape
        finder.im_size = M_in.shape

    if padding:
        M = padMatrix(M)

    if expand:
        M = expandMatrix(M)

    M_partial = []

    (Nrows, Ncols) = M.shape
    dx = np.array([0])

    M = M[np.newaxis, :, :]

    for m in range(1,
                   int(math.log(Nrows, 2)) + 1):  # loop over logarithmic steps

        M_prev = M
        dx_prev = dx

        Nrows = M.shape[1]  # number of rows in M_prev!

        max_dx = 2**(m) - 1
        dx = range(-max_dx, max_dx + 1)
        M = np.zeros((len(dx), Nrows / 2, Ncols), dtype=M.dtype)

        counter = 0

        for i in range(
                Nrows /
                2):  # loop over pairs of rows  (number of rows in new M)

            for j in range(len(dx)):  # loop over different shifts

                # find the value and index of the previous shift
                dx_in_prev = int(float(dx[j]) / 2)
                j_in_prev = dx_in_prev + int(len(dx_prev) / 2)
                # print "dx[%d]= %d | dx_prev[%d]= %d | dx_in_prev= %d" % (j, dx[j], j_in_prev, dx_prev[j_in_prev], dx_in_prev)
                gap_x = dx[j] - dx_in_prev  # additional shift needed

                M1 = M_prev[j_in_prev, counter, :]
                M2 = M_prev[j_in_prev, counter + 1, :]

                M[j, i, :] = shift_add(M1, M2, -gap_x)

            counter += 2

        if finder:
            finder.scan(M, transpose)
        if partial:
            M_partial.append(M)

    # end of loop on m

    M_out = np.transpose(M, (0, 2, 1))[:, :, 0]  # lose the empty dimension

    if not empty(finder):
        finder.finalizeFRT(M_in, transpose, M_out)

    if partial:
        if not empty(output):
            np.copyto(output, M_partial)
        return M_partial

    else:
        if not empty(output):
            np.copyto(output, M_out)
        return M_out