Example #1
0
def generate_qc(fn_in, fn_seg, args, path_qc):
    """
    Generate a QC entry allowing to quickly review the segmentation process.
    """

    import spinalcordtoolbox.reports.qc as qc
    import spinalcordtoolbox.reports.slice as qcslice
    from spinalcordtoolbox.resample.nipy_resample import resample_file

    # Resample to fixed resolution (see #2063)
    tmp_folder = sct.TempFolder()
    fn_in_r = os.path.join(tmp_folder.path_tmp, 'img_r.nii.gz')
    # Orient to RPI and retrieve pixel size in IS direction (z)
    im_fn = Image(fn_in).change_orientation('RPI').save(fn_in_r)
    resample_file(fn_in_r, fn_in_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm', 'nn',
                  0)
    fn_seg_r = os.path.join(tmp_folder.path_tmp, 'seg_r.nii.gz')
    Image(fn_seg).change_orientation('RPI').save(fn_seg_r)
    resample_file(fn_seg_r, fn_seg_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm',
                  'nn', 0)

    qc.add_entry(
        src=fn_in,
        process="sct_propseg",
        args=args,
        path_qc=path_qc,
        plane='Axial',
        qcslice=qcslice.Axial([Image(fn_in_r), Image(fn_seg_r)]),
        qcslice_operations=[qc.QcImage.listed_seg],
        qcslice_layout=lambda x: x.mosaic(),
    )
def generate_qc(fn_in, fn_seg, args, path_qc):
    """Generate a QC entry allowing to quickly review the segmentation process."""
    import spinalcordtoolbox.reports.qc as qc
    import spinalcordtoolbox.reports.slice as qcslice
    from spinalcordtoolbox.resample.nipy_resample import resample_file

    # Resample to fixed resolution (see #2063)
    tmp_folder = sct.TempFolder()
    fn_in_r = os.path.join(tmp_folder.path_tmp, 'img_r.nii.gz')
    # Orient to RPI and retrieve pixel size in IS direction (z)
    im_fn = Image(fn_in).change_orientation('RPI').save(fn_in_r)
    resample_file(fn_in_r, fn_in_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm', 'nn',
                  0)
    fn_seg_r = os.path.join(tmp_folder.path_tmp, 'seg_r.nii.gz')
    Image(fn_seg).change_orientation('RPI').save(fn_seg_r)
    resample_file(fn_seg_r, fn_seg_r, '0.5x0.5x' + str(im_fn.dim[6]), 'mm',
                  'nn', 0)

    # TODO: investigate the following issue further (Julien 2018-12-01):
    # fn_in_r and fn_seg_r should be in nii.gz format, otherwise qcslice.Axial outputs an memmap instead of
    # an array.
    qc.add_entry(
        src=fn_in,
        process="sct_deepseg_sc",
        args=args,
        path_qc=path_qc,
        plane='Axial',
        qcslice=qcslice.Axial([Image(fn_in_r), Image(fn_seg_r)]),
        qcslice_operations=[qc.QcImage.listed_seg],
        qcslice_layout=lambda x: x.mosaic(),
    )
Example #3
0
def test_propseg(t2_image, t2_seg_image, tmp_path):
    param = qc.Params(t2_image.absolutepath, 'sct_propseg', ['-a'], 'Axial', str(tmp_path))
    report = qc.QcReport(param, 'Test usage')

    @qc.QcImage(report, 'none', [qc.QcImage.listed_seg, ], process=param.command)
    def test(qslice):
        return qslice.mosaic()

    test(qcslice.Axial([t2_image, t2_seg_image]))
    assert os.path.isfile(param.abs_bkg_img_path())
    assert os.path.isfile(param.abs_overlay_img_path())
    assert os.path.isfile(param.qc_results)
def test_propseg(t2_image, t2_seg_image):
    param = qc.Params(t2_image, 'sct_propseg', ['-a'], 'Axial', '/tmp')
    report = qc.QcReport(param, 'Test usage')

    @qc.QcImage(report, 'none', [
        qc.QcImage.listed_seg,
    ])
    def test(qslice):
        return qslice.mosaic()

    test(qcslice.Axial(t2_image, t2_seg_image))
    assert os.path.isfile(param.abs_bkg_img_path())
    assert os.path.isfile(param.abs_overlay_img_path())
    assert os.path.isfile(param.qc_results)
def generate_qc(fn_in, fn_seg, args, path_qc):
    """Generate a QC entry allowing to quickly review the segmentation process."""
    import spinalcordtoolbox.reports.qc as qc
    import spinalcordtoolbox.reports.slice as qcslice

    qc.add_entry(
        src=fn_in,
        process="sct_deepseg_sc",
        args=args,
        path_qc=path_qc,
        plane='Axial',
        qcslice=qcslice.Axial([Image(fn_in), Image(fn_seg)]),
        qcslice_operations=[qc.QcImage.listed_seg],
        qcslice_layout=lambda x: x.mosaic(),
    )
def generate_qc(fn_in, fn_wm, args, path_qc):
    """
    Generate a QC entry allowing to quickly review the warped template.
    """

    import spinalcordtoolbox.reports.qc as qc
    import spinalcordtoolbox.reports.slice as qcslice

    qc.add_entry(
     src=fn_in,
     process="sct_warp_template",
     args=args,
     path_qc=path_qc,
     plane='Axial',
     qcslice=qcslice.Axial([Image(fn_in), Image(fn_wm)]),
     qcslice_operations=[qc.QcImage.template],
     qcslice_layout=lambda x: x.mosaic(),
    )
def generate_qc(fname_data, fname_template2anat, fname_seg, args, path_qc):
    """
    Generate a QC entry allowing to quickly review the straightening process.
    """

    import spinalcordtoolbox.reports.qc as qc
    import spinalcordtoolbox.reports.slice as qcslice

    qc.add_entry(
        src=fname_data,
        process="sct_register_to_template",
        args=args,
        path_qc=path_qc,
        plane="Axial",
        qcslice=qcslice.Axial(
            [Image(fname_data),
             Image(fname_template2anat),
             Image(fname_seg)]),
        qcslice_operations=[qc.QcImage.no_seg_seg],
        qcslice_layout=lambda x: x.mosaic()[:2],
    )
Example #8
0
def generate_qc(fname_in, fname_gm, fname_wm, param_seg, args, path_qc):
    """
    Generate a QC entry allowing to quickly review the segmentation process.
    """

    import spinalcordtoolbox.reports.qc as qc
    import spinalcordtoolbox.reports.slice as qcslice

    im_org = Image(fname_in)
    im_gm = Image(fname_gm)
    im_wm = Image(fname_wm)

    # create simple compound segmentation image for QC purposes

    if param_seg.type_seg == 'bin':
        im_wm.data[im_wm.data == 1] = 1
        im_gm.data[im_gm.data == 1] = 2
    else:
        # binarize anyway
        im_wm.data[im_wm.data >= param_seg.thr_bin] = 1
        im_wm.data[im_wm.data < param_seg.thr_bin] = 0
        im_gm.data[im_gm.data >= param_seg.thr_bin] = 2
        im_gm.data[im_gm.data < param_seg.thr_bin] = 0

    im_seg = im_gm
    im_seg.data += im_wm.data

    s = qcslice.Axial([im_org, im_seg])

    qc.add_entry(
        src=fname_in,
        process="sct_segment_graymatter",
        args=args,
        path_qc=path_qc,
        plane='Axial',
        qcslice=s,
        qcslice_operations=[qc.QcImage.listed_seg],
        qcslice_layout=lambda x: x.mosaic(),
    )
Example #9
0
def generate_qc(fname_in1, fname_in2=None, fname_seg=None, angle_line=None, args=None, path_qc=None,
                dataset=None, subject=None, path_img=None, process=None, fps=None):
    """
    Generate a QC entry allowing to quickly review results. This function is the entry point and is called by SCT
    scripts (e.g. sct_propseg).

    :param fname_in1: str: File name of input image #1 (mandatory)
    :param fname_in2: str: File name of input image #2
    :param fname_seg: str: File name of input segmentation
    :param angle_line: list: Angle [in rad, wrt. vertical line, must be between -pi and pi] to apply to the line overlaid on the image, for\
    each slice, for slice that don't have an angle to display, a nan is expected. To be used for assessing cord orientation.
    :param args: args from parent function
    :param path_qc: str: Path to save QC report
    :param dataset: str: Dataset name
    :param subject: str: Subject name
    :param path_img: dict: Path to image to display (e.g., a graph), instead of computing the image from MRI.
    :param process: str: Name of SCT function. e.g., sct_propseg
    :param fps: float: Number of frames per second for output gif images. Used only for sct_frmi_moco and sct_dmri_moco.
    :return: None
    """
    logger.info('\n*** Generate Quality Control (QC) html report ***')
    dpi = 300
    plane = None
    qcslice_type = None
    qcslice_operations = None
    qcslice_layout = None

    # Get QC specifics based on SCT process
    # Axial orientation, switch between two input images
    if process in ['sct_register_multimodal', 'sct_register_to_template']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_in2), Image(fname_seg)])
        qcslice_operations = [QcImage.no_seg_seg]
        def qcslice_layout(x): return x.mosaic()[:2]
    # Rotation visualisation
    elif process in ['rotation']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_seg)])
        qcslice_operations = [QcImage.line_angle]
        def qcslice_layout(x): return x.mosaic(return_center=True)
    # Axial orientation, switch between the image and the segmentation
    elif process in ['sct_propseg', 'sct_deepseg_sc', 'sct_deepseg_gm']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_seg)])
        qcslice_operations = [QcImage.listed_seg]
        def qcslice_layout(x): return x.mosaic()
    # Axial orientation, switch between the image and the centerline
    elif process in ['sct_get_centerline']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_seg)])
        qcslice_operations = [QcImage.label_centerline]
        def qcslice_layout(x): return x.mosaic()
    # Axial orientation, switch between the image and the white matter segmentation (linear interp, in blue)
    elif process in ['sct_warp_template']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_seg)])
        qcslice_operations = [QcImage.template]
        def qcslice_layout(x): return x.mosaic()
    # Axial orientation, switch between gif image (before and after motion correction) and grid overlay
    elif process in ['sct_dmri_moco', 'sct_fmri_moco']:
        plane = 'Axial'
        if fname_seg is None:
            raise Exception("Segmentation is needed to ensure proper cropping around spinal cord.")
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_in2), Image(fname_seg)])
        qcslice_operations = [QcImage.grid]
        def qcslice_layout(x): return x.mosaics_through_time()
    # Sagittal orientation, display vertebral labels
    elif process in ['sct_label_vertebrae']:
        plane = 'Sagittal'
        dpi = 100  # bigger picture is needed for this special case, hence reduce dpi
        qcslice_type = qcslice.Sagittal([Image(fname_in1), Image(fname_seg)], p_resample=None)
        qcslice_operations = [QcImage.label_vertebrae]
        def qcslice_layout(x): return x.single()
    #  Sagittal orientation, display posterior labels
    elif process in ['sct_label_utils']:
        plane = 'Sagittal'
        dpi = 100  # bigger picture is needed for this special case, hence reduce dpi
        # projected_image = projected(Image(fname_seg))
        qcslice_type = qcslice.Sagittal([Image(fname_in1), Image(fname_seg)], p_resample=None)
        qcslice_operations = [QcImage.label_utils]
        def qcslice_layout(x): return x.single()
    # Sagittal orientation, display PMJ box
    elif process in ['sct_detect_pmj']:
        plane = 'Sagittal'
        qcslice_type = qcslice.Sagittal([Image(fname_in1), Image(fname_seg)], p_resample=None)
        qcslice_operations = [QcImage.highlight_pmj]
        def qcslice_layout(x): return x.single()
    # Sagittal orientation, static image
    elif process in ['sct_straighten_spinalcord']:
        plane = 'Sagittal'
        dpi = 100
        qcslice_type = qcslice.Sagittal([Image(fname_in1), Image(fname_in1)], p_resample=None)
        qcslice_operations = [QcImage.vertical_line]
        def qcslice_layout(x): return x.single()
    # Metric outputs (only graphs)
    elif process in ['sct_process_segmentation']:
        assert os.path.isfile(path_img)
    else:
        raise ValueError("Unrecognized process: {}".format(process))

    add_entry(
        src=fname_in1,
        process=process,
        args=args,
        path_qc=path_qc,
        dataset=dataset,
        subject=subject,
        plane=plane,
        path_img=path_img,
        dpi=dpi,
        qcslice=qcslice_type,
        qcslice_operations=qcslice_operations,
        qcslice_layout=qcslice_layout,
        stretch_contrast_method='equalized',
        angle_line=angle_line,
        fps=fps,
    )
Example #10
0
def generate_qc(fname_in1,
                fname_in2=None,
                fname_seg=None,
                args=None,
                path_qc=None,
                dataset=None,
                subject=None,
                process=None):
    """
    Generate a QC entry allowing to quickly review results. This function is called by SCT scripts (e.g. sct_propseg).

    :param fname_in1: str: File name of input image #1 (mandatory)
    :param fname_in2: str: File name of input image #2
    :param fname_seg: str: File name of input segmentation
    :param args: args from parent function
    :param path_qc: str: Path to save QC report
    :param dataset: str: Dataset name
    :param subject: str: Subject name
    :param process: str: Name of SCT function. e.g., sct_propseg
    :return: None
    """
    dpi = 300
    # Get QC specifics based on SCT process
    # Axial orientation, switch between two input images
    if process in ['sct_register_multimodal', 'sct_register_to_template']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial(
            [Image(fname_in1),
             Image(fname_in2),
             Image(fname_seg)])
        qcslice_operations = [QcImage.no_seg_seg]
        qcslice_layout = lambda x: x.mosaic()[:2]
    # Axial orientation, switch between the image and the segmentation
    elif process in ['sct_propseg', 'sct_deepseg_sc', 'sct_deepseg_gm']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_seg)])
        qcslice_operations = [QcImage.listed_seg]
        qcslice_layout = lambda x: x.mosaic()
    # Axial orientation, switch between the image and the white matter segmentation (linear interp, in blue)
    elif process in ['sct_warp_template']:
        plane = 'Axial'
        qcslice_type = qcslice.Axial([Image(fname_in1), Image(fname_seg)])
        qcslice_operations = [QcImage.template]
        qcslice_layout = lambda x: x.mosaic()
    # Sagittal orientation, display vertebral labels
    elif process in ['sct_label_vertebrae']:
        plane = 'Sagittal'
        dpi = 100  # bigger picture is needed for this special case, hence reduce dpi
        qcslice_type = qcslice.Sagittal(
            [Image(fname_in1), Image(fname_seg)], p_resample=None)
        qcslice_operations = [QcImage.label_vertebrae]
        qcslice_layout = lambda x: x.single()
    # Sagittal orientation, display PMJ box
    elif process in ['sct_detect_pmj']:
        plane = 'Sagittal'
        qcslice_type = qcslice.Sagittal(
            [Image(fname_in1), Image(fname_seg)], p_resample=None)
        qcslice_operations = [QcImage.highlight_pmj]
        qcslice_layout = lambda x: x.single()
    else:
        raise ValueError("Unrecognized process: {}".format(process))

    add_entry(
        src=fname_in1,
        process=process,
        args=args,
        path_qc=path_qc,
        dataset=dataset,
        subject=subject,
        plane=plane,
        dpi=dpi,
        qcslice=qcslice_type,
        qcslice_operations=qcslice_operations,
        qcslice_layout=qcslice_layout,
        stretch_contrast_method='equalized',
    )
Example #11
0
    if '-qc' in arguments and not arguments.get('-noqc', False):
        qc_path = arguments['-qc']

        import spinalcordtoolbox.reports.qc as qc
        import spinalcordtoolbox.reports.slice as qcslice

        param = qc.Params(fname_input_data, 'sct_propseg', args, 'Axial',
                          qc_path)
        report = qc.QcReport(param, '')

        @qc.QcImage(report, 'none', [
            qc.QcImage.listed_seg,
        ])
        def test(qslice):
            return qslice.mosaic()

        try:
            test(qcslice.Axial(Image(fname_input_data), Image(fname_seg)))
            sct.log.info('Sucessfully generated the QC results in %s' %
                         param.qc_results)
            sct.log.info(
                'Use the following command to see the results in a browser:')
            sct.log.info('sct_qc -folder %s' % qc_path)
        except:
            sct.log.warning('Issue when creating QC report.')

    sct.printv('\nDone! To view results, type:', verbose)
    sct.printv(
        "fslview " + fname_input_data + " " + fname_seg +
        " -l Red -b 0,1 -t 0.7 &\n", verbose, 'info')
Example #12
0
def main():
    # find all the images of interest and store the mid slice in slice_lst
    slice_lst = []
    for x in os.walk(i_folder):
        for file in glob.glob(
                os.path.join(x[0], 'sub' + im_string)
        ):  # prefixe sub: to prevent from fetching warp files
            print('\nLoading: ' + file)
            # load data
            if plane == 'ax':
                file_seg = glob.glob(os.path.join(x[0], 'sub' + seg_string))[0]

                # workaround to save some time
                img, seg = Image(file).change_orientation('RPI'), Image(
                    file_seg).change_orientation('RPI')
                mid_slice_idx = int(float(img.dim[2]) // 2)
                nii_mid = nib.nifti1.Nifti1Image(img.data[:, :, mid_slice_idx],
                                                 affine)
                nii_mid_seg = nib.nifti1.Nifti1Image(
                    seg.data[:, :, mid_slice_idx], affine)
                img_mid = Image(img.data[:, :, mid_slice_idx],
                                hdr=nii_mid.header,
                                dim=nii_mid.header.get_data_shape())
                seg_mid = Image(seg.data[:, :, mid_slice_idx],
                                hdr=nii_mid_seg.header,
                                dim=nii_mid_seg.header.get_data_shape())
                del img, seg

                qcslice_cur = qcslice.Axial([img_mid, seg_mid])
                center_x_lst, center_y_lst = qcslice_cur.get_center(
                )  # find seg center of mass
                mid_slice = qcslice_cur.get_slice(qcslice_cur._images[0].data,
                                                  0)  # get the mid slice
                # crop image around SC seg
                mid_slice = qcslice_cur.crop(mid_slice, int(center_x_lst[0]),
                                             int(center_y_lst[0]), 30, 30)
            else:
                sag_im = Image(file).change_orientation('RSP')
                if not np.isclose(
                        sag_im.dim[5],
                        sag_im.dim[6]):  # in case data is anisotropic
                    sag_im = resample_nib(
                        sag_im.copy(),
                        new_size=[sag_im.dim[4], sag_im.dim[5], sag_im.dim[5]],
                        new_size_type='mm')
                mid_slice_idx = int(sag_im.dim[0] // 2)
                mid_slice = sag_im.data[mid_slice_idx, :, :]
                del sag_im

            # histogram equalization using CLAHE
            slice_cur = equalized(mid_slice, winsize)
            # scale intensities of all slices (ie of all subjects) in a common range of values
            slice_cur = scale_intensity(slice_cur)

            # resize all slices with the shape of the first loaded slice
            if len(slice_lst):
                slice_cur = resize(slice_cur, slice_size, anti_aliasing=True)
            else:
                slice_size = slice_cur.shape

            slice_lst.append(slice_cur)

    # create a new Image object containing the samples to display
    data = np.stack(slice_lst, axis=-1)
    nii = nib.nifti1.Nifti1Image(data, affine)
    img = Image(data, hdr=nii.header, dim=nii.header.get_data_shape())

    nb_img = img.data.shape[2]
    nb_items_mosaic = nb_column * nb_row
    nb_mosaic = np.ceil(float(nb_img) / (nb_items_mosaic))
    for i in range(int(nb_mosaic)):
        if nb_mosaic == 1:
            fname_out = o_fname
        else:
            fname_out = os.path.splitext(o_fname)[0] + '_' + str(i).zfill(
                3) + os.path.splitext(o_fname)[1]
        print('\nCreating: ' + fname_out)

        # create mosaic
        idx_end = (i + 1) * nb_items_mosaic if (
            i + 1) * nb_items_mosaic <= nb_img else nb_img
        data_mosaic = img.data[:, :, i * (nb_items_mosaic):idx_end]
        mosaic = get_mosaic(data_mosaic, nb_column, nb_row)

        # save mosaic
        plt.figure()
        plt.subplot(1, 1, 1)
        plt.axis("off")
        plt.imshow(mosaic,
                   interpolation='bilinear',
                   cmap='gray',
                   aspect='equal')
        plt.savefig(fname_out, dpi=300, bbox_inches='tight', pad_inches=0)
        plt.close()
Example #13
0
def main():
    args = get_parameters()
    print(args)
    im_string = args.input
    # i_folder = args.input_folder
    # seg_string = args.segmentation
    plane = args.plane
    nb_column = int(args.col)
    nb_row = int(args.row)
    winsize = int(args.winsize_CLAHE)
    o_fname = args.output
    # List input folders
    files = glob.glob(os.path.join(args.input_folder, '**/sub' + im_string), recursive=True)
    files.sort()
    # Initialize list that will store each mosaic element
    slice_lst = []
    for file in files:
        print("Processing ({}/{}): {}".format(files.index(file), len(files), file))
        if plane == 'ax':
            file_seg = add_suffix(file, args.segmentation)
            # Extract the mid-slice
            img, seg = Image(file).change_orientation('RPI'), Image(file_seg).change_orientation('RPI')
            mid_slice_idx = int(float(img.dim[2]) // 2)
            nii_mid = nib.nifti2.Nifti2Image(img.data[:, :, mid_slice_idx], img.hdr.get_best_affine())
            nii_mid_seg = nib.nifti2.Nifti2Image(seg.data[:, :, mid_slice_idx], seg.hdr.get_best_affine())
            img_mid = Image(img.data[:, :, mid_slice_idx], hdr=nii_mid.header, dim=nii_mid.header.get_data_shape())
            seg_mid = Image(seg.data[:, :, mid_slice_idx], hdr=nii_mid_seg.header, dim=nii_mid_seg.header.get_data_shape())
            # Instantiate spinalcordtoolbox.reports.slice.Axial class
            qcslice_cur = qcslice.Axial([img_mid, seg_mid])
            # Find center of mass of the segmentation
            center_x_lst, center_y_lst = qcslice_cur.get_center()
            # Select the mid-slice
            mid_slice = qcslice_cur.get_slice(qcslice_cur._images[0].data, 0)
            # Crop image around SC seg
            mid_slice = qcslice_cur.crop(mid_slice,
                                         int(center_x_lst[0]), int(center_y_lst[0]),
                                         20, 20)
        elif plane == 'sag':
            sag_im = Image(file).change_orientation('RSP')
            # check if data is not isotropic resolution
            if not np.isclose(sag_im.dim[5], sag_im.dim[6]):
                sag_im = resample_nib(sag_im.copy(), new_size=[sag_im.dim[4], sag_im.dim[5], sag_im.dim[5]], new_size_type='mm')
            mid_slice_idx = int(sag_im.dim[0] // 2)
            mid_slice = sag_im.data[mid_slice_idx, :, :]
            del sag_im

        # Histogram equalization using CLAHE
        slice_cur = equalized(mid_slice, winsize)
        # Scale intensities of all slices (ie of all subjects) in a common range of values
        slice_cur = scale_intensity(slice_cur)

        # Resize all slices with the shape of the first loaded slice
        if len(slice_lst):
            slice_cur = resize(slice_cur, slice_size, anti_aliasing=True)
        else:
            slice_size = slice_cur.shape

        slice_lst.append(slice_cur)

    # Create a 2d array containing the samples to display
    data = np.stack(slice_lst, axis=-1)
    nb_img = data.shape[2]
    nb_items_mosaic = nb_column * nb_row
    nb_mosaic = np.ceil(float(nb_img) / nb_items_mosaic)
    for i in range(int(nb_mosaic)):
        if nb_mosaic == 1:
            fname_out = o_fname
        else:
            fname_out = os.path.splitext(o_fname)[0] + '_' + str(i).zfill(3) + os.path.splitext(o_fname)[1]
        # create mosaic
        idx_end = (i+1)*nb_items_mosaic if (i+1)*nb_items_mosaic <= nb_img else nb_img
        data_mosaic = data[:, :, i*nb_items_mosaic: idx_end]
        mosaic = get_mosaic(data_mosaic, nb_column, nb_row)
        # save mosaic
        plt.figure()
        plt.subplot(1, 1, 1)
        plt.axis("off")
        plt.imshow(mosaic, interpolation='bilinear', cmap='gray', aspect='equal')
        plt.savefig(fname_out, dpi=300, bbox_inches='tight', pad_inches=0)
        plt.close()
        print('\nCreated: {}'.format(fname_out))