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))
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
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
Beispiel #8
0
    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)
Beispiel #10
0
    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
Beispiel #11
0
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()
Beispiel #13
0
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)
Beispiel #15
0
    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)
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
    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
Beispiel #19
0
    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
Beispiel #21
0
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
Beispiel #24
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
Beispiel #25
0
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
Beispiel #26
0
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
Beispiel #27
0
    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)
Beispiel #29
0
    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