示例#1
0
def do_epoch(model, data, vocab):
    before_loss = 0
    all_losses = []
    current_loss = 0
    for epoch in range(1, hparam.steps + 1):
        label_name, label_tensor, line, line_tensor = randomTrainingPair(
            data, vocab)
        output, loss = train(model, label_tensor, line_tensor)
        current_loss += loss
        # Print epoch number, loss, name and guess
        if epoch % hparam.every == 0:
            ll = itos(line, vocab)
            guess, guess_i = categoryFromOutput(vocab, output)
            correct = '✓' if guess == label_name else '✗ (%s)' % label_name
            logger.info(
                'step:%d %.2f%% loss: %.4f %s / %s %s' %
                (epoch, epoch / hparam.steps * 100, loss, ll, guess, correct))
            if loss < before_loss or before_loss == 0:
                ckpt = "model_%s_%.4f.pt" % (epoch, loss)
                utils.save(logger, model.rnn, hparam.save_train, ckpt)
                before_loss = loss

            all_losses.append(current_loss / hparam.every)
            current_loss = 0
            f = open(os.path.join(hparam.save_train,
                                  '%s_loss.json' % hparam.loss),
                     'w',
                     encoding='utf-8')
            json.dump(all_losses, f, indent=0)
示例#2
0
def make_data(vocab, path, flag):
    label_lines = {}
    logger.info("process %s data..." % flag)
    logger.info("load vocab...size: [%s]" % vocab.size)
    for id, filename in enumerate(
            utils.findFiles(os.path.join(path, hparam.files))):
        logger.info("proess file: %s" % filename)
        category = filename.split('/')[-1].split('.')[0]
        lines = utils.readLines(filename)
        new_lines = []
        for id in tqdm(range(len(lines))):
            line = lines[id]
            if flag == "train":
                if len(line) < hparam.max_length:
                    chars_id = utils.stoi(line, vocab)
                    new_lines.append(chars_id)
                    label_lines[category] = new_lines
                else:
                    logger.info("not proess: %s" % len(line))
            else:
                chars_id = utils.stoi(line, vocab)
                new_lines.append(chars_id)
                label_lines[category] = new_lines

    logger.info("process: %s size: [%s]" % (category, len(new_lines)))
    data = utils.data(label_lines)
    utils.save(logger, data, hparam.save_data, "%s.pt" % flag)
示例#3
0
def load_vocab():

    if os.path.exists(hparam.vocab):
        vocab = torch.load(hparam.vocab)
    else:
        logger.info("make vocab")
        vocab, label = make_vocab()
        if not os.path.exists(hparam.save_data):
            os.mkdir(hparam.save_data)
        vocab_path = os.path.join(hparam.save_data, "vocab.txt")
        with codecs.open(vocab_path, 'w', encoding="utf-8") as f:
            f.write(utils.UNK + "\n")
            for char in vocab:
                f.write(char + "\n")
        vocab = utils.readLines(vocab_path)
        logger.info("load vocab...size: [%s]" % len(vocab))
        vocab = utils.vocabulary(label, vocab)
        utils.save(logger, vocab, hparam.save_data, "vocab.pt")

    return vocab
示例#4
0
def deblur_module(pic,
                  filename,
                  dest_path,
                  blur_width,
                  confidence=10,
                  bias=1e-4,
                  step=1e-3,
                  bits=8,
                  iterations=200,
                  sharpness=0,
                  mask=None,
                  display=True,
                  neighbours=8,
                  correlation=False):
    """
    API to call the debluring process

    :param pic: an image memory object, from PIL or tifffile
    :param filename: string, the name of the file to save
    :param dest_path: string, the path where to save the file
    :param blur_width: integer, the diameter of the blur e.g. the size of the PSF
    :param confidence: float, default 1, max 100, set the confidence you have in your sample. For example, on noisy pictures, 
    use 1 to 10. For a clean low-ISO picture, you can go all the way to 100. A low factor will reduce the convergence, a high 
    factor will allow more noise amplification.
    :param bias: float, the blending parameter between sharp and blurred pictures. Ensure    the convergence of the sharp image.
    Usually between 0.0001 and 0.1
    :param step: float, the gradient-descent factor. Normal is 1e-3. Increase it to converge faster, but be careful because
    it could diverge more as well.
    :param bits: integer, default is 8 meaning the input image is encoded with 8 bits/channel. Use 16 if you input 16 bits
    tiff files.
    :param iterations: float, default is 1, meaning that the base number of iterations to perform are the width of the blur.
    While this works in most cases, complicated blurs need extra care. Set it > 1 in conjunction with a smaller step
    when more iterations are needed.
    :param mask: list of 4 integers, the region on which the blur will be estimated to speed-up the process.
    :param neighbours: set the number of pixels used to compute the total variation regularization. 2 is fast but will over-smooth
    and thus reduce the convergence. 4 is good but might blur the edges, 8 is better but slower and might be too permissive in some cases. 
    With 8, you might want to decrease the confidence factor.
    :param sharpness: this applies a final unsharp mask using the input picture as the blurry version and the deconvoluted picture as the sharp one. Put 0
    to disable this feature, and 1 to apply it full throttle.

    :param display: Pop-up a control window at the end of the blur estimation to check the solution before runing it on
    the whole picture
    :return:
    """
    # TODO : refocus http://web.media.mit.edu/~bandy/refocus/PG07refocus.pdf
    # TODO : extract foreground only https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_grabcut/py_grabcut.html#grabcut

    pic = np.ascontiguousarray(pic, dtype=np.float32)

    # Load the FFTW wisdom if the file exists
    if isfile('fftw_wisdom.pickle'):
        with open('fftw_wisdom.pickle', 'rb') as f:
            pyfftw.import_wisdom(pickle.load(f))
            print("FFT profiles loaded")
    else:
        print(
            "No FFT profiles detected. They will be created at the end of the session"
        )

    # Verifications
    if blur_width < 3:
        raise ValueError("The blur width should be at least 3 pixels.")

    if blur_width % 2 == 0:
        raise ValueError("The blur width should be odd. You can use %i." %
                         (blur_width + 1))

    if confidence > 100:
        raise ValueError(
            "The confidence factor is limited to 100. Try increasing the bias instead"
        )

    # Set the bit-depth
    samples = 2**bits - 1

    # Rescale the RGB values between 0 and 1
    pic = pic / samples

    # Make the picture dimensions odd to avoid ringing on the border of even pictures. We just replicate the last row/column
    odd_vert = False
    odd_hor = False

    if pic.shape[0] % 2 == 0:
        pic = pad_image(pic, ((1, 0), (0, 0))).astype(np.float32)
        odd_vert = True
        print("Padded vertically")

    if pic.shape[1] % 2 == 0:
        pic = pad_image(pic, ((0, 0), (1, 0))).astype(np.float32)
        odd_hor = True
        print("Padded horizontally")

    # Construct a blank PSF
    psf = utils.uniform_kernel(blur_width)
    psf = np.dstack((psf, psf, psf))

    # Get the dimensions once for all
    MK = blur_width
    M = pic.shape[0]
    N = pic.shape[1]
    C = pic.shape[2]

    # Rescale the lambda parameter
    confidence = 1000 * confidence

    # Set the error
    epsilon = dc.best_param(pic, confidence, M, N)

    print("\n===== BLIND ESTIMATION OF BLUR =====")

    # Construct the mask for the blur estimation
    if mask:
        # Check the mask size
        if ((mask[1] - mask[0]) % 2 == 0 or (mask[3] - mask[2]) % 2 == 0):
            raise ValueError(
                "The mask dimensions should be odd. You could use at least %i×%i pixels."
                % (blur_width + 2, blur_width + 2))

        u_masked = pic[mask[0]:mask[1], mask[2]:mask[3], ...].copy()
        i_masked = pic[mask[0]:mask[1], mask[2]:mask[3], ...]

    else:
        u_masked = pic.copy()
        i_masked = pic

    # Build the intermediate sizes and factors
    images, kernels, lambdas = build_pyramid(MK, confidence)
    k_prec = MK
    for i, k, l in zip(reversed(images), reversed(kernels), reversed(lambdas)):
        print("======== Pyramid step %1.3f ========" % i)

        # Resize blured, deblured images and PSF from previous step
        if i != 1:
            im = ndimage.zoom(i_masked, (i, i, 1)).astype(np.float32)
        else:
            im = i_masked

        psf = ndimage.zoom(psf, (k / k_prec, k / k_prec, 1)).astype(np.float32)
        dc.normalize_kernel(psf, k)

        u_masked = ndimage.zoom(u_masked, (im.shape[0] / u_masked.shape[0],
                                           im.shape[1] / u_masked.shape[1], 1))

        vert_odd = False
        hor_odd = False

        # Pad to ensure oddity
        if pic.shape[0] % 2 == 0:
            hor_odd = True
            im = pad_image(im, ((1, 0), (0, 0))).astype(np.float32)
            u_masked = pad_image(u_masked, ((1, 0), (0, 0))).astype(np.float32)
            print("Padded vertically")

        if pic.shape[1] % 2 == 0:
            vert_odd = True
            im = pad_image(im, ((0, 0), (1, 0))).astype(np.float32)
            u_masked = pad_image(u_masked, ((0, 0), (1, 0))).astype(np.float32)
            print("Padded horizontally")

        # Pad for FFT
        pad = np.floor(k / 2).astype(int)
        u_masked = pad_image(u_masked, (pad, pad))

        # Make a blind Richardson-Lucy deconvolution on the RGB signal
        dc.richardson_lucy_MM(im,
                              u_masked,
                              psf,
                              epsilon,
                              im.shape[0],
                              im.shape[1],
                              3,
                              k,
                              int(iterations / i),
                              step,
                              l,
                              epsilon,
                              neighbours,
                              blind=True,
                              correlation=correlation)

        # Unpad FFT because this image is resized/reused the next step
        u_masked = u_masked[pad:-pad, pad:-pad, ...]

        # Unpad oddity for same reasons
        if vert_odd:
            u_masked = u_masked[1:, :, ...]

        if hor_odd:
            u_masked = u_masked[:, 1:, ...]

        k_prec = k

    # Display the control preview
    if display:
        psf_check = (psf - np.amin(psf))
        psf_check = psf_check / np.amax(psf_check)
        plt.imshow(psf_check,
                   interpolation="lanczos",
                   filternorm=1,
                   aspect="equal",
                   vmin=0,
                   vmax=1)
        plt.show()
        plt.imshow(u_masked,
                   interpolation="lanczos",
                   filternorm=1,
                   aspect="equal",
                   vmin=0,
                   vmax=1)
        plt.show()

    print("\n===== REGULAR DECONVOLUTION =====")

    u = pic.copy()

    # Build the intermediate sizes and factors
    k_prec = MK
    for i, k, l in zip(reversed(images), reversed(kernels), reversed(lambdas)):
        print("======== Pyramid step %1.3f ========" % i)

        # Resize blured, deblured images and PSF from previous step
        if i != 1:
            im = ndimage.zoom(pic, (i, i, 1)).astype(np.float32)
            psf_loc = ndimage.zoom(psf, (k / k_prec, k / k_prec, 1)).astype(
                np.float32)
            dc.normalize_kernel(psf_loc, k)
        else:
            im = pic
            psf_loc = psf

        u = ndimage.zoom(
            u, (im.shape[0] / u.shape[0], im.shape[1] / u.shape[1], 1))

        vert_odd = False
        hor_odd = False

        # Pad to ensure oddity
        if pic.shape[0] % 2 == 0:
            hor_odd = True
            im = pad_image(im, ((1, 0), (0, 0))).astype(np.float32)
            u = pad_image(u, ((1, 0), (0, 0))).astype(np.float32)
            print("Padded vertically")

        if pic.shape[1] % 2 == 0:
            vert_odd = True
            im = pad_image(im, ((0, 0), (1, 0))).astype(np.float32)
            u = pad_image(u, ((0, 0), (1, 0))).astype(np.float32)
            print("Padded horizontally")

        # Pad for FFT
        pad = np.floor(k / 2).astype(int)
        u = pad_image(u, (pad, pad))

        # Make a non-blind Richardson-Lucy deconvolution on the RGB signal
        dc.richardson_lucy_MM(im,
                              u,
                              psf_loc,
                              bias,
                              im.shape[0],
                              im.shape[1],
                              3,
                              k,
                              int(iterations / i),
                              step,
                              l,
                              epsilon,
                              neighbours,
                              blind=False)

        # Unpad FFT because this image is resized/reused the next step
        u = u[pad:-pad, pad:-pad, ...]

        # Unpad oddity for same reasons
        if vert_odd:
            u = u[1:, :, ...]

        if hor_odd:
            u = u[:, 1:, ...]
    """ 
            
    # Pad to ensure oddity
    if pic.shape[0] % 2 == 0:
        hor_odd = True
        im = pad_image(im, ((1, 0), (0, 0))).astype(np.float32)
        u = pad_image(u, ((1, 0), (0, 0))).astype(np.float32)
        print("Padded vertically")

    if pic.shape[1] % 2 == 0:
        vert_odd = True
        im = pad_image(im, ((0, 0), (1, 0))).astype(np.float32)
        u = pad_image(u, ((0, 0), (1, 0))).astype(np.float32)
        print("Padded horizontally")
        
    # Pad for FFT
    pad = np.floor(MK / 2).astype(int)
    u = pad_image(u, (pad, pad))
        
    # Make a non-blind Richardson-Lucy deconvolution on the RGB signal
    dc.richardson_lucy_MM(pic, u, psf, bias, M, N, 3, k, iterations, step, l, epsilon, neighbours, blind=False)

    # Unpad FFT because this image is resized/reused the next step
    u = u[pad:-pad, pad:-pad, ...]

    # Unpad oddity for same reasons
    if vert_odd:
        u = u[1:, :, ...]

    if hor_odd:
        u = u[:, 1:, ...]
    """

    # Unsharp mask to boost a bit the sharpness
    u = (1 + sharpness) * u - sharpness * pic

    # if the picture has been padded to make it odd, unpad it to get the original size
    if odd_hor:
        u = u[:, 1:, ...]
    if odd_vert:
        u = u[1:, :, ...]

    # Clip extreme values
    np.clip(u, 0, 1, out=u)

    # Convert to 16 bits RGB
    u = u * (2**16 - 1)

    # Save the pic
    utils.save(u, filename, dest_path)

    # Save the FFTW wisdom for later use
    with open('fftw_wisdom.pickle', 'wb') as f:
        pickle.dump(pyfftw.export_wisdom(), f)
def deblur_module(pic,
                  filename,
                  dest_path,
                  blur_width,
                  confidence=10,
                  tolerance=1,
                  quality="normal",
                  bits=8,
                  mask=None,
                  display=True,
                  blur="static",
                  preview=False,
                  p=1,
                  order=2,
                  norm=1,
                  priority=0,
                  mask_size=255,
                  iterations=200,
                  refocus=False):
    """
    API to call the debluring process

    :param pic: an image memory object, from PIL or tifffile
    :param filename: string, the name of the file to save
    :param dest_path: string, the path where to save the file
    :param blur_width: integer, the diameter of the blur e.g. the size of the PSF
    :param confidence: float, default 1, max 100, set the confidence you have in your sample. For example, on noisy pictures,
    use 1 to 10. For a clean low-ISO picture, you can go all the way to 100. A low factor will reduce the convergence, a high
    factor will allow more noise amplification.
    :param tolerance: float, between 0 and 100. The amount of error you can accept in the solution in %.
    :param bits: integer, default is 8 meaning the input image is encoded with 8 bits/channel. Use 16 if you input 16 bits
    tiff files.
    :param mask: list of 2 integers, the center of th region on which the blur will be estimated to speed-up the process.
    :param display: Pop-up a control window at the end of the blur estimation to check the solution before runing it on
    the whole picture
    :param p: float, the power of the Total Variation used to regularize the deblurring. Set > 2 to increase the convergence rate but this might favor the blurry picture as well.
    It will be refined during the process anyway.
    :return:
    """
    # TODO : refocus http://web.media.mit.edu/~bandy/refocus/PG07refocus.pdf
    # TODO : extract foreground only https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_grabcut/py_grabcut.html#grabcut

    pic = np.ascontiguousarray(pic, dtype=np.float32)

    # Extrapad for safety
    pic = pad_image(pic, (1, 1)).astype(np.float32)

    # Set the bit-depth
    samples = 2**bits - 1

    # Rescale the RGB values between 0 and 1
    pic = pic / samples

    # Undo the gamma corrector
    pic = pic**(1 / 2.2)

    # Map the quality to gradient descent step
    if quality == "normal":
        step = 1e-3
    elif quality == "high":
        step = 5e-4
    elif quality == "veryhigh":
        step = 1e-4
    elif quality == "low":
        step = 5e-3

    # Blur verifications
    if blur_width < 3:
        raise ValueError("The blur width should be at least 3 pixels.")
    elif blur_width % 2 == 0:
        raise ValueError("The blur width should be odd. You can use %i." %
                         (blur_width + 1))

    #TODO : automatically evaluate blur size : https://www.researchgate.net/publication/257069815_Blind_Deconvolution_of_Blurred_Images_with_Fuzzy_Size_Detection_of_Point_Spread_Function

    # Get the dimensions once for all
    MK = blur_width  # PSF size
    M = pic.shape[0]  # Image height
    N = pic.shape[1]  # Image width
    C = 3  # RGB channels

    # Define a minimum mask size for the blind deconvolution
    if mask is None:
        # By default, set the mask in the center of the picture
        mask = [M // 2, N // 2]

    # Create the coordinates of the masking box
    top = mask[0] - mask_size // 2
    bottom = mask[0] + mask_size // 2
    left = mask[1] - mask_size // 2
    right = mask[1] + mask_size // 2

    print("Mask size :", (bottom - top + 1), "×", (right - left + 1))

    if top > 0 and bottom < M and left > 0 and right < N:
        pass
    else:
        raise ValueError(
            "The mask is outside the picture boundaries. Move its center inside or reduce the blur size."
        )

    # Adjust the blur type.
    # For motion blur, we enforce the RGB of the PSF to have the same coefficients
    # This is to help the solver converge.
    if blur == "static":
        correlation = False
    elif blur == "motion":
        correlation = True

    # Rescale the tolerance
    tolerance /= 100.

    # Make the picture dimensions odd to avoid ringing on the border of even pictures. We just replicate the last row/column
    odd_vert = False
    odd_hor = False

    if pic.shape[0] % 2 == 0:
        pic = pad_image(pic, ((1, 0), (0, 0))).astype(np.float32)
        odd_vert = True
        print("Padded vertically")

    if pic.shape[1] % 2 == 0:
        pic = pad_image(pic, ((0, 0), (1, 0))).astype(np.float32)
        odd_hor = True
        print("Padded horizontally")

    # Construct a uniform PSF : ones everywhere
    psf = utils.uniform_kernel(blur_width)
    psf = np.dstack((psf, psf, psf))

    # Build the pyramid
    images, kernels = build_pyramid(blur_width, confidence)

    # Convergence flag
    # When a pyramid step does not converge inside the amount of iterations fixed
    # it's usually because the blur size is ill-chosen
    # so the convergence flag is raised and the deconvolution is stopped
    convergence_flag = False

    try:

        # Launch the pyramid deconvolution
        for case in ["blind", "non-blind"]:
            print("\n===== %s DECONVOLUTION =====" % case)

            deblured_image = pic.copy()

            # The algorithm is designed to make lambda converge no matter what
            # But a kick on the right path is nice
            lambd = confidence * 1000
            p_temp = p
            norm_temp = norm

            for i, k in zip(reversed(images), reversed(kernels)):
                # Still not sure if we should process the pyramid in a non-blind setup
                if True:  #case == "blind" or i == 1.:
                    print("======== Pyramid step %1.3f ========" % i)

                    # Compute the new sizes of the mask
                    temp_top = int(i * top)
                    temp_bottom = int(i * bottom)
                    temp_left = int(i * left)
                    temp_right = int(i * right)

                    # Make sure the mask dimensions will be odd and square
                    if int(temp_bottom - temp_top) % 2 == 0:
                        if int(temp_bottom - temp_top) < int(temp_right -
                                                             temp_left):
                            temp_bottom += 1
                        elif int(temp_bottom - temp_top) > int(temp_right -
                                                               temp_left):
                            temp_top += 1
                        else:
                            temp_top -= 1

                    if int(temp_right - temp_left) % 2 == 0:
                        if int(temp_bottom - temp_top) < int(temp_right -
                                                             temp_left):
                            temp_left += 1
                        elif int(temp_bottom - temp_top) > int(temp_bottom -
                                                               temp_top):
                            temp_right += 1
                        else:
                            temp_right -= -1

                    # Compute the new size of the picture
                    temp_width = int(np.floor(i * N))
                    temp_height = int(np.floor(i * M))

                    # Ensure oddity on the picture
                    if temp_width % 2 == 0:
                        temp_width += 1
                    if temp_height % 2 == 0:
                        temp_height += 1

                    shape = (temp_height, temp_width, 3)

                    # Resize blured, deblured images and PSF from previous step
                    temp_blurry_image = resize(pic,
                                               shape,
                                               order=3,
                                               mode="edge",
                                               preserve_range=True).astype(
                                                   np.float32)
                    deblured_image = resize(deblured_image,
                                            shape,
                                            order=3,
                                            mode="edge",
                                            preserve_range=True).astype(
                                                np.float32)

                    if case == "blind":
                        psf_copy = resize(psf, (k, k, 3),
                                          order=3,
                                          mode="edge",
                                          preserve_range=True).astype(
                                              np.float32)
                        dc.normalize_kernel(psf_copy, k)
                    else:
                        psf_copy = psf.copy()
                        k = kernels[0]

                    # Extra safety padding - Remember the gradient is not evaluated on borders
                    temp_blurry_image = pad_image(temp_blurry_image,
                                                  (1, 1)).astype(np.float32)
                    deblured_image = pad_image(deblured_image,
                                               (1, 1)).astype(np.float32)

                    # Pad for FFT
                    pad = int(np.floor(k / 2))

                    # Debug
                    print("Image size", temp_blurry_image.shape)
                    print("u size", deblured_image.shape)
                    print("Mask size", (temp_bottom - temp_top),
                          (temp_right - temp_left))
                    print("PSF size", psf_copy.shape)

                    # Disallow tolerance on lower pyramid sizes
                    # This because creating noise while downscaled will result in smudges when upscaled
                    if i == 1.:
                        tolerance_temp = tolerance
                    else:
                        tolerance_temp = 0

                    # Make a blind Richardson-Lucy deconvolution on the RGB signal
                    if case == "blind":
                        deblured_image[temp_top - 1:temp_bottom + 1,
                                       temp_left - 1:temp_right + 1,
                                       ...] = dc.richardson_lucy_MM(
                                           temp_blurry_image[temp_top -
                                                             1:temp_bottom + 1,
                                                             temp_left -
                                                             1:temp_right + 1,
                                                             ...],
                                           deblured_image[temp_top - pad -
                                                          1:temp_bottom + pad +
                                                          1, temp_left - pad -
                                                          1:temp_right + pad +
                                                          1, ...],
                                           psf_copy,
                                           pad + 1,
                                           temp_bottom - temp_top - pad - 1,
                                           pad + 1,
                                           temp_bottom - temp_top - pad - 1,
                                           0,
                                           temp_bottom - temp_top + 2,
                                           temp_right - temp_left + 2,
                                           3,
                                           k,
                                           iterations,
                                           step,
                                           lambd,
                                           blind=True,
                                           p=p_temp,
                                           correlation=correlation,
                                           order=order,
                                           norm=2,
                                           priority=0,
                                           refocus=refocus)
                        # Update the PSF
                        psf = psf_copy.copy()

                    elif case != "blind" and preview:
                        deblured_image[temp_top - 1:temp_bottom + 1,
                                       temp_left - 1:temp_right + 1,
                                       ...] = dc.richardson_lucy_MM(
                                           temp_blurry_image[temp_top -
                                                             1:temp_bottom + 1,
                                                             temp_left -
                                                             1:temp_right + 1,
                                                             ...],
                                           deblured_image[temp_top - pad -
                                                          1:temp_bottom + pad +
                                                          1, temp_left - pad -
                                                          1:temp_right + pad +
                                                          1, ...],
                                           psf_copy,
                                           pad + 1,
                                           temp_bottom - temp_top - pad - 1,
                                           pad + 1,
                                           temp_bottom - temp_top - pad - 1,
                                           tolerance_temp,
                                           temp_bottom - temp_top + 2,
                                           temp_right - temp_left + 2,
                                           3,
                                           k,
                                           iterations,
                                           step,
                                           lambd,
                                           blind=False,
                                           p=p_temp,
                                           order=order,
                                           norm=2,
                                           priority=priority,
                                           refocus=refocus)
                    else:
                        # Pad for FFT
                        deblured_image = pad_image(
                            deblured_image, (pad, pad)).astype(np.float32)
                        deblured_image[pad:-pad, pad:-pad,
                                       ...] = dc.richardson_lucy_MM(
                                           temp_blurry_image,
                                           deblured_image,
                                           psf_copy,
                                           pad + 1,
                                           temp_bottom - temp_top - pad - 1,
                                           pad + 1,
                                           temp_bottom - temp_top - pad - 1,
                                           tolerance_temp,
                                           temp_height + 2,
                                           temp_width + 2,
                                           3,
                                           k,
                                           iterations,
                                           step,
                                           lambd,
                                           blind=False,
                                           p=p_temp,
                                           order=order,
                                           norm=2,
                                           priority=priority,
                                           refocus=refocus)

                        # Unpad FFT because this image is resized/reused the next step
                        deblured_image = deblured_image[pad:-pad, pad:-pad,
                                                        ...]

                    if convergence_flag:
                        raise RuntimeError(
                            "The optimization didn't converge. It usually means your blur size is ill-chosen."
                        )

                    # Remove the extra safety padding
                    temp_blurry_image = temp_blurry_image[1:-1, 1:-1, ...]
                    deblured_image = deblured_image[1:-1, 1:-1, ...]

                    # Update the norm
                    norm_temp /= 2

                    k_prec = k

            # Display the control preview
            if display and case == "blind":
                psf_check = (psf - np.amin(psf)) / (np.amax(psf) -
                                                    np.amin(psf))
                plt.imshow(psf_check,
                           interpolation="lanczos",
                           filternorm=1,
                           aspect="equal",
                           vmin=0,
                           vmax=1)
                plt.show()
                plt.imshow((deblured_image[top:bottom, left:right, ...] *
                            255).astype(np.uint8),
                           interpolation="lanczos",
                           filternorm=1,
                           aspect="equal",
                           vmin=0,
                           vmax=255)
                plt.show()

    except KeyboardInterrupt:
        # Nasty trick to be able to hard-shutdown the iterations and still get the output
        # Don't try this at home
        # Seriously, don't.
        pass

    # Clip extreme values
    np.clip(deblured_image, 0., 1., out=deblured_image)

    # Redo the gamma corrector
    deblured_image = deblured_image**(2.2)

    # Convert to 16 bits RGB
    deblured_image = deblured_image * (2**16 - 1)

    # Save the pic
    if preview:
        filename = filename + "-preview"
        deblured_image = deblured_image[top:bottom, left:right, ...]
    else:
        # if the picture has been padded to make it odd, unpad it to get the original size
        if odd_hor:
            deblured_image = deblured_image[:, 1:, ...]
        if odd_vert:
            deblured_image = deblured_image[1:, :, ...]

        # Remove the extra pad
        deblured_image = deblured_image[1:-1, 1:-1, ...]

    utils.save(deblured_image, filename, dest_path)