예제 #1
0
파일: data_io.py 프로젝트: Borda/pyImSegm
def merge_image_channels(img_ch1, img_ch2, img_ch3=None):
    """ merge 2 or 3 image channels into single image

    :param ndarray img_ch1: image channel
    :param ndarray img_ch2: image channel
    :param ndarray img_ch3: image channel
    :return ndarray:

    >>> np.random.seed(0)
    >>> merge_image_channels(np.random.random((150, 125)),
    ...                      np.random.random((150, 125))).shape
    (150, 125, 3)
    >>> merge_image_channels(np.random.random((150, 125)),
    ...                      np.random.random((150, 125)),
    ...                      np.random.random((150, 125))).shape
    (150, 125, 3)
    """
    if img_ch1.ndim != 2:
        raise ImageDimensionError(
            'image as to strictly 2D and single channel, got %r' %
            img_ch1.shape)
    if img_ch1.shape != img_ch2.shape:
        raise ImageDimensionError('channel dimension has to match: %r vs %r' %
                                  (img_ch1.shape, img_ch2.shape))
    if img_ch3 is None:
        img_ch3 = np.zeros(img_ch1.shape)
    else:
        if img_ch1.shape != img_ch3.shape:
            raise ImageDimensionError(
                'channel dimension has to match: %r vs %r' %
                (img_ch1.shape, img_ch3.shape))
    img_rgb = np.rollaxis(np.array([img_ch1, img_ch2, img_ch3]), 0, 3)
    return img_rgb
예제 #2
0
def export_cut_objects(df_row,
                       path_out,
                       padding,
                       use_mask=True,
                       bg_color=None):
    """ cut and expert objects in image according given segmentation

    :param df_row:
    :param str path_out: path for exporting image
    :param int padding: set padding around segmented object
    """
    annot, _ = tl_data.load_image_2d(df_row['path_1'])
    img, name = tl_data.load_image_2d(df_row['path_2'])
    if annot.shape[:2] != img.shape[:2]:
        raise ImageDimensionError('image sizes not match %r vs %r' %
                                  (annot.shape, img.shape))

    uq_objects = np.unique(annot)
    if len(uq_objects) == 1:
        return

    for idx in uq_objects[1:]:
        img_new = tl_data.cut_object(img, annot == idx, padding, use_mask,
                                     bg_color)
        path_img = os.path.join(path_out, '%s_%i.png' % (name, idx))
        logging.debug('saving image "%s"', path_img)
        tl_data.io_imsave(path_img, img_new)
예제 #3
0
def segm_labels_assignment(segm, segm_gt):
    """ create labels assign to the particular regions

    :param ndarray segm: input segmentation
    :param ndarray segm_gt: true segmentation
    :return:

    >>> slic = np.array([[0] * 3 + [1] * 3 + [2] * 3 + [3] * 3] * 4 +
    ...                 [[4] * 3 + [5] * 3 + [6] * 3 + [7] * 3] * 4)
    >>> segm = np.zeros(slic.shape, dtype=int)
    >>> segm[4:, 6:] = 1
    >>> segm_labels_assignment(slic, segm)  # doctest: +NORMALIZE_WHITESPACE
    {0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     6: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
     7: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
    """
    if segm_gt.shape != segm.shape:
        raise ImageDimensionError('segm %r and annot %r should match' %
                                  (segm.shape, segm_gt.shape))
    labels = np.unique(segm)
    # label_hist = {}
    # for lb in labels:
    #     label_hist[lb] = (segm_gt[segm == lb].tolist())
    label_hist = {lb: [] for lb in labels}
    segm_gt_flat = segm_gt.ravel()
    segm_flat = segm.ravel()
    for i, lb in enumerate(segm_flat):
        label_hist[lb].append(segm_gt_flat[i])
    return label_hist
예제 #4
0
def load_image(path_img, img_type=TYPES_LOAD_IMAGE[0]):
    """ load image and annotation according chosen type

    :param str path_img:
    :param str img_type:
    :return ndarray:
    """
    path_img = tl_data.update_path(path_img)
    if not os.path.isfile(path_img):
        raise FileNotFoundError('missing: "%s"' % path_img)
    if img_type == '2d_split':
        img, _ = tl_data.load_img_double_band_split(path_img)
        if img.ndim != 2:
            raise ImageDimensionError('image dims: %r' % img.shape)
        # img = np.rollaxis(np.tile(img, (3, 1, 1)), 0, 3)
        # if img.max() > 1:
        #     img = (img / 255.)
    elif img_type == '2d_rgb':
        img, _ = tl_data.load_image_2d(path_img)
        # if img.max() > 1:
        #     img = (img / 255.)
    elif img_type == '2d_segm':
        img, _ = tl_data.load_image_2d(path_img)
        if img.ndim == 3:
            img = img[:, :, 0]
        if ANNOT_RELABEL_SEQUENCE:
            img, _, _ = segmentation.relabel_sequential(img)
    else:
        logging.error('not supported loading img_type: %s', img_type)
        img = None
    return img
예제 #5
0
파일: data_io.py 프로젝트: Borda/pyImSegm
def export_image(path_img, img, stretch_range=True):
    """ export an image with given path and optional pattern for image name

    :param str path_img: path to the output image
    :param ndarray img: image np.array<height, width>
    :param bool stretch_range:
    :return str: path to the image

    >>> # PNG image
    >>> np.random.seed(0)
    >>> img = np.random.random([5, 10])
    >>> path_img = export_image(os.path.join('.', 'testing-image'), img)
    >>> os.path.basename(path_img)
    'testing-image.png'
    >>> os.path.exists(path_img)
    True
    >>> im, name = load_image_2d(path_img)
    >>> im.shape
    (5, 10)
    >>> im
    array([[143, 186, 157, 141, 110, 168, 114, 232, 251,  99],
           [206, 137, 148, 241,  18,  22,   5, 216, 202, 226],
           [255, 208, 120, 203,  30, 166,  37, 246, 135, 108],
           [ 68, 201, 118, 148,   4, 160, 159, 160, 245, 177],
           [ 93, 113, 181,  15, 173, 174,  54,  33,  82,  94]], dtype=uint8)
    >>> os.remove(path_img)

    >>> # TIFF image
    >>> img = np.random.random([5, 20, 25])
    >>> path_img = export_image(os.path.join('.', 'testing-image'), img)
    >>> os.path.basename(path_img)
    'testing-image.tiff'
    >>> os.path.exists(path_img)
    True
    >>> im, name = load_image_2d(path_img)
    >>> im.shape
    (5, 20, 25)
    >>> os.remove(path_img)
    """
    if img.ndim < 2:
        raise ImageDimensionError('wrong image dim: %r' % img.shape)
    if not os.path.isdir(os.path.dirname(path_img)):
        return ''
    logging.debug(' .. saving image %r with %r to "%s"', img.shape,
                  np.unique(img), path_img)
    if img.ndim == 2 or (img.ndim == 3 and img.shape[2] == 3):
        if stretch_range and img.max() > 0:
            img = img / float(img.max()) * 255
        path_img = os.path.splitext(path_img)[0] + '.png'
        io_imsave(path_img, img.astype(np.uint8))
    elif img.ndim == 3:
        if stretch_range and img.max() > 0:
            img = img / float(img.max()) * 255**2
        path_img = os.path.splitext(path_img)[0] + '.tiff'
        io_imsave(path_img, img)
        # tif = libtiff.TIFF.open(path_img, mode='w')
        # tif.write_image(img_clip.astype(np.uint16))
    else:
        logging.warning('not supported image format: %r', img.shape)
    return path_img
예제 #6
0
def histogram_regions_labels_counts(slic, segm):
    """ histogram or overlaping region between two segmentations,
    the typical usage is label superpixel from annotation

    :param ndarray slic: input superpixel segmenatation
    :param ndarray segm: reference segmentation
    :return ndarray:

    >>> slic = np.array([[0] * 3 + [1] * 3 + [2] * 3] * 4 +
    ...                 [[4] * 3 + [5] * 3 + [6] * 3] * 4)
    >>> segm = np.zeros(slic.shape, dtype=int)
    >>> segm[4:, 5:] = 2
    >>> histogram_regions_labels_counts(slic, segm)
    array([[ 12.,   0.,   0.],
           [ 12.,   0.,   0.],
           [ 12.,   0.,   0.],
           [  0.,   0.,   0.],
           [ 12.,   0.,   0.],
           [  8.,   0.,   4.],
           [  0.,   0.,  12.]])
    """
    if slic.shape != segm.shape:
        raise ImageDimensionError('dimension does not agree')
    if np.sum(np.unique(segm) < 0) != 0:
        raise ValueError('only positive labels are allowed')
    segm_flat = slic.ravel()
    annot_flat = segm.ravel()
    idx_max = slic.max()
    label_max = segm.max()
    matrix_hist = np.zeros((idx_max + 1, label_max + 1))

    for i, lb in enumerate(segm_flat):
        matrix_hist[lb, annot_flat[i]] += 1

    return matrix_hist
def load_image_annot_compute_features_labels(idx_row,
                                             params,
                                             show_debug_imgs=SHOW_DEBUG_IMAGES
                                             ):
    """ load image and annotation, and compute superpixel features and labels

    :param (int, {...}) idx_row: row from table with paths
    :param dict params: segmentation parameters
    :param bool show_debug_imgs: whether show debug images
    :return (...):
    """
    def _path_out_img(params, dir_name, name):
        return os.path.join(params['path_exp'], dir_name, name + '.png')

    idx, row = idx_row
    idx_name = get_idx_name(idx, row['path_image'])
    img = load_image(row['path_image'], params['img_type'])
    annot = load_image(row['path_annot'], '2d_segm')
    logging.debug('.. processing: %s', idx_name)
    if img.shape[:2] != annot.shape[:2]:
        raise ImageDimensionError(
            'individual size of image %r and seg_pipe %r for "%s" - "%s"' %
            (img.shape, annot.shape, row['path_image'], row['path_annot']))
    if show_debug_imgs:
        plt.imsave(_path_out_img(params, FOLDER_IMAGE, idx_name),
                   img,
                   cmap=plt.cm.gray)
        plt.imsave(_path_out_img(params, FOLDER_ANNOT, idx_name), annot)

    # duplicate gray band to be as rgb
    # if img.ndim == 2:
    #     img = np.rollaxis(np.tile(img, (3, 1, 1)), 0, 3)
    slic = seg_spx.segment_slic_img2d(img,
                                      sp_size=params['slic_size'],
                                      relative_compact=params['slic_regul'])
    img = tl_data.convert_img_color_from_rgb(img,
                                             params.get('clr_space', 'rgb'))
    logging.debug('computed SLIC with %i labels', slic.max())
    if show_debug_imgs:
        img_rgb = use_rgb_image(img)
        img_slic = segmentation.mark_boundaries(img_rgb,
                                                slic,
                                                color=(1, 0, 0),
                                                mode='subpixel')
        plt.imsave(_path_out_img(params, FOLDER_SLIC, idx_name),
                   np.clip(img_slic, 0, 1))
    slic_label_hist = seg_label.histogram_regions_labels_norm(slic, annot)
    labels = np.argmax(slic_label_hist, axis=1)
    slic_annot = labels[slic]
    if show_debug_imgs:
        plt.imsave(_path_out_img(params, FOLDER_SLIC_ANNOT, idx_name),
                   np.clip(slic_annot, 0, slic_annot.max()))

    features, feature_names = seg_fts.compute_selected_features_img2d(
        img, slic, params['features'])
    return idx_name, img, annot, slic, features, labels, slic_label_hist, feature_names
예제 #8
0
def image_inpaint_pixels(img, valid_mask):
    if img.shape != valid_mask.shape:
        raise ImageDimensionError(
            'image size %r and mask size %r should be equal' %
            (img.shape, valid_mask.shape))
    coords = np.array(np.nonzero(valid_mask)).T
    values = img[valid_mask]
    it = interpolate.NearestNDInterpolator(coords, values)
    img_paint = it(list(np.ndindex(img.shape))).reshape(img.shape)
    return img_paint
예제 #9
0
파일: drawing.py 프로젝트: Borda/pyImSegm
def figure_image_segm_results(img,
                              seg,
                              subfig_size=9,
                              mid_labels_alpha=0.2,
                              mid_image_gray=True):
    """ creating subfigure with original image, overlapped segmentation contours
    and clean result segmentation...
    it turns the sequence in vertical / horizontal according major image dim

    :param ndarray img: image as background
    :param ndarray seg: segmentation
    :param int subfig_size: max image size
    :param fool mid_image_gray: used color image as bacround in middele
    :param float mid_labels_alpha: alpha for middle segmentation overlap
    :return Figure:

    >>> img = np.random.random((100, 150, 3))
    >>> seg = np.random.randint(0, 2, (100, 150))
    >>> fig = figure_image_segm_results(img, seg)
    >>> isinstance(fig, matplotlib.figure.Figure)
    True
    """
    if img.shape[:2] != seg.shape[:2]:
        raise ImageDimensionError('different image %r & seg_pipe %r sizes' %
                                  (img.shape, seg.shape))
    if img.ndim == 2:  # for gray images of ovary
        # img = np.rollaxis(np.tile(img, (3, 1, 1)), 0, 3)
        img = color.gray2rgb(img)

    fig, axarr = create_figure_by_image(img.shape[:2],
                                        subfig_size,
                                        nb_subfigs=3)
    axarr[0].set_title('original image')
    axarr[0].imshow(img)

    # visualise the 3th label
    axarr[1].set_title('original image w. segment overlap')
    img_bg = color.rgb2gray(img) if mid_image_gray else img
    axarr[1].imshow(img_bg, cmap=plt.cm.Greys_r)
    axarr[1].imshow(seg, alpha=mid_labels_alpha, cmap=plt.cm.jet)
    axarr[1].contour(seg, levels=np.unique(seg), linewidths=2, cmap=plt.cm.jet)

    axarr[2].set_title('segmentation - all labels')
    axarr[2].imshow(seg, cmap=plt.cm.jet)

    for ax in axarr:
        ax.axis('off')
        ax.axes.get_xaxis().set_ticklabels([])
        ax.axes.get_yaxis().set_ticklabels([])

    fig.subplots_adjust(wspace=0.01, hspace=0.01)
    fig.tight_layout()
    return fig
예제 #10
0
파일: drawing.py 프로젝트: Borda/pyImSegm
def figure_ellipse_fitting(img, seg, ellipses, centers, crits, fig_size=9):
    """ show figure with result of the ellipse fitting

    :param ndarray img: image
    :param ndarray seg: segmentation
    :param list(tuple(int,int,int,int,float)) ellipses: collection of ellipse parameters
        ell. parameters: (x, y, height, width, orientation)
    :param list(tuple(int,int)) centers: points
    :param list(float) crits:
    :param float fig_size: maximal figure size
    :return Figure:

    >>> img = np.random.random((100, 150, 3))
    >>> seg = np.random.randint(0, 2, (100, 150))
    >>> ells = np.random.random((3, 5)) * 25
    >>> centers = np.random.random((3, 2)) * 25
    >>> crits = np.random.random(3)
    >>> fig = figure_ellipse_fitting(img[:, :, 0], seg, ells, centers, crits)
    >>> isinstance(fig, matplotlib.figure.Figure)
    True
    """
    if not len(ellipses) == len(centers) == len(crits):
        raise ValueError(
            'number of ellipses (%i) and centers (%i) and criteria (%i) should match'
            % (len(ellipses), len(centers), len(crits)))

    fig, ax = create_figure_by_image(img.shape[:2], fig_size)
    if img.ndim != 2:
        raise ImageDimensionError(
            'required image dimension is 2 to instead %r' % img.shape)
    ax.imshow(img, cmap=plt.cm.Greys_r)

    for i, params in enumerate(ellipses):
        c1, c2, h, w, phi = params
        rr, cc = ellipse_perimeter(int(c1), int(c2), int(h), int(w), phi)
        ax.plot(cc,
                rr,
                '.',
                color=COLORS[i % len(COLORS)],
                label='#%i with crit=%d' % ((i + 1), int(crits[i])))
    ax.legend(loc='lower right')

    # plt.plot(centers[:, 1], centers[:, 0], 'ow')
    for i in range(len(centers)):
        ax.plot(centers[i, 1],
                centers[i, 0],
                'o',
                color=COLORS[i % len(COLORS)])
    ax.set(xlim=[0, seg.shape[1]], ylim=[seg.shape[0], 0])
    ax.axis('off')
    fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
    return fig
예제 #11
0
파일: drawing.py 프로젝트: Borda/pyImSegm
def draw_image_clusters_centers(ax,
                                img,
                                centres,
                                points=None,
                                labels_centre=None,
                                segm=None):
    """ draw imageas bacround and clusters centers

    :param ax: figure axis
    :param ndarray img: image
    :param ndarray centres: points
    :param ndarray points: optional list of all points
    :param list(int) labels_centre: optional list of labels for points
    :param ndarray segm: optional segmentation

    >>> img = np.random.randint(0, 256, (100, 100, 3))
    >>> seg = np.random.randint(0, 3, (100, 100))
    >>> centres = np.random.randint(0, 100, (3, 2))
    >>> points = np.random.randint(0, 100, (25, 2))
    >>> labels = np.random.randint(0, 4, 25)
    >>> draw_image_clusters_centers(plt.Figure().gca(), img[:, :, 0], centres, points, labels, seg)
    """
    if img is not None:
        img = (img / float(np.max(img)))
        if img.ndim != 2:
            raise ImageDimensionError(
                'required image dimension is 2 to instead %r' % img.shape)
        ax.imshow(img, cmap=plt.cm.Greys_r)
        ax.set(xlim=[0, img.shape[1]], ylim=[img.shape[0], 0])
    if segm is not None:
        ax.imshow(segm, alpha=0.1)
        ax.contour(segm)
    if points is not None and len(points) > 0 and labels_centre is not None:
        points = np.array(points)
        for i in range(max(labels_centre) + 1):
            select = points[np.asarray(labels_centre) == i]
            ax.plot(select[:, 1], select[:, 0], '.')
    # ax.plot(np.asarray(centres)[:, 1], np.asarray(centres)[:, 0], 'oy')
    # ax.plot(np.asarray(centres)[:, 1], np.asarray(centres)[:, 0], 'xr')
    if len(centres) == 0:
        return
    centres = np.asarray(centres)
    for s, clr in [
        (3e3, '#ccff33'),
        (1e3, '#ff3333'),
        (1e2, '#00ffff'),
    ]:
        ax.scatter(centres[:, 1], centres[:, 0], s=s, c=clr)
    ax.axes.get_xaxis().set_ticklabels([])
    ax.axes.get_yaxis().set_ticklabels([])
예제 #12
0
파일: data_io.py 프로젝트: Borda/pyImSegm
def load_tiff_volume_split_double_band(path_img, im_range=None):
    """ load TIFF volume  assuming that there are two bands in zip style:
    c1, c2, c1, c2, c1, ...
    and split each odd index belong to one of two bands

    :param str path_img: path to the input image
    :param float im_range: range to scale image values (1. or 255)
    :return ndarray, ndarray:

    >>> p_img = os.path.join(update_path('data-images'), 'drosophila_ovary_3D', 'AU10-13_f0011.tif')
    >>> img_b1, img_b2 = load_tiff_volume_split_double_band(p_img)
    >>> img_b1.shape, img_b2.shape
    ((15, 323, 512), (15, 323, 512))
    >>> p_img = os.path.join(update_path('data-images'), 'drosophila_ovary_slice', 'image', 'insitu7545.tif')
    >>> img_b1, img_b2 = load_tiff_volume_split_double_band(p_img)
    >>> img_b1.shape, img_b2.shape
    ((1, 647, 1024), (1, 647, 1024))
    >>> img = np.random.randint(0, 255, (1, 3, 250, 200))
    >>> p_img = './sample-multistack.tif'
    >>> io.imsave(p_img, img)
    >>> img_b1, img_b2 = load_tiff_volume_split_double_band(p_img)
    >>> img_b1.shape, img_b2.shape
    ((1, 250, 200), (1, 250, 200))
    >>> os.remove(p_img)
    """
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        img = load_image_tiff_volume(path_img, im_range)

    if img.shape[2] == 3:
        img_b1 = img[np.newaxis, ..., 0]
        img_b2 = img[np.newaxis, ..., 1]
    elif img.shape[0] == 3:
        img_b1 = img[np.newaxis, 0, ...]
        img_b2 = img[np.newaxis, 1, ...]
    else:  # true volume
        img_b1 = np.array(img[0::2])
        img_b2 = np.array(img[1::2])
        if not img_b2.size:
            # loading also 2d images with rgb bands
            if img_b1.ndim != 4:
                raise ImageDimensionError('image is not stack of RGB')
            img_b2 = np.array([img_b1[0, :, :, 1]])
            img_b1 = np.array([img_b1[0, :, :, 0]])
    if img_b1.shape[0] != img_b2.shape[0]:
        raise ValueError('not equal slice number for %r and %r' %
                         (img_b1.shape, img_b2.shape))
    return img_b1, img_b2
예제 #13
0
파일: drawing.py 프로젝트: Borda/pyImSegm
def figure_image_segm_centres(img,
                              segm,
                              centers=None,
                              cmap_contour=plt.cm.Blues):
    """ visualise the input image and segmentation in common frame

    :param ndarray img: image
    :param ndarray segm: segmentation
    :param [tuple(int,int)]|ndarray centers: or np.array
    :param obj cmap_contour:
    :return Figure:

    >>> img = np.random.random((100, 150, 3))
    >>> seg = np.random.randint(0, 2, (100, 150))
    >>> centre = [[55, 60]]
    >>> fig = figure_image_segm_centres(img, seg, centre)
    >>> isinstance(fig, matplotlib.figure.Figure)
    True
    """
    fig, ax = plt.subplots()

    ax.imshow(img)
    if np.sum(segm) > 0:
        segm_show = segm
        if segm.ndim > 2:
            segm_show = np.argmax(segm, axis=2)
        ax.contour(segm_show, cmap=cmap_contour, linewidths=0.5)
    if isinstance(centers, list):
        ax.plot(np.array(centers)[:, 1],
                np.array(centers)[:, 0],
                'o',
                color=COLOR_ORANGE)
    elif isinstance(centers, np.ndarray):
        if img.shape[:2] != centers.shape[:2]:
            raise ImageDimensionError(
                'image size %r and centers %r should match' %
                (img.shape, centers.shape))
        ax.contour(centers, levels=np.unique(centers), cmap=plt.cm.YlOrRd)

    ax.set(xlim=[0, img.shape[1]], ylim=[img.shape[0], 0])
    fig.tight_layout()

    return fig
예제 #14
0
def load_image(path_img, img_type=TYPE_LOAD_IMAGE):
    """ load image from given path according specification

    :param str path_img:
    :param str img_type:
    :return ndarray:
    """
    path_img = os.path.abspath(os.path.expanduser(path_img))
    if not os.path.isfile(path_img):
        raise FileNotFoundError('missing: "%s"' % path_img)
    if img_type == 'segm':
        img = tl_data.io_imread(path_img)
    elif img_type == '2d_struct':
        img, _ = tl_data.load_img_double_band_split(path_img)
        if img.ndim != 2:
            raise ImageDimensionError('image can be only single color')
    else:
        logging.error('not supported loading img_type: %s', img_type)
        img = tl_data.io_imread(path_img)
    logging.debug('image shape: %r, value range %f - %f', img.shape, img.min(), img.max())
    return img
예제 #15
0
def compute_metrics(row):
    """ load segmentation and compute similarity metrics

    :param dict row:
    :return {str: float}:
    """
    logging.debug('loading annot "%s"\n and segm "%s"', row['path_annot'],
                  row['path_egg-segm'])
    annot, _ = tl_data.load_image_2d(row['path_annot'])
    segm, _ = tl_data.load_image_2d(row['path_egg-segm'])
    if annot.shape != segm.shape:
        raise ImageDimensionError('dimension do mot match %r - %r' %
                                  (annot.shape, segm.shape))
    jacobs = []
    segm = seg_lbs.relabel_max_overlap_unique(annot, segm, keep_bg=True)
    for lb in np.unique(annot)[1:]:
        annot_obj = (annot == lb)
        segm_obj = (segm == lb)
        # label_hist = seg_lb.histogram_regions_labels_counts(segm, annot_obj)
        # segm_obj = np.argmax(label_hist, axis=1)[segm]
        sum_or = np.sum(np.logical_or(annot_obj, segm_obj))
        jaccoby = np.sum(np.logical_and(annot_obj, segm_obj)) / float(sum_or)
        jacobs.append(jaccoby)
    if not jacobs:
        jacobs.append(0)

    # avg_weight = 'samples' if len(np.unique(annot)) > 2 else 'binary'
    y_true, y_pred = annot.ravel(), segm.ravel()
    dict_eval = {
        'name': os.path.basename(row['path_annot']),
        'ARS': metrics.adjusted_rand_score(y_true, y_pred),
        'Jaccard': np.mean(jacobs),
        'f1': metrics.f1_score(y_true, y_pred, average='micro'),
        'accuracy': metrics.accuracy_score(y_true, y_pred),
        'precision': metrics.precision_score(y_true, y_pred, average='micro'),
        'recall': metrics.recall_score(y_true, y_pred, average='micro'),
    }

    return dict_eval
예제 #16
0
def estim_points_compute_features(name, img, segm, params):
    """ determine points (center candidates) using slic
    and for each compute feature vector with their names

    :param str name:
    :param ndarray img:
    :param ndarray segm:
    :param {str: any} params:
    :return (str, ndarray, [(int, int)], [[float]], list(str)):
    """
    # superpixels on image
    if img.shape[:2] != segm.shape[:2]:
        raise ImageDimensionError('not matching shapes: %r : %r' %
                                  (img.shape, segm.shape))
    slic = seg_spx.segment_slic_img2d(img, params['slic_size'],
                                      params['slic_regul'])
    slic_centers = seg_spx.superpixel_centers(slic)
    # slic_edges = seg_spx.make_graph_segm_connect_grid2d_conn4(slic)

    features, feature_names = compute_points_features(segm, slic_centers,
                                                      params)

    return name, slic, slic_centers, features, feature_names
예제 #17
0
def histogram_regions_labels_norm(slic, segm):
    """ normalised histogram or overlapping region between two segmentation,
    the typical usage is label superpixel from annotation - relative overlap

    :param ndarray slic: input superpixel segmentation
    :param ndarray segm: reference segmentation
    :return ndarray:

    >>> slic = np.array([[0] * 3 + [1] * 3 + [2] * 3] * 4 +
    ...                 [[4] * 3 + [5] * 3 + [6] * 3] * 4)
    >>> segm = np.zeros(slic.shape, dtype=int)
    >>> segm[4:, 5:] = 2
    >>> histogram_regions_labels_norm(slic, segm)  # doctest: +ELLIPSIS
    array([[ 1.        ,  0.        ,  0.        ],
           [ 1.        ,  0.        ,  0.        ],
           [ 1.        ,  0.        ,  0.        ],
           [ 0.        ,  0.        ,  0.        ],
           [ 1.        ,  0.        ,  0.        ],
           [ 0.66666667,  0.        ,  0.33333333],
           [ 0.        ,  0.        ,  1.        ]])
    """
    if slic.shape != segm.shape:
        raise ImageDimensionError(
            'dimension of SLIC %r and segm %r should match' %
            (slic.shape, segm.shape))
    if np.sum(np.unique(segm) < 0) != 0:
        raise ValueError('only positive labels are allowed')
    matrix_hist = histogram_regions_labels_counts(slic, segm)
    region_sums = np.tile(np.sum(matrix_hist, axis=1),
                          (matrix_hist.shape[1], 1)).T
    # prevent dividing by 0
    region_sums[region_sums == 0] = -1.
    matrix_hist = (matrix_hist / region_sums)
    matrix_hist = np.nan_to_num(matrix_hist)
    # preventing negative zeros
    matrix_hist[matrix_hist == 0] = 0
    return matrix_hist
예제 #18
0
def compute_labels_overlap_matrix(seg1, seg2):
    """ compute overlap between tho segmentation atlasess) with same sizes

    :param ndarray seg1: np.array<height, width>
    :param ndarray seg2: np.array<height, width>
    :return ndarray: np.array<height, width>

    >>> seg1 = np.zeros((7, 15), dtype=int)
    >>> seg1[1:4, 5:10] = 3
    >>> seg1[5:7, 6:13] = 2
    >>> seg2 = np.zeros((7, 15), dtype=int)
    >>> seg2[2:5, 7:12] = 1
    >>> seg2[4:7, 7:14] = 3
    >>> compute_labels_overlap_matrix(seg1, seg1)
    array([[76,  0,  0,  0],
           [ 0,  0,  0,  0],
           [ 0,  0, 14,  0],
           [ 0,  0,  0, 15]])
    >>> compute_labels_overlap_matrix(seg1, seg2)
    array([[63,  4,  0,  9],
           [ 0,  0,  0,  0],
           [ 2,  0,  0, 12],
           [ 9,  6,  0,  0]])
    """
    logging.debug('computing overlap of two seg_pipe of shapes %r <-> %r',
                  seg1.shape, seg2.shape)
    if seg1.shape != seg2.shape:
        raise ImageDimensionError('segm %r and segm %r should match' %
                                  (seg1.shape, seg2.shape))
    maxims = [np.max(seg1) + 1, np.max(seg2) + 1]
    overlap = np.zeros(maxims, dtype=int)
    for lb1, lb2 in zip(seg1.ravel(), seg2.ravel()):
        if lb1 >= 0 and lb2 >= 0:
            overlap[lb1, lb2] += 1
    # logging.debug(res)
    return overlap
예제 #19
0
def compute_boundary_distances(segm_ref, segm):
    """ compute distances among boundaries of two segmentation

    :param ndarray segm_ref: reference segmentation
    :param ndarray segm: input segmentation
    :return ndarray:

    >>> segm_ref = np.zeros((6, 10), dtype=int)
    >>> segm_ref[3:4, 4:5] = 1
    >>> segm = np.zeros((6, 10), dtype=int)
    >>> segm[:, 2:9] = 1
    >>> pts, dist = compute_boundary_distances(segm_ref, segm)
    >>> pts
    array([[2, 4],
           [3, 3],
           [3, 4],
           [3, 5],
           [4, 4]])
    >>> dist.tolist()
    [2.0, 1.0, 2.0, 3.0, 2.0]
    """
    if segm_ref.shape != segm.shape:
        raise ImageDimensionError('Ref. segm %r and segm %r should match' %
                                  (segm_ref.shape, segm.shape))
    grid_y, grid_x = np.meshgrid(range(segm_ref.shape[1]),
                                 range(segm_ref.shape[0]))
    segr_boundary = sk_segm.find_boundaries(segm_ref, mode='thick')
    points = np.array(
        [grid_x[segr_boundary].ravel(), grid_y[segr_boundary].ravel()]).T
    segm_boundary = sk_segm.find_boundaries(segm, mode='thick')
    segm_distance = ndimage.distance_transform_edt(~segm_boundary)
    dist = segm_distance[segr_boundary].ravel()

    if len(points) != len(dist):
        raise ValueError('number of points and distances should be equal')
    return points, dist
예제 #20
0
def wrapper_compute_color2d_slic_features_labels(img_annot, sp_size, sp_regul,
                                                 dict_features, label_purity):
    img, annot = img_annot
    # in case of binary annotation convert it to integers labels
    annot = annot.astype(int)
    if img.shape[:2] != annot.shape[:2]:
        raise ImageDimensionError('image %r and annot %r should match' %
                                  (img.shape, annot.shape))
    slic, features = compute_color2d_superpixels_features(img,
                                                          dict_features,
                                                          sp_size=sp_size,
                                                          sp_regul=sp_regul)
    neg_label = np.max(annot) + 1 if np.sum(annot < 0) > 0 else None
    if neg_label is not None:
        annot[annot < 0] = neg_label

    label_hist = histogram_regions_labels_norm(slic, annot)
    labels = np.argmax(label_hist, axis=1)
    purity = np.max(label_hist, axis=1)

    if neg_label is not None:
        labels[labels == neg_label] = -1
    labels[purity < label_purity] = -1
    return slic, features, labels
예제 #21
0
파일: drawing.py 프로젝트: Borda/pyImSegm
def figure_segm_boundary_dist(segm_ref, segm, subfig_size=9):
    """ visualise the boundary distances between two segmentation

    :param ndarray segm_ref: reference segmentation
    :param ndarray segm: estimated segmentation
    :param int subfig_size: maximal sub-figure size
    :return Figure:

    >>> seg = np.zeros((100, 100))
    >>> seg[35:80, 10:65] = 1
    >>> fig = figure_segm_boundary_dist(seg, seg.T)
    >>> isinstance(fig, matplotlib.figure.Figure)
    True
    """
    if segm_ref.shape != segm.shape:
        raise ImageDimensionError('ref segm %r and segm %r should match' %
                                  (segm_ref.shape, segm.shape))
    segr_boundary = segmentation.find_boundaries(segm_ref, mode='thick')
    segm_boundary = segmentation.find_boundaries(segm, mode='thick')
    segm_distance = ndimage.distance_transform_edt(~segm_boundary)

    norm_size = np.array(segm_ref.shape[:2]) / float(np.max(segm_ref.shape))
    fig_size = norm_size[::-1] * subfig_size * np.array([2, 1])
    fig, axarr = plt.subplots(ncols=2, figsize=fig_size)

    axarr[0].set_title('boundary distances with reference contour')
    im = axarr[0].imshow(segm_distance, cmap=plt.cm.Greys)
    plt.colorbar(im, ax=axarr[0])
    axarr[0].contour(segm_ref, cmap=plt.cm.jet)

    segm_distance[~segr_boundary] = 0
    axarr[1].set_title('distance projected to ref. boundary')
    im = axarr[1].imshow(segm_distance, cmap=plt.cm.Reds)
    plt.colorbar(im, ax=axarr[1])

    return fig
예제 #22
0
def relabel_max_overlap_merge(seg_ref, seg_relabel, keep_bg=False):
    """ relabel the second segmentation cu that maximise relative overlap
    for each pattern (object), if one pattern in reference atlas is likely
    composed from multiple patterns in relabel atlas, it merge them

    .. note:: it skips background class - 0

    :param ndarray seg_ref: reference segmentation
    :param ndarray seg_relabel: segmentation for relabeling
    :param bool keep_bg: the label 0 holds
    :return ndarray: resulting segentation

    >>> atlas1 = np.zeros((7, 15), dtype=int)
    >>> atlas1[1:4, 5:10] = 1
    >>> atlas1[5:7, 3:13] = 2
    >>> atlas2 = np.zeros((7, 15), dtype=int)
    >>> atlas2[0:3, 7:12] = 1
    >>> atlas2[3:7, 1:7] = 2
    >>> atlas2[4:7, 7:14] = 3
    >>> atlas2[:2, :3] = 5
    >>> relabel_max_overlap_merge(atlas1, atlas2, keep_bg=True)
    array([[1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0]])
    >>> relabel_max_overlap_merge(atlas2, atlas1, keep_bg=True)
    array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0],
           [0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0]])
    >>> relabel_max_overlap_merge(atlas1, atlas2, keep_bg=False)
    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, 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, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0]])
    """
    if seg_ref.shape != seg_relabel.shape:
        raise ImageDimensionError('Ref. segm %r and segm %r should match' %
                                  (seg_ref.shape, seg_relabel.shape))
    overlap = compute_labels_overlap_matrix(seg_ref, seg_relabel)
    # ref_ptn_size = np.bincount(seg_ref.ravel())
    # overlap = overlap.astype(float) / np.tile(ref_ptn_size, (overlap.shape[1], 1)).T
    # overlap = np.nan_to_num(overlap)
    max_axis = 1 if overlap.shape[0] > overlap.shape[1] else 0
    if keep_bg:
        id_max = np.argmax(overlap[1:, 1:], axis=max_axis) + 1
        lut = np.array([0] + id_max.tolist())
    else:
        lut = np.argmax(overlap, axis=max_axis)
    # in case there is no overlap
    ptn_sum = np.sum(overlap, axis=0)
    if 0 in ptn_sum:
        lut[ptn_sum == 0] = np.arange(len(lut))[ptn_sum == 0]
    seg_new = lut[seg_relabel].astype(int)
    # hold all negative labels
    seg_new[seg_relabel < 0] = seg_relabel[seg_relabel < 0]
    return seg_new
예제 #23
0
def image_segmentation(idx_row, params, debug_export=DEBUG_EXPORT):
    """ image segmentation which prepare inputs (imsegm, centres)
    and perform segmentation of various imsegm methods

    :param (int, str) idx_row: input image and centres
    :param dict params: segmentation parameters
    :return str: image name
    """
    _, row_path = idx_row
    for k in dict(row_path):
        if isinstance(k, str) and k.startswith('path_'):
            row_path[k] = tl_data.update_path(row_path[k], absolute=True)
    logging.debug('segmenting image: "%s"', row_path['path_image'])
    name = os.path.splitext(os.path.basename(row_path['path_image']))[0]

    img = load_image(row_path['path_image'])
    # make the image like RGB
    img_rgb = np.rollaxis(np.tile(img, (3, 1, 1)), 0, 3)
    seg = load_image(row_path['path_segm'], 'segm')
    if img_rgb.shape[:2] != seg.shape:
        raise ImageDimensionError('image %r and segm %r do not match' % (img_rgb.shape[:2], seg.shape))
    if not os.path.isfile(row_path['path_centers']):
        logging.warning('no center was detected for "%s"', name)
        return name
    centers = tl_data.load_landmarks_csv(row_path['path_centers'])
    centers = tl_data.swap_coord_x_y(centers)
    if not list(centers):
        logging.warning('no center was detected for "%s"', name)
        return name
    # img = seg / float(seg.max())
    slic = seg_spx.segment_slic_img2d(img_rgb, sp_size=params['slic_size'], relative_compact=params['slic_regul'])

    path_segm = os.path.join(params['path_exp'], 'input', name + '.png')
    export_draw_image_segm(path_segm, img_rgb, segm_obj=seg, centers=centers)

    seg_simple = simplify_segm_3cls(seg)
    path_segm = os.path.join(params['path_exp'], 'simple', name + '.png')
    export_draw_image_segm(path_segm, seg_simple - 1.)

    dict_segment = create_dict_segmentation(params, slic, seg, img, centers)

    image_name = name + '.png'
    centre_name = name + '.csv'

    # iterate over segmentation methods and perform segmentation on this image
    for method in dict_segment:
        (fn, args) = dict_segment[method]
        logging.debug(' -> %s on "%s"', method, name)
        path_dir = os.path.join(params['path_exp'], method)  # n.split('_')[0]
        path_segm = os.path.join(path_dir, image_name)
        path_centre = os.path.join(path_dir + DIR_CENTRE_POSIX, centre_name)
        path_fig = os.path.join(path_dir + DIR_VISUAL_POSIX, image_name)
        path_debug = os.path.join(path_dir + DIR_DEBUG_POSIX, name)
        # assuming that segmentation may fail
        try:
            t = time.time()
            if debug_export and 'rg2sp' in method:
                os.mkdir(path_debug)
                segm_obj, centers, dict_export = fn(*args, debug_export=path_debug)
            else:
                segm_obj, centers, dict_export = fn(*args)

            # also export ellipse params here or inside the segm fn
            if dict_export is not None:
                for k, v in dict_export.items():
                    export_partial(k, v, path_dir, name)

            logging.info('running time of %r on image "%s" is %d s', fn.__name__, image_name, time.time() - t)
            tl_data.io_imsave(path_segm, segm_obj.astype(np.uint8))
            export_draw_image_segm(path_fig, img_rgb, seg, segm_obj, centers)
            # export also centers
            centers = tl_data.swap_coord_x_y(centers)
            tl_data.save_landmarks_csv(path_centre, centers)
        except Exception:
            logging.exception('segment fail for "%s" via %s', name, method)

    return name
예제 #24
0
파일: drawing.py 프로젝트: Borda/pyImSegm
def draw_image_segm_points(
    ax,
    img,
    points,
    labels=None,
    slic=None,
    color_slic='w',
    lut_label_marker=DICT_LABEL_MARKER,
    seg_contour=None,
):
    """ on plane draw background image or segmentation, overlap with SLIC
     contours, add contour of adative segmentation like annot. for centers
     plot point with specific property (shape and colour) according label

    :param ax: figure axis
    :param ndarray img: image
    :param list(tuple(int,int)) points: collection of points
    :param list(int) labels: LUT labels for superpixels
    :param ndarray slic: superpixel segmentation
    :param str color_slic: color dor superpixels
    :param dict lut_label_marker: dictionary {int: (str, str)} of label and markers
    :param ndarray seg_contour: segmentation contour

    >>> img = np.random.randint(0, 256, (100, 100))
    >>> points = np.random.randint(0, 100, (25, 2))
    >>> labels = np.random.randint(0, 5, len(points))
    >>> slic = np.random.randint(0, 256, (100, 100))
    >>> draw_image_segm_points(plt.Figure().gca(), img, points, labels, slic)
    """
    # background image or segmentation
    if img.ndim == 2:
        ax.imshow(img, alpha=0.3, cmap=plt.cm.gist_earth)
    else:
        ax.imshow(img)

    if slic is not None:
        ax.contour(slic,
                   levels=np.unique(slic),
                   alpha=0.5,
                   colors=color_slic,
                   linewidths=0.5)
    # fig.gca().imshow(mark_boundaries(img, slic))
    if seg_contour is not None and isinstance(seg_contour, np.ndarray):
        if img.shape[:2] != seg_contour.shape[:2]:
            raise ImageDimensionError(
                'image size %r and segm. %r should match' %
                (img.shape, seg_contour.shape))
        ax.contour(seg_contour, linewidths=3, levels=np.unique(seg_contour))
    if labels is not None:
        if len(points) != len(labels):
            raise ValueError(
                'number of points (%i) and labels (%i) should match' %
                (len(points), len(labels)))
        for lb in lut_label_marker:
            marker, clr = lut_label_marker[lb]
            ax.plot(points[(labels == lb), 1],
                    points[(labels == lb), 0],
                    marker,
                    color=clr)
    else:
        ax.plot(points[:, 1], points[:, 0], 'o', color=COLOR_ORANGE)
    ax.set(xlim=[0, img.shape[1]], ylim=[img.shape[0], 0])
예제 #25
0
def relabel_max_overlap_unique(seg_ref, seg_relabel, keep_bg=False):
    """ relabel the second segmentation cu that maximise relative overlap
    for each pattern (object), the relation among patterns is 1-1

    .. note:: it skips background class - 0

    :param ndarray seg_ref: reference segmentation
    :param ndarray seg_relabel: segmentation for relabeling
    :param bool keep_bg: keep the background
    :return ndarray: resulting segentation

    >>> atlas1 = np.zeros((7, 15), dtype=int)
    >>> atlas1[1:4, 5:10] = 1
    >>> atlas1[5:7, 3:13] = 2
    >>> atlas2 = np.zeros((7, 15), dtype=int)
    >>> atlas2[0:3, 7:12] = 1
    >>> atlas2[3:7, 1:7] = 2
    >>> atlas2[4:7, 7:14] = 3
    >>> atlas2[:2, :3] = 5
    >>> relabel_max_overlap_unique(atlas1, atlas2, keep_bg=True)
    array([[5, 5, 5, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [5, 5, 5, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0]])
    >>> relabel_max_overlap_unique(atlas2, atlas1, keep_bg=True)
    array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0],
           [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0]])
    >>> relabel_max_overlap_unique(atlas1, atlas2, keep_bg=False)
    array([[5, 5, 5, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [5, 5, 5, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
           [0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0],
           [0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0]])
    >>> atlas2[0, 0] = -1
    >>> relabel_max_overlap_unique(atlas1, atlas2, keep_bg=True)
    array([[-1,  5,  5,  0,  0,  0,  0,  1,  1,  1,  1,  1,  0,  0,  0],
           [ 5,  5,  5,  0,  0,  0,  0,  1,  1,  1,  1,  1,  0,  0,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  0,  0,  0],
           [ 0,  3,  3,  3,  3,  3,  3,  0,  0,  0,  0,  0,  0,  0,  0],
           [ 0,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,  2,  0],
           [ 0,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,  2,  0],
           [ 0,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,  2,  0]])
    """
    if seg_ref.shape != seg_relabel.shape:
        raise ImageDimensionError(
            'Reference segm. %r and input segm. %r should match' %
            (seg_ref.shape, seg_relabel.shape))
    overlap = compute_labels_overlap_matrix(seg_ref, seg_relabel)

    lut = [-1] * (np.max(seg_relabel) + 1)
    if keep_bg:  # keep the background label
        lut[0] = 0
        overlap[0, :] = 0
        overlap[:, 0] = 0
    # select always the maximal value and reset it
    for i in range(max(overlap.shape) + 1):
        if np.sum(overlap) == 0:
            break
        lb_ref, lb_est = np.argwhere(overlap.max() == overlap)[0]
        lut[lb_est] = lb_ref
        overlap[lb_ref, :] = 0
        overlap[:, lb_est] = 0
    # fill all not used by its equal idx it is not used yet
    for i, lb in enumerate(lut):
        if lb == -1 and i not in lut:
            lut[i] = i
    # fill by any unused yet
    for i, lb in enumerate(lut):
        if lb > -1:
            continue
        for j in range(len(lut)):
            if j not in lut:
                lut[i] = j
    # lut[lut == -1] = 0

    seg_new = np.array(lut)[seg_relabel].astype(int)
    # hold all negative labels
    seg_new[seg_relabel < 0] = seg_relabel[seg_relabel < 0]
    return seg_new