def processFrame(self, frame): self.frames.append(frame) index = self.index # update index for next iteration self.index += 1 # decide whether to move the stage finished = self.index >= self.positions.shape[0] if not finished: self.move = self.stage.moveTo(self.positions[self.index], 'slow') # calculate offset (while stage moves no next location) if index == 0: offset = (0, 0) else: compareIndex = max(0, index - 10) offset, _ = imreg_dft.translation( frame.getImage(), self.frames[compareIndex].getImage()) px = self.camera.getPixelSize() offset = self.offsets[compareIndex] + offset.astype( float) * [px.x(), px.y()] self.offsets[index] = offset # finish up if there are no more positions if finished: pg.disconnect(self.camera.sigNewFrame, self.newFrame) self.analyze()
def tran(): # basedir = os.path.join('.', 'examples/banklogo') # the TEMPLATE # im0 = sp.misc.imread(os.path.join(basedir, "moban.jpg"), True) # float32 # the image to be transformed # im1 = sp.misc.imread(os.path.join(basedir, "bxz1.jpg"), True) # im3 = cv2.imread(os.path.join(basedir, "moban.jpg")) # im3 = cv2.cvtColor(im3,cv2.COLOR_BGR2GRAY) # unit8 # # im4 = im0.astype(dtype=np.uint8) # cv2.imshow('im0', im4) # cv2.waitKey(0) result = ird.translation(im0, im1) tvec = result["tvec"].round(4) # the Transformed IMaGe. timg = ird.transform_img(im1, tvec=tvec) # Maybe we don't want to show plots all the time ird.imshow(im0, im1, timg) plt.show() print("Translation is {}, success rate {:.4g}".format( tuple(tvec), result["success"]))
def align_videos(self, filenames, template_frame): """Return filenames of generated videos""" progress_global = QProgressDialog('Total progress aligning all files', 'Abort', 0, 100, self) progress_global.setAutoClose(True) progress_global.setMinimumDuration(0) def callback_global(x): progress_global.setValue(x * 100) QApplication.processEvents() callback_global(0) ret_filenames = [] for i, filename in enumerate(filenames): callback_global(i / float(len(filenames))) progress_shifts = QProgressDialog('Finding best shifts for ' + filename, 'Abort', 0, 100, self) progress_shifts.setAutoClose(True) progress_shifts.setMinimumDuration(0) progress_apply = QProgressDialog('Applying shifts for ' + filename, 'Abort', 0, 100, self) progress_apply.setAutoClose(True) progress_apply.setMinimumDuration(0) def callback_apply(x): progress_apply.setValue(x * 100) QApplication.processEvents() progress_load = QProgressDialog('Loading ' + filename, 'Abort', 0, 100, self) progress_load.setAutoClose(True) progress_load.setMinimumDuration(0) def callback_load(x): progress_load.setValue(x * 100) QApplication.processEvents() frames = file_io.load_file(filename, callback_load) callback_load(1) reference_frame = frames[self.ref_no.value()] # todo: this needs to be renamed... reference frame is already established as something else if not self.use_shift_checkbox.isChecked(): if self.scaling_checkbox.isChecked() or self.rotation_checkbox.isChecked(): shift = ird.similarity(template_frame, reference_frame) if not self.rotation_checkbox.isChecked(): shift['angle'] = 0.0 if not self.scaling_checkbox.isChecked(): shift['scale'] = 1.0 else: shift = ird.translation(template_frame, reference_frame) if progress_shifts.wasCanceled(): return else: shift = {'tvec': [self.tvec_y_sb.value(), self.tvec_x_sb.value()], 'angle': self.rotation_sb.value(), 'scale': self.scale_sb.value()} shifted_frames = self.apply_shifts(frames, shift, callback_apply) path = pfs.save_project(filename, self.project, shifted_frames, self.Defaults.manip, 'video') pfs.refresh_list(self.project, self.video_list, self.video_list_indices, self.Defaults.list_display_type, self.toolbutton_values) ret_filenames.append(path) callback_global(1) return ret_filenames
def compute_shift(self, ref_frame, frame): if self.scaling_checkbox.isChecked() or self.rotation_checkbox.isChecked(): shift = ird.similarity(ref_frame, frame) if not self.rotation_checkbox.isChecked(): shift['angle'] = 0.0 if not self.scaling_checkbox.isChecked(): shift['scale'] = 1.0 else: shift = ird.translation(ref_frame, frame) shift['scale'] = 1.0 return shift
def compute_shifts(self, template_frame, frames, progress_shifts): def callback_shifts(x): progress_shifts.setValue(x * 100) QApplication.processEvents() results = [] for i, frame in enumerate(frames): if progress_shifts.wasCanceled(): return callback_shifts(i / float(len(frames))) results = results + [ird.translation(template_frame, frame)] callback_shifts(1) return results
def find_shift(self, master_stack, stack_to_move): """Finds the necessary shift to correct shift_to_move in order to match master stack. Parameters ---------- master_stack : numpy.ndarray 2D Image to use as reference stack_to_move : numpy.ndarray 2D Image that is to be shifted to match the reference """ result = ird.translation(master_stack, stack_to_move) self.tvec = [int(this) for this in result['tvec']]
def RegisterStack(self): # self.imageData[0,:,:] *= 0.33 # self.imageData[1,:,:] *= 0.66 # self.imageData[-1,:,:] *= 0.33 # self.imageData[-2,:,:] *= 0.66 # flatten stacks m = self.imageData.max(axis=0) print 'shape: ', m.shape nreg = self.imageData.shape[0] print 'num reg:', nreg ireg = 10 # int(nreg/2) # get one near the start of the sequence. print 'ireg: ', ireg # correct for lateral motion #off = imreg_dft.translation(self.imageData[ireg], self.imageData[0]) # print 'off', off off = [imreg_dft.translation(self.imageData[ireg], self.imageData[i])['tvec'] for i in range(0, self.imageData.shape[0])] # print 'off', off offt = np.array(off).T # find boundaries of outer rectangle including all images as registered minx = np.min(offt[0]) maxx = np.max(offt[0]) miny = np.min(offt[1]) maxy = np.max(offt[1]) print 'shape: ', m.shape print 'min/max x: ', minx, maxx print 'min/max y: ', miny, maxy # build canvas canvas = np.zeros(shape=(self.imageData.shape[0], self.imageData.shape[1]-minx+maxx, self.imageData.shape[2]-miny+maxy), dtype=self.imageData.dtype) # set initial image (offsets were computed relative to this, so it has no offset) # canvas[0, -minx:-minx+m.shape[1], -miny:-miny+m.shape[2]] = m[0] for i in range(0, self.imageData.shape[0]): ox = offt[0][i] - minx oy = offt[1][i] - miny canvas[i, ox:(ox+self.imageData.shape[1]), oy:(oy+self.imageData.shape[2])] = self.imageData[i] self.imageData = canvas self.updateAvgStdImage() #pg.image(self.imageData,title='image after registration') # def Image_Background(self): # self.background=[] # background = self.imageData[self.times<1] # pg.image(np.mean(background,axis=0), title='average background ') # self.background = np.mean(background,axis=0) # return return
def translation(self, img): if type(img.translation) != np.ndarray: # img on top top_img = self.image(img.row-1, img.col) # img to the left left_img = self.image(img.row, img.col-1) if top_img: img1 = imread(top_img.path) img2 = imread(img.path) y_translation, _ = imreg.translation(img1, img2) else: y_translation = (0, 0) if left_img: img1 = imread(left_img.path) img2 = imread(img.path) x_translation, _ = imreg.translation(img1, img2) else: x_translation = (0, 0) img.translation = np.array((y_translation, x_translation)) return img.translation
def cache(self, path): Bs = [os.path.join(path, p) for p in os.listdir(path) if p == 'Histograms.tif'] LRs = [os.path.join(path, p) for p in os.listdir(path) if p == 'WF_TMR_calibrated.tif'] ImgBs, PathBs, ImgLRs, PathLRs= [], [], [], [] for p in Bs: img = np.array(Image.open(p)) img = np.expand_dims(img, axis=2) if img.ndim == 2 else img ImgBs.append(img) PathBs.append(p) for p in LRs: try: imgStack = Image.open(p) indexes = [i for i in range(imgStack.n_frames)] random.shuffle(indexes) c = min(len(indexes), 20) for i in indexes[:c]: imgStack.seek(i) img = np.array(imgStack) dtype = img.dtype assert img.ndim == 2 if self.drift_correction: import imreg_dft as ird from skimage import exposure b = ImgBs[0][:, :, 0] b = exposure.equalize_hist(b) b = scipy.ndimage.filters.gaussian_filter(b, sigma=(6, 6)) b = scipy.misc.imresize(b, img.shape[:2]) ts = ird.translation(b, img) tvec = ts["tvec"] # the Transformed IMaGe. img = ird.transform_img(img, tvec=tvec) if self.scale_LR == True: img = scipy.misc.imresize(img, ImgBs[0].shape[:2]) elif type(self.scale_LR) is list: img = scipy.misc.imresize(img, self.scale_LR) img = np.expand_dims(img, axis=2) img = img.astype(dtype) ImgLRs.append(img) PathLRs.append(p) except KeyboardInterrupt: raise except Exception as e: print('error when reading file ', p) import traceback, sys traceback.print_exc(file=sys.stdout) self.__cache[path] = { 'B': ImgBs, 'A':ImgLRs, 'path': path, 'pathB': PathBs, 'pathA': PathLRs} return True
def compute_translation(path, src, target): ''' Compute translation of src tile to target tile contained in /path/ Inputs: path: folder containing tiles src: source tile in .png format target: target tile in .png format Outputs: tvec: translation vector measured in pixels ''' im0 = sp.misc.imread(path + src, True) im1 = sp.misc.imread(path + target, True) result = ird.translation(im0, im1) tvec = result['tvec'] return tvec
def xcorr_hybes(hybe_dict, reg_ref='hybe1', bead_thresh=10000): """ Find the translation xcorr between hybes. """ tvecs = {} ref_img = hybe_dict[reg_ref] # img_bg = gaussian_filter(ref_img, (10, 10)) # ref_hpass = ref_img-img_bg # np.place(ref_hpass, ref_hpass<bead_thresh, 0.01) for h, img in hybe_dict.items(): if h == reg_ref: tvecs[h] = (0, 0) else: # img_bg = gaussian_filter(img, (10, 10)) # img_hpass = img-img_bg # np.place(img_hpass, img_hpass<bead_thresh, 0.01) xcorr_result = ird.translation(ref_img, img) tvecs[h] = xcorr_result['tvec'] return tvecs
def calc_offset_points(points_1, points_2, shape, plot=False): "Calculate the offset between a pair of ordered points -- e.g., an xy list of star positions, and and xy list of model postns." "Returned offset is integer pixels as tuple (dy, dx)." diam_kernel = 5 # If this is 11, that is too big, and we gt the wrong answer. Very sensitive. image_1 = hbt.image_from_list_points(points_1, shape, diam_kernel) image_2 = hbt.image_from_list_points(points_2, shape, diam_kernel) t0,t1 = ird.translation(image_1, image_2) # Return shift, with t0 = (dy, dx). t1 is a flag or quality or something. (dy,dx) = t0 if (plot): xrange = (0, shape[0]) yrange = (0, shape[1]) figs = plt.figure() ax1 = figs.add_subplot(1,2,1) # nrows, ncols, plotnum. Returns an 'axis' ax1.set_aspect('equal') # Need to explicitly set aspect ratio here, otherwise in a multi-plot, it will be rectangular # fig1 = plt.imshow(np.log(image_1)) plt.plot(points_1[:,0], points_1[:,1], marker='o', color='lightgreen', markersize=4, ls='None', label = 'Photometric') plt.plot(points_2[:,0], points_2[:,1], marker='o', color='red', markersize=4, ls='None', label = 'Cat') plt.legend() plt.xlim(xrange) # Need to set this explicitly so that points out of image range are clipped plt.ylim(yrange) plt.title('Raw') ax2 = figs.add_subplot(1,2,2) # nrows, ncols, plotnum. Returns an 'axis' plt.plot(points_1[:,0], points_1[:,1], marker='o', color='lightgreen', markersize=9, ls='None') plt.plot(points_2[:,0] + t0[1], points_2[:,1] + t0[0], marker='o', color='red', markersize=4, ls='None') ax2.set_aspect('equal') plt.xlim(xrange) # Need to set this explicitly so that points out of image range are clipped plt.ylim(yrange) plt.title('Shifted, dx=' + repr(dx) + ', dy = ' + repr(dy)) plt.show() return t0
def comp_trans(src, target): ''' Compute translation of src array to target array Inputs: src: numpy array representing source tile target: numpy array representing target tile Outputs: tvec: translation vector measured in pixels ''' tvec = [0, 0] range_target = target.max() - target.min() if range_target > 0: try: result = ird.translation(src, target) tvec = result['tvec'] except OverflowError: tvec = float('nan') return tvec
def translation(template_array, sub_array, scale_exponent=0.4): """ Aligns array 'sub_array' to the template 'template_array' using translational transformation. Args: template_array : Template array which will be used as a reference for translational aligning sub_array : Subject array which will be aligned with the template_array using translation scale_exponent : Exponent to which the array will be scaled Returns: mod_array : Modified subject array aligned using translational transformation """ list_array = modify_array(np.array([template_array, sub_array]), scale_exponent) template_arrayslice = list_array[0] sub_arrayslice = list_array[1] dict_shift = ird.translation(template_arrayslice, sub_arrayslice) list_shift = list(dict_shift['tvec']) mod_array = np.empty_like(sub_array) shift(sub_array, list_shift, output=mod_array, mode='nearest') print("Translation In X & Y = {0}".format(list_shift)) return mod_array
def register(self, T, reg_ch): if T.ndim == 3: reg_ch = None def _reg(x): return x if reg_ch is None else x[reg_ch] R = [T[0]] print('Running drift correction...') for frame in tqdm(T[1:]): result = ird.translation(_reg(R[-1]), _reg(frame)) if reg_ch is None: freg = ird.transform_img(frame, tvec=result["tvec"]) else: freg = np.stack( [ird.transform_img(c, tvec=result["tvec"]) for c in frame]) R.append(freg) reg = np.stack(R) return reg
def navigate_image_stellar(im, wcs_in, name_catalog='', do_plot=True, method='fft', title=''): """ Navigate frame based on stellar images. Result returns is pixel shift (dy, dx). WCS paramaters are returned, *and* modified in place. """ import imreg_dft as ird from astropy.wcs import WCS # from astropy.vo.client import conesearch # Virtual Observatory, ie star catalogs # DEPRECATED! from astroquery.vo_conesearch import conesearch # New home of conesearch # Inputs are the image array, and the WCS structure. # This routine does not do any file IO. The image array and header must be already loaded. # The image is assumed to be stretched properly s.t. stars can be found using DAOphot. NUM_STARS_PHOT = 100 # How many stars to use from DAOPhot. For noisy images, DAO will find a lot of # fake stars, so we need to crank this up higher than the # of cat stars. NUM_STARS_CAT = 50 # How many stars to use from star catalog DO_GSC1 = False DO_GSC12 = True DO_USNOA2 = False #============================================================================== # Calculate the image radius, in radians, based on the size and the pixel scale #============================================================================== dx_pix = hbt.sizex(im) dy_pix = hbt.sizey(im) radec_corner = wcs_in.wcs_pix2world(0, dy_pix/2, 0) radec_center = wcs_in.wcs_pix2world(dx_pix/2, dy_pix/2, 0) (ra_corner, dec_corner) = radec_corner (ra_center, dec_center) = radec_center radius_image = math.sqrt((dec_corner-dec_center)**2 + ((ra_corner-ra_center) / np.cos(dec_corner*hbt.d2r))**2) * hbt.d2r radius_search_deg = radius_image * hbt.r2d # Read the WCS coordinates center_deg = wcs_in.wcs.crval # degrees. # crval is a two-element array of [RA, Dec], in degrees # Stretch the image. This is just for display -- no processing. stretch_percent = 90 stretch = astropy.visualization.PercentileInterval(stretch_percent) # PI(90) scales array to 5th .. 95th %ile. # Display it if (do_plot): plt.imshow(stretch(im)) #============================================================================== # Get stars from star catalogs #============================================================================== if (DO_GSC1): name_cat = u'The HST Guide Star Catalog, Version 1.1 (Lasker+ 1992) 1' # works, but 1' errors; investigating stars = conesearch.conesearch(center_deg, radius_search_deg, cache=True, catalog_db = name_cat) ra_stars = np.array(stars.array['RAJ2000'])*hbt.d2r # Convert to radians dec_stars = np.array(stars.array['DEJ2000'])*hbt.d2r # Convert to radians # table_stars = Table(stars.array.data) if (DO_GSC12): # name_cat = u'The HST Guide Star Catalog, Version 1.2 (Lasker+ 1996) 1' name_cat = u'Guide Star Catalog v2 1' # Works from gobi, not tomato url_cat = 'http://gsss.stsci.edu/webservices/vo/ConeSearch.aspx?CAT=GSC23&' # Works always with data.conf.set_temp('remote_timeout', 30): # This is the very strange syntax to set a timeout delay. # The default is 3 seconds, and that times out often. with warnings.catch_warnings(): warnings.simplefilter("ignore") # stars = conesearch.conesearch(wcs_in.wcs.crval, radius_search_deg, cache=True, catalog_db = url_cat) # The various functions of conesearch/ConeSearch/etc are quite confusing, and are in flux. # This line below seems to work. It does not allow an explicit catalog suggstion, but it does the job. c = astropy.coordinates.SkyCoord(wcs_in.wcs.crval[0], wcs_in.wcs.crval[1], unit='deg') stars = ConeSearch.query_region(c, f'{radius_search_deg} deg') ra_stars = np.array(stars.array['ra'])*hbt.d2r # Convert to radians dec_stars = np.array(stars.array['dec'])*hbt.d2r # Convert to radians mag = np.array(stars.array['Mag']) print("Stars downloaded: N = {}; mag = {:.2f} .. {:.2f}".format(np.size(mag), np.nanmin(mag), np.nanmax(mag))) print("RA = {:.2f} .. {:.2f}".format(np.nanmin(ra_stars)*hbt.r2d, np.nanmax(ra_stars)*hbt.r2d)) # Now sort by magnitude, and keep the 100 brightest # This is because this GSC catalog is huge -- typically 2000 stars in LORRI FOV. # We need to reduce its size to fit in our fixed astropy table string length. order = np.argsort(mag) order = np.array(order)[0:NUM_STARS_CAT] ra_stars = ra_stars[order] # Returned as radians dec_stars = dec_stars[order] if (DO_USNOA2): name_cat = u'The USNO-A2.0 Catalogue (Monet+ 1998) 1' # Works but gives stars down to v=17; I want to v=13 stars = conesearch.conesearch(wcs_in.wcs.crval, 0.3, cache=False, catalog_db = name_cat) table_stars = Table(stars.array.data) mask = table_stars['Bmag'] < 13 table_stars_m = table_stars[mask] ra_stars = table_stars_m['RAJ2000']*hbt.d2r # Convert to radians dec_stars = table_stars_m['DEJ2000']*hbt.d2r # Convert to radians ra_stars_cat = ra_stars dec_stars_cat = dec_stars radec_stars_cat = np.transpose(np.array((ra_stars_cat, dec_stars_cat))) (x_stars_cat, y_stars_cat) = wcs_in.wcs_world2pix( radec_stars_cat[:,0]*hbt.r2d, radec_stars_cat[:,1]*hbt.r2d, 0) points_stars_cat = np.transpose((y_stars_cat, x_stars_cat)) # Yes, order is supposed to be (y,x) #============================================================================== # Use DAOphot to search the image for stars. #============================================================================== points_stars_phot = hbt.find_stars(im, num=NUM_STARS_PHOT) # Returns N x 2 aray. 0 = Row = y; 1 = Column = x. y_stars_phot =(points_stars_phot[:,0]) # xy is correct -- see above x_stars_phot =(points_stars_phot[:,1]) # #============================================================================== # Make a plot showing the DAO stars on the image #============================================================================== color_phot = 'red' # Color for stars found photometrically color_cat = 'lightgreen' # Color for stars in catalog DO_PLOT_DAO = False # Plot an intermediate result? if (DO_PLOT_DAO): plt.imshow(stretch(im)) plt.plot(x_stars_phot, y_stars_phot, linestyle='none', marker='o', markersize=9, mec=color_cat, mew=1, color='none', label = 'DAO photometric stars') # plot() uses x, y plt.plot(x_stars_cat, y_stars_cat, linestyle='none', marker='o', markersize=5, color='lightgreen', label = 'Cat stars') # plot() uses x, y plt.title(title) plt.ylim((hbt.sizey(im)),0) plt.xlim((0,hbt.sizex(im))) plt.legend(loc = 'upper left') plt.show() # Up til here, x and y are correct #============================================================================== # Look up the shift between the photometry and the star catalog. # Do this by making a pair of fake images, and then looking up image registration on them. #============================================================================== # I call this pointing process 'opnav'. # It is returned in order (y,x) because that is what imreg_dft uses, even though it is a bit weird. diam_kernel = 11 # How many pixels across are our synthetic stellar images? Should be odd number. Not critical. do_binary = True # For the stellar images, do a binary 1/0 (recommended), or a pixel distance? shape = np.shape(im) # Set shape of output array image_cat = hbt.image_from_list_points(points_stars_cat, shape, diam_kernel, do_binary=do_binary) image_phot = hbt.image_from_list_points(points_stars_phot, shape, diam_kernel, do_binary=do_binary) if (method == 'fft'): # Very fast method # Set up a constraint for the fit. It should be different for 1x1 and 4x4. # For 1x1, it works well to be 100 pixels. if (hbt.sizex(im) == 1024): # For LORRI 1x1 constraint_tx = (0,100) # Mean and stdev. i.e., returned value will be within stdev of mean. constraint_ty = (0,100) if (hbt.sizex(im) == 256): # For LORRI 4x4 constraint_tx = (0,25) # Mean and stdev. i.e., returned value will be within stdev of mean. constraint_ty = (0,25) constraint_angle = 0 # With one value, it is a fixed constraint. constraints = {'tx' : constraint_tx, 'ty' : constraint_ty, 'angle' : constraint_angle} ird.translation(image_cat, image_phot, constraints=constraints) (dy, dx) = ird.translation(image_cat, image_phot, constraints=constraints)['tvec'] dy_opnav = -dy dx_opnav = -dx if (method == 'bruteforce'): # Very slow method ((dx, dy), mat) = hbt.get_translation_images_bruteforce(image_cat, image_phot) dx_opnav = -dx dy_opnav = -dy #============================================================================== # Make a plot, showing DAO positions + catalog positions #============================================================================== do_plot = True if (do_plot): # hbt.figsize((10,10)) plt.imshow(stretch(im)) # Plot the stars -- catalog, and DAO plt.plot(x_stars_cat + dx_opnav, y_stars_cat + dy_opnav, marker='o', ls='None', color=color_cat, alpha = 0.5, ms=12, mew=1, label = 'Cat Stars, adjusted') plt.plot(x_stars_cat, y_stars_cat, marker='o', ls='None', color=color_cat, alpha = 1, ms=4, mew=1, label = 'Cat Stars, raw') plt.plot(x_stars_phot, y_stars_phot, marker='o', ls='None', color='none', markersize=10, mew=1, mec=color_phot, alpha = 1, label = 'DAOfind Stars') plt.title('After navigation, with dx = {:.1f}, dy = {:.1f}, {}'.format(dx_opnav, dy_opnav, title)) plt.legend() # Draw legend. Might be irrel since remove() might keep it; not sure. plt.imshow(stretch(im)) plt.show() #============================================================================== # Return results and exit #============================================================================== # Results are returned in terms of pixel offset and a revised WCS structure. # I don't seem to be able to copy a WCS structure, so I modify the one in place! # Get the pixel location of the center position crpix = wcs_in.wcs.crpix # Center position, in pixels, old # Get the new RA, Dec center of the array. It is just the old location, plus the offset ORIGIN_FORMAT = 1 # 0 for Numpy-style indexing, 1 for Fortran-style and FITS-style. # So what do I used for FITS files in python? Experimentally, 1 is right and 0 is not. (ra_new, dec_new) = wcs_in.wcs_pix2world(crpix[0] - dx_opnav, crpix[1] - dy_opnav, ORIGIN_FORMAT) # Set it wcs_in.wcs.crval = (ra_new, dec_new) return(wcs_in, (dy_opnav, dx_opnav))
def execute_primary_function(self, input_paths=None): """Return filenames of generated videos""" [reference_frame, to_align_paths] = self.get_alignment_inputs(input_paths) assert ([ os.path.splitext(os.path.basename(path))[0] for path in to_align_paths ] == self.shift_table_data[self.Labels.shift_table_col1]) progress_global = QProgressDialog('Total progress aligning all files', 'Abort', 0, 100, self) progress_global.setAutoClose(True) progress_global.setMinimumDuration(0) def callback_global(x): progress_global.setValue(x * 100) QApplication.processEvents() callback_global(0) progress_global.canceled.connect( functools.partial(self.cancel_progress_dialog, progress_global)) ret_filenames = [] shifts = {} for i, filename in enumerate(to_align_paths): callback_global(i / float(len(to_align_paths))) progress_shifts = QProgressDialog( 'Finding best shifts for ' + filename, 'Abort', 0, 100, self) progress_shifts.setAutoClose(True) progress_shifts.setMinimumDuration(0) progress_shifts.canceled.connect( functools.partial(self.cancel_progress_dialog, progress_shifts)) progress_load = QProgressDialog('Loading ' + filename, 'Abort', 0, 100, self) progress_load.setAutoClose(True) progress_load.setMinimumDuration(0) def callback_load(x): progress_load.setValue(x * 100) QApplication.processEvents() frames = file_io.load_file(filename, callback_load) callback_load(1) to_align_frame = frames[self.ref_no.value()] frame_no, h, w = frames.shape to_align_frame = np.reshape(to_align_frame, (1, h, w)) to_align_frame = self.crop_border( self.spatial_filter(to_align_frame))[0] # to_align_frame = to_align_frame[0] if self.scaling_checkbox.isChecked( ) or self.rotation_checkbox.isChecked(): shift = ird.similarity(reference_frame, to_align_frame) if not self.rotation_checkbox.isChecked(): shift['angle'] = 0.0 if not self.scaling_checkbox.isChecked(): shift['scale'] = 1.0 else: shift = ird.translation(reference_frame, to_align_frame) # if not self.use_shift_checkbox.isChecked(): # else: # shift = {'tvec': [self.tvec_y_sb.value(), self.tvec_x_sb.value()], 'angle': self.rotation_sb.value(), # 'scale': self.scale_sb.value()} shifts[filename] = shift # Apply the found shift (row i) to all stacks in row i for col_key in self.shift_table_data.keys(): # i = row filename = os.path.normpath( os.path.join(self.project.path, self.shift_table_data[col_key][i])) + '.npy' frames = file_io.load_file(filename, callback_load) callback_load(1) progress_apply = QProgressDialog( 'Applying shifts for ' + filename, 'Abort', 0, 100, self) progress_apply.setAutoClose(True) progress_apply.setMinimumDuration(0) def callback_apply(x): progress_apply.setValue(x * 100) QApplication.processEvents() progress_apply.canceled.connect( functools.partial(self.cancel_progress_dialog, progress_apply)) shifted_frames = self.apply_shifts(frames, shift, callback_apply) path = pfs.save_project(filename, self.project, shifted_frames, self.Defaults.manip, 'video') pfs.refresh_list(self.project, self.video_list, [], self.Defaults.list_display_type, self.toolbutton_values) ret_filenames.append(path) callback_global(1) # save shifts to csv save_loc = QFileDialog.getSaveFileName( self, 'Save Shifts', QSettings().value('path_of_last_project'), '(*.csv)')[0] if save_loc: # fields = list(shifts[list(shifts.keys())[0]].keys()) # for key in shifts.keys(): # for field in fields: # if field not in ['angle', 'tvec', 'scale', 'success']: # del shifts[key][field] # else: # shifts[key][field.encode('ascii')] = shifts[key][field] # del shifts[key][field] # keys_copy = list(shifts.keys()) # for key in keys_copy: # shifts[key.encode('ascii')] = shifts[key] # del shifts[key] # # for field in fields: # # if field not in ["File aligned", 'angle', 'tvec', 'scale', 'success']: # # for key in shifts.keys(): # # del shifts[key][field] # # else: # # shifts[] # keys_copy = list(shifts.keys()) # for key in keys_copy: # x, y = shifts[key][b'tvec'] # shifts[key][b'tvec-x'] = x # shifts[key][b'tvec-y'] = y # del shifts[key][b'tvec'] fields = list(shifts[list(shifts.keys())[0]].keys()) for key in list(shifts.keys()): for field in fields: if field not in ['angle', 'tvec', 'scale', 'success']: del shifts[key][field] for key in list(shifts.keys()): x, y = shifts[key]['tvec'] shifts[key]['tvec-x'] = x shifts[key]['tvec-y'] = y del shifts[key]['tvec'] fields = ["File aligned"] + list(shifts[list( shifts.keys())[0]].keys()) with open(save_loc, "w", newline='') as f: w = csv.DictWriter(f, fields) w.writeheader() for k in shifts: w.writerow( {field: shifts[k].get(field) or k for field in fields}) # for key, val in sorted(shifts.items()): # row = {"File aligned": key} # row.update(val) # w.writerow(row) # with open(save_loc, 'wb') as csv_file: # writer = csv.writer(csv_file) # for key, value in shifts.items(): # writer.writerow([key, value]) return ret_filenames
def execute_primary_function(self, input_paths=None): """Return filenames of generated videos""" [reference_frame, to_align_paths] = self.get_alignment_inputs(input_paths) assert([os.path.splitext(os.path.basename(path))[0] for path in to_align_paths] == self.shift_table_data[self.Labels.shift_table_col1]) progress_global = QProgressDialog('Total progress aligning all files', 'Abort', 0, 100, self) progress_global.setAutoClose(True) progress_global.setMinimumDuration(0) def callback_global(x): progress_global.setValue(x * 100) QApplication.processEvents() callback_global(0) progress_global.canceled.connect(functools.partial(self.cancel_progress_dialog, progress_global)) ret_filenames = [] shifts = {} for i, filename in enumerate(to_align_paths): callback_global(i / float(len(to_align_paths))) progress_shifts = QProgressDialog('Finding best shifts for ' + filename, 'Abort', 0, 100, self) progress_shifts.setAutoClose(True) progress_shifts.setMinimumDuration(0) progress_shifts.canceled.connect(functools.partial(self.cancel_progress_dialog, progress_shifts)) progress_load = QProgressDialog('Loading ' + filename, 'Abort', 0, 100, self) progress_load.setAutoClose(True) progress_load.setMinimumDuration(0) def callback_load(x): progress_load.setValue(x * 100) QApplication.processEvents() frames = file_io.load_file(filename, callback_load) callback_load(1) to_align_frame = frames[self.ref_no.value()] frame_no, h, w = frames.shape to_align_frame = np.reshape(to_align_frame, (1, h, w)) to_align_frame = self.crop_border(self.spatial_filter(to_align_frame))[0] # to_align_frame = to_align_frame[0] if self.scaling_checkbox.isChecked() or self.rotation_checkbox.isChecked(): shift = ird.similarity(reference_frame, to_align_frame) if not self.rotation_checkbox.isChecked(): shift['angle'] = 0.0 if not self.scaling_checkbox.isChecked(): shift['scale'] = 1.0 else: shift = ird.translation(reference_frame, to_align_frame) # if not self.use_shift_checkbox.isChecked(): # else: # shift = {'tvec': [self.tvec_y_sb.value(), self.tvec_x_sb.value()], 'angle': self.rotation_sb.value(), # 'scale': self.scale_sb.value()} shifts[filename] = shift # Apply the found shift (row i) to all stacks in row i for col_key in self.shift_table_data.keys(): # i = row filename = os.path.normpath(os.path.join(self.project.path, self.shift_table_data[col_key][i])) + '.npy' frames = file_io.load_file(filename, callback_load) callback_load(1) progress_apply = QProgressDialog('Applying shifts for ' + filename, 'Abort', 0, 100, self) progress_apply.setAutoClose(True) progress_apply.setMinimumDuration(0) def callback_apply(x): progress_apply.setValue(x * 100) QApplication.processEvents() progress_apply.canceled.connect(functools.partial(self.cancel_progress_dialog, progress_apply)) shifted_frames = self.apply_shifts(frames, shift, callback_apply) path = pfs.save_project(filename, self.project, shifted_frames, self.Defaults.manip, 'video') pfs.refresh_list(self.project, self.video_list, [], self.Defaults.list_display_type, self.toolbutton_values) ret_filenames.append(path) callback_global(1) # save shifts to csv save_loc = QFileDialog.getSaveFileName(self, 'Save Shifts', QSettings().value('path_of_last_project'), '(*.csv)') if save_loc: # fields = list(shifts[list(shifts.keys())[0]].keys()) # for key in shifts.keys(): # for field in fields: # if field not in ['angle', 'tvec', 'scale', 'success']: # del shifts[key][field] # else: # shifts[key][field.encode('ascii')] = shifts[key][field] # del shifts[key][field] # keys_copy = list(shifts.keys()) # for key in keys_copy: # shifts[key.encode('ascii')] = shifts[key] # del shifts[key] # # for field in fields: # # if field not in ["File aligned", 'angle', 'tvec', 'scale', 'success']: # # for key in shifts.keys(): # # del shifts[key][field] # # else: # # shifts[] # keys_copy = list(shifts.keys()) # for key in keys_copy: # x, y = shifts[key][b'tvec'] # shifts[key][b'tvec-x'] = x # shifts[key][b'tvec-y'] = y # del shifts[key][b'tvec'] fields = list(shifts[list(shifts.keys())[0]].keys()) for key in list(shifts.keys()): for field in fields: if field not in ['angle', 'tvec', 'scale', 'success']: del shifts[key][field] for key in list(shifts.keys()): x, y = shifts[key]['tvec'] shifts[key]['tvec-x'] = x shifts[key]['tvec-y'] = y del shifts[key]['tvec'] fields = ["File aligned"] + list(shifts[list(shifts.keys())[0]].keys()) with open(save_loc, "w", newline='') as f: w = csv.DictWriter(f, fields) w.writeheader() for k in shifts: w.writerow({field: shifts[k].get(field) or k for field in fields}) # for key, val in sorted(shifts.items()): # row = {"File aligned": key} # row.update(val) # w.writerow(row) # with open(save_loc, 'wb') as csv_file: # writer = csv.writer(csv_file) # for key, value in shifts.items(): # writer.writerow([key, value]) return ret_filenames
def crossCorr_imreg_dft(img1, img2): shift = ird.translation(img1, img2, filter_pcorr=8, odds=1) # ~ filter_pcorr (int) – Radius of the minimum spectrum filter for translation detection, use the filter when detection fails. Values > 3 are likely not useful. shift = shift["tvec"].round(4) return np.asarray([shift[1], shift[0]])
import os import scipy as sp import scipy.misc import imreg_dft as ird basedir = os.path.join('..', 'examples') # the TEMPLATE im0 = sp.misc.imread(os.path.join(basedir, "sample1.png"), True) # the image to be transformed im1 = sp.misc.imread(os.path.join(basedir, "sample2.png"), True) result = ird.translation(im0, im1) tvec = result["tvec"].round(4) # the Transformed IMaGe. timg = ird.transform_img(im1, tvec=tvec) # Maybe we don't want to show plots all the time if os.environ.get("IMSHOW", "yes") == "yes": import matplotlib.pyplot as plt ird.imshow(im0, im1, timg) plt.show() print("Translation is {}, success rate {:.4g}".format(tuple(tvec), result["success"]))
import os import scipy as sp import scipy.misc import imreg_dft as ird basedir = os.path.join('..', 'examples') # the TEMPLATE im0 = sp.misc.imread(os.path.join(basedir, "sample1.png"), True) # the image to be transformed im1 = sp.misc.imread(os.path.join(basedir, "sample2.png"), True) result = ird.translation(im0, im1) tvec = result["tvec"].round(4) # the Transformed IMaGe. timg = ird.transform_img(im1, tvec=tvec) # Maybe we don't want to show plots all the time if os.environ.get("IMSHOW", "yes") == "yes": import matplotlib.pyplot as plt ird.imshow(im0, im1, timg) plt.show() print("Translation is {}, success rate {:.4g}" .format(tuple(tvec), result["success"]))
import os import scipy as sp import scipy.misc import matplotlib.pyplot as plt import imreg_dft as ird basedir = os.path.join('..', 'examples') # the TEMPLATE im0 = sp.misc.imread(os.path.join(basedir, "sample1.png"), True) # the image to be transformed im1 = sp.misc.imread(os.path.join(basedir, "sample2.png"), True) t0, t1 = ird.translation(im0, im1) # the Transformed IMaGe. timg = ird.transform_img(im1, tvec=(t0, t1)) ird.imshow(im0, im1, timg) plt.show() print(t0, t1)
def process(self, obj_data): """ Coregister images @param obj_data: Image data wrapper """ reg_type = self.ap_paramList[0]() master_burst_list = None for label, data in obj_data.getIterator(): if master_burst_list == None: master_burst_list = select_valid_lines( data, obj_data.info(label)['Tree'], cut=False) master_valid_lines = get_valid_lines( obj_data.info(label)['Tree'], per_burst=True) else: burst_valid_lines = get_valid_lines( obj_data.info(label)['Tree'], per_burst=True) valid_lines = [ np.logical_and(master_lines, burst_lines) for master_lines, burst_lines in zip( master_valid_lines, burst_valid_lines) ] burst_list = select_valid_lines(data, obj_data.info(label)['Tree'], cut=False) lines_per_burst = int( obj_data.info(label)['Tree'].find( 'swathTiming/linesPerBurst').text) samples_per_burst = int( obj_data.info(label)['Tree'].find( 'swathTiming/samplesPerBurst').text) lines, samples = np.meshgrid(np.arange(lines_per_burst), np.arange(samples_per_burst), indexing='ij') ramp = SentinelRamp(obj_data.info(label)) for index, (burst_lines, burst) in enumerate(zip(valid_lines, burst_list)): start_valid_line = np.argmax(burst_lines) end_valid_line = lines_per_burst - np.argmax( burst_lines[::-1]) if self._image_limits == None: line_slice = slice(start_valid_line, end_valid_line) sample_slice = slice(0, samples_per_burst) elif self._image_limits[index] != None: line_slice = self._image_limits[index][0] sample_slice = self._image_limits[index][1] if line_slice.start == None or \ line_slice.start < start_valid_line: line_slice_start = start_valid_line else: line_slice_start = line_slice.start if line_slice.stop == None or \ line_slice.stop > end_valid_line: line_slice_stop = end_valid_line else: line_slice_stop = line_slice.stop line_slice = slice(line_slice_start, line_slice_stop) else: continue master_burst = master_burst_list[index][line_slice, sample_slice] burst = burst[line_slice, sample_slice] deramp = -ramp(lines[line_slice, sample_slice], samples[line_slice, sample_slice], index) if reg_type == 'imreg_translation': for i in range(self._num_iterations): shift = ird.translation(np.abs(master_burst), np.abs(burst)) transform_matrix = np.array( [[1, 0, shift['tvec'][1]], [0, 1, shift['tvec'][0]]]) burst, deramp = transform_slc( burst, deramp, transform_matrix) elif reg_type == 'imreg_affine': shift = ird.similarity(np.abs(master_burst), np.abs(burst), numiter=self._num_iterations) im_angle = np.deg2rad(shift['angle']) im_scale = shift['scale'] im_tl = shift['tvec'] transform_matrix = np.array( [[ im_scale * np.cos(im_angle), -im_scale * np.sin(im_angle), im_tl[1] ], [ im_scale * np.sin(im_angle), im_scale * np.cos(im_angle), im_tl[0] ]], dtype=np.float32) burst = transform_slc(burst, deramp, transform_matrix)[0] if index != 0: pass elif reg_type == 'keypoints': transform_matrix = keypoints_align( scale_image(np.abs(master_burst)), scale_image(np.abs(burst))) burst = transform_slc(burst, deramp, transform_matrix)[0] if line_slice.start == None: line_start = 0 elif line_slice.start < 0: line_start = lines_per_burst + line_slice.start else: line_start = line_slice.start if line_slice.stop == None: line_end = lines_per_burst elif line_slice.stop < 0: line_end = lines_per_burst + line_slice.stop else: line_end = line_slice.stop full_data_slice = slice( lines_per_burst * index + line_start, lines_per_burst * (index) + line_end) data[full_data_slice, sample_slice] = burst
def ratio(verbose, logger, work_out_path, crop, res, register, union, h5_save, tiff_save, frange): # Start time time_start = timer() # Input background subtracted image stack try: f = h5py.File(work_out_path + '_back.h5', 'r') except: raise ImportError(work_out_path + "_back.h5 not found") # Input acceptor stack try: acceptor = np.array(f['acceptor']) acceptor_frange = np.array(f.attrs['acceptor_frange']) except: raise ImportError("Acceptor stack background not processed") # Input donor stack try: donor = np.array(f['donor']) donor_frange = np.array(f.attrs['donor_frange']) except: raise ImportError("Donor stack background not processed") f.close() # Find frame dimensions and intersection between processed frames and input frames Ydim, Xdim = acceptor.shape[1:] brange = np.intersect1d(frange, acceptor_frange, return_indices=True)[2] # Set default values for crop if (crop[2] == 0): crop[2] = Xdim if (crop[3] == 0): crop[3] = Ydim # Testing input values test.assert_array_equal( acceptor_frange, donor_frange), "Acceptor and Donor stacks have different frame numbers" assert ( sum(~np.isin(frange, acceptor_frange)) == 0 ), "background subtracted stacks have not been processed for all frame values" assert (crop[2] >= crop[0]), "crop[2] must be greater than crop[0]" assert (crop[3] >= crop[1]), "crop[3] must be greater than crop[1]" assert (crop[0] >= 0), "crop[0] must be >= 0" assert (crop[2] <= Xdim), "crop[2] must be <= than the width of the image" assert (crop[1] >= 0), "crop[1] must be >= 0" assert (crop[3] <= Ydim), "crop[3] must be <= than the height of the image" # Image crop acceptorc = acceptor[:, crop[1]:crop[3], crop[0]:crop[2]] donorc = donor[:, crop[1]:crop[3], crop[0]:crop[2]] # Search for saved ratio images try: # Input files into dictionaries f2 = h5py.File(work_out_path + '_ratio_back.h5', 'r') ratio_frange = np.array(f2.attrs['ratio_frange']) acceptori = dict(list(zip(ratio_frange, np.array(f2['acceptori'])))) donori = dict(list(zip(ratio_frange, np.array(f2['donori'])))) f2.close() except: # Initialize empty dictionaries for intensities acceptori, donori = {}, {} # Initialize empty dictionaries for pixel counts acceptornz, donornz = {}, {} # Set up constants for loop mult = np.float32(255) / np.float32(res) ires = 100 / np.float32(res) ipix = 100 / (Xdim * Ydim) # Loop through frames for count, frame in list(zip(frange, brange)): if (verbose): print("(Ratio Processing) Frame Number: " + str(count + 1)) # Image registration for donor channel if (register): trans = ird.translation(acceptorc[frame, :, :], donorc[frame, :, :]) tvec = trans["tvec"].round(4) donorc[frame, :, :] = np.round( ird.transform_img(donorc[frame, :, :], tvec=tvec)) # Thresholding acceptors = np.uint8(np.float32(acceptorc[frame, :, :]) * mult) donors = np.uint8(np.float32(donorc[frame, :, :]) * mult) # Check for max image intensity if np.uint32(np.amax(acceptors)) + np.uint32(np.amax(donors)) > 70: # Otsu thresholding for normal intensity images _, A_thresh = cv2.threshold(acceptors, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) _, B_thresh = cv2.threshold(donors, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) else: # Simple thresholding for low intensity images _, A_thresh = cv2.threshold(acceptors, 3, 255, cv2.THRESH_BINARY) _, B_thresh = cv2.threshold(donors, 3, 255, cv2.THRESH_BINARY) # Setting values below threshold to zero acceptorc[frame, :, :] *= np.uint16(A_thresh / 255) donorc[frame, :, :] *= np.uint16(B_thresh / 255) # Consider only foreground pixel intensity overlapping between donor and acceptor channels to ensure channels overlap perfectly if (union): # Create mask for overlapping pixels C = np.multiply(A_thresh, B_thresh) C[C > 0] = 1 # Set non-overlapping pixels to zero acceptorc[frame, :, :] *= C donorc[frame, :, :] *= C # Count number of non-zero pixels by total pixels per frame acceptornz[count] = np.count_nonzero(A_thresh) * ipix donornz[count] = np.count_nonzero(B_thresh) * ipix # Find the ratio of the median non-zero intensity pixels and the bit depth per frame for the acceptor stack if (np.amax(acceptorc[frame, :, :]) > 0.0): acceptori[count] = ndimage.median(acceptorc[frame, :, :], labels=A_thresh / 255) * ires else: acceptori[count] = 0 # Find the ratio of the median non-zero intensity pixels and the bit depth per frame for the donor stack if (np.amax(donorc[frame, :, :]) > 0.0): donori[count] = ndimage.median(donorc[frame, :, :], labels=B_thresh / 255) * ires else: donori[count] = 0 # End time time_end = timer() time_elapsed = str(int(time_end - time_start) + 1) if (verbose): print(("(Ratio Processing) Time: " + time_elapsed + " second(s)")) # Update log file to save stack metrics print_range = [x + 1 for x in frange] if (max(np.ediff1d(frange, to_begin=frange[0])) > 1): logger.info('(Ratio Processing) ' + 'frames: ' + ",".join(list(map(str, print_range))) + ', time: ' + time_elapsed + ' sec, save: ' + str(h5_save)) else: logger.info('(Ratio Processing) ' + 'frames: ' + str(print_range[0]) + '-' + str(print_range[-1]) + ', time: ' + time_elapsed + ' sec, save: ' + str(h5_save)) # Create plot to showcase median intensity over frame number and the number of foreground pixels per channel (NON-bleach corrected) time_evolution(acceptori, donori, work_out_path, '_intensity_nonbleach.png', 'Median Intensity/Bit Depth', h5_save) time_evolution(acceptornz, donornz, work_out_path, '_pixelcount.png', 'Foreground/Total Image Pixels', h5_save) # Calculate 8-bit ratio image with NON-bleach corrected donor and acceptor channels if (h5_save or tiff_save): # Calculate ratio stack ratio = ratio_calc(acceptorc, donorc) # Save processed images, non-zero pixel count, median intensity and ratio processed images in HDF5 format if (h5_save): acceptori_brange = np.array([acceptori[a] for a in brange]) donori_brange = np.array([donori[a] for a in brange]) h5_time_start = timer() h5(acceptorc[brange, :, :], 'acceptor', work_out_path + '_ratio_back.h5', frange) h5(donorc[brange, :, :], 'donor', work_out_path + '_ratio_back.h5', frange) h5(acceptori_brange, 'acceptori', work_out_path + '_ratio_back.h5', frange) h5(donori_brange, 'donori', work_out_path + '_ratio_back.h5', frange) h5(ratio[brange, :, :], 'ratio', work_out_path + '_ratio_back.h5', frange) h5_time_end = timer() if (verbose): print(("Saving Acceptor, Donor and Ratio stacks in " + work_out_path + '_ratio_back.h5' + ' [Time: ' + str(int(h5_time_end - h5_time_start) + 1) + " second(s)]")) # Save NON-bleach corrected ratio image as TIFF if (tiff_save): tiff_time_start = timer() tiff(ratio, work_out_path + '_ratio_back.tif') tiff_time_end = timer() if (verbose): print(("Saving unbleached Ratio TIFF stack in " + work_out_path + '_ratio_back.tif' + ' [Time: ' + str(int(tiff_time_end - tiff_time_start) + 1) + " second(s)]"))
def calc_offset_points(points_1, points_2, shape, diam_kernel = 9, labels=['', ''], do_binary = True, do_plot_before=False, do_plot_after=False, do_plot_raw=False): """ points_1, points_2: """ import hbt import matplotlib.pyplot as plt import imreg_dft as ird import numpy as np """ Calculate the offset between a pair of ordered points -- e.g., an xy list of star positions, and and xy list of model postns. Returned offset is integer pixels as tuple (dy, dx). Input lists are of shape N x 2. Y = column 0 X = column 1 The sizes of the two lists do not need to be identical. """ # diam_kernel = 5 # Set the value of the fake stellar image to plot # diam_kernel = 5 is best for LORRI. 11 is too big, and we get the wrong answer. Very sensitive. diam_kernel = 9 image_1 = hbt.image_from_list_points(points_1, shape, diam_kernel, do_binary=do_binary) image_2 = hbt.image_from_list_points(points_2, shape, diam_kernel, do_binary=do_binary) # (dy,dx) = get_image_translation(image_1, image_2) # Get the shift, using FFT method (dy,dx) = ird.translation(image_1, image_2)['tvec'] # Return shift, with t0 = (dy, dx). # ** API changed ~ Sep-16, Anaconda 4.2? # DO_PLOT_INPUT_FRAMES = False if (do_plot_raw): # Plot the raw frames generated to calculate the shift plt.imshow(image_1) plt.title('Image 1 = ' + labels[0] + ', diam_kernel = {}'.format(diam_kernel)) plt.show() plt.imshow(image_2) plt.title('Image 2 = ' + labels[1]) plt.show() plt.imshow(image_1 + image_2) plt.title('Image 1+2 = ' + labels[1]) plt.show() print("dx={}, dy={}".format(dx,dy)) if (do_plot_before): xrange = (0, shape[0]) # Set xlim (aka xrange) s.t. yrange = (shape[1], 0) # yrange = (0, shape[1]) plt.plot(points_1[:,1], points_1[:,0], marker='o', color='none', markersize=10, ls='None', label = labels[0], mew=1, mec='red') plt.plot(points_2[:,1], points_2[:,0], marker='o', color='lightgreen', markersize=4, ls='None', label = labels[1]) plt.title('Before shift of dx={:.1f}, dy={:.1f}'.format(dx, dy)) plt.legend(framealpha=0.5) # plt.set_aspect('equal') plt.xlim(xrange) # Need to set this explicitly so that points out of image range are clipped plt.ylim(yrange) plt.show() if (do_plot_after): xrange = (0, shape[0]) # Set xlim (aka xrange) s.t. yrange = (shape[1], 0) plt.plot(points_1[:,1], points_1[:,0], marker='o', color='none', markersize=10, ls='None', label = labels[0], mec='red', mew=1) plt.plot(points_2[:,1] + dy, points_2[:,0] + dx, marker='o', color='lightgreen', markersize=4, ls='None', label = labels[1]) plt.legend(framealpha=0.5) plt.title('After shift of dx={:.1f}, dy={:.1f}'.format(dx, dy)) plt.xlim(xrange) # Need to set this explicitly so that points out of image range are clipped plt.ylim(yrange) plt.show() return (dy, dx)
def calc_blob_threads(self): """ calculates blob threads Parameters ---------- infill : bool whether or not to infill the """ for i in range(self.t): im1 = self.im.get_t() im1 = medFilter2d(im1) im1 = gaussian3d(im1, self.gaussian) if self.threed: peaks = findpeaks3d( np.array(im1 * np.array(im1 > np.quantile(im1, self.quantile)))) else: peaks = findpeaks2d( np.array(im1 * np.array(im1 > np.quantile(im1, self.quantile)))) peaks = reg_peaks(im1, peaks, thresh=self.reg_peak_dist) if self.register and i != 0: _off = ird.translation( self.im.get_tbyf(i - 1, self.frames[int(len(self.frames) / 2)]), im1[int(len(self.frames) / 2)])['tvec'] _off = np.insert(_off, 0, 0) #peaks = peaks+ _off #print(_off) #print(peaks) self.spool.reel(peaks, self.anisotropy, offset=_off) else: self.spool.reel(peaks, self.anisotropy) if not self.suppress_output: print('\r' + 'Frames Processed: ' + str(i + 1) + '/' + str(self.t), sep='', end='', flush=True) print('\nInfilling...') self.spool.infill() imshape = tuple([len(self.frames)]) + self.im.sizexy def collided(positions, imshape, window=3): for i in [1, 2]: if np.sum(positions[:, i].astype(int) < window) != 0: return True if np.sum(imshape[i] - positions[:, i].astype(int) < window + 1) != 0: return True if np.sum(positions[:, 0].astype(int) < 0) != 0 or np.sum( positions[:, 0].astype(int) > imshape[0] - 1) != 0: return True #if positions[0] < 0 or int(positions[0]) == imshape[0]: # return True return False #for i in range(1,len(p)): # if p[i] < window: # return True # elif s[i]-p[i] < window: # return True print('Removing bad threads') #self.remove_bad_threads() destroy = [] _a = len(self.spool.threads) for i in range(_a): if collided(self.spool.threads[i].positions, imshape): destroy.append(i) print('\r' + 'Blob Threads Checked: ' + str(i + 1) + '/' + str(_a), sep='', end='', flush=True) print('\n') destroy = sorted(list(set(destroy)), reverse=True) if destroy: for item in destroy: self.spool.threads.pop(item) self._merge_within_z() self.spool.make_allthreads() print('Saving blob timeseries as numpy object...') mkdir(self.root + 'extractor-objects') file_pi = open(self.root + 'extractor-objects/threads.obj', 'wb') pickle.dump(self.spool, file_pi) file_pi.close()
def nh_find_simulated_rings_lorri(): # ============================================================================= # Now go thru the synthetic ring images. # Load and stack the synthetic implanted images. # Load and stack the original 'raw' frames # Difference them, and see if we can find a ring in there. # ============================================================================= dir_porter = '/Users/throop/Dropbox/Data/NH_KEM_Hazard/Porter_Sep17/' dir_synthetic = '/Users/throop/Dropbox/Data/NH_KEM_Hazard/synthetic/' do_subpixel = False # Flag: Do we use sub-pixel shifting when doing the flattening? # It is slower and in theory better, but in reality makes a trivial difference. # Start up SPICE file_kernel = 'kernels_kem.tm' sp.furnsh(file_kernel) # Load the images into a table images_raw = image_stack(dir_porter) images_syn = image_stack(dir_synthetic, do_force=False) stretch = astropy.visualization.PercentileInterval(95) plt.set_cmap('Greys_r') # ============================================================================= # If desired, do a one-time routine for the synthetic images: # extract the I/F and ring size from the filenames, and append that to the table. # This routine should be run after creating new synthetic images (e.g., adding an I/F value) # ============================================================================= DO_APPEND = False if (DO_APPEND): t_syn = images_syn.t num_images_syn = (np.shape(t_syn))[0] iof_ring = np.zeros(num_images_syn, dtype=float) size_ring = np.zeros(num_images_syn, dtype='U30') for i in range(num_images_syn): f = t_syn['filename_short'][i] m = re.search('ring_(.*)_iof(.*)_K', f) # Call regexp to parse it. iof_ring[i] = eval(m.group(2)) size_ring[i] = m.group(1) t_syn['size_ring'] = size_ring t_syn['iof_ring'] = iof_ring images_syn.t = t_syn images_syn.save() # Save the whole pickle archive (including images and table) back to disk data_raw = images_raw.data data_syn = images_syn.data t_raw = images_raw.t t_syn = images_syn.t num_images_raw = (np.shape(t_raw))[0] num_images_syn = (np.shape(t_syn))[0] # Look up the time offset, from the image title. (Would be better to have it stored in table, but this will do.) match = re.search('_K(.*)d', t_syn['filename_short'][0]) dt_ca = ((match.group(1)*u.day).to('s')) # Weird: we don't need .value here. I can't explain it. utc_ca = '2019 1 Jan 05:33' et_ca = sp.utc2et(utc_ca) et_obs = et_ca + dt_ca # Set the pixel scale vec,lt = sp.spkezr('2014 MU69', et_obs, 'J2000', 'LT', 'New Horizons') vec_sc_targ = vec[0:3] dist_target_km = (sp.vnorm(vec_sc_targ)*u.km).value scale_pix_lorri_1x1_rad = 0.3*hbt.d2r / 1024 scale_pix_lorri_4x4_rad = scale_pix_lorri_1x1_rad * 4 scale_pix_km_dict = {'1X1' : scale_pix_lorri_1x1_rad * dist_target_km, '4X4' : scale_pix_lorri_4x4_rad * dist_target_km} # We are # Create a bunch of possible image sets, based on various parameters # Indices for 'raw' images indices_sep17_raw = t_raw['et'] > sp.utc2et('15 sep 2017') # The positon of MU69 has changed a few pixels. # We can't blindly co-add between sep and pre-sep indices_jan17_raw = t_raw['et'] < sp.utc2et('1 sep 2017') indices_rot0_raw = t_raw['angle'] < 180 # One rotation angle indices_rot90_raw = t_raw['angle'] > 180 # The other rotation angle indices_10sec_raw = np.logical_and( t_raw['exptime'] < 10, t_raw['exptime'] > 5 ) indices_20sec_raw = np.logical_and( t_raw['exptime'] < 20, t_raw['exptime'] > 10 ) indices_30sec_raw = np.logical_and( t_raw['exptime'] < 30, t_raw['exptime'] > 20 ) indices_1x1_raw = t_raw['naxis1'] == 1024 indices_4x4_raw = t_raw['naxis1'] == 256 indices_30sec_4x4_raw = np.logical_and(indices_4x4_raw, indices_30sec_raw) # 94 # Indices for synthetic images indices_ring_small_syn = t_syn['size_ring'] == 'small' indices_ring_large_syn = t_syn['size_ring'] == 'large' indices_iof_1em7_syn = t_syn['iof_ring'] == 1e-7 indices_iof_3em7_syn = t_syn['iof_ring'] == 3e-7 indices_iof_1em6_syn = t_syn['iof_ring'] == 1e-6 indices_iof_1em5_syn = t_syn['iof_ring'] == 1e-5 indices_iof_1em4_syn = t_syn['iof_ring'] == 1e-4 indices_small_1em7_syn = np.logical_and(indices_iof_1em7_syn, indices_ring_small_syn) indices_small_3em7_syn = np.logical_and(indices_iof_3em7_syn, indices_ring_small_syn) indices_small_1em6_syn = np.logical_and(indices_iof_1em6_syn, indices_ring_small_syn) indices_small_1em5_syn = np.logical_and(indices_iof_1em5_syn, indices_ring_small_syn) indices_small_1em4_syn = np.logical_and(indices_iof_1em4_syn, indices_ring_small_syn) indices_large_1em7_syn = np.logical_and(indices_iof_1em7_syn, indices_ring_large_syn) indices_large_3em7_syn = np.logical_and(indices_iof_3em7_syn, indices_ring_large_syn) indices_large_1em6_syn = np.logical_and(indices_iof_1em6_syn, indices_ring_large_syn) indices_large_1em5_syn = np.logical_and(indices_iof_1em5_syn, indices_ring_large_syn) indices_large_1em4_syn = np.logical_and(indices_iof_1em4_syn, indices_ring_large_syn) # Choose which indiex. ** THIS IS WHERE WE SET THE RING TO USE!! indices_raw = indices_30sec_4x4_raw.copy() # 94 of 344 indices_syn = indices_small_1em6_syn.copy() # 94 of 752 # Now take the first half of the synthetic indices, and the second half of the raw ones # This is to assure that we are using different images for the two stacks! Otherwise, the results are trivial. frames_max = int(np.sum(indices_raw) / 2) # Total number of frames (94) w = np.where(indices_raw)[0] indices_raw[w[frames_max]:] = False # De-activate all frames *below* frames_max w = np.where(indices_syn)[0] indices_syn[:w[frames_max]] = False # De-activate all frames above frames_max # Set the indices images_raw.set_indices(indices_raw) images_syn.set_indices(indices_syn) # Do the flattening arr_raw = images_raw.flatten(do_subpixel=do_subpixel) arr_syn = images_syn.flatten(do_subpixel=do_subpixel) # arr_raw_sub = images_raw.flatten(do_subpixel=True) # arr_syn_sub = images_syn.flatten(do_subpixel=True) # Extract various fields from the data table. We can look up from any of the images -- they should be all the same. t_syn = images_syn.t # Get the data table iof_ring = t_syn[indices_syn]['iof_ring'][0] size_ring = t_syn[indices_syn]['size_ring'][0] exptime = t_syn[indices_syn]['exptime'][0] # The two flattened images need some offsetting. Do that. shift = ird.translation(arr_raw, arr_syn)['tvec'] # shift = np.round(shift).astype('int') # arr_syn_shift = np.roll(np.roll(arr_syn, int(round(shift[0])), axis=0), int(round(shift[1])), axis=1) arr_syn_shift = scipy.ndimage.shift(arr_syn, shift, order=5) # This allows sub-pixel shifts, apparently. *NO*! # a = arr_syn.copy() # a_05_05 = scipy.ndimage.shift(arr_syn, (0.5, 0.5), order=5) # Ugh. 0.5, 0.5 and 1, 1 are *exactly* the same. # a_1_05 = scipy.ndimage.shift(arr_syn, (1, 0.5), order=5) # a_1_1 = scipy.ndimage.shift(arr_syn, (1, 1), order=5) # a_1_15 = scipy.ndimage.shift(arr_syn, (1, 1.5), order=5) # a_1_0 = scipy.ndimage.shift(arr_syn, (1, 0), order=5) # a_05_0 = scipy.ndimage.shift(arr_syn, (0.5, 0), order=5) arr_diff = arr_syn_shift - arr_raw pos = (images_raw.y_pix_mean*4, images_raw.x_pix_mean*4) # Set the binning width of the radial profiles binning_pix = 5 # Extract the radial profiles (dist_pix_1d, profile_1d_median) = get_radial_profile_circular(arr_diff, pos, method='median', width=binning_pix) (dist_pix_1d, profile_1d_mean) = get_radial_profile_circular(arr_diff, pos, method='mean', width=binning_pix) str_title = ('Synthetic ring - raw, I/F = {:.0e}, {}, {} x {:.1f}s'.format( iof_ring, size_ring, frames_max, exptime)) plt.imshow(stretch(arr_diff)) plt.title(str_title) plt.plot(pos[1], pos[0], marker='.', color='red') plt.show() # Set the scale for the effective mode of these observations. Many are taken as 4x4, but we've rebinned to 1x1 if (np.shape(arr_raw)[0] == 1024): scale_mode = '1X1' else: scale_mode = '4X4' scale_pix_km = scale_pix_km_dict[scale_mode] # Make a plot of the radial profile. Don't plot the innermost bin. It is useless, since it has so few pixels in it. hbt.figsize((12,8)) plt.plot(dist_pix_1d[1:] * scale_pix_km, profile_1d_median[1:], label = 'Annulus median', alpha = 0.7) # plt.plot(dist_pix_1d[1:] * scale_pix_km, profile_1d_mean[1:], label = 'Mean', alpha = 0.2) plt.xlabel('Distance [km]') plt.ylabel('DN per pixel') plt.title(str_title + ', binning = {}'.format(binning_pix)) plt.xlim((0,30000)) # Set the y axis range. This is really stupid. Can't matplotlib figure this out itself? ax = plt.gca() lims = ax.get_xlim() i = np.where( (dist_pix_1d * scale_pix_km > lims[0]) & (dist_pix_1d*scale_pix_km < lims[1]) )[0] ax.set_ylim( profile_1d_median[i].min(), profile_1d_median[i].max() ) plt.legend() plt.show() plt.savefig()
def navigate_image_stellar(im, wcs_in, name_catalog='', do_plot=True, method='fft', title=''): """ Navigate frame based on stellar images. Result returns is pixel shift (dy, dx). WCS paramaters are returned, *and* modified in place. """ import imreg_dft as ird from astropy.wcs import WCS # from astropy.vo.client import conesearch # Virtual Observatory, ie star catalogs # DEPRECATED! from astroquery.vo_conesearch import conesearch # New home of conesearch # Inputs are the image array, and the WCS structure. # This routine does not do any file IO. The image array and header must be already loaded. # The image is assumed to be stretched properly s.t. stars can be found using DAOphot. NUM_STARS_PHOT = 100 # How many stars to use from DAOPhot. For noisy images, DAO will find a lot of # fake stars, so we need to crank this up higher than the # of cat stars. NUM_STARS_CAT = 50 # How many stars to use from star catalog DO_GSC1 = False DO_GSC12 = True DO_USNOA2 = False #============================================================================== # Calculate the image radius, in radians, based on the size and the pixel scale #============================================================================== dx_pix = hbt.sizex(im) dy_pix = hbt.sizey(im) radec_corner = wcs_in.wcs_pix2world(0, dy_pix / 2, 0) radec_center = wcs_in.wcs_pix2world(dx_pix / 2, dy_pix / 2, 0) (ra_corner, dec_corner) = radec_corner (ra_center, dec_center) = radec_center radius_image = math.sqrt((dec_corner - dec_center)**2 + ( (ra_corner - ra_center) / np.cos(dec_corner * hbt.d2r))**2) * hbt.d2r radius_search_deg = radius_image * hbt.r2d # Read the WCS coordinates center_deg = wcs_in.wcs.crval # degrees. # crval is a two-element array of [RA, Dec], in degrees # Stretch the image. This is just for display -- no processing. stretch_percent = 90 stretch = astropy.visualization.PercentileInterval( stretch_percent) # PI(90) scales array to 5th .. 95th %ile. # Display it if (do_plot): plt.imshow(stretch(im)) #============================================================================== # Get stars from star catalogs #============================================================================== if (DO_GSC1): name_cat = u'The HST Guide Star Catalog, Version 1.1 (Lasker+ 1992) 1' # works, but 1' errors; investigating stars = conesearch.conesearch(center_deg, radius_search_deg, cache=True, catalog_db=name_cat) ra_stars = np.array( stars.array['RAJ2000']) * hbt.d2r # Convert to radians dec_stars = np.array( stars.array['DEJ2000']) * hbt.d2r # Convert to radians # table_stars = Table(stars.array.data) if (DO_GSC12): # name_cat = u'The HST Guide Star Catalog, Version 1.2 (Lasker+ 1996) 1' name_cat = u'Guide Star Catalog v2 1' # Works from gobi, not tomato url_cat = 'http://gsss.stsci.edu/webservices/vo/ConeSearch.aspx?CAT=GSC23&' # Works always with data.conf.set_temp( 'remote_timeout', 30): # This is the very strange syntax to set a timeout delay. # The default is 3 seconds, and that times out often. with warnings.catch_warnings(): warnings.simplefilter("ignore") # stars = conesearch.conesearch(wcs_in.wcs.crval, radius_search_deg, cache=True, catalog_db = url_cat) # The various functions of conesearch/ConeSearch/etc are quite confusing, and are in flux. # This line below seems to work. It does not allow an explicit catalog suggstion, but it does the job. c = astropy.coordinates.SkyCoord(wcs_in.wcs.crval[0], wcs_in.wcs.crval[1], unit='deg') stars = ConeSearch.query_region(c, f'{radius_search_deg} deg') ra_stars = np.array(stars.array['ra']) * hbt.d2r # Convert to radians dec_stars = np.array( stars.array['dec']) * hbt.d2r # Convert to radians mag = np.array(stars.array['Mag']) print("Stars downloaded: N = {}; mag = {:.2f} .. {:.2f}".format( np.size(mag), np.nanmin(mag), np.nanmax(mag))) print("RA = {:.2f} .. {:.2f}".format( np.nanmin(ra_stars) * hbt.r2d, np.nanmax(ra_stars) * hbt.r2d)) # Now sort by magnitude, and keep the 100 brightest # This is because this GSC catalog is huge -- typically 2000 stars in LORRI FOV. # We need to reduce its size to fit in our fixed astropy table string length. order = np.argsort(mag) order = np.array(order)[0:NUM_STARS_CAT] ra_stars = ra_stars[order] # Returned as radians dec_stars = dec_stars[order] if (DO_USNOA2): name_cat = u'The USNO-A2.0 Catalogue (Monet+ 1998) 1' # Works but gives stars down to v=17; I want to v=13 stars = conesearch.conesearch(wcs_in.wcs.crval, 0.3, cache=False, catalog_db=name_cat) table_stars = Table(stars.array.data) mask = table_stars['Bmag'] < 13 table_stars_m = table_stars[mask] ra_stars = table_stars_m['RAJ2000'] * hbt.d2r # Convert to radians dec_stars = table_stars_m['DEJ2000'] * hbt.d2r # Convert to radians ra_stars_cat = ra_stars dec_stars_cat = dec_stars radec_stars_cat = np.transpose(np.array((ra_stars_cat, dec_stars_cat))) (x_stars_cat, y_stars_cat) = wcs_in.wcs_world2pix(radec_stars_cat[:, 0] * hbt.r2d, radec_stars_cat[:, 1] * hbt.r2d, 0) points_stars_cat = np.transpose( (y_stars_cat, x_stars_cat)) # Yes, order is supposed to be (y,x) #============================================================================== # Use DAOphot to search the image for stars. #============================================================================== points_stars_phot = hbt.find_stars( im, num=NUM_STARS_PHOT) # Returns N x 2 aray. 0 = Row = y; 1 = Column = x. y_stars_phot = (points_stars_phot[:, 0]) # xy is correct -- see above x_stars_phot = (points_stars_phot[:, 1]) # #============================================================================== # Make a plot showing the DAO stars on the image #============================================================================== color_phot = 'red' # Color for stars found photometrically color_cat = 'lightgreen' # Color for stars in catalog DO_PLOT_DAO = False # Plot an intermediate result? if (DO_PLOT_DAO): plt.imshow(stretch(im)) plt.plot(x_stars_phot, y_stars_phot, linestyle='none', marker='o', markersize=9, mec=color_cat, mew=1, color='none', label='DAO photometric stars') # plot() uses x, y plt.plot(x_stars_cat, y_stars_cat, linestyle='none', marker='o', markersize=5, color='lightgreen', label='Cat stars') # plot() uses x, y plt.title(title) plt.ylim((hbt.sizey(im)), 0) plt.xlim((0, hbt.sizex(im))) plt.legend(loc='upper left') plt.show() # Up til here, x and y are correct #============================================================================== # Look up the shift between the photometry and the star catalog. # Do this by making a pair of fake images, and then looking up image registration on them. #============================================================================== # I call this pointing process 'opnav'. # It is returned in order (y,x) because that is what imreg_dft uses, even though it is a bit weird. diam_kernel = 11 # How many pixels across are our synthetic stellar images? Should be odd number. Not critical. do_binary = True # For the stellar images, do a binary 1/0 (recommended), or a pixel distance? shape = np.shape(im) # Set shape of output array image_cat = hbt.image_from_list_points(points_stars_cat, shape, diam_kernel, do_binary=do_binary) image_phot = hbt.image_from_list_points(points_stars_phot, shape, diam_kernel, do_binary=do_binary) if (method == 'fft'): # Very fast method # Set up a constraint for the fit. It should be different for 1x1 and 4x4. # For 1x1, it works well to be 100 pixels. if (hbt.sizex(im) == 1024): # For LORRI 1x1 constraint_tx = ( 0, 100 ) # Mean and stdev. i.e., returned value will be within stdev of mean. constraint_ty = (0, 100) if (hbt.sizex(im) == 256): # For LORRI 4x4 constraint_tx = ( 0, 25 ) # Mean and stdev. i.e., returned value will be within stdev of mean. constraint_ty = (0, 25) constraint_angle = 0 # With one value, it is a fixed constraint. constraints = { 'tx': constraint_tx, 'ty': constraint_ty, 'angle': constraint_angle } ird.translation(image_cat, image_phot, constraints=constraints) (dy, dx) = ird.translation(image_cat, image_phot, constraints=constraints)['tvec'] dy_opnav = -dy dx_opnav = -dx if (method == 'bruteforce'): # Very slow method ((dx, dy), mat) = hbt.get_translation_images_bruteforce(image_cat, image_phot) dx_opnav = -dx dy_opnav = -dy #============================================================================== # Make a plot, showing DAO positions + catalog positions #============================================================================== do_plot = True if (do_plot): # hbt.figsize((10,10)) plt.imshow(stretch(im)) # Plot the stars -- catalog, and DAO plt.plot(x_stars_cat + dx_opnav, y_stars_cat + dy_opnav, marker='o', ls='None', color=color_cat, alpha=0.5, ms=12, mew=1, label='Cat Stars, adjusted') plt.plot(x_stars_cat, y_stars_cat, marker='o', ls='None', color=color_cat, alpha=1, ms=4, mew=1, label='Cat Stars, raw') plt.plot(x_stars_phot, y_stars_phot, marker='o', ls='None', color='none', markersize=10, mew=1, mec=color_phot, alpha=1, label='DAOfind Stars') plt.title('After navigation, with dx = {:.1f}, dy = {:.1f}, {}'.format( dx_opnav, dy_opnav, title)) plt.legend( ) # Draw legend. Might be irrel since remove() might keep it; not sure. plt.imshow(stretch(im)) plt.show() #============================================================================== # Return results and exit #============================================================================== # Results are returned in terms of pixel offset and a revised WCS structure. # I don't seem to be able to copy a WCS structure, so I modify the one in place! # Get the pixel location of the center position crpix = wcs_in.wcs.crpix # Center position, in pixels, old # Get the new RA, Dec center of the array. It is just the old location, plus the offset ORIGIN_FORMAT = 1 # 0 for Numpy-style indexing, 1 for Fortran-style and FITS-style. # So what do I used for FITS files in python? Experimentally, 1 is right and 0 is not. (ra_new, dec_new) = wcs_in.wcs_pix2world(crpix[0] - dx_opnav, crpix[1] - dy_opnav, ORIGIN_FORMAT) # Set it wcs_in.wcs.crval = (ra_new, dec_new) return (wcs_in, (dy_opnav, dx_opnav))
def cache(self, path): As = [ os.path.join(path, p) for p in os.listdir(path) if p.startswith('A') and p.endswith(self.ext) ] Bs = [ os.path.join(path, p) for p in os.listdir(path) if p.startswith('B') and p.endswith(self.ext) ] LRs = [ os.path.join(path, p) for p in os.listdir(path) if p.startswith('LR') and p.endswith(self.ext) ] if os.path.exists(os.path.join('mask_A' + self.ext)): m = np.array(Image.open(os.path.join('mask_A' + self.ext))) m = np.expand_dims(m, axis=2) if m.ndim == 2 else m maskA = m < m.max() / 2 else: maskA = None if os.path.exists(os.path.join('mask_B' + self.ext)): m = np.array(Image.open(os.path.join('mask_B' + self.ext))) m = np.expand_dims(m, axis=2) if m.ndim == 2 else m maskB = m < m.max() / 2 else: maskB = None ImgAs, PathAs, ImgBs, PathBs, ImgLRs, PathLRs = [], [], [], [], [], [] for p in As: try: img = np.array(Image.open(p)) img = np.expand_dims(img, axis=2) if img.ndim == 2 else img if maskA: img[maskA] = img.min() ImgAs.append(img) PathAs.append(p) except KeyboardInterrupt: raise except Exception as e: print('error when reading file ', p) assert len(ImgAs) > 0, 'no file found for "A"' for p in Bs: try: img = np.array(Image.open(p)) img = np.expand_dims(img, axis=2) if img.ndim == 2 else img if maskB: img[maskB] = img.min() ImgBs.append(img) PathBs.append(p) except KeyboardInterrupt: raise except Exception as e: print('error when reading file ', p) for p in LRs: try: img = np.array(Image.open(p)) assert img.ndim == 2 if self.drift_correction: import imreg_dft as ird from skimage import exposure b = ImgBs[0][:, :, 0] b = exposure.equalize_hist(b) b = scipy.ndimage.filters.gaussian_filter(b, sigma=(6, 6)) b = scipy.misc.imresize(b, img.shape[:2]) ts = ird.translation(b, img) tvec = ts["tvec"].round(4) # the Transformed IMaGe. img = ird.transform_img(img, tvec=tvec) img = scipy.misc.imresize(img, ImgBs[0].shape[:2]) img = np.expand_dims(img, axis=2) ImgLRs.append(img) PathLRs.append(p) except KeyboardInterrupt: raise except Exception as e: print('error when reading file ', p) import traceback, sys traceback.print_exc(file=sys.stdout) self.__cache[path] = { 'A': ImgAs, 'B': ImgBs, 'LR': ImgLRs, 'path': path, 'pathA': PathAs, 'pathB': PathBs, 'pathLR': PathLRs } return True
def calc_offset_points(points_1, points_2, shape, diam_kernel=9, labels=['', ''], do_binary=True, do_plot_before=False, do_plot_after=False, do_plot_raw=False): """ points_1, points_2: """ import hbt import matplotlib.pyplot as plt import imreg_dft as ird import numpy as np """ Calculate the offset between a pair of ordered points -- e.g., an xy list of star positions, and and xy list of model postns. Returned offset is integer pixels as tuple (dy, dx). Input lists are of shape N x 2. Y = column 0 X = column 1 The sizes of the two lists do not need to be identical. """ # diam_kernel = 5 # Set the value of the fake stellar image to plot # diam_kernel = 5 is best for LORRI. 11 is too big, and we get the wrong answer. Very sensitive. diam_kernel = 9 image_1 = hbt.image_from_list_points(points_1, shape, diam_kernel, do_binary=do_binary) image_2 = hbt.image_from_list_points(points_2, shape, diam_kernel, do_binary=do_binary) # (dy,dx) = get_image_translation(image_1, image_2) # Get the shift, using FFT method (dy, dx) = ird.translation( image_1, image_2)['tvec'] # Return shift, with t0 = (dy, dx). # ** API changed ~ Sep-16, Anaconda 4.2? # DO_PLOT_INPUT_FRAMES = False if (do_plot_raw): # Plot the raw frames generated to calculate the shift plt.imshow(image_1) plt.title('Image 1 = ' + labels[0] + ', diam_kernel = {}'.format(diam_kernel)) plt.show() plt.imshow(image_2) plt.title('Image 2 = ' + labels[1]) plt.show() plt.imshow(image_1 + image_2) plt.title('Image 1+2 = ' + labels[1]) plt.show() print("dx={}, dy={}".format(dx, dy)) if (do_plot_before): xrange = (0, shape[0]) # Set xlim (aka xrange) s.t. yrange = (shape[1], 0) # yrange = (0, shape[1]) plt.plot(points_1[:, 1], points_1[:, 0], marker='o', color='none', markersize=10, ls='None', label=labels[0], mew=1, mec='red') plt.plot(points_2[:, 1], points_2[:, 0], marker='o', color='lightgreen', markersize=4, ls='None', label=labels[1]) plt.title('Before shift of dx={:.1f}, dy={:.1f}'.format(dx, dy)) plt.legend(framealpha=0.5) # plt.set_aspect('equal') plt.xlim( xrange ) # Need to set this explicitly so that points out of image range are clipped plt.ylim(yrange) plt.show() if (do_plot_after): xrange = (0, shape[0]) # Set xlim (aka xrange) s.t. yrange = (shape[1], 0) plt.plot(points_1[:, 1], points_1[:, 0], marker='o', color='none', markersize=10, ls='None', label=labels[0], mec='red', mew=1) plt.plot(points_2[:, 1] + dy, points_2[:, 0] + dx, marker='o', color='lightgreen', markersize=4, ls='None', label=labels[1]) plt.legend(framealpha=0.5) plt.title('After shift of dx={:.1f}, dy={:.1f}'.format(dx, dy)) plt.xlim( xrange ) # Need to set this explicitly so that points out of image range are clipped plt.ylim(yrange) plt.show() return (dy, dx)