Beispiel #1
0
def show_validation(M, nb_it, PY_before, PY_after):
    '''
    Plots the landmarks corresponding to the mean shape in the model coordinate frame
    and the landmarks corresponding to the current points in the model coordinate frame
    @param M:               the model for the tooth
    @param nb_it:           the number of this iteration
    @param PY_before:       the current points for the target tooth before validation
                            in the model coordinate frame
    @param PY_after:        the current points for the target tooth after validation
                            in the model coordinate frame
    '''
    mxs, mys = mu.extract_coordinates(M)
    mxs = mu.make_circular(mxs)
    mys = mu.make_circular(mys)
    rxs, rys = mu.extract_coordinates(PY_before)
    rxs = mu.make_circular(rxs)
    rys = mu.make_circular(rys)
    gxs, gys = mu.extract_coordinates(PY_after)
    gxs = mu.make_circular(gxs)
    gys = mu.make_circular(gys)

    pyplot.figure(1)
    # x coordinates , y coordinates
    pyplot.plot(mxs, mys, '-+b')
    pyplot.plot(rxs, rys, '-+r')
    pyplot.plot(gxs, gys, '-+g')
    txt = 'Model Coordinate Frame - Iteration: ' + str(nb_it)
    pyplot.title(txt)
    pyplot.xlabel('x\'')
    pyplot.ylabel('y\'')
    pyplot.gca().invert_yaxis()
    pyplot.axis('equal')
    pyplot.show()
Beispiel #2
0
def mark_results(img,
                 PS,
                 color_lines=np.array(
                     [np.array([0, 0, 255]),
                      np.array([0, 255, 0])]),
                 color_init=np.array([0, 255, 255]),
                 color_mid=np.array([255, 0, 255]),
                 color_end=np.array([255, 255, 0])):
    for p in range(PS.shape[0]):
        pxs, pys = mu.extract_coordinates(PS[p, :])
        for k in range(c.get_nb_landmarks()):
            px = int(pxs[k])
            py = int(pys[k])
            if (k == c.get_nb_landmarks() - 1):
                px_succ = int(pxs[0])
                py_succ = int(pys[0])
            else:
                px_succ = int(pxs[(k + 1)])
                py_succ = int(pys[(k + 1)])
            cv2.line(img, (px, py), (px_succ, py_succ), color_lines[p])

    for p in range(PS.shape[0]):
        pxs, pys = mu.extract_coordinates(PS[p, :])
        for k in range(c.get_nb_landmarks()):
            px = int(pxs[k])
            py = int(pys[k])
            if (k == 0):
                img[py, px] = color_init
            elif (k == c.get_nb_landmarks() - 1):
                img[py, px] = color_end
            else:
                img[py, px] = color_mid
    return img
Beispiel #3
0
def create_landmarks_and_models_images(color_init=np.array([0,255,255]), color_mid=np.array([255,0,255]), color_end=np.array([255,255,0]), color_line=np.array([0,0,255]), color_model_line=np.array([255,0,0]), method=''):
    '''
    Stores all the preprocessed images corresponding to the given method with the landmarks
    of the training samples and models (transformed to the image coordinate system) marked.
    @param color_init:  the BGR color for the first landmark 
    @param color_mid:   the BGR color for all landmarks except the first and last landmark
    @param color_end:   the BGR color for the last landmark
    @param color_line:  the BGR color for the line between two consecutive landmarks of the training samples
    @param color_model_line:    the BGR color for the line between two consecutive landmarks of the models
    @param method:      the method used for preproccesing
    '''
    for i in c.get_trainingSamples_range():
        fname = c.get_fname_vis_pre(i, method)
        img = cv2.imread(fname)
        for j in range(c.get_nb_teeth()):
            xs, ys = mu.extract_coordinates(XS[j,(i-1),:])
            mxs, mys = mu.extract_coordinates(mu.full_align_with(MS[j], XS[j,(i-1),:]))
            
            for k in range(c.get_nb_landmarks()):
                x = int(xs[k] - offsetX)
                y = int(ys[k] - offsetY)
                mx = int(mxs[k] - offsetX)
                my = int(mys[k] - offsetY)
                if (k == c.get_nb_landmarks()-1):
                    x_succ = int(xs[0] - offsetX)
                    y_succ = int(ys[0] - offsetY)
                    mx_succ = int(mxs[0] - offsetX)
                    my_succ = int(mys[0] - offsetY)
                else:
                    x_succ = int(xs[(k+1)] - offsetX)
                    y_succ = int(ys[(k+1)] - offsetY)
                    mx_succ = int(mxs[(k+1)] - offsetX)
                    my_succ = int(mys[(k+1)] - offsetY)
                cv2.line(img, (x,y), (x_succ,y_succ), color_line)
                cv2.line(img, (mx,my), (mx_succ,my_succ), color_model_line)
          
            for k in range(c.get_nb_landmarks()):
                x = int(xs[k] - offsetX)
                y = int(ys[k] - offsetY)
                mx = int(mxs[k] - offsetX)
                my = int(mys[k] - offsetY)
                if (k == 0):
                    img[y,x] = color_init
                    img[my,mx] = color_init
                elif (k == c.get_nb_landmarks()-1):
                    img[y,x] = color_end
                    img[my,mx] = color_end
                else:
                    img[y,x] = color_mid
                    img[my,mx] = color_mid
                
            fname = c.get_fname_vis_ff_landmarks_and_models(i, method)
            cv2.imwrite(fname, img) 
Beispiel #4
0
def classify_positives(method=''):
    XS = l.create_full_XS()
    
    for s in c.get_trainingSamples_range():
        trainingSamples = c.get_trainingSamples_range()
        trainingSamples.remove(s)
        try:
            info_name_upper = c.get_dir_prefix() + 'data/Visualizations/Classified Samples/info' + method + str(s) + '-u' + '.txt' 
            info_name_lower = c.get_dir_prefix() + 'data/Visualizations/Classified Samples/info' + method + str(s) + '-l' + '.txt' 

            info_file_upper = open(info_name_upper, "w")
            info_file_lower = open(info_name_lower, "w")
            
            for i in trainingSamples:
                
                s = ''    
                if (i < 10):
                    s = '0'
                img_name = s + str(i) + '.png'
                
                min_y = min_x = float("inf")
                max_y = max_x = 0

                for j in range(0, c.get_nb_teeth()/2):
                    x_coords, y_coords = mu.extract_coordinates(XS[j, i-1, :])
                    for k in range(c.get_nb_landmarks()):
                        if x_coords[k] < min_x: min_x = x_coords[k]
                        if x_coords[k] > max_x: max_x = x_coords[k]
                        if y_coords[k] < min_y: min_y = y_coords[k]
                        if y_coords[k] > max_y: max_y = y_coords[k]
                
                line = 'rawdata/' + method + img_name + ' 1 ' + str(int(min_x - fu.offsetX)) + ' ' + str(int(min_y - fu.offsetY)) + ' ' + str(int(max_x - min_x)) + ' ' + str(int(max_y - min_y)) + '\n' 
                info_file_upper.write(line)
                
                min_y = min_x = float("inf")
                max_y = max_x = 0
                
                for j in range(c.get_nb_teeth()/2, c.get_nb_teeth()):
                    x_coords, y_coords = mu.extract_coordinates(XS[j, i-1, :])
                    for k in range(c.get_nb_landmarks()):
                        if x_coords[k] < min_x: min_x = x_coords[k]
                        if x_coords[k] > max_x: max_x = x_coords[k]
                        if y_coords[k] < min_y: min_y = y_coords[k]
                        if y_coords[k] > max_y: max_y = y_coords[k] 
                
                line = 'rawdata/' + method + img_name + ' 1 ' + str(int(min_x - fu.offsetX)) + ' ' + str(int(min_y - fu.offsetY)) + ' ' + str(int(max_x - min_x)) + ' ' + str(int(max_y - min_y)) + '\n' 
                info_file_lower.write(line) 

        finally:
            info_file_upper.close()
            info_file_lower.close()
def plot_landmarks(X, nr_trainingSample=1, nr_tooth=1, closed_curve=False):
    '''
    Plots the landmarks corresponding to the given training sample
    in the image coordinate frame.
    @param X:                   the training samples
    @param nr_trainingSample:   the training sample to select
    @param nr_tooth:            the number of the tooth
                                (just used for the title of the plot)
    @param closed_curve:        must the curve be closed
    '''
    xCoords, yCoords = mu.extract_coordinates(X[(nr_trainingSample - 1), :])
    if (closed_curve):
        xCoords = mu.make_circular(xCoords)
        yCoords = mu.make_circular(yCoords)

    pyplot.figure(1)
    pyplot.title('Training sample nr: ' + str(nr_trainingSample) + ' | ' +
                 'Tooth nr: ' + str(nr_tooth))
    # x coordinates , y coordinates
    pyplot.plot(xCoords, yCoords, '-+r')
    pyplot.gca().invert_yaxis()
    pyplot.axis('equal')
    pyplot.xlabel('x')
    pyplot.ylabel('y')
    pyplot.show()
def store_plotted_landmarks(closed_curve=False):
    '''
    Stores the plots of the landmarks corresponding to all the
    training samples and all the teeth in the image coordinate frame
    in seperate plots for each training sample, for each tooth.
    The scale and limits of the axes are the same for each plot.
    @param closed_curve:        must the curve be closed
    '''
    XS = l.create_full_XS()
    (ymin, ymax, xmin, xmax) = pre.learn_offsets_safe(XS)

    for j in range(XS.shape[0]):
        for i in range(XS.shape[1]):
            xCoords, yCoords = mu.extract_coordinates(XS[j, i, :])
            if (closed_curve):
                xCoords = mu.make_circular(xCoords)
                yCoords = mu.make_circular(yCoords)

            pyplot.figure()
            pyplot.title('Training sample nr: ' + str((i + 1)) + ' | ' +
                         'Tooth nr: ' + str((j + 1)))
            # x coordinates , y coordinates
            pyplot.plot(xCoords, yCoords, '-+r')
            pyplot.axis([xmin, xmax, ymin, ymax])
            pyplot.gca().set_aspect('equal', adjustable='box')
            pyplot.xlabel('x')
            pyplot.ylabel('y')
            pyplot.gca().invert_yaxis()
            fname = c.get_fname_vis_landmark((i + 1), (j + 1))
            pyplot.savefig(fname, bbox_inches='tight')
            #You get a runtime warning if you open more than 20 figures
            #Closing comes with a performance penalty
            pyplot.close()
Beispiel #7
0
def learn_offsets(XS):
    '''
    Learns the minimal y coordinate, maximal y coordinate, 
    minimal x coordinate and maximal x coordinate for the
    training samples.
    @param  XS:                 the samples with dimensions:
                                (nb_teeth x nb_trainingSamples x nb_dim)
    @return the learned minimal y coordinate, learned maximal y coordinate, 
            learned minimal x coordinate and learned maximal x coordinate for the
            training samples.
    '''
    xmin = ymin = float("inf")
    xmax = ymax = 0
    for j in range(XS.shape[0]):
        for i in range(XS.shape[1]):
            xCoords, yCoords = mu.extract_coordinates(XS[j,i,:])
            #looping twice with one check has approximately same
            #complexity as looping once with two checks
            xmin = min(xmin, np.amin(xCoords))
            xmax = max(xmax, np.amax(xCoords))
            #looping twice with one check has approximately same
            #complexity as looping once with two checks
            ymin = min(ymin, np.amin(yCoords))
            ymax = max(ymax, np.amax(yCoords))
    return (ymin, ymax, xmin, xmax)
def limit(img, P):
    pxs, pys = mu.extract_coordinates(P)
    pys[pys < 0] = 0
    pys[pys >= img.shape[0]] = img.shape[0] - 1
    pxs[pxs < 0] = 0
    pxs[pxs >= img.shape[1]] = img.shape[1] - 1
    P = mu.zip_coordinates(pxs, pys)
    return P
def store_plotted_vary_mode_param(closed_curve=False):
    '''
    Stores the plots of the effects of varying one mode parameter / shape parameter
    in -3 s.d. / -2 s.d. / -1 s.d. / M / +1 s.d. / +2 s.d. / +3 s.d.
    in the model coordinate frame for all the teeth.
    @param closed_curve:        must the curve be closed
    '''
    for t in c.get_teeth_range():
        M, Y = pa.PA(l.create_full_X(nr_tooth=t))
        E, W, MU = pca.pca_percentage(Y)

        YS = np.zeros(np.array([E.shape[0], 7, c.get_nb_dim()]))
        for e in range(YS.shape[0]):
            se = math.sqrt(E[e])
            C = np.zeros(W.shape[1])
            C[e] = 1
            j = 0
            for i in range(-3, 4):
                YS[e, j, :] = pca.reconstruct(W, C * i * se, M)
                j += 1
        (ymin, ymax, xmin, xmax) = pre.learn_offsets(YS)
        ymin -= 0.01
        ymax += 0.01
        xmin -= 0.01
        xmax += 0.01

        for e in range(YS.shape[0]):
            pyplot.figure()
            for j in range(1, YS.shape[1] + 1):
                xCoords, yCoords = mu.extract_coordinates(YS[e, (j - 1), :])
                if (closed_curve):
                    xCoords = mu.make_circular(xCoords)
                    yCoords = mu.make_circular(yCoords)
                # x coordinates , y coordinates
                pyplot.subplot(1, 7, j)
                pyplot.plot(xCoords, yCoords, '-+r')
                pyplot.axis([xmin, xmax, ymin, ymax])
                pyplot.gca().set_aspect('equal', adjustable='box')
                if j == 1:
                    pyplot.ylabel('y\'')
                if j == 4:
                    pyplot.title('Tooth nr: ' + str(t) + ' | ' +
                                 'Eigenvalue: ' + str(E[e]))
                    pyplot.xlabel('x\'')
                frame = pyplot.gca()
                if j % 2 == 0:
                    frame.axes.get_xaxis().set_ticks([])
                else:
                    frame.axes.get_xaxis().set_ticks([xmin, xmax])
                pyplot.gca().invert_yaxis()
                #pyplot.subplots_adjust(right=1)
            fname = c.get_fname_vis_pca(t, (e + 1))
            pyplot.savefig(fname, bbox_inches='tight')
            pyplot.close()
Beispiel #10
0
def create_profile_normals_images(k=5, color_init=np.array([0,255,255]), color_mid=np.array([255,0,255]), color_end=np.array([255,255,0]), color_line=np.array([255,0,0]), color_profile_point=np.array([0,255,0]), method=''):
    '''
    Stores all the preprocessed images corresponding to the given method with the landmarks
    of the models (transformed to the image coordinate system) and the points along the profile
    normals marked.
    @param k:                       the number of profile points along either side
                                    of the profile normal and profile tangent
    @param color_init:              the BGR color for the first landmark 
    @param color_mid:               the BGR color for all landmarks except the first and last landmark
    @param color_end:               the BGR color for the last landmark
    @param color_line:              the BGR color for the line between two consecutive landmarks
    @param color_profile_point:     the BGR color for the profile points
    @param method:                  the method used for preproccesing
    '''
    for i in c.get_trainingSamples_range():
        fname = c.get_fname_vis_pre(i, method)
        img = cv2.imread(fname)
        for j in range(c.get_nb_teeth()):
            xs, ys = mu.extract_coordinates(mu.full_align_with(MS[j], XS[j,(i-1),:]))
            
            for h in range(c.get_nb_landmarks()):
                x = int(xs[h] - offsetX)
                y = int(ys[h] - offsetY)
                if (h == c.get_nb_landmarks()-1):
                    x_succ = int(xs[0] - offsetX)
                    y_succ = int(ys[0] - offsetY)
                else:
                    x_succ = int(xs[(h+1)] - offsetX)
                    y_succ = int(ys[(h+1)] - offsetY)
                cv2.line(img, (x,y), (x_succ,y_succ), color_line)
          
            for h in range(c.get_nb_landmarks()):
                x = int(xs[h] - offsetX)
                y = int(ys[h] - offsetY)
                tx, ty, nx, ny = ff.create_ricos(img, h, xs, ys)
                for n in range(-k, k+1):
                    kx = round(x + n * nx)
                    ky = round(y + n * ny)
                    img[ky, kx] = color_profile_point
                for t in range(-k, k+1):
                    kx = round(x + t * tx)
                    ky = round(y + t * ty)
                    img[ky, kx] = color_profile_point
                
                if (h == 0):
                    img[y,x] = color_init
                elif (h == c.get_nb_landmarks()-1):
                    img[y,x] = color_end
                else:
                    img[y,x] = color_mid
                
            fname = c.get_fname_vis_ff_profile_normals(i, method)
            cv2.imwrite(fname, img) 
Beispiel #11
0
def create_negatives(method=''):
    XS = l.create_full_XS()
    
    for i in c.get_trainingSamples_range():
        fname = c.get_fname_vis_pre(i, method)
        img = cv2.imread(fname)
        
        s = ''    
        if (i < 10): s = '0'
        
        min_y = min_x = float("inf")
        max_y = max_x = 0
    
        for j in range(0, c.get_nb_teeth()/2):
            x_coords, y_coords = mu.extract_coordinates(XS[j, i-1, :])
            for k in range(c.get_nb_landmarks()):
                if x_coords[k] < min_x: min_x = x_coords[k]
                if x_coords[k] > max_x: max_x = x_coords[k]
                if y_coords[k] < min_y: min_y = y_coords[k]
                if y_coords[k] > max_y: max_y = y_coords[k]
        
        fname = c.get_dir_prefix() + 'data/Visualizations/Classified Samples/' + method + str(s) + str(i) + '-l' + '.png'    
        cv2.imwrite(fname, img[max_y-fu.offsetY+1:,:])
        
        min_y = min_x = float("inf")
        max_y = max_x = 0
        
        for j in range(c.get_nb_teeth()/2, c.get_nb_teeth()):
            x_coords, y_coords = mu.extract_coordinates(XS[j, i-1, :])
            for k in range(c.get_nb_landmarks()):
                if x_coords[k] < min_x: min_x = x_coords[k]
                if x_coords[k] > max_x: max_x = x_coords[k]
                if y_coords[k] < min_y: min_y = y_coords[k]
                if y_coords[k] > max_y: max_y = y_coords[k] 
        
        fname = c.get_dir_prefix() + 'data/Visualizations/Classified Samples/' + method + str(s) + str(i) + '-u' + '.png'    
        cv2.imwrite(fname, img[:min_y-fu.offsetY,:])
def display_landmarks(X, nr_trainingSample=1, color=np.array([0, 0, 255])):
    '''
    Displays the landmarks corresponding to the given training sample
    on the corresponding radiograph in the image coordinate frame.
    @param X:                   the training samples
    @param nr_trainingSample:   the training sample to select
    @param color:               the color used for displaying
    '''
    img = cv2.imread(c.get_fname_radiograph(nr_trainingSample))

    xCoords, yCoords = mu.extract_coordinates(X[(nr_trainingSample - 1), :])
    for i in range(c.get_nb_landmarks()):
        #y coordinate , x coordinate
        img[yCoords[i], xCoords[i], :] = color
    #cv2.imshow("test", img)
    # writing instead of showing, because the displayed image is too large
    cv2.imwrite("test.tif", img)
Beispiel #13
0
def create_individual_bboxes(method=''):
    XS = l.create_full_XS()
    IBS = np.zeros((XS.shape[0], XS.shape[1], 4))
    for j in range(XS.shape[0]):
        for i in range(XS.shape[1]):
            min_y = min_x = float("inf")
            max_y = max_x = 0
            x_coords, y_coords = mu.extract_coordinates(XS[j,i,:])
            for k in range(x_coords.shape[0]):
                if x_coords[k] < min_x: min_x = x_coords[k]
                if x_coords[k] > max_x: max_x = x_coords[k]
                if y_coords[k] < min_y: min_y = y_coords[k]
                if y_coords[k] > max_y: max_y = y_coords[k]
            IBS[j,i,0] = min_x - fu.offsetX
            IBS[j,i,1] = max_x - fu.offsetX
            IBS[j,i,2] = min_y - fu.offsetY
            IBS[j,i,3] = max_y - fu.offsetY
    return IBS 
def create_partial_GS(trainingSamples,
                      XS,
                      MS,
                      level=0,
                      offsetX=0,
                      offsetY=0,
                      k=5,
                      method=''):
    '''
    Creates the matrix GNS which contains for each tooth, for each of the given training samples,
    for each landmark, a normalized sample (along the profile normal through the landmarks).
    Creates the matrix GTS which contains for each tooth, for each of the given training samples,
    for each landmark, a normalized sample (along the profile tangent through the landmarks).
    @param trainingSamples: the number of the training samples (not the test training samples!)
    @param XS:              contains for each tooth, for each training sample, all landmarks (in the image coordinate frame)
    @param MS:              contains for each tooth, the tooth model (in the model coordinate frame)
    @param level:           the current level
    @param offsetX:         the possible offset in x direction (used when working with cropped images and non-cropped landmarks)
    @param offsetY:         the possible offset in y direction (used when working with cropped images and non-cropped landmarks)
    @param k:               the number of pixels to sample either side for each of the model points along the profile normal
    @param method:          the method used for preprocessing
    @return The matrix GNS which contains for each tooth, for each of the given training samples,
            for each landmark, a normalized sample (along the profile normal through that landmark).
            The matrix GTS which contains for each tooth, for each of the given training samples,
            for each landmark, a normalized sample (along the profile tangent through that landmark).
    '''
    GNS = np.zeros((c.get_nb_teeth(), len(trainingSamples),
                    c.get_nb_landmarks(), 2 * k + 1))
    GTS = np.zeros((c.get_nb_teeth(), len(trainingSamples),
                    c.get_nb_landmarks(), 2 * k + 1))
    for j in range(c.get_nb_teeth()):
        index = 0
        for i in trainingSamples:
            # model of tooth j from model coordinate frame to image coordinate frame
            xs, ys = mu.extract_coordinates(
                mu.full_align_with(MS[j], XS[j, index, :]))
            fname = c.get_fname_vis_pre(i, method)
            img = cv2.imread(fname)
            pyramid = gip.get_gaussian_pyramid_at(img, level)
            GN, GT = create_G(pyramid, k, xs, ys, offsetX, offsetY)
            GNS[j, index, :] = GN
            GTS[j, index, :] = GT
            index += 1
    return GNS, GTS
Beispiel #15
0
def multi_resolution_search(img,
                            P,
                            tooth_index,
                            fitting_function=1,
                            show=False):
    '''
    Fits the tooth corresponding to the given tooth index in the given image.
    @param img:                 the image  
    @param P:                   the start points for the target tooth
    @param tooth_index:         the index of the the target tooth (used in MS, EWS, fs)
    @param fitting_function:    the fitting function used
                                * 0: fitting function along profile normal through landmark +
                                     fitting function along profile gradient through landmark
                                * 1: fitting function along profile normal through landmark
                                * 2: fitting function along profile gradient through landmark
    @param show:                must the intermediate results (after each iteration) be displayed
    @return The fitted points for the tooth corresponding to the given tooth index
            and the number of iterations used.
    '''
    nb_it = 0
    level = max_level
    pyramids = gip.get_gaussian_pyramids(img, level)

    # Compute model point positions in image at coarsest level
    P = np.around(np.divide(P, 2**level))

    while (level >= 0):
        nb_it += 1
        pxs, pys = mu.extract_coordinates(P)
        for i in range(c.get_nb_landmarks()):
            tx, ty, nx, ny = ff.create_ricos(pyramids[level], i, pxs, pys)
            f_optimal = float("inf")

            if (fitting_function == 0):
                rn = rt = range(-(m - k), (m - k) + 1)
            elif (fitting_function == 1):
                rn = range(-(m - k), (m - k) + 1)
                rt = [0]
            else:
                rn = [0]
                rt = range(-(m - k), (m - k) + 1)

            for n in rn:
                for t in rt:
                    x = round(pxs[i] + n * nx + t * tx)
                    y = round(pys[i] + n * ny + t * ty)
                    try:
                        fn = fns[level][tooth_index][i](ff.normalize_Gi(
                            ff.create_Gi(pyramids[level], k, x, y, nx, ny)))
                        ft = fts[level][tooth_index][i](ff.normalize_Gi(
                            ff.create_Gi(pyramids[level], k, x, y, tx, ty)))
                    except (IndexError):
                        continue
                    f = fu.evaluate_fitting(fn=fn,
                                            ft=ft,
                                            fitting_function=fitting_function)
                    if f < f_optimal:
                        f_optimal = f
                        cx = x
                        cy = y
            pxs[i] = cx
            pys[i] = cy

        P_new = validate(pyramids[level], tooth_index,
                         mu.zip_coordinates(pxs, pys), nb_it, show)
        nb_close_points = nb_closest_points(P, P_new)
        P = P_new

        # Repeat unless more than pclose of the points are found close to the current position
        # or nmax iterations have been applied at this resolution
        print 'Level:' + str(level) + ', Iteration: ' + str(
            nb_it) + ', Ratio: ' + str(
                (2 * nb_close_points / float(P.shape[0])))

        converged = (2 * nb_close_points / float(P.shape[0]) >= pclose)
        if (converged or nb_it >= max_it):
            if (level > 0):
                level -= 1
                nb_it = 0
                P = P * 2
            else:
                break

    return P