def update(val): tx = stx.val ty = sty.val angle = sangle.val t = tf.EuclideanTransform(translation=-center) + tf.EuclideanTransform( rotation=angle, translation=(tx, ty)) + tf.EuclideanTransform(translation=center) nonlocal moved moved = np.round(tf.warp(moving, t.inverse, preserve_range=True)).astype(np.uint8) imshow()
def _apply_rigid_transformation(inverse): parser = argparse.ArgumentParser( description='Apply {} transformation to a image.'.format( 'inverse rigid' if inverse else 'rigid')) parser.add_argument('input', help="Input image", metavar='<input>') parser.add_argument('params', help="Input transformation parameter", metavar='<param>') parser.add_argument('output', help="Output image", metavar='<output>') parser.add_argument('--size', help="Output image size", metavar='<pix>', type=int, nargs=2) args = parser.parse_args() params = load_params(args.params) moving = np.array(Image.open(args.input)) if args.size: output_shape = np.array(args.size[::-1]) else: output_shape = np.array((max(moving.shape[:2]), ) * 2) warped_shape = np.max(np.stack((moving.shape[:2], output_shape)), axis=0) center = np.array(warped_shape) / 2 - .5 ds = np.array(output_shape[::-1]) - np.array(moving.shape[:2][::-1]) if inverse: t = tf.EuclideanTransform( translation=-np.clip(ds, 0, None) // 2) + tf.EuclideanTransform( translation=-center) + tf.EuclideanTransform( translation=-np.array(params[1:])) + tf.EuclideanTransform( rotation=-params[0]) + tf.EuclideanTransform( translation=center) + tf.EuclideanTransform( translation=-np.clip(-ds, 0, None) // 2) else: t = tf.EuclideanTransform( translation=-np.clip(ds, 0, None) // 2) + tf.EuclideanTransform( translation=-center) + tf.EuclideanTransform( rotation=params[0], translation=params[1:]) + tf.EuclideanTransform( translation=center) moved = tf.warp(moving, t, order=1, preserve_range=True, output_shape=warped_shape) pad = np.clip(-ds[::-1], 0, None) // 2 moved = moved[pad[0]:(moved.shape[0] - pad[0]), pad[1]:(moved.shape[1] - pad[1])] Image.fromarray(np.round(moved).astype(np.uint8)).save( os.path.join(args.output))
def extract_rectangle(image: np.ndarray, *, bbox: np.ndarray): """Extract rectangle from image. The image is straightened using an Euclidean transform. Parameters ---------- image : 2D np.ndarray Image to extract rectangle from. bbox : (4,2) np.ndarray Four coordinate describing the corners of the bounding box. Returns ------- warped : 2D np.ndarray The warped input image extracted from the bounding box. """ a = int(np.linalg.norm(bbox[0] - bbox[1])) b = int(np.linalg.norm(bbox[1] - bbox[2])) src = np.array([[0, 0], [0, a], [b, a], [b, 0]]) dst = np.array(bbox) tform3 = transform.EuclideanTransform() tform3.estimate(src, dst) warped = transform.warp(image, tform3, output_shape=(a, b)) return warped
def refocus(lf, shift): canvas = np.zeros((372, 540, 3)) for u in range(7): for v in range(7): t = transform.EuclideanTransform(translation=((v - 3) * shift, (u - 3) * shift)) canvas += transform.warp(lf[u, v], t.params) return canvas / 49
def convert2DBins(images_df, binSize, flip=False, translate=False): image_dir = '/Users/parkersimpson/PycharmProjects/CS4342/DogBreedProject/dog-breed-identification/resized/' for i in range(images_df.shape[0]): label = images_df.breed.iloc[i] image_name = images_df.id.iloc[i] image = io.imread(image_dir+image_name+'.jpg') # (200,200,3) if flip: image = np.fliplr(image) if translate: x, y = 10, 10 trans_locs = [(x, y), (-x, y), (-x, -y), (x, -y), (x, 0), (-x, 0), (0, y), (0, -y)] trans = np.random.randint(0, len(trans_locs), 1)[0] trans_mx = tf.EuclideanTransform(translation=trans_locs[trans]) image = tf.warp(image, trans_mx) binNum = int(256/binSize) image_bin = np.zeros((binNum, 3)) for j in range(binNum): count1 = np.sum((image[:,:,0] >= (j*binSize)) * (image[:,:,0] < binSize+(j*binSize))) count2 = np.sum((image[:,:,1] >= (j*binSize)) * (image[:,:,1] < binSize+(j*binSize))) count3 = np.sum((image[:,:,2] >= (j*binSize)) * (image[:,:,2] < binSize+(j*binSize))) image_bin[j,:] = [count1, count2, count3] image_bin = image_bin.ravel() if i == 0: X = image_bin[None,:] Y = np.array([label]) else: X = np.concatenate((X, image_bin[None, :]), axis=0) Y = np.concatenate((Y, np.array([label])), axis=0) return X, Y
def create_transect(origin, orientation, length): """ Create a 2D transect of points with 1m interval. Arguments: ----------- origin: np.array contains the X and Y coordinates of the origin of the transect orientation: int angle of the transect (anti-clockwise from North) in degrees length: int length of the transect in metres Returns: ----------- transect: np.array contains the X and Y coordinates of the transect """ x0 = origin[0] y0 = origin[1] # orientation of the transect phi = (90 - orientation) * np.pi / 180 # create a vector with points at 1 m intervals x = np.linspace(0, length, length + 1) y = np.zeros(len(x)) coords = np.zeros((len(x), 2)) coords[:, 0] = x coords[:, 1] = y # translate and rotate the vector using the origin and orientation tf = transform.EuclideanTransform(rotation=phi, translation=(x0, y0)) transect = tf(coords) return transect
def test_3d_ellipsoid_axis_lengths(): """Verify that estimated axis lengths are correct. Uses an ellipsoid at an arbitrary position and orientation. """ # generate a centered ellipsoid with non-uniform half-lengths (radii) half_lengths = (20, 10, 50) e = draw.ellipsoid(*half_lengths).astype(int) # Pad by asymmetric amounts so the ellipse isn't centered. Also, pad enough # that the rotated ellipse will still be within the original volume. e = np.pad(e, pad_width=[(30, 18), (30, 12), (40, 20)], mode='constant') # apply rotations to the ellipsoid R = transform.EuclideanTransform(rotation=[0.2, 0.3, 0.4], dimensionality=3) e = ndi.affine_transform(e, R.params) # Compute regionprops rp = regionprops(e)[0] # estimate principal axis lengths via the inertia tensor eigenvalues evs = rp.inertia_tensor_eigvals axis_lengths = _inertia_eigvals_to_axes_lengths_3D(evs) expected_lengths = sorted([2 * h for h in half_lengths], reverse=True) for ax_len_expected, ax_len in zip(expected_lengths, axis_lengths): # verify accuracy to within 1% assert abs(ax_len - ax_len_expected) < 0.01 * ax_len_expected # verify that the axis length regionprops also agree assert abs(rp.axis_major_length - axis_lengths[0]) < 1e-7 assert abs(rp.axis_minor_length - axis_lengths[-1]) < 1e-7
def shift_stabilization(img1, img2, rows, cols, print_result=None): """ Perform shift stabilization on two images using phase correlation with hanning window :param img1 source image :param img2: target image :param rows: rows of result image :param cols: columns of result image :param print_result: gathered information during stabilization :return: result_image: stabilized (shifted) image print_result: collected information during shift stabilization """ img1_gray = ImageProcess.to_gray(img1) img2_gray = ImageProcess.to_gray(img2) hanning = cv2.createHanningWindow((cols, rows), cv2.CV_32F) (cx, cy), _ = cv2.phaseCorrelate(np.float32(img2_gray), np.float32(img1_gray)) # (cx, cy) = (round(cx, 2), round(cy, 2)) M = np.float32([[1, 0, cx], [0, 1, cy]]) print_result['x'] = cx print_result['y'] = cy t_form = transform.EuclideanTransform(translation=(cx, cy)) result_image = transform.warp(img2, t_form) return img_as_ubyte(result_image), print_result
def main(): path_to_files = '../os-shot_182_450/' os.chdir(path_to_files) files = os.listdir() each_result = [] images = [] for image in files: try: im = io.imread(image) except ValueError as e: print('ERROR') print(e) continue images.append(im) im = sk.color.rgb2gray(im) #find all contours conturs = findContours(im) #find the biggest myContour = conturs[maxContour(conturs)] #divide it partContour = get_part_contour(myContour) #get linear equations lineParams = get_line_params(partContour) #solve linear systems (get intersection) resultPoints = get_image_corners(lineParams) #save it each_result.append(resultPoints) images = np.asarray(images) each_result = np.asarray(each_result) #find average average_points = all_images_average(each_result) estimation = tf.EuclideanTransform() warped_images = [] for image, dots in zip(images, each_result): dst = average_points src = dots tform = tf.estimate_transform('Euclidean', src, dst) estimation.estimate(src, dst) img = tf.warp(image, tform) warped_images.append(img) warped_images = np.asarray(warped_images) # uses optical flow to stable images # warped_images = optical_flow_correction(warped_images) os.mkdir('result') os.chdir('result') for name, result in zip(files, warped_images): io.imsave(name, result)
def run(self): """ Run nowcasting calculations. Returns ------- nowcasts : 3D numpy array of shape (lead_steps, dim_x, dim_y). """ # define available transformations dictionary transformations = {'euclidean': sktf.EuclideanTransform(), 'similarity': sktf.SimilarityTransform(), 'affine': sktf.AffineTransform(), 'projective': sktf.ProjectiveTransform()} # scale input data to uint8 [0-255] with self.scaler data_scaled, c1, c2 = self.scaler(self.input_data) # set up transformer object trf = transformations[self.warper] # obtain source and target points if self.extrapolation == "linear": pts_source, pts_target_container = _sparse_linear(data_instance=data_scaled, of_params=self.of_params, lead_steps=self.lead_steps) elif self.extrapolation == "simple_delta": pts_source, pts_target_container = _sparse_sd(data_instance=data_scaled, of_params=self.of_params, lead_steps=self.lead_steps) # now we can start to find nowcasted image # for every candidate of projected sets of points # container for our nowcasts last_frame = data_scaled[-1] nowcst_frames = [] for lead_step, pts_target in enumerate(pts_target_container): # estimate transformation matrix # based on source and traget points trf.estimate(pts_source, pts_target) # make a nowcast nowcst_frame = sktf.warp(last_frame/255, trf.inverse) # transformations dealing with strange behaviour nowcst_frame = (nowcst_frame*255).astype('uint8') # add to the container nowcst_frames.append(nowcst_frame) nowcst_frames = np.stack(nowcst_frames, axis=0) nowcst_frames = self.inverse_scaler(nowcst_frames, c1, c2) return nowcst_frames
def transformFrame(i): if i == 0: matrix = getTranslationMatrix(0,0) else: matrix = getTranslationMatrix(-(averageDrift_df[('X-drift', 'mean')][i]/107),(-averageDrift_df[('Y-drift', 'mean')][i]/107)) tform = transform.EuclideanTransform(matrix) img = img_as_float(data[i]) *60000 tf_img = transform.warp(img, tform.inverse) print('\rFrame # {} of {} finished'.format(i,n), end='', flush=True) return tf_img.astype('uint16')
def test_compare(self): return # REMARK: sktransform.warp needs change-of-frame (i.e. map dest keypoints -> src keypoints) o1 = sktransform.EuclideanTransform( translation=[10, 20], rotation=np.radians(0.1) ) o2 = sktransform.EuclideanTransform() src = np.vstack([[0, 0.0], [5, 3.0]]) dest = src + np.array([[10, 20]]) o2.estimate(dest, src) kwargs = {"cval": 0, "order": 1, "mode": "constant"} img = data.camera()[::-1, :] for o in [o1]: for i in range(100): cof = o.params # shift: takes transformation vector for coordinates (y,x) img1 = ndtransform.shift(img, -cof[1::-1, 2], **kwargs) # affine_transform: takes change-of-frame matrix for coordinates (y,x) img2 = ndtransform.affine_transform( img, cof[0:2, 0:2].T, offset=cof[1::-1, 2], **kwargs ) # warp: takes change-of-frame matrix img3 = sktransform.warp(img, o, **kwargs) if np.isclose(o.rotation, 0): np.testing.assert_allclose(img1, img2) np.testing.assert_allclose(img2, img3) if False: import matplotlib.pyplot as plt fig, (ax1, ax2, ax3) = plt.subplots( ncols=3, figsize=(8, 4), sharex=True, sharey=True ) ax1.imshow(img1, origin="lower") ax2.imshow(img2, origin="lower") ax3.imshow(img3, origin="lower") plt.show()
def random_low_res(image, n, psf, downsample, translation_range, rot_range, noise_scale): """ Parameters ---------- image : ndarray The original high-resolution image n : Integer The number of low-resolution images to generate psf : ndarray Point spread function to convolve with the image for blur downsample : Integer Factor to down-sample the hr image translation_range : tuple Min and max value in pixels that the random translation can be selected from rot_range : tuple Min and max value that the random rotation angle can be in degrees (counter-clockwise) noise_scale : float Variance of the added gaussian noise Returns ------- """ low_res, tf_matrices = [], [] for index in range(n): im = image # Random motion in the form of translation and rotation transform_mat = random_tf_matrix(translation_range, rot_range) tform = tf.EuclideanTransform(transform_mat) im = tf.warp(im, tform) # Blur with the PSF im = convolve2d(im, psf, 'same') # Down-sample the image by averaging local blocks im = block_reduce(im, (downsample, downsample), func=np.mean) # Add gaussian noise. Default mean 0 variance noise = np.random.normal(size=im.shape, scale=noise_scale) im += noise low_res.append(im) tf_matrices.append(transform_mat) return low_res, tf_matrices
def ok(event): plt.close() tx = stx.val ty = sty.val angle = sangle.val params = [-angle, ty * scale, -tx * scale] if args.params: write_params(args.params, params) else: print(params) if args.cmp: center = np.array(moving_original.shape[:2][::-1]) / 2 - .5 t = tf.EuclideanTransform( translation=-center) + tf.EuclideanTransform( rotation=angle, translation=(tx * scale, ty * scale) ) + tf.EuclideanTransform(translation=center) moved = np.round( tf.warp(moving_original, t.inverse, preserve_range=True)).astype(np.uint8) Image.fromarray( np.stack( (fixed_original, moved, np.zeros_like(fixed_original)), axis=-1)).save(args.cmp)
def test_transform(self): im = data.camera() # Apply a translation to an image tx, ty = 25, 15 H = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]]) tform = tf.EuclideanTransform(H) actual_translated = tf.warp(im, tform.inverse) # Apply the same translation using the operator tf_operator = transformation_matrix(H, im.shape) op_translated = tf_operator * im.flat op_translated = np.reshape(op_translated, (512, 512)) op_translated = normalize(op_translated, 0, 1) npt.assert_equal(actual_translated, op_translated)
def get_interpolate_points(pts_sl): pts_world_interp = np.expand_dims(np.array([np.nan, np.nan]), axis=0) for k in range(len(pts_sl) - 1): pt_dist = np.linalg.norm(pts_sl[k, :] - pts_sl[k + 1, :]) xvals = np.arange(0, pt_dist) yvals = np.zeros(len(xvals)) pt_coords = np.zeros((len(xvals), 2)) pt_coords[:, 0] = xvals pt_coords[:, 1] = yvals phi = 0 deltax = pts_sl[k + 1, 0] - pts_sl[k, 0] deltay = pts_sl[k + 1, 1] - pts_sl[k, 1] phi = np.pi / 2 - np.math.atan2(deltax, deltay) tf = transform.EuclideanTransform(rotation=phi, translation=pts_sl[k, :]) pts_world_interp = np.append(pts_world_interp, tf(pt_coords), axis=0) pts_world_interp = np.delete(pts_world_interp, 0, axis=0) return pts_world_interp
def createTransect(endPts): import skimage.transform as transform m = 90 - np.degrees( np.tan((endPts[1][0] - endPts[0][0]) / (endPts[1][1] - endPts[0][1]))) if np.sign(endPts[1][1] - endPts[0][1]) == -1: phi = (m + 180) * np.pi / 180 else: phi = (m) * np.pi / 180 length = np.sqrt((endPts[1][0] - endPts[0][0])**2 + (endPts[1][1] - endPts[0][1])**2) x = np.linspace(0, length, length + 1) y = np.zeros(len(x)) coords = np.zeros((len(x), 2)) coords[:, 0] = x coords[:, 1] = y tf = transform.EuclideanTransform(rotation=phi, translation=(endPts[0][0], endPts[0][1])) transect = tf(coords) return transect
def RegisterOn(self, ref_frame: np_.ndarray) -> None: """ RegisterOn method (target frame, reference frame) - Calculates the between channel shift using the python "register_translation" method - Gets the transformation (translation) by the python "EuclideanTransform" method - Applies the transformation to the target frame using the python "warp" method """ # calculate the shift shift = ft_.register_translation( ref_frame, self.contents, upsample_factor=8, space="real", return_error=False, ) # get the transformation: translation by the calculated shift translation = tf_.EuclideanTransform(translation=shift) self.contents = tf_.warp(self.contents, translation) # warped image
def transform_points2background(self, img_file, upsampling=5): img1 = tifffile.imread(img_file) img2 = tifffile.imread(self.image) print('image load complete') img1_small = cv2.resize( img1, (int(img1.shape[1] / upsampling), int(img1.shape[0] / upsampling))) self._scaffold_image = img1_small img2_small = cv2.resize( img2, (int(img2.shape[1] / upsampling), int(img2.shape[0] / upsampling))) self._original_image = img2_small warp_matirx = self._match_features(self._find_features(img2_small), self._find_features(img1_small)) warp_matirx = transform.EuclideanTransform(matrix=np.concatenate( [warp_matirx[::, :], np.array([0, 0, 1])[None, :]])).params self._tform = warp_matirx #don't forget to scale warp_matirx = np.array([[upsampling, 0., 0.], [ 0., upsampling, 0.], [ 0., 0., 1.]]) @\ np.linalg.inv(warp_matirx) @\ np.array([[1/upsampling, 0., 0.], [ 0., 1/upsampling, 0.], [ 0., 0., 1.]]) data = warp_matirx @ np.array([ self.data['PosX'], self.data['PosY'], np.ones(self.data['PosY'].shape[0]) ]) self.data['PosX'] = data[0, :] self.data['PosY'] = data[1, :] self.image = img_file self.spatial_dims = self.get_img_size(self.image) if self.masks_svg is not None: self.ducts = self._get_ducts(self.masks_svg)
def apply_wcs_transformation(header, tform): crpix1, crpix2, cd11, cd12, cd21, cd22 = wcs_keyword = tel.WCS_keywords() header['CRPIX1'] = header[crpix1] - tform.translation[0] header['CRPIX2'] = header[crpix2] - tform.translation[1] cd = np.array([header[cd11], header[cd12], header[cd21], header[cd22]]).reshape(2, 2) cd_matrix = tf.EuclideanTransform(rotation=tform.rotation) cd_transformed = tf.warp(cd, cd_matrix) header['CD1_1'] = cd_transformed[0][0] header['CD1_2'] = cd_transformed[0][1] header['CD2_1'] = cd_transformed[1][0] header['CD2_2'] = cd_transformed[1][1] header['CTYPE1'] = 'RA---TAN' header['CTYPE2'] = 'DEC--TAN' old_keywords = tel.WCS_keywords_old() for old in old_keywords: try: del header[old] except: pass return header
def get_leaf(image, prop): img_height = image.shape[0] img_width = image.shape[1] img_center = 0.5 * np.array([img_width, img_height]) leaf_center = swap_axes(np.array(prop["centroid"])) angle = -prop["orientation"] * 180.0 / math.pi trans1 = transform.EuclideanTransform(translation=leaf_center - img_center) transformed_image = transform.warp(image, trans1, mode='constant') transformed_image = transform.rotate(transformed_image, angle=angle, center=img_center) major_axis = prop["major_axis_length"] minor_axis = prop["minor_axis_length"] # Final size of the image H = major_axis + 200 W = minor_axis + 150 # How much we have to crop original image to get (H x W) image top_crop = 0.5 * (img_height - H) bottom_crop = top_crop left_crop = 0.5 * (img_width - W) right_crop = left_crop crop_dims = [[top_crop, bottom_crop], [left_crop, right_crop]] # makes sure that the crop function works for color images too if len(image.shape) == 3: crop_dims.append([0, 0]) transformed_image = util.crop(transformed_image, crop_dims) return transformed_image
rotate = False translate = True for image_file in os.listdir(pathString): try: temp_img = Image.open(os.path.join(pathString, image_file)) temp_img = ImageOps.grayscale(temp_img) temp_img = np.asarray(temp_img) if flipped: flip_img = np.fliplr(temp_img) if rotate: rot_img = tf.rotate(temp_img, 7) if translate: x,y = 10,10 trans_locs = [(x,y), (-x,y), (-x,-y), (x,-y), (x,0), (-x, 0), (0,y), (0,-y)] trans = np.random.randint(0, len(trans_locs), 1)[0] trans_mx = tf.EuclideanTransform(translation=trans_locs[trans]) trans_img = tf.warp(temp_img, trans_mx) # fig, axes = plt.subplots(nrows=1, ncols=2) # axes[0].imshow(temp_img) # axes[1].imshow(flip_img) # axes[2].imshow(rot_img) # axes[1].imshow(trans_img) # plt.show() temp_img = temp_img.reshape((-1, 1)) if flipped: flip_img = flip_img.reshape((-1, 1)) if translate: trans_img = trans_img.reshape((-1, 1)) if rotate: rot_img = rot_img.reshape((-1,1))
def main(): parser = argparse.ArgumentParser( description='Finalize registration.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('input', help="Input image directory", metavar='<input>') parser.add_argument('--first', help="First transformation parameters directory", metavar='<first>', default='fine_params') parser.add_argument( '--nonrigid', help="Output directory of inverted non-rigid registration", metavar='<name>', default='inverted_nonrigid_output') parser.add_argument('--second', help="Second transformation parameters directory", metavar='<second>', default='re_rigid_params') parser.add_argument('--output', help="Output directory", metavar='<second>', default='final_output') parser.add_argument('--cval', help="cval for skimage.transform.warp", metavar='<value>', default=255, type=int) args = parser.parse_args() filenames = os.listdir(args.input) moving = np.array(Image.open(os.path.join(args.input, filenames[0]))) output_shape = (np.max(moving.shape[:2]), ) * 2 pad = (np.array(output_shape) - np.array(moving.shape[:2])) // 2 pad = np.append(pad, 0) pad = np.repeat(np.expand_dims(pad, 1), 2, axis=1) padded = np.pad(moving, pad, 'constant', constant_values=args.cval) Image.fromarray(padded).save( os.path.join(args.output, os.path.splitext(filenames[0])[0] + '.png')) #pad first image and save t = tf.EuclideanTransform( translation=-(np.array(output_shape) - np.array(moving.shape[:2][::-1])) // 2) for filename in tqdm.tqdm(filenames[1:]): png_filename = os.path.splitext(filename)[0] + '.png' txt_filename = os.path.splitext(filename)[0] + '.txt' if os.path.exists(os.path.join(args.second, txt_filename)): params = load_params(os.path.join(args.second, txt_filename)) else: params = load_params(os.path.join(args.first, txt_filename)) if os.path.exists(os.path.join(args.nonrigid, png_filename)): moving = np.array( Image.open(os.path.join(args.nonrigid, png_filename))) else: moving = np.array(Image.open(os.path.join(args.input, filename))) center = np.array(moving.shape[:2][::-1]) / 2 - .5 t = t + ( tf.EuclideanTransform(translation=-center) + tf.EuclideanTransform(rotation=params[0], translation=params[1:]) + tf.EuclideanTransform(translation=center)) moved = tf.warp(moving, t, order=1, preserve_range=True, output_shape=output_shape, cval=args.cval) Image.fromarray(np.round(moved).astype(np.uint8)).save( os.path.join(args.output, png_filename)) return 0
def center_and_rotate_pharynxes( fl_images: xr.DataArray, seg_images: xr.DataArray ) -> (xr.DataArray, xr.DataArray): """ Given a fluorescence stack and a pharyngeal mask stack, center and rotate each frame of both the FL and mask such that the pharynx is in the center of the image, with its anterior on the left. Parameters ---------- fl_images The fluorescence images to rotate and align seg_images The segmented images to rotate and align Returns ------- (rotated_fl_stack, rotated_seg_stack) A 2-tuple where the first item is the rotated fluorescence stack and the second is the rotated mask stack """ img_center_y, img_center_x = ( fl_images.y.size // 2, fl_images.x.size // 2, ) fl_rotated_stack = fl_images.copy() seg_rotated_stack = seg_images.copy() # STACK_ITERATION for img_idx in range(fl_images.animal.size): for wvl in fl_images.wavelength.data: for pair in fl_images.pair.data: for tp in fl_images.timepoint.values: # Optimization potential here... # this recalculates all region properties for the reference each time img = fl_images.isel(animal=img_idx).sel( wavelength=wvl, pair=pair, timepoint=tp ) try: # Old data, had wavelength attached ref_seg = seg_images.isel(animal=img_idx, wavelength=0).sel( pair=pair, timepoint=tp ) except ValueError: ref_seg = seg_images.isel(animal=img_idx).sel( pair=pair, timepoint=tp ) try: props = measure.regionprops(measure.label(ref_seg))[0] except IndexError: raise ValueError( f"No binary objects found in image @ [idx={img_idx} ; wvl={wvl} ; pair={pair}]" ) # pharynx_center_y, pharynx_center_x = props.centroid pharynx_center_y, pharynx_center_x = np.mean( np.nonzero(ref_seg), axis=1 ) pharynx_orientation = props.orientation translation_matrix = transform.EuclideanTransform( translation=( -(img_center_x - pharynx_center_x), -(img_center_y - pharynx_center_y), ) ) rotated_img = rotate( img.data, translation_matrix, pharynx_orientation ) rotated_seg = rotate( ref_seg.data, translation_matrix, pharynx_orientation, order=0, preserve_range=True, ) fl_rotated_stack.loc[dict(wavelength=wvl, pair=pair, timepoint=tp)][ img_idx ] = rotated_img seg_rotated_stack.loc[dict(pair=pair, timepoint=tp)][ img_idx ] = rotated_seg fl_rotated_stack.values = fl_rotated_stack.values.astype(fl_images.dtype) return fl_rotated_stack, seg_rotated_stack
def get_reference_sl(metadata, settings): """ Allows the user to manually digitize a reference shoreline that is used seed the shoreline detection algorithm. The reference shoreline helps to detect the outliers, making the shoreline detection more robust. KV WRL 2018 Arguments: ----------- metadata: dict contains all the information about the satellite images that were downloaded settings: dict with the following keys 'inputs': dict input parameters (sitename, filepath, polygon, dates, sat_list) 'cloud_thresh': float value between 0 and 1 indicating the maximum cloud fraction in the cropped image that is accepted 'cloud_mask_issue': boolean True if there is an issue with the cloud mask and sand pixels are erroneously being masked on the images 'output_epsg': int output spatial reference system as EPSG code Returns: ----------- reference_shoreline: np.array coordinates of the reference shoreline that was manually digitized. This is also saved as a .pkl and .geojson file. """ sitename = settings['inputs']['sitename'] filepath_data = settings['inputs']['filepath'] pts_coords = [] # check if reference shoreline already exists in the corresponding folder filepath = os.path.join(filepath_data, sitename) filename = sitename + '_reference_shoreline.pkl' # if it exist, load it and return it if filename in os.listdir(filepath): print('Reference shoreline already exists and was loaded') with open(os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'rb') as f: refsl = pickle.load(f) return refsl # otherwise get the user to manually digitise a shoreline on S2, L8 or L5 images (no L7 because of scan line error) else: # first try to use S2 images (10m res for manually digitizing the reference shoreline) if 'S2' in metadata.keys(): satname = 'S2' filepath = SDS_tools.get_filepath(settings['inputs'],satname) filenames = metadata[satname]['filenames'] # if no S2 images, try L8 (15m res in the RGB with pansharpening) elif not 'S2' in metadata.keys() and 'L8' in metadata.keys(): satname = 'L8' filepath = SDS_tools.get_filepath(settings['inputs'],satname) filenames = metadata[satname]['filenames'] # if no S2 images and no L8, use L5 images (L7 images have black diagonal bands making it # hard to manually digitize a shoreline) elif not 'S2' in metadata.keys() and not 'L8' in metadata.keys() and 'L5' in metadata.keys(): satname = 'L5' filepath = SDS_tools.get_filepath(settings['inputs'],satname) filenames = metadata[satname]['filenames'] else: raise Exception('You cannot digitize the shoreline on L7 images (because of gaps in the images), add another L8, S2 or L5 to your dataset.') # create figure fig, ax = plt.subplots(1,1, figsize=[18,9], tight_layout=True) mng = plt.get_current_fig_manager() mng.window.showMaximized() # loop trhough the images for i in range(len(filenames)): # read image fn = SDS_tools.get_filenames(filenames[i],filepath, satname) im_ms, georef, cloud_mask, im_extra, im_QA, im_nodata = preprocess_single(fn, satname, settings['cloud_mask_issue']) # calculate cloud cover cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))), (cloud_mask.shape[0]*cloud_mask.shape[1])) # skip image if cloud cover is above threshold if cloud_cover > settings['cloud_thresh']: continue # rescale image intensity for display purposes im_RGB = rescale_image_intensity(im_ms[:,:,[2,1,0]], cloud_mask, 99.9) # plot the image RGB on a figure ax.axis('off') ax.imshow(im_RGB) # decide if the image if good enough for digitizing the shoreline ax.set_title('Press <right arrow> if image is clear enough to digitize the shoreline.\n' + 'If the image is cloudy press <left arrow> to get another image', fontsize=14) # set a key event to accept/reject the detections (see https://stackoverflow.com/a/15033071) # this variable needs to be immuatable so we can access it after the keypress event skip_image = False key_event = {} def press(event): # store what key was pressed in the dictionary key_event['pressed'] = event.key # let the user press a key, right arrow to keep the image, left arrow to skip it # to break the loop the user can press 'escape' while True: btn_keep = plt.text(1.1, 0.9, 'keep ⇨', size=12, ha="right", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) btn_skip = plt.text(-0.1, 0.9, '⇦ skip', size=12, ha="left", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) btn_esc = plt.text(0.5, 0, '<esc> to quit', size=12, ha="center", va="top", transform=ax.transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) plt.draw() fig.canvas.mpl_connect('key_press_event', press) plt.waitforbuttonpress() # after button is pressed, remove the buttons btn_skip.remove() btn_keep.remove() btn_esc.remove() # keep/skip image according to the pressed key, 'escape' to break the loop if key_event.get('pressed') == 'right': skip_image = False break elif key_event.get('pressed') == 'left': skip_image = True break elif key_event.get('pressed') == 'escape': plt.close() raise StopIteration('User cancelled checking shoreline detection') else: plt.waitforbuttonpress() if skip_image: ax.clear() continue else: # create two new buttons add_button = plt.text(0, 0.9, 'add', size=16, ha="left", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) end_button = plt.text(1, 0.9, 'end', size=16, ha="right", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k',fc='w')) # add multiple reference shorelines (until user clicks on <end> button) pts_sl = np.expand_dims(np.array([np.nan, np.nan]),axis=0) geoms = [] while 1: add_button.set_visible(False) end_button.set_visible(False) # update title (instructions) ax.set_title('Click points along the shoreline (enough points to capture the beach curvature).\n' + 'Start at one end of the beach.\n' + 'When finished digitizing, click <ENTER>', fontsize=14) plt.draw() # let user click on the shoreline pts = ginput(n=50000, timeout=1e9, show_clicks=True) pts_pix = np.array(pts) # convert pixel coordinates to world coordinates pts_world = SDS_tools.convert_pix2world(pts_pix[:,[1,0]], georef) # interpolate between points clicked by the user (1m resolution) pts_world_interp = np.expand_dims(np.array([np.nan, np.nan]),axis=0) for k in range(len(pts_world)-1): pt_dist = np.linalg.norm(pts_world[k,:]-pts_world[k+1,:]) xvals = np.arange(0,pt_dist) yvals = np.zeros(len(xvals)) pt_coords = np.zeros((len(xvals),2)) pt_coords[:,0] = xvals pt_coords[:,1] = yvals phi = 0 deltax = pts_world[k+1,0] - pts_world[k,0] deltay = pts_world[k+1,1] - pts_world[k,1] phi = np.pi/2 - np.math.atan2(deltax, deltay) tf = transform.EuclideanTransform(rotation=phi, translation=pts_world[k,:]) pts_world_interp = np.append(pts_world_interp,tf(pt_coords), axis=0) pts_world_interp = np.delete(pts_world_interp,0,axis=0) # save as geometry (to create .geojson file later) geoms.append(geometry.LineString(pts_world_interp)) # convert to pixel coordinates and plot pts_pix_interp = SDS_tools.convert_world2pix(pts_world_interp, georef) pts_sl = np.append(pts_sl, pts_world_interp, axis=0) ax.plot(pts_pix_interp[:,0], pts_pix_interp[:,1], 'r--') ax.plot(pts_pix_interp[0,0], pts_pix_interp[0,1],'ko') ax.plot(pts_pix_interp[-1,0], pts_pix_interp[-1,1],'ko') # update title and buttons add_button.set_visible(True) end_button.set_visible(True) ax.set_title('click on <add> to digitize another shoreline or on <end> to finish and save the shoreline(s)', fontsize=14) plt.draw() # let the user click again (<add> another shoreline or <end>) pt_input = ginput(n=1, timeout=1e9, show_clicks=False) pt_input = np.array(pt_input) # if user clicks on <end>, save the points and break the loop if pt_input[0][0] > im_ms.shape[1]/2: add_button.set_visible(False) end_button.set_visible(False) plt.title('Reference shoreline saved as ' + sitename + '_reference_shoreline.pkl and ' + sitename + '_reference_shoreline.geojson') plt.draw() ginput(n=1, timeout=3, show_clicks=False) plt.close() break pts_sl = np.delete(pts_sl,0,axis=0) # convert world image coordinates to user-defined coordinate system image_epsg = metadata[satname]['epsg'][i] pts_coords = SDS_tools.convert_epsg(pts_sl, image_epsg, settings['output_epsg']) # save the reference shoreline as .pkl filepath = os.path.join(filepath_data, sitename) with open(os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'wb') as f: pickle.dump(pts_coords, f) # also store as .geojson in case user wants to drag-and-drop on GIS for verification for k,line in enumerate(geoms): gdf = gpd.GeoDataFrame(geometry=gpd.GeoSeries(line)) gdf.index = [k] gdf.loc[k,'name'] = 'reference shoreline ' + str(k+1) # store into geodataframe if k == 0: gdf_all = gdf else: gdf_all = gdf_all.append(gdf) gdf_all.crs = {'init':'epsg:'+str(image_epsg)} # convert from image_epsg to user-defined coordinate system gdf_all = gdf_all.to_crs({'init': 'epsg:'+str(settings['output_epsg'])}) # save as geojson gdf_all.to_file(os.path.join(filepath, sitename + '_reference_shoreline.geojson'), driver='GeoJSON', encoding='utf-8') print('Reference shoreline has been saved in ' + filepath) break # check if a shoreline was digitised if len(pts_coords) == 0: raise Exception('No cloud free images are available to digitise the reference shoreline,'+ 'download more images and try again') return pts_coords
def get_reference_sl(metadata, settings): """ Allows the user to manually digitize a reference shoreline that is used seed the shoreline detection algorithm. The reference shoreline helps to detect the outliers, making the shoreline detection more robust. KV WRL 2018 Arguments: ----------- metadata: dict contains all the information about the satellite images that were downloaded settings: dict contains the following fields: 'cloud_thresh': float value between 0 and 1 indicating the maximum cloud fraction in the image that is accepted 'sitename': string name of the site (also name of the folder where the images are stored) 'output_epsg': int epsg code of the desired spatial reference system Returns: ----------- reference_shoreline: np.array coordinates of the reference shoreline that was manually digitized """ sitename = settings['inputs']['sitename'] filepath_data = settings['inputs']['filepath'] # check if reference shoreline already exists in the corresponding folder filepath = os.path.join(filepath_data, sitename) filename = sitename + '_reference_shoreline.pkl' if filename in os.listdir(filepath): print('Reference shoreline already exists and was loaded') with open( os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'rb') as f: refsl = pickle.load(f) return refsl else: # first try to use S2 images (10m res for manually digitizing the reference shoreline) if 'S2' in metadata.keys(): satname = 'S2' filepath = SDS_tools.get_filepath(settings['inputs'], satname) filenames = metadata[satname]['filenames'] # if no S2 images, try L8 (15m res in the RGB with pansharpening) elif not 'S2' in metadata.keys() and 'L8' in metadata.keys(): satname = 'L8' filepath = SDS_tools.get_filepath(settings['inputs'], satname) filenames = metadata[satname]['filenames'] # if no S2 images and no L8, use L5 images (L7 images have black diagonal bands making it # hard to manually digitize a shoreline) elif not 'S2' in metadata.keys() and not 'L8' in metadata.keys( ) and 'L5' in metadata.keys(): satname = 'L5' filepath = SDS_tools.get_filepath(settings['inputs'], satname) filenames = metadata[satname]['filenames'] else: raise Exception( 'You cannot digitize the shoreline on L7 images, add another L8, S2 or L5 to your dataset.' ) # loop trhough the images for i in range(len(filenames)): # read image fn = SDS_tools.get_filenames(filenames[i], filepath, satname) im_ms, georef, cloud_mask, im_extra, imQA = preprocess_single( fn, satname, settings['cloud_mask_issue']) # calculate cloud cover cloud_cover = np.divide( sum(sum(cloud_mask.astype(int))), (cloud_mask.shape[0] * cloud_mask.shape[1])) # skip image if cloud cover is above threshold if cloud_cover > settings['cloud_thresh']: continue # rescale image intensity for display purposes im_RGB = rescale_image_intensity(im_ms[:, :, [2, 1, 0]], cloud_mask, 99.9) # plot the image RGB on a figure fig = plt.figure() fig.set_size_inches([18, 9]) fig.set_tight_layout(True) plt.axis('off') plt.imshow(im_RGB) # decide if the image if good enough for digitizing the shoreline plt.title( 'click <keep> if image is clear enough to digitize the shoreline.\n' + 'If not (too cloudy) click on <skip> to get another image', fontsize=14) keep_button = plt.text(0, 0.9, 'keep', size=16, ha="left", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) skip_button = plt.text(1, 0.9, 'skip', size=16, ha="right", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) mng = plt.get_current_fig_manager() mng.window.showMaximized() # let user click on the image once pt_input = ginput(n=1, timeout=1e9, show_clicks=False) pt_input = np.array(pt_input) # if clicks next to <skip>, show another image if pt_input[0][0] > im_ms.shape[1] / 2: plt.close() continue else: # remove keep and skip buttons keep_button.set_visible(False) skip_button.set_visible(False) # create two new buttons add_button = plt.text(0, 0.9, 'add', size=16, ha="left", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) end_button = plt.text(1, 0.9, 'end', size=16, ha="right", va="top", transform=plt.gca().transAxes, bbox=dict(boxstyle="square", ec='k', fc='w')) # add multiple reference shorelines (until user clicks on <end> button) pts_sl = np.expand_dims(np.array([np.nan, np.nan]), axis=0) geoms = [] while 1: add_button.set_visible(False) end_button.set_visible(False) # update title (instructions) plt.title( 'Click points along the shoreline (enough points to capture the beach curvature).\n' + 'Start at one end of the beach.\n' + 'When finished digitizing, click <ENTER>', fontsize=14) plt.draw() # let user click on the shoreline pts = ginput(n=50000, timeout=1e9, show_clicks=True) pts_pix = np.array(pts) # convert pixel coordinates to world coordinates pts_world = SDS_tools.convert_pix2world( pts_pix[:, [1, 0]], georef) # interpolate between points clicked by the user (1m resolution) pts_world_interp = np.expand_dims(np.array( [np.nan, np.nan]), axis=0) for k in range(len(pts_world) - 1): pt_dist = np.linalg.norm(pts_world[k, :] - pts_world[k + 1, :]) xvals = np.arange(0, pt_dist) yvals = np.zeros(len(xvals)) pt_coords = np.zeros((len(xvals), 2)) pt_coords[:, 0] = xvals pt_coords[:, 1] = yvals phi = 0 deltax = pts_world[k + 1, 0] - pts_world[k, 0] deltay = pts_world[k + 1, 1] - pts_world[k, 1] phi = np.pi / 2 - np.math.atan2(deltax, deltay) tf = transform.EuclideanTransform( rotation=phi, translation=pts_world[k, :]) pts_world_interp = np.append(pts_world_interp, tf(pt_coords), axis=0) pts_world_interp = np.delete(pts_world_interp, 0, axis=0) # save as geometry (to create .geojson file later) geoms.append(geometry.LineString(pts_world_interp)) # convert to pixel coordinates and plot pts_pix_interp = SDS_tools.convert_world2pix( pts_world_interp, georef) pts_sl = np.append(pts_sl, pts_world_interp, axis=0) plt.plot(pts_pix_interp[:, 0], pts_pix_interp[:, 1], 'r--') plt.plot(pts_pix_interp[0, 0], pts_pix_interp[0, 1], 'ko') plt.plot(pts_pix_interp[-1, 0], pts_pix_interp[-1, 1], 'ko') # update title and buttons add_button.set_visible(True) end_button.set_visible(True) plt.title( 'click <add> to digitize another shoreline or <end> to finish and save the shoreline(s)', fontsize=14) plt.draw() # let the user click again (<add> another shoreline or <end>) pt_input = ginput(n=1, timeout=1e9, show_clicks=False) pt_input = np.array(pt_input) # if user clicks on <end>, save the points and break the loop if pt_input[0][0] > im_ms.shape[1] / 2: add_button.set_visible(False) end_button.set_visible(False) plt.title('Reference shoreline saved as ' + sitename + '_reference_shoreline.pkl and ' + sitename + '_reference_shoreline.geojson') plt.draw() ginput(n=1, timeout=3, show_clicks=False) plt.close() break pts_sl = np.delete(pts_sl, 0, axis=0) # convert world image coordinates to user-defined coordinate system image_epsg = metadata[satname]['epsg'][i] pts_coords = SDS_tools.convert_epsg(pts_sl, image_epsg, settings['output_epsg']) # save the reference shoreline as .pkl filepath = os.path.join(filepath_data, sitename) with open( os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'wb') as f: pickle.dump(pts_coords, f) # also store as .geojson in case user wants to drag-and-drop on GIS for verification for k, line in enumerate(geoms): gdf = gpd.GeoDataFrame(geometry=gpd.GeoSeries(line)) gdf.index = [k] gdf.loc[k, 'name'] = 'reference shoreline ' + str(k + 1) # store into geodataframe if k == 0: gdf_all = gdf else: gdf_all = gdf_all.append(gdf) gdf_all.crs = {'init': 'epsg:' + str(image_epsg)} # convert from image_epsg to user-defined coordinate system gdf_all = gdf_all.to_crs( {'init': 'epsg:' + str(settings['output_epsg'])}) # save as geojson gdf_all.to_file(os.path.join( filepath, sitename + '_reference_shoreline.geojson'), driver='GeoJSON', encoding='utf-8') print('Reference shoreline has been saved in ' + filepath) break return pts_coords
def run(self): """ The method for running the Sparse model with the parameters were specified in the Sparse class parameters. For running with the default parameters only the input data instance is required (.input_data parameter). Args: None Returns: numpy.ndarray: 3D numpy.ndarray of precipitation nowcasting (frames, dim_x, dim_y) """ # check input data if not isinstance(self.input_data, np.ndarray) \ or not self.input_data.dtype == "uint8" \ or not len(self.input_data.shape) == 3: raise TypeError( "input data must be np.ndarray(shape=(frames, nrows, ncols), dtype='uint8')" ) transformations = { 'euclidean': sktf.EuclideanTransform(), 'similarity': sktf.SimilarityTransform(), 'affine': sktf.AffineTransform(), 'projective': sktf.ProjectiveTransform(), } # find features to track old_corners = cv2.goodFeaturesToTrack(self.input_data[0], mask=None, **self.params['st_pars']) # Set containers to collect results (time steps in rows, detected corners in columns) # corner x coords x = np.full((self.input_data.shape[0], len(old_corners)), np.nan) # corner y coords y = np.full((self.input_data.shape[0], len(old_corners)), np.nan) # Assign persistent corner IDs ids = np.arange(len(old_corners)) # fill in first values x[0, :] = old_corners[:, 0, 0] y[0, :] = old_corners[:, 0, 1] # track corners by optical flow algorithm for i in range(1, self.input_data.shape[0]): new_corners, st, err = cv2.calcOpticalFlowPyrLK( prevImg=self.input_data[i - 1], nextImg=self.input_data[i], prevPts=old_corners, nextPts=None, **self.params['lk_pars']) # select only good attempts for corner tracking success = st.ravel() == 1 # use only sucessfull ids for filling ids = ids[success] # fill in results x[i, ids] = new_corners[success, 0, 0] y[i, ids] = new_corners[success, 0, 1] # new corners will be old in the next loop old_corners = new_corners[success] # consider only full paths full_paths_without_nan = [ np.sum(np.isnan(x[:, i])) == 0 for i in range(x.shape[1]) ] x = x[:, full_paths_without_nan].copy() y = y[:, full_paths_without_nan].copy() # containers for corners predictions x_new = np.full((self.lead_steps, x.shape[1]), np.nan) y_new = np.full((self.lead_steps, y.shape[1]), np.nan) for i in range(x.shape[1]): x_train = x[:, i] y_train = y[:, i] X = np.arange(x.shape[0] + self.lead_steps) if self.extrapolator["features"] == "polynomial": polyfeatures = PolynomialFeatures(2) X = polyfeatures.fit_transform(X.reshape(-1, 1)) X_train = X[:x.shape[0], :] X_pred = X[x.shape[0]:, :] else: X = X.reshape(-1, 1) X_train = X[:x.shape[0], :] X_pred = X[x.shape[0]:, :] x_pred = self.extrapolator["model"].fit(X_train, x_train).predict(X_pred) y_pred = self.extrapolator["model"].fit(X_train, y_train).predict(X_pred) x_new[:, i] = x_pred y_new[:, i] = y_pred # define last frame - the general source of our transforming last_frame = self.input_data[-1] # define source corners in appropriate format pts_source = np.hstack( [x[-1, :].reshape(-1, 1), y[-1, :].reshape(-1, 1)]) # define container for targets in appropriate format pts_target_container = [ np.hstack([x_new[i, :].reshape(-1, 1), y_new[i, :].reshape(-1, 1)]) for i in range(x_new.shape[0]) ] # set up transformer object trf = transformations[self.warper] # now we can start to find nowcasted image # for every candidate of projected sets of points # container for our nowcasts nowcst_frames = [] for lead_step, pts_target in enumerate(pts_target_container): # estimate transformation matrix # based on source and traget points trf.estimate(pts_source, pts_target) # make a nowcast nowcst_frame = sktf.warp(last_frame / 255, trf.inverse) # transformations dealing with strange behaviour nowcst_frame = (nowcst_frame * 255).astype('uint8') # add to the container nowcst_frames.append(nowcst_frame) forecast = np.dstack(nowcst_frames) return np.moveaxis(forecast, -1, 0).copy()
import matplotlib.pyplot as plt from skimage import data from skimage import transform from skimage import img_as_float ###################################################################### # Euclidean (rigid) transformation # ================================= # # A `Euclidean transformation <https://en.wikipedia.org/wiki/Rigid_transformation>`_, # also called rigid transformation, preserves the Euclidean distance between # pairs of points. It can be described as a rotation about the origin # followed by a translation. tform = transform.EuclideanTransform(rotation=np.pi / 12., translation=(100, -20)) print(tform.params) ###################################################################### # Now let's apply this transformation to an image. Because we are trying # to reconstruct the *image* after transformation, it is not useful to see # where a *coordinate* from the input image ends up in the output, which is # what the transform gives us. Instead, for every pixel (coordinate) in the # output image, we want to figure out where in the input image it comes from. # Therefore, we need to use the inverse of ``tform``, rather than ``tform`` # directly. img = img_as_float(data.chelsea()) tf_img = transform.warp(img, tform.inverse) fig, ax = plt.subplots() ax.imshow(tf_img)
def run(self): """ The method for running the SparseSD model with the parameters were specified in the SparseSD class parameters. For running with the default parameters only the input data instance is required (.input_data parameter). Args: None Returns: numpy.ndarray: 3D numpy.ndarray of precipitation nowcasting (frames, dim_x, dim_y) """ # check input data if not isinstance(self.input_data, np.ndarray) \ or not self.input_data.dtype == "uint8" \ or not len(self.input_data.shape) == 3: raise TypeError( "input data must be np.ndarray(shape=(frames, nrows, ncols), dtype='uint8')" ) transformations = { 'euclidean': sktf.EuclideanTransform(), 'similarity': sktf.SimilarityTransform(), 'affine': sktf.AffineTransform(), 'projective': sktf.ProjectiveTransform(), } # define penult and last frames penult_frame = self.input_data[-2] last_frame = self.input_data[-1] # find features to track old_corners = cv2.goodFeaturesToTrack(self.input_data[0], mask=None, **self.params['st_pars']) # track corners by optical flow algorithm new_corners, st, err = cv2.calcOpticalFlowPyrLK( prevImg=penult_frame, nextImg=last_frame, prevPts=old_corners, nextPts=None, **self.params['lk_pars']) # select only good attempts for corner tracking success = st.ravel() == 1 new_corners = new_corners[success].copy() old_corners = old_corners[success].copy() # calculate Simple Delta delta = new_corners.reshape(-1, 2) - old_corners.reshape(-1, 2) # simplificate furher transformations pts_source = new_corners.reshape(-1, 2) # propagate our corners through time pts_target_container = [] for lead_step in range(self.lead_steps): pts_target_container.append(pts_source + delta * (lead_step + 1)) # set up transformer object trf = transformations[self.warper] # now we can start to find nowcasted image # for every candidate of projected sets of points # container for our nowcasts nowcst_frames = [] for lead_step, pts_target in enumerate(pts_target_container): # estimate transformation matrix # based on source and traget points trf.estimate(pts_source, pts_target) # make a nowcast nowcst_frame = sktf.warp(last_frame / 255, trf.inverse) # transformations dealing with strange behaviour nowcst_frame = (nowcst_frame * 255).astype('uint8') # add to the container nowcst_frames.append(nowcst_frame) forecast = np.dstack(nowcst_frames) return np.moveaxis(forecast, -1, 0).copy()
def solve_wcs(input_file, telescope, sex_config_dir='./Config', static_mask=None, proc=None, log=None): #start time t_start = time.time() if log: log.info('Running solve_wcs version: ' + str(__version__)) #import telescope parameter file global tel try: tel = importlib.import_module('tel_params.' + telescope) except ImportError: print('No such telescope file, please check that you have entered the'+\ ' correct name or this telescope is available.''') sys.exit(-1) #get data and header info hdr = fits.open(input_file) data = hdr[tel.wcs_extension()].data data_y, data_x = np.shape(data) header = hdr[tel.wcs_extension()].header ra = header['CRVAL1'] dec = header['CRVAL2'] #run sextractor if log: log.info('Running SExtractor.') cat_name = input_file.replace('.fits', '.cat') table = run_sextractor(input_file, cat_name, tel, sex_config_dir=sex_config_dir, log=log) #mask and sort table if log: log.info('Masking sources and applying brightness cuts.') table = table[(table['FLAGS'] == 0) & (table['EXT_NUMBER'] == tel.wcs_extension() + 1) & (table['MAGERR_BEST'] != 99)] if static_mask: with fits.open(static_mask) as mask_hdu: stat_mask = mask_hdu[0].data table = table[(stat_mask[table['YWIN_IMAGE'].astype(int) - 1, table['XWIN_IMAGE'].astype(int) - 1] != 0)] table.sort('MAG_BEST') table = table[table['MAG_BEST'] < (np.median(table['MAG_BEST']) - np.std(table['MAG_BEST']))] if log: log.info('Total of %d usable stars' % len(table)) #make quads using brightest stars if log: log.info('Making quads with the brightest 50 stars.') ind = list(itertools.combinations(np.arange(4), 2)) stars, d, ds, ratios = make_quads(table['XWIN_IMAGE'], table['YWIN_IMAGE'], use=50) #query gaia if log: log.info( 'Searching GAIA DR3 for stars within 10 arcmins of WCS in header.') Gaia.ROW_LIMIT = -1 job = Gaia.cone_search_async(SkyCoord(ra * u.deg, dec * u.deg, frame='icrs'), 10 * u.arcmin, table_name='gaiaedr3.gaia_source') gaiaorig = job.get_results() gaiaorig.sort('phot_g_mean_mag') if log: log.info(str(len(gaiaorig)) + ' GAIA stars found.') # Mask catalog so all data are inside nominal image if log: log.info('Applying image mask to GAIA sources.') mask = mask_catalog_for_wcs(gaiaorig, wcs.WCS(header), 1, data_x, data_y) gaia = gaiaorig[mask] if log: log.info( str(len(gaia)) + ' GAIA stars found within the image footprint.') #make quads using stars brigher than 20 mag if log: log.info( 'Making quads with the brightest stars brighter than 20 mag and fainter than 14 mag.' ) gaia = gaia[gaia['phot_g_mean_mag'] < 20] gaia = gaia[gaia['phot_g_mean_mag'] > 14] gaiastars, gaiad, gaiads, gaiaratios = make_quads(gaia['ra'], gaia['dec'], use=50, sky_coords=True) #match quads if log: log.info('Matching quads between the detected and cataloged stars.') try: starsx, starsy, gaiastarsra, gaiastarsdec = match_quads( stars, gaiastars, d, gaiad, ds, gaiads, ratios, gaiaratios, sky_coords=True) if log: log.info('Found ' + str(len(starsx)) + ' unique star matches.') #calculate inital transformation if log: log.info( 'Calculating the initial transformation between the matched stars.' ) gaiax, gaiay = wcs.utils.skycoord_to_pixel( SkyCoord(gaiastarsra, gaiastarsdec, unit='deg'), wcs.WCS(header), 1) tform = tf.estimate_transform('euclidean', np.c_[starsx, starsy], np.c_[gaiax, gaiay]) #apply initial transformation to header if log: log.info( 'Applying the initial transformation to the existing WCS in the header.' ) header_new = apply_wcs_transformation(header, tform) except: if log: log.error('Unique star matching failed.') crpix1, crpix2, cd11, cd12, cd21, cd22 = wcs_keyword = tel.WCS_keywords( ) header['CD1_1'] = header[cd11] header['CD1_2'] = header[cd12] header['CD2_1'] = header[cd21] header['CD2_2'] = header[cd22] header['CTYPE1'] = 'RA---TAN' header['CTYPE2'] = 'DEC--TAN' old_keywords = tel.WCS_keywords_old() for old in old_keywords: try: del header[old] except: pass header_new = header #matching all stars to catalog if log: log.info('Matching all stars to the catalog.') stars_ra, stars_dec = (wcs.WCS(header_new)).all_pix2world( table['XWIN_IMAGE'], table['YWIN_IMAGE'], 1) stars_radec = SkyCoord(stars_ra * u.deg, stars_dec * u.deg) gaia_radec = SkyCoord(gaia['ra'], gaia['dec'], unit='deg') idx, d2, d3 = gaia_radec.match_to_catalog_sky(stars_radec) match = d2 < 5.0 * u.arcsec idx = idx[match] starx_match = [table['XWIN_IMAGE'][x] for x in idx] stary_match = [table['YWIN_IMAGE'][x] for x in idx] gaia_radec = SkyCoord(gaia[match]['ra'], gaia[match]['dec'], unit='deg') gaiax_match, gaiay_match = wcs.utils.skycoord_to_pixel( gaia_radec, wcs.WCS(header_new), 1) if log: log.info('Found ' + str(len(starx_match)) + ' star matches within 5".') #calculate and apply full transformation if log: log.info( 'Calculating the full transformation between the matched stars.') tform = tf.estimate_transform('euclidean', np.c_[starx_match, stary_match], np.c_[gaiax_match, gaiay_match]) if log: log.info( 'Applying the full transformation to the existing WCS in the header.' ) header_new['CRPIX1'] = header_new['CRPIX1'] - tform.translation[0] header_new['CRPIX2'] = header_new['CRPIX2'] - tform.translation[1] cd = np.array([ header_new['CD1_1'], header_new['CD1_2'], header_new['CD2_1'], header_new['CD2_2'] ]).reshape(2, 2) cd_matrix = tf.EuclideanTransform(rotation=tform.rotation) cd_transformed = tf.warp(cd, cd_matrix) header_new['CD1_1'] = cd_transformed[0][0] header_new['CD1_2'] = cd_transformed[0][1] header_new['CD2_1'] = cd_transformed[1][0] header_new['CD2_2'] = cd_transformed[1][1] header_new['WCS_REF'] = ('GAIA-DR2', 'Reference catalog for astrometric solution.') header_new['WCS_NUM'] = (len(starx_match), 'Number of stars used for astrometric solution.') #calculate polynomial distortion if log: log.info( 'Calculating and applying the higher order polynomial distortion.') header_dist = apply_wcs_distortion(header_new, starx_match, stary_match, gaia[match]['ra'], gaia[match]['dec']) #calculate error. This error now uses dvrms() instead of np.median. if log: log.info('Calculating the rms on the astrometry.') stars_ra, stars_dec = (wcs.WCS(header_dist)).all_pix2world( starx_match, stary_match, 1) stars_radec = SkyCoord(stars_ra * u.deg, stars_dec * u.deg) error = calculate_error(stars_radec, gaia_radec, header_dist) #write out new fits fits.writeto(input_file.replace('.fits', '_wcs.fits'), data, header_dist, overwrite=True) #end and run time t_end = time.time() if log: log.info('Solve_wcs ran in ' + str(t_end - t_start) + ' sec') else: print('Solve_wcs ran in ' + str(t_end - t_start) + ' sec') return 'Median rms on astrometry is %.3f arcsec.' % error