def test_morphsnakes_simple_shape_chan_vese():
    img = gaussian_blob()
    ls1 = circle_level_set(img.shape, (5, 5), 3)
    ls2 = circle_level_set(img.shape, (5, 5), 6)

    acwe_ls1 = morphological_chan_vese(img, iterations=10, init_level_set=ls1)
    acwe_ls2 = morphological_chan_vese(img, iterations=10, init_level_set=ls2)

    assert_array_equal(acwe_ls1, acwe_ls2)

    assert acwe_ls1.dtype == acwe_ls2.dtype == np.int8
def test_morphsnakes_simple_shape_chan_vese():
    img = gaussian_blob()
    ls1 = circle_level_set(img.shape, (5, 5), 3)
    ls2 = circle_level_set(img.shape, (5, 5), 6)

    acwe_ls1 = morphological_chan_vese(img, iterations=10, init_level_set=ls1)
    acwe_ls2 = morphological_chan_vese(img, iterations=10, init_level_set=ls2)

    assert_array_equal(acwe_ls1, acwe_ls2)

    assert acwe_ls1.dtype == acwe_ls2.dtype == np.int8
def test_morphsnakes_black():

    img = np.zeros((11, 11))
    ls = circle_level_set(img.shape, (5, 5), 3)

    ref_zeros = np.zeros(img.shape, dtype=np.int8)
    ref_ones = np.ones(img.shape, dtype=np.int8)

    acwe_ls = morphological_chan_vese(img, iterations=6, init_level_set=ls)
    assert_array_equal(acwe_ls, ref_zeros)

    gac_ls = morphological_geodesic_active_contour(img,
                                                   iterations=6,
                                                   init_level_set=ls)
    assert_array_equal(gac_ls, ref_zeros)

    gac_ls2 = morphological_geodesic_active_contour(img,
                                                    iterations=6,
                                                    init_level_set=ls,
                                                    balloon=1,
                                                    threshold=-1,
                                                    smoothing=0)
    assert_array_equal(gac_ls2, ref_ones)

    assert acwe_ls.dtype == gac_ls.dtype == gac_ls2.dtype == np.int8
def test_morphsnakes_simple_shape_geodesic_active_contour():
    img = np.float_(circle_level_set((11, 11), (5, 5), 3.5))
    gimg = inverse_gaussian_gradient(img, alpha=10.0, sigma=1.0)
    ls = circle_level_set(img.shape, (5, 5), 6)

    ref = np.array(
        [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
         [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
         [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
         [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
        dtype=np.int8)

    gac_ls = morphological_geodesic_active_contour(gimg,
                                                   iterations=10,
                                                   init_level_set=ls,
                                                   balloon=-1)
    assert_array_equal(gac_ls, ref)
    assert gac_ls.dtype == np.int8
def test_morphsnakes_simple_shape_geodesic_active_contour():
    img = np.float_(circle_level_set((11, 11), (5, 5), 3.5))
    gimg = inverse_gaussian_gradient(img, alpha=10.0, sigma=1.0)
    ls = circle_level_set(img.shape, (5, 5), 6)

    ref = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
                    [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
                    [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
                    [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
                    [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
                    [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
                    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                   dtype=np.int8)

    gac_ls = morphological_geodesic_active_contour(gimg, iterations=10,
                                                   init_level_set=ls,
                                                   balloon=-1)
    assert_array_equal(gac_ls, ref)
    assert gac_ls.dtype == np.int8
def test_morphsnakes_black():
    img = np.zeros((11, 11))
    ls = circle_level_set(img.shape, (5, 5), 3)

    ref_zeros = np.zeros(img.shape, dtype=np.int8)
    ref_ones = np.ones(img.shape, dtype=np.int8)

    acwe_ls = morphological_chan_vese(img, iterations=6, init_level_set=ls)
    assert_array_equal(acwe_ls, ref_zeros)

    gac_ls = morphological_geodesic_active_contour(img, iterations=6,
                                                   init_level_set=ls)
    assert_array_equal(gac_ls, ref_zeros)

    gac_ls2 = morphological_geodesic_active_contour(img, iterations=6,
                                                    init_level_set=ls,
                                                    balloon=1, threshold=-1,
                                                    smoothing=0)
    assert_array_equal(gac_ls2, ref_ones)

    assert acwe_ls.dtype == gac_ls.dtype == gac_ls2.dtype == np.int8
Beispiel #7
0
def _init(x0, y0, x1, y1):
    a = (x0, y0)
    b = (x1, y1)
    radius = math.sqrt(sum([(c - d)**2 for c, d in zip(a, b)]))
    init = circle_level_set((512, 512), (y0, x0), radius)
    return init
Beispiel #8
0
def test_deprecated_circle_level_set():
    img = gaussian_blob()
    with expected_warnings(['circle_level_set is deprecated']):
        ls1 = circle_level_set(img.shape, (5, 5), 3)
def main():

    current_path = os.path.abspath(os.path.dirname(__file__))
    dice_results = []
    hausdorff_results = []

    # Final report text file initialization

    summary_file = open('contour_report.txt', 'w+')
    summary_file.write('Script run: {a}\n'.format(
        a=datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

    for image_number in range(1, 110):

        image_path = os.path.join(current_path, 'input_images',
                                  '{}_no_contour.jpg'.format(image_number))
        image = io.imread(image_path)
        image = color.rgb2gray(image)

        # Displaying the image to get the clicks coordinates

        fig = plt.figure()
        plt.imshow(image, cmap='gray')
        plt.axis('Off')

        cid = fig.canvas.mpl_connect('button_press_event', onclick)
        plt.show()

        # Calculating the radius, used to initialize the level set algorithm

        radius = np.sqrt(
            np.square(coords[1][0] - coords[0][0]) +
            np.square(coords[1][1] - coords[0][1]))

        # Main part of the script - image processing and creating the contour:
        # 1. Histogram equalization
        # 2. Gaussian filtration
        # 3. Anisotropic diffusion filtration
        # 4. Inverse Gaussian Gradient
        # 5. Unsharp Masking
        # 6. Morphological snakes
        # Hyperparameter optimization were performed using grid search for every method.

        equalized_image = equalize_hist(image)
        filtered_image = nd.gaussian_filter(equalized_image, sigma=5)
        diff_image = anisodiff(filtered_image, niter=50)
        gimage = inverse_gaussian_gradient(diff_image, alpha=50)
        sharpened_image = unsharp_mask(gimage, radius=100, amount=1.0)
        init_level_set = circle_level_set(gimage.shape, coords[0], radius)
        level_set = morphological_geodesic_active_contour(sharpened_image,
                                                          250,
                                                          init_level_set,
                                                          smoothing=4,
                                                          balloon=1)
        level_set = nd.morphology.binary_fill_holes(level_set).astype(int)

        contour_access = os.path.join('input_images',
                                      '{}_contour.jpg'.format(image_number))
        given_contour = contour_to_binary_mask(contour_access, level_set.shape)
        dice_result = dice(given_contour, level_set)
        hausdorff_result = hausdorff(given_contour, level_set)

        print('Dice =', dice_result)
        print('Hausdorff =', hausdorff_result)

        # Updating the report with new coefficients.

        summary_file.write('\n{a}:\nDice: {b}\nHausdorff: {c}\n'.format(
            a=image_number,
            b=format(dice_result, '.2f'),
            c=format(hausdorff_result, '.2f')))

        dice_results.append(dice_result)
        hausdorff_results.append(hausdorff_result)

        # Displaying the result

        plt.figure()
        plt.imshow(image, cmap="gray")
        plt.axis('Off')
        plt.contour(level_set, [0.5], colors='r')
        plt.show()

    # Calculating and appending mean and standard deviation of the test to the end of the report

    summary_file.write('\n\nDICE:\nMEAN: {d}\nSTD DEV: {e}'.format(
        d=format(mean(dice_results), '.2f'),
        e=format(stddev(dice_results), '.2f')))
    summary_file.write('\n\nHAUSDORFF:\nMEAN: {d}\nSTD DEV: {e}'.format(
        d=format(mean(hausdorff_results), '.2f'),
        e=format(stddev(hausdorff_results), '.2f')))
    summary_file.close()
Beispiel #10
0
    def process(self, args, sigma=0.8, iterations=25, writeback=False):
        """
        use expansive active contours to transform point geometries into best-fit boundary polygons demarking object footprints
        """

        # open centroid shape file
        centroid = self.openCentroidFile(args.centroid_pathname)

        # open image file
        image = self.openImageFile(args.image_pathname)

        # create footprint shape file
        footprint = self.createOutputFile(args.out_pathname, image)

        # check validity of files
        if centroid is not None and image is not None and footprint is not None:

            # convert centroid locations to pixel coordinates
            coords = self.getCentroidImageCoordinates(centroid, image)
            # random.shuffle( coords )

            # for each x, y centroid location
            for idx, coord in enumerate(coords):

                # check valid sub-image
                if coord[0] + self._object_size < image[
                        'ds'].RasterXSize and coord[
                            1] + self._object_size < image['ds'].RasterYSize:

                    # extract sub-image - check for error
                    sub_image = image['band'].ReadAsArray(
                        coord[0], coord[1], self._object_size,
                        self._object_size)
                    sub_image = gaussian_filter(sub_image, sigma=sigma)
                    sub_image = sub_image / (2 ^ 16 - 1)

                    # define initial state of active contour
                    init_ls = circle_level_set(
                        sub_image.shape,
                        (self._object_halfsize, self._object_halfsize), 5)

                    if writeback is True:

                        # callback for animation
                        ls = morphological_chan_vese(
                            sub_image,
                            iterations=iterations,
                            init_level_set=init_ls,
                            smoothing=1,
                            lambda1=1,
                            lambda2=1,
                            iter_callback=self.visualCallback(sub_image))

                    else:

                        # callback for animation
                        ls = morphological_chan_vese(sub_image,
                                                     iterations=iterations,
                                                     init_level_set=init_ls,
                                                     smoothing=1,
                                                     lambda1=1,
                                                     lambda2=1)

                    # compute simplified polygon
                    polyline = self.getPolyline(ls)

                    # convert polyline to geometry
                    wkt = self.getGeometry(polyline, coord, image['transform'])

                    # create new polygon feature
                    feature = ogr.Feature(footprint['defn'])
                    feature.SetField('id', idx)
                    feature.SetGeometry(ogr.CreateGeometryFromWkt(wkt))

                    # add feature
                    footprint['layer'].CreateFeature(feature)
                    feature = None

                    # create animated gif
                    if idx == 5 and writeback:
                        imageio.mimsave(
                            'C:\\Users\\Chris.Williams\\Desktop\\animate.gif',
                            self._images,
                            fps=5,
                            palettesize=64,
                            subrectangles=True)
                        break

            # delete variables to force write
            footprint['layer'] = None
            footprint['ds'] = None

        # buffer up footprint polygons by 3 metres
        self.getBufferedPolygons(args.out_pathname, 3)

        return
def morphSnakes(fp, frame, alph, sig, thresh):
    """{ 
    ******************************************************************
    *  Fnc:     read_arf() 
    *  Desc:    reads an arf file and returns dictionary of parsed info
    *  Inputs:  fp - full path to arf file
    *  Outputs: 
    ******************************************************************
    }"""
    ############################ Read Public Release Data ##################################################
    #    print('Reading data...')
    _, fn = os.path.split(fp)
    basename, _ext = os.path.splitext(fn)
    df = pd.read_csv(f"{DATA_DIR}/Metric/{basename}.bbox_met",
                     header=None,
                     names=[
                         "site", "unknown1", "unknown2", "sensor", "scenario",
                         "frame", "ply_id", "unknown3", "unknown4", "upperx",
                         "uppery", "unknown5", "unknown6", "unknown7",
                         "unknown8", "unknown9", "unknown10", "unknown11"
                     ])
    agt = read_agt(f"{DATA_DIR}/cegr/agt/{basename}.agt")
    f = open(fp, "rb")
    header = f.read(8 * 4)
    header = list(struct.iter_unpack(">I", header))
    fptr = np.memmap(fp,
                     dtype="uint16",
                     mode='r',
                     shape=(header[5][0], header[2][0], header[3][0]),
                     offset=32)
    frames, cols, rows = fptr.shape
    #    print('Data loaded!')
    im = fptr[frame].T.byteswap()
    tgtx, tgty = map(
        int, agt['Agt']['TgtSect'][f'TgtUpd.{frame}'][f'Tgt.{frame}']
        ['PixLoc'].split())
    upper_left_x, upper_left_y = df[df['frame'] == frame +
                                    1][['upperx', 'uppery']].iloc[0]
    tgt_width = 2 * (tgtx - upper_left_x)
    tgt_height = 2 * (tgty - upper_left_y)

    ######################################### Find BB with GAC ################################################
    def store_evolution_in(lst):
        """Returns a callback function to store the evolution of the level sets in
        the given list.
        """
        def _store(x):
            lst.append(np.copy(x))

        return _store

    plt.ioff()
    fig, axes = plt.subplots(2, 2, figsize=(8, 8))
    ax = axes.flatten()
    # Morphological GAC
    image = img_as_float(im.T)
    gimage = inverse_gaussian_gradient(image, alpha=alph, sigma=sig)
    ax[0].imshow(image, cmap="gray")
    ax[0].set_axis_off()
    ax[0].set_title("MWIR", fontsize=12)
    ax[1].imshow(gimage, cmap="gray")
    ax[1].set_axis_off()
    ax[1].set_title("Inverse Gaussian Gradient", fontsize=12)
    ######################################## Set Initial Contour ###########################################
    ## Here you will want to set it as the bounding box, then you can set the morph snake to shrink instead
    ## of dialate.
    ########################################################################################################
    # Initial level set
    init_ls = circle_level_set(image.shape, center=(tgty, tgtx), radius=5)
    #    init_ls[10:-10, 10:-10] = 1
    # List with intermediate results for plotting the evolution
    evolution = []
    callback = store_evolution_in(evolution)
    ## Initialize the Morph Snake,  you will want it to shrink (balloon=-1)
    ls = morphological_geodesic_active_contour(gimage,
                                               230,
                                               init_ls,
                                               smoothing=1,
                                               balloon=1,
                                               threshold=thresh,
                                               iter_callback=callback)
    ax[2].imshow(image, cmap="gray")
    ax[2].set_axis_off()
    ax[2].contour(ls, [0.5], colors='r')
    ax[2].set_title("Center Point GAC Segmentation", fontsize=12)
    ax[3].imshow(ls, cmap="gray")
    ax[3].set_axis_off()
    contour = ax[3].contour(evolution[0], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 0")
    contour = ax[3].contour(evolution[100], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 100")
    contour = ax[3].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 230")
    ax[3].legend(loc="upper right")
    title = "Center Point GAC Evolution"
    ax[3].set_title(title, fontsize=12)

    fig.tight_layout()
    fig.tight_layout(rect=[0, 0.03, 1, 0.95])
    plt.suptitle(
        f'Morph Snakes, alpha={alph}, sigma={sig}, threshold={thresh}')
    #    plt.savefig(f"{DATA_DIR}\\morphSnakes\\{basename}_{alph}_{sig}_{thresh}.png")
    #    plt.close(fig)
    plt.show()
Beispiel #12
0
def level_set3D(data,
                seed3D,
                resol,
                lambda1=1,
                lambda2=4,
                smoothing=0,
                iterations=100,
                rad=3,
                method='ACWE',
                alpha=100,
                sigma=2,
                balloon=1):

    ## init
    res_fac = resol[1] / resol[
        0]  # resolution factor to scale chunk to real dimensions
    N = 60  # approximately the chunk size (in mm) around nodule
    num_slices = int(round(N / res_fac))  # number of slices before
    reg = 30  # window of interest centered around seed point
    s = seed3D[1:]  # 2D seed point for (x,y)-plane
    num = seed3D[0]  # slice number seed point is selected from

    # apply lungmask on each slice of interest around seed point
    tmp_data = np.zeros((num_slices, data.shape[1], data.shape[2]))
    ii = 0
    for i in range(data.shape[0]):
        if (i >= num - int(round(num_slices / 2))
                and i <= num + int(round(num_slices / 2)) - 1):
            mask = lungmask_pro(data[i, :, :].copy())
            tmp = data[i, :, :].copy()
            tmp[mask == 0] = np.amin(
                tmp
            )  # OBS: -1024 maybe not far away enough from lung intensities
            tmp_data[ii, :, :] = tmp.copy()
            ii += 1

    # only apply level set on small volume around seed point -> increases speed and accuracy for fixed number of iterations
    tmp = tmp_data.copy()
    tmp = tmp[:, s[0] - reg:s[0] + reg, s[1] - reg:s[1] + reg]

    # transform chunk to true size (in mm) by stretching the slice-axis relevant to a resolution factor
    tmp = zoom(tmp.copy(), zoom=[res_fac, 1, 1], order=1)

    # apply 3D level set from single seed point from initial 3D blob around current seed point
    inits = circle_level_set(tmp.shape,
                             center=(int(num_slices / 2 * res_fac), reg, reg),
                             radius=rad)

    # choose between two types of level set methods
    if method == 'ACWE':
        tmp = morphological_chan_vese(tmp.copy(),
                                      iterations=iterations,
                                      init_level_set=inits,
                                      smoothing=smoothing,
                                      lambda1=lambda1,
                                      lambda2=lambda2).astype(int)
    elif method == 'GAC':
        tmp = tmp.astype(np.float32)
        tmp = inverse_gaussian_gradient(tmp, alpha=alpha, sigma=alpha)
        tmp = morphological_geodesic_active_contour(tmp,
                                                    iterations=iterations,
                                                    init_level_set=inits,
                                                    smoothing=smoothing,
                                                    threshold='auto',
                                                    balloon=1)
    else:
        print('Please choose a valid method!')
        return None

    # if no nodule was segmented, break
    if (len(np.unique(tmp)) == 1):
        #print('No nodule was segmented. Try changing parameters...')
        return None

    # check if leakage has occured
    #if ((tmp[0,0,0] > 0) or (tmp[0,0,-1] > 0) or (tmp[0,-1,0] > 0) or (tmp[0,-1,-1] > 0) or (tmp[-1,0,0] > 0) or (tmp[-1,-1,0] > 0) or (tmp[-1,0,-1] > 0) or (tmp[-1,-1,-1] > 0)):
    # if ((len(np.unique(tmp[0,:,:])) > 1) or (len(np.unique(tmp[:,0,:])) > 1) or (len(np.unique(tmp[:,:,0])) > 1) or
    #  (len(np.unique(tmp[-1,:,:])) > 1) or (len(np.unique(tmp[:,-1,:])) > 1) or (len(np.unique(tmp[:,:,-1])) > 1)):
    # 	print("Leakage problems? Growing reached boundaries... Discards segmentation")
    # 	return None

    # only keep segments connected to seed point (blood vessels will hopefully not be connected with nodule after level set, if leakage has occured)
    labels_tmp = label(tmp.copy())
    res = np.zeros(tmp.shape)
    if (labels_tmp[int(num_slices / 2 * res_fac), reg, reg] > 0):
        res[labels_tmp == labels_tmp[int(num_slices / 2 * res_fac), reg,
                                     reg]] = 1

    # need to transform chunk back to original size
    res = zoom(res.copy(), zoom=[1 / res_fac, 1, 1], order=1)

    # # just in case some parts are not connected anymore after interpolation -> remove not connected components
    # labels_tmp = label(res.copy())
    # res = np.zeros(res.shape)
    # if (labels_tmp[int(num_slices/2), reg, reg] > 0):
    # 	res[labels_tmp == labels_tmp[int(num_slices/2), reg, reg]] = 1

    # get the final nodule mask to the original image stack shape
    # but handle cases where seed point is selected at ends of image stack, and window is outside of range
    new_res = np.zeros(data.shape)
    if (num + int(num_slices / 2) > new_res.shape[0]):
        new_res[num - int(num_slices / 2):num + int(num_slices / 2),
                s[0] - reg:s[0] + reg, s[1] - reg:s[1] +
                reg] = res[:num + int(num_slices / 2) - new_res.shape[0]]
    elif (num - int(num_slices / 2) < 0):
        new_res[0:num + int(num_slices / 2), s[0] - reg:s[0] + reg,
                s[1] - reg:s[1] + reg] = res[:num + int(num_slices / 2)]
    else:
        new_res[num - int(np.floor(num_slices / 2)):num +
                int(np.ceil(num_slices / 2)), s[0] - reg:s[0] + reg,
                s[1] - reg:s[1] + reg] = res

    return new_res