def extract_parameters(filename, param_names, _verb=False):
    ''' read parameters values in filename.txt
    and save it in a dictionary'''

    # read values in txt
    param_values = search_value_in_txt(filename, param_names)

    print('\n ***  Parameters : \n')
    # create dictionary of parameters
    parameters = {}
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        if _verb:
            print(' - {} : {}'.format(p_name, param_values[i]))
    if _verb: print('\n \n')
    return parameters
Beispiel #2
0
def extract_parameters(filename):
    ''' read parameters values in filename.txt
    and save it in a dictionary'''

    param_names = ['res_xy', 'res_z', 'save_binary_sections']

    # read values in txt
    param_values = search_value_in_txt(filename, param_names)

    print(' ***  Parameters : \n')
    # create dictionary of parameters
    parameters = {}
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        print(' - {} : {}'.format(p_name, param_values[i]))
    print('\n \n')
    return parameters
Beispiel #3
0
def extract_parameters(filename):
    ''' read parameters values in filename.txt
    and save it in a dictionary'''

    param_names = [
        'res_xy', 'res_z', 't_rate_info', 'clahe_ksize', 'clip_clahe',
        'K_cluster', 'K_true', 't_ratio_holes', 'save_clahe',
        'save_binary_mask', 'save_segmented', 'save_contours',
        'save_countourned'
    ]

    # read values in txt
    param_values = search_value_in_txt(filename, param_names)

    print(' ***  Parameters : \n')
    # create dictionary of parameters
    parameters = {}
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        print(' - {} : {}'.format(p_name, param_values[i]))
    print('\n \n')
    return parameters
def extract_parameters(filename):
    ''' read parameters values in filename.txt
    and save it in a dictionary'''

    param_names = [
        'num_of_slices_P', 'sarc_length', 'res_xy', 'res_z',
        'threshold_on_cell_ratio', 'threshold_on_peak_ratio',
        'threshold_on_hyperbole', 'psd0_hyperbole_psd_ratio',
        'k_hyperbole_psd_ratio', 'y0_hyperbole_psd_ratio', 'sigma',
        'local_disorder_xy_side', 'local_disorder_z_side', 'neighbours_lim',
        'isolated'
    ]

    # read values in txt
    param_values = search_value_in_txt(filename, param_names)

    print('\n ***  Parameters : \n')
    # create dictionary of parameters
    parameters = {}
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        print(' - {} : {}'.format(p_name, param_values[i]))
    print('\n \n')
    return parameters
def main(parser):

    ### Extract input information FROM TERMINAL =======================
    args = parser.parse_args()
    source_path = manage_path_argument(args.source_path)
    param_filename = args.parameters_filename[0]

    # preferences
    _verbose = args.verbose
    _deep_verbose = args.deep_verbose
    _save_csv = args.csv
    _save_hist = args.histogram
    _save_maps = args.maps

    if _verbose:
        print(Bcolors.FAIL + ' *** VERBOSE MODE *** ' + Bcolors.ENDC)
    if _deep_verbose:
        print(Bcolors.FAIL + ' *** DEBUGGING MODE *** ' + Bcolors.ENDC)
    ### ===============================================================

    # extract filenames and folder names
    stack_name = os.path.basename(source_path)
    process_folder = os.path.basename(os.path.dirname(source_path))
    base_path = os.path.dirname(os.path.dirname(source_path))
    param_filepath = os.path.join(base_path, process_folder, param_filename)
    stack_prefix = stack_name.split('.')[0]

    # create introductory information
    mess_strings = list()
    mess_strings.append(Bcolors.OKBLUE +
                        '\n\n*** Structure Tensor Orientation Analysis ***\n' +
                        Bcolors.ENDC)
    mess_strings.append(' > Source path:        {}'.format(source_path))
    mess_strings.append(' > Stack name:         {}'.format(stack_name))
    mess_strings.append(' > Process folder:     {}'.format(process_folder))
    mess_strings.append(' > Base path:          {}'.format(base_path))
    mess_strings.append(' > Parameter filename: {}'.format(param_filename))
    mess_strings.append(' > Parameter filepath: {}'.format(param_filepath))
    mess_strings.append('')
    mess_strings.append(' > PREFERENCES:')
    mess_strings.append('   - _verbose       {}'.format(_verbose))
    mess_strings.append('   - _deep_verbose  {}'.format(_deep_verbose))
    mess_strings.append('   - _save_csv      {}'.format(_save_csv))
    mess_strings.append('   - _save_hist     {}'.format(_save_hist))
    mess_strings.append('   - _save_maps     {}'.format(_save_maps))

    # extract parameters
    param_names = [
        'roi_xy_pix', 'px_size_xy', 'px_size_z', 'mode_ratio',
        'threshold_on_cell_ratio', 'local_disarray_xy_side',
        'local_disarray_z_side', 'neighbours_lim', 'fwhm_xy', 'fwhm_z'
    ]

    param_values = search_value_in_txt(param_filepath, param_names)

    # create parameter dictionary
    parameters = {}
    mess_strings.append('\n\n*** Parameters used:')
    mess_strings.append(
        ' > Parameters extracted from {}\n'.format(param_filename))
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        mess_strings.append('> {} - {}'.format(p_name, parameters[p_name]))

    # acquisition system characteristics: ratio of the pixel size along the z and x-y axes
    ps_ratio = parameters['px_size_z'] / parameters['px_size_xy']

    # size of the analyzed block along the z axis
    shape_P = np.array(
        (int(parameters['roi_xy_pix']), int(parameters['roi_xy_pix']),
         int(parameters['roi_xy_pix'] / ps_ratio))).astype(np.int32)

    mess_strings.append('\n *** Analysis configuration:')
    mess_strings.append(
        ' > Pixel size ratio (z / xy) = {0:0.2f}'.format(ps_ratio))
    mess_strings.append(
        ' > Number of selected stack slices for each ROI ({} x {}): {}'.format(
            shape_P[0], shape_P[1], shape_P[2]))
    mess_strings.append(' > Parallelepiped size: ({0},{1},{2}) pixel ='
                        '  [{3:2.2f} {4:2.2f} {5:2.2f}] um'.format(
                            shape_P[0], shape_P[1], shape_P[2],
                            shape_P[0] * parameters['px_size_xy'],
                            shape_P[1] * parameters['px_size_xy'],
                            shape_P[2] * parameters['px_size_z']))

    # create .txt report file
    txt_info_filename = 'Orientations_INFO_' + stack_prefix + '_' \
                   + str(int(parameters['roi_xy_pix'] * parameters['px_size_xy'])) + 'um.txt'
    txt_info_path = os.path.join(os.path.dirname(source_path),
                                 txt_info_filename)

    # print analysis report to screen and write into .txt file all introductory information
    write_on_txt(mess_strings, txt_info_path, _print=True, mode='w')

    # clear list of strings
    mess_strings.clear()

    # 1 - OPEN STACK ------------------------------------------------------------------------

    # extract data (entire volume 'V')
    volume = InputFile(source_path).whole()

    # change axes: (r, c, z) -> (z, y, x)
    volume = np.moveaxis(volume, 0, -1)

    # compute volume size
    shape_V = np.array(volume.shape)
    pixel_for_slice = shape_V[0] * shape_V[1]
    total_voxel_V = pixel_for_slice * shape_V[2]

    # print volume size information
    mess_strings.append('\n\n*** Loaded volume size')
    mess_strings.append(' > Size of entire volume:  ({}, {}, {})'.format(
        shape_V[0], shape_V[1], shape_V[2]))
    mess_strings.append(
        ' > Pixels for stack slice:  {}'.format(pixel_for_slice))
    mess_strings.append(' > Total number of voxels:  {}'.format(total_voxel_V))

    # extract list of math information (strings) about the volume.npy variable
    info = print_info(volume,
                      text='\nVolume informations:',
                      _std=False,
                      _return=True)
    mess_strings = mess_strings + info

    # print volume information to screen and add it to the report .txt file
    write_on_txt(mess_strings, txt_info_path, _print=True, mode='a')

    # clear list of strings
    mess_strings.clear()

    # 2 - LOOP FOR BLOCK EXTRACTION and ANALYSIS -------------------------------------------
    print('\n\n')
    print(Bcolors.OKBLUE + '*** Start Structure Tensor Analysis... ' +
          Bcolors.ENDC)

    t_start = time.time()

    # create empty result matrix
    R, shape_R = create_R(shape_V, shape_P)

    # conduct analysis on input volume
    R, count = iterate_orientation_analysis(volume, R, parameters, shape_R,
                                            shape_P, _verbose)
    mess_strings.append('\n > Orientation analysis complete.')

    # retrieve information about the analyzed data
    block_with_cell = np.count_nonzero(R[Param.CELL_INFO])
    block_with_info = np.count_nonzero(R[Param.ORIENT_INFO])
    p_rejec_cell = 100 * (1 - (block_with_cell / count))
    p_rejec_info_tot = 100 * (1 - (block_with_info / count))
    p_rejec_info = 100 * (1 - (block_with_info / block_with_cell))

    # get analysis time
    t_process = time.time() - t_start

    # create result strings
    mess_strings.append('\n\n*** Results of Orientation analysis:')
    mess_strings.append(' > Expected iterations: {}'.format(np.prod(shape_R)))
    mess_strings.append(' > Total    iterations: {}'.format(count))
    mess_strings.append(' > Time elapsed:        {0:.3f} s'.format(t_process))
    mess_strings.append('\n > Total blocks analyzed: {}'.format(count))
    mess_strings.append(
        ' > Block with cell: {0}, rejected from total: {1} ({2:0.1f}%)'.format(
            block_with_cell, count - block_with_cell, p_rejec_cell))
    mess_strings.append(
        ' > Block with gradient information: {}'.format(block_with_info))
    mess_strings.append(
        ' > rejected from total:             {0} ({1:0.1f}%)'.format(
            count - block_with_info, p_rejec_info_tot))
    mess_strings.append(
        ' > rejected from block with cell:   {0} ({1:0.1f}%)'.format(
            block_with_cell - block_with_info, p_rejec_info))

    mess_strings.append(
        '\n > R matrix created ( shape: ({}, {}, {}) cells (zyx) )'.format(
            R.shape[0], R.shape[1], R.shape[2]))

    # print information to screen and add it to the report .txt file
    write_on_txt(mess_strings, txt_info_path, _print=True, mode='a')

    # clear list of strings
    mess_strings.clear()

    # 3 - DISARRAY AND FRACTIONAL ANISOTROPY ESTIMATION -------------------------------------

    # estimate local disarrays and fractional anisotropy, write estimated values also inside R
    mtrx_of_disarrays, mtrx_of_local_fa, shape_G, R = estimate_local_disarray(
        R, parameters, ev_index=2, _verb=_verbose, _verb_deep=_deep_verbose)

    # 4a - SAVE R ( updated with estimate_local_disarray() ) TO NUMPY FILE ------------------

    # create result matrix (R) filename:
    R_filename = 'R_' + stack_prefix + '_' + str(
        int(parameters['roi_xy_pix'] * parameters['px_size_xy'])) + 'um.npy'
    R_prefix = R_filename.split('.')[0]
    R_filepath = os.path.join(base_path, process_folder, R_filename)

    # save results to R.npy
    np.save(R_filepath, R)
    mess_strings.append('\n> R matrix saved to: {}'.format(
        os.path.dirname(source_path)))
    mess_strings.append('> with name: {}'.format(R_filename))
    mess_strings.append('\n> Information .txt file saved to: {}'.format(
        os.path.dirname(txt_info_path)))
    mess_strings.append('> with name: {}'.format(txt_info_filename))

    # print information to screen and add it to the report .txt file
    write_on_txt(mess_strings, txt_info_path, _print=True, mode='a')

    # clear list of strings
    mess_strings.clear()

    # 4b - SAVE DISARRAY TO NUMPY FILE AND COMPILE RESULTS TXT FILE -------------------------

    # save disarray matrices (computed with arithmetic and weighted means) to numpy file
    disarray_np_filename = dict()
    for mode in [att for att in vars(Mode) if str(att)[0] is not '_']:
        disarray_np_filename[getattr(Mode, mode)] = save_in_numpy_file(
            mtrx_of_disarrays[getattr(Mode, mode)],
            R_prefix,
            shape_G,
            parameters,
            base_path,
            process_folder,
            data_prefix='MatrixDisarray_{}_'.format(mode))

    # save fractional anisotropy to numpy file
    fa_np_filename = save_in_numpy_file(mtrx_of_local_fa,
                                        R_prefix,
                                        shape_G,
                                        parameters,
                                        base_path,
                                        process_folder,
                                        data_prefix='FA_local_')

    mess_strings.append(
        '\n> Disarray and Fractional Anisotropy matrices saved to:')
    mess_strings.append('> {}'.format(os.path.join(base_path, process_folder)))
    mess_strings.append('with name: \n > {}\n > {}\n > {}\n'.format(
        disarray_np_filename[Mode.ARITH], disarray_np_filename[Mode.WEIGHT],
        fa_np_filename))
    mess_strings.append('\n')

    # 5 - STATISTICAL ANALYSIS, HISTOGRAMS AND SAVINGS --------------------------------------

    # estimate statistics (see class Stat) of disarray and fractional anisotropy matrices
    disarray_ARITM_stats = statistics_base(mtrx_of_disarrays[Mode.ARITH],
                                           invalid_value=CONST.INV)
    disarray_WEIGHT_stats = statistics_base(mtrx_of_disarrays[Mode.WEIGHT],
                                            w=mtrx_of_local_fa,
                                            invalid_value=CONST.INV)

    fa_stats = statistics_base(mtrx_of_local_fa, invalid_value=CONST.INV)

    # compile/append strings of statistical results
    s1 = compile_results_strings(mtrx_of_disarrays[Mode.ARITH], 'Disarray',
                                 disarray_ARITM_stats, 'ARITH', '%')
    s2 = compile_results_strings(mtrx_of_disarrays[Mode.WEIGHT], 'Disarray',
                                 disarray_WEIGHT_stats, 'WEIGHT', '%')
    s3 = compile_results_strings(mtrx_of_local_fa, 'Fractional Anisotropy',
                                 fa_stats)
    disarray_and_fa_results_strings = s1 + ['\n\n\n'] + s2 + ['\n\n\n'] + s3

    # update mess strings
    mess_strings = mess_strings + disarray_and_fa_results_strings

    # create results .txt filename and path
    txt_results_filename = 'results_disarray_by_{}_G({},{},{})_limNeig{}.txt'.format(
        R_prefix, int(shape_G[0]), int(shape_G[1]), int(shape_G[2]),
        int(parameters['neighbours_lim']))

    # save to .csv
    if _save_csv:
        mess_strings.append('\n> CSV files saved to:')

        # save disarray and fractional anisotropy matrices to .csv file
        for (mtrx, np_fname) in zip([
                mtrx_of_disarrays[Mode.ARITH], mtrx_of_disarrays[Mode.WEIGHT],
                mtrx_of_local_fa
        ], [
                disarray_np_filename[Mode.ARITH],
                disarray_np_filename[Mode.WEIGHT], fa_np_filename
        ]):

            # extract only valid values (different from INV = -1)
            values = mtrx[mtrx != CONST.INV]

            # create .csv file path and save data
            csv_filename = np_fname.split('.')[0] + '.csv'
            csv_filepath = os.path.join(base_path, process_folder,
                                        csv_filename)
            np.savetxt(csv_filepath, values, delimiter=",", fmt='%f')
            mess_strings.append('> {}'.format(csv_filepath))

    # save histograms
    if _save_hist:
        mess_strings.append('\n> Histogram plots saved to:')

        # zip matrices, description and filenames
        for (mtrx, lbl, np_fname) in zip([
                mtrx_of_disarrays[Mode.ARITH], mtrx_of_disarrays[Mode.WEIGHT],
                mtrx_of_local_fa
        ], [
                'Local Disarray % (arithmetic mean)',
                'Local Disarray % (weighted mean)',
                'Local Fractional Anisotropy'
        ], [
                disarray_np_filename[Mode.ARITH],
                disarray_np_filename[Mode.WEIGHT], fa_np_filename
        ]):

            # extract only valid values (different from INV = -1)
            values = mtrx[mtrx != CONST.INV]

            # create file path
            hist_fname = '.'.join(np_fname.split('.')[:-1]) + '.tiff'
            hist_filepath = os.path.join(base_path, process_folder, hist_fname)

            # create histograms and save them to image files
            plot_histogram(values,
                           xlabel=lbl,
                           ylabel='Sub-volume occurrence',
                           filepath=hist_filepath)
            mess_strings.append('> {}'.format(hist_filepath))

    # save disarray and fa maps
    if _save_maps:
        mess_strings.append(
            '\n> Disarray and Fractional Anisotropy plots saved to:')

        # disarray value normalization:
        #  - in order to preserve the little differences between ARITM and WEIGH disarray matrices,
        #    these are normalized together
        #  - invalid values are NOT removed for preserving the original matrix (image) shape
        #  - invalid values (if present) are set to the minimum value
        abs_max = np.max([
            mtrx_of_disarrays[Mode.ARITH].max(),
            mtrx_of_disarrays[Mode.WEIGHT].max()
        ])
        abs_min = np.min([
            mtrx_of_disarrays[Mode.ARITH].min(),
            mtrx_of_disarrays[Mode.WEIGHT].min()
        ])
        dis_norm_A = 255 * ((mtrx_of_disarrays[Mode.ARITH] - abs_min) /
                            (abs_max - abs_min))
        dis_norm_W = 255 * ((mtrx_of_disarrays[Mode.WEIGHT] - abs_min) /
                            (abs_max - abs_min))

        # define destination folder
        dest_folder = os.path.join(base_path, process_folder)

        # create and save data frames (disarray and fractional anisotropy)
        for (mtrx,
             np_fname) in zip([dis_norm_A, dis_norm_W, mtrx_of_local_fa], [
                 disarray_np_filename[Mode.ARITH],
                 disarray_np_filename[Mode.WEIGHT], fa_np_filename
             ]):

            # plot frames and save them inside a sub_folder (folder_path)
            folder_path = plot_map_and_save(mtrx, np_fname, dest_folder,
                                            shape_G, shape_P)
            mess_strings.append('> {}'.format(folder_path))

    # print information to screen and add it to the results .txt file
    txt_results_filepath = os.path.join(base_path, process_folder,
                                        txt_results_filename)
    write_on_txt(mess_strings, txt_results_filepath, _print=True, mode='w')
def main(parser):

    ## Extract input information FROM TERMINAL =======================
    args = parser.parse_args()
    R_filepath = manage_path_argument(args.R_path)
    param_filename = args.parameters_filename[0]

    # preferences
    _verbose = args.verbose
    _deep_verbose = args.deep_verbose
    _save_csv = args.csv
    _save_hist = args.histogram
    _save_maps = args.maps

    if _verbose:
        print(Bcolors.FAIL + ' *** VERBOSE MODE *** ' + Bcolors.ENDC)
    if _deep_verbose:
        print(Bcolors.FAIL + ' *** DEBUGGING MODE *** ' + Bcolors.ENDC)
    ### ===============================================================

    # extract filenames and folders
    R_filename = os.path.basename(R_filepath)
    process_folder = os.path.basename(os.path.dirname(R_filepath))
    base_path = os.path.dirname(os.path.dirname(R_filepath))
    param_filepath = os.path.join(base_path, process_folder, param_filename)
    stack_prefix = R_filepath.split('.')[1]

    # create introductory information
    mess_strings = list()
    mess_strings.append(Bcolors.OKBLUE + '\n\n*** Disarray Analysis ***\n' +
                        Bcolors.ENDC)
    mess_strings.append(' > R matrix:           {}'.format(R_filename))
    mess_strings.append(' > Base path:          {}'.format(base_path))
    mess_strings.append(' > Parameter filename: {}'.format(param_filename))
    mess_strings.append(' > Parameter filepath: {}'.format(param_filepath))
    mess_strings.append('')
    mess_strings.append(' > PREFERENCES:')
    mess_strings.append('   - _verbose       {}'.format(_verbose))
    mess_strings.append('   - _deep_verbose  {}'.format(_deep_verbose))
    mess_strings.append('   - _save_csv      {}'.format(_save_csv))
    mess_strings.append('   - _save_hist     {}'.format(_save_hist))
    mess_strings.append('   - _save_maps     {}'.format(_save_maps))

    # extract parameters
    param_names = [
        'roi_xy_pix', 'px_size_xy', 'px_size_z', 'mode_ratio',
        'threshold_on_cell_ratio', 'local_disarray_xy_side',
        'local_disarray_z_side', 'neighbours_lim', 'fwhm_xy', 'fwhm_z'
    ]

    param_values = search_value_in_txt(param_filepath, param_names)

    # create dictionary of parameters
    parameters = {}
    mess_strings.append('\n\n*** Parameters used:')
    mess_strings.append(
        ' > Parameters extracted from {}\n'.format(param_filename))
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        mess_strings.append('> {} - {}'.format(p_name, parameters[p_name]))

    # acquisition system characteristics: ratio of the pixel size along the z and x-y axes
    ps_ratio = parameters['px_size_z'] / parameters['px_size_xy']

    # size of the analyzed block along the z axis
    shape_P = np.array(
        (int(parameters['roi_xy_pix']), int(parameters['roi_xy_pix']),
         int(parameters['roi_xy_pix'] / ps_ratio))).astype(np.int32)

    mess_strings.append('\n *** Analysis configuration')
    mess_strings.append(
        ' > Pixel size ratio (z / xy) = {0:0.2f}'.format(ps_ratio))
    mess_strings.append(
        ' > Number of selected stack slices for each ROI ({} x {}): {}'.format(
            shape_P[0], shape_P[1], shape_P[2]))
    mess_strings.append(' > Parallelepiped size: ({0},{1},{2}) pixel ='
                        '  [{3:2.2f} {4:2.2f} {5:2.2f}] um'.format(
                            shape_P[0], shape_P[1], shape_P[2],
                            shape_P[0] * parameters['px_size_xy'],
                            shape_P[1] * parameters['px_size_xy'],
                            shape_P[2] * parameters['px_size_z']))

    # print to screen
    for s in mess_strings:
        print(s)

    # clear list of strings
    mess_strings.clear()

    # load R array
    R = np.load((R_filepath))

    # --- DISARRAY AND FRACTIONAL ANISOTROPY ESTIMATION -------------------------------------

    # estimate local disarrays and fractional anisotropy, write estimated values also inside R
    mtrx_of_disarrays, mtrx_of_local_fa, shape_G, R = estimate_local_disarray(
        R, parameters, ev_index=2, _verb=_verbose, _verb_deep=_deep_verbose)

    # ---  SAVE R TO NUMPY FILE -------------------------------------------------------------

    # retrieve R array prefix
    R_prefix = R_filename.split('.')[0]

    # save results to R.npy
    np.save(R_filepath, R)
    mess_strings.append('\n> R matrix saved to: {}'.format(
        os.path.dirname(R_filepath)))
    mess_strings.append('> with name: {}'.format(R_filename))

    # print to screen
    for l in mess_strings:
        print(l)

    # clear list of strings
    mess_strings.clear()

    # --- SAVE DISARRAY MATRICES TO NUMPY FILE AND COMPILE RESULTS TXT FILE ------------------

    # save disarray matrices (computed with arithmetic and weighted means) to numpy file
    disarray_np_filename = dict()
    for mode in [att for att in vars(Mode) if str(att)[0] is not '_']:
        disarray_np_filename[getattr(Mode, mode)] = save_in_numpy_file(
            mtrx_of_disarrays[getattr(Mode, mode)],
            R_prefix,
            shape_G,
            parameters,
            base_path,
            process_folder,
            data_prefix='MatrixDisarray_{}_'.format(mode))

    # save fractional anisotropy to numpy file
    fa_np_filename = save_in_numpy_file(mtrx_of_local_fa,
                                        R_prefix,
                                        shape_G,
                                        parameters,
                                        base_path,
                                        process_folder,
                                        data_prefix='FA_local_')

    mess_strings.append(
        '\n> Disarray and Fractional Anisotropy matrices saved to:')
    mess_strings.append('> {}'.format(os.path.join(base_path, process_folder)))
    mess_strings.append('with name: \n > {}\n > {}\n > {}\n'.format(
        disarray_np_filename[Mode.ARITH], disarray_np_filename[Mode.WEIGHT],
        fa_np_filename))
    mess_strings.append('\n')

    # --- STATISTICAL ANALYSIS, HISTOGRAMS AND SAVINGS --------------------------------------

    # estimate statistics (see class Stat) of disarray and fractional anisotropy matrices
    disarray_ARITM_stats = statistics_base(mtrx_of_disarrays[Mode.ARITH],
                                           invalid_value=CONST.INV)
    disarray_WEIGHT_stats = statistics_base(mtrx_of_disarrays[Mode.WEIGHT],
                                            w=mtrx_of_local_fa,
                                            invalid_value=CONST.INV)

    fa_stats = statistics_base(mtrx_of_local_fa, invalid_value=CONST.INV)

    # compile/append strings of statistical results
    s1 = compile_results_strings(mtrx_of_disarrays[Mode.ARITH], 'Disarray',
                                 disarray_ARITM_stats, 'ARITH', '%')
    s2 = compile_results_strings(mtrx_of_disarrays[Mode.WEIGHT], 'Disarray',
                                 disarray_WEIGHT_stats, 'WEIGHT', '%')
    s3 = compile_results_strings(mtrx_of_local_fa, 'Fractional Anisotropy',
                                 fa_stats)
    disarray_and_fa_results_strings = s1 + ['\n\n\n'] + s2 + ['\n\n\n'] + s3

    # update mess strings
    mess_strings = mess_strings + disarray_and_fa_results_strings

    # create results .txt filename and path
    txt_results_filename = 'results_disarray_by_{}_G({},{},{})_limNeig{}.txt'.format(
        R_prefix, int(shape_G[0]), int(shape_G[1]), int(shape_G[2]),
        int(parameters['neighbours_lim']))

    # save to .csv
    if _save_csv:
        mess_strings.append('\n> CSV files saved to:')

        # save disarray and fractional anisotropy matrices to .csv file
        for (mtrx, np_fname) in zip([
                mtrx_of_disarrays[Mode.ARITH], mtrx_of_disarrays[Mode.WEIGHT],
                mtrx_of_local_fa
        ], [
                disarray_np_filename[Mode.ARITH],
                disarray_np_filename[Mode.WEIGHT], fa_np_filename
        ]):

            # extract only valid values (different from INV = -1)
            values = mtrx[mtrx != CONST.INV]

            # create .csv file path and save data
            csv_filename = np_fname.split('.')[0] + '.csv'
            csv_filepath = os.path.join(base_path, process_folder,
                                        csv_filename)
            np.savetxt(csv_filepath, values, delimiter=",", fmt='%f')
            mess_strings.append('> {}'.format(csv_filepath))

    # save histograms
    if _save_hist:
        mess_strings.append('\n> Histogram plots are saved in:')

        # zip matrices, description and filenames
        for (mtrx, lbl, np_fname) in zip([
                mtrx_of_disarrays[Mode.ARITH], mtrx_of_disarrays[Mode.WEIGHT],
                mtrx_of_local_fa
        ], [
                'Local Disarray % (Arithmetic mean)',
                'Local Disarray % (Weighted mean)',
                'Local Fractional Anisotropy'
        ], [
                disarray_np_filename[Mode.ARITH],
                disarray_np_filename[Mode.WEIGHT], fa_np_filename
        ]):

            # extract only valid values (different of INV = -1)
            values = mtrx[mtrx != CONST.INV]

            # create file path
            hist_fname = '.'.join(np_fname.split('.')[:-1]) + '.tiff'
            hist_filepath = os.path.join(base_path, process_folder, hist_fname)

            # create histograms and save them to image files
            plot_histogram(values,
                           xlabel=lbl,
                           ylabel='Sub-volume occurrence',
                           filepath=hist_filepath)
            mess_strings.append('> {}'.format(hist_filepath))

    # save disarray and fa maps
    if _save_maps:
        mess_strings.append(
            '\n> Disarray and Fractional Anisotropy plots saved to:')

        # disarray value normalization:
        #  - in order to preserve the little differences between ARITM and WEIGH disarray matrices,
        #    these are normalized together
        #  - invalid values are NOT removed for preserving the original matrix (image) shape
        #  - invalid values (if present) are set to the minimum value
        abs_max = np.max([
            mtrx_of_disarrays[Mode.ARITH].max(),
            mtrx_of_disarrays[Mode.WEIGHT].max()
        ])
        abs_min = np.min([
            mtrx_of_disarrays[Mode.ARITH].min(),
            mtrx_of_disarrays[Mode.WEIGHT].min()
        ])
        dis_norm_A = 255 * ((mtrx_of_disarrays[Mode.ARITH] - abs_min) /
                            (abs_max - abs_min))
        dis_norm_W = 255 * ((mtrx_of_disarrays[Mode.WEIGHT] - abs_min) /
                            (abs_max - abs_min))

        # define destination folder
        dest_folder = os.path.join(base_path, process_folder)

        # create and save data frames (disarray and fractional anisotropy)
        for (mtrx,
             np_fname) in zip([dis_norm_A, dis_norm_W, mtrx_of_local_fa], [
                 disarray_np_filename[Mode.ARITH],
                 disarray_np_filename[Mode.WEIGHT], fa_np_filename
             ]):

            # plot frames and save them inside a sub_folder (folder_path)
            folder_path = plot_map_and_save(mtrx, np_fname, dest_folder,
                                            shape_G, shape_P)
            mess_strings.append('> {}'.format(folder_path))

    # print information to screen and add it to the results .txt file
    txt_results_filepath = os.path.join(base_path, process_folder,
                                        txt_results_filename)
    write_on_txt(mess_strings, txt_results_filepath, _print=True, mode='w')
Beispiel #7
0
def main(parser):

    args = parser.parse_args()

    # Extract input information
    source_path = manage_path_argument(args.source_path)
    parameter_filename = args.parameters_filename[0]

    # extract filenames and folders
    stack_name = os.path.basename(source_path)
    process_folder = os.path.basename(os.path.dirname(source_path))
    base_path = os.path.dirname(os.path.dirname(source_path))
    parameter_filepath = os.path.join(base_path, process_folder,
                                      parameter_filename)
    stack_prefix = stack_name.split('.')[0]

    # extract other preferences
    _verbose = args.verbose
    _save_csv = args.csv

    # create sointroductiveme informations
    mess_strings = list()
    mess_strings.append('\n\n*** ST orientation Analysis ***\n')
    mess_strings.append(' > source path: {}'.format(source_path))
    mess_strings.append(' > stack name: {}'.format(stack_name))
    mess_strings.append(' > process folder: {}'.format(process_folder))
    mess_strings.append(' > base path: {}'.format(base_path))
    mess_strings.append(' > Parameter filename: {}'.format(parameter_filename))
    mess_strings.append(' > Parameter filepath: {}'.format(parameter_filepath))
    mess_strings.append('')

    # TODO here added local_disarray_z_side and local_disarray_xy_side
    # extract parameters
    param_names = [
        'roi_xy_pix', 'px_size_xy', 'px_size_z', 'mode_ratio',
        'threshold_on_cell_ratio', 'local_disarray_xy_side',
        'local_disarray_z_side', 'neighbours_lim', 'fwhm_xy', 'fwhm_z'
    ]

    param_values = search_value_in_txt(parameter_filepath, param_names)

    # create dictionary of parameters
    parameters = {}
    mess_strings.append('\n\n*** Parameters used:')
    mess_strings.append(
        ' > Parameters extracted from {}\n'.format(parameter_filename))
    for i, p_name in enumerate(param_names):
        parameters[p_name] = float(param_values[i])
        mess_strings.append('> {} - {}'.format(p_name, parameters[p_name]))

    # Parameters of Acquisition System:
    # ratio between pixel size in z and xy
    ps_ratio = parameters['px_size_z'] / parameters['px_size_xy']

    # analysis block dimension in z-axis
    num_of_slices_P = int(parameters['roi_xy_pix'] / ps_ratio)

    row_P = col_P = int(parameters['roi_xy_pix'])
    shape_P = np.array((row_P, col_P, num_of_slices_P)).astype(np.int32)

    mess_strings.append('\n *** Analysis configuration')
    mess_strings.append(
        ' > Rapporto fra Pixel Size (z / xy) = {0:0.2f}'.format(ps_ratio))
    mess_strings.append(
        ' > Numero di slice selezionate per ogni ROI ({} x {}): {}'.format(
            row_P, col_P, num_of_slices_P))
    mess_strings.append(
        ' > Dimension of Parallelepiped: ({0},{1},{2}) pixel  ='
        '  [{3:2.2f} {4:2.2f} {5:2.2f}] um'.format(
            shape_P[0], shape_P[1], shape_P[2],
            row_P * parameters['px_size_xy'], col_P * parameters['px_size_xy'],
            num_of_slices_P * parameters['px_size_z']))

    # create result.txt filename:
    txt_filename = 'Orientations_' + stack_prefix + '_' \
                   + str(int(parameters['roi_xy_pix'] * parameters['px_size_xy'])) + 'um.txt'
    txt_path = os.path.join(os.path.dirname(source_path), txt_filename)

    # print and write into .txt introductive informations
    write_on_txt(mess_strings, txt_path, _print=True)
    # clear list of strings
    mess_strings.clear()

    # 1 ----------------------------------------------------------------------------------------------------
    # OPEN STACK

    # extract data - entire Volume: 'V'
    volume = InputFile(source_path).whole()
    # NB - in futuro va cambiata gestion assi
    volume = np.moveaxis(volume, 0, -1)  # (r, c, z) -> (z, y, x)

    # calculate dimension
    shape_V = np.array(volume.shape)
    pixel_for_slice = shape_V[0] * shape_V[1]
    total_voxel_V = pixel_for_slice * shape_V[2]

    mess_strings.append('\n\n*** Entire loaded Volume dimension:')
    mess_strings.append(' > Dimension if entire Volume : ({}, {}, {})'.format(
        shape_V[0], shape_V[1], shape_V[2]))
    mess_strings.append(
        ' > Pixel for slice            : {}'.format(pixel_for_slice))
    mess_strings.append(
        ' > Total voxel in Volume      : {}'.format(total_voxel_V))

    # extract list of math informations (as strings) about volume.npy variable
    info = print_info(volume,
                      text='\nVolume informations:',
                      _std=False,
                      _return=True)
    mess_strings = mess_strings + info

    # print and write into .txt
    write_on_txt(mess_strings, txt_path, _print=True)
    # clear list of strings
    mess_strings.clear()

    # 2 ----------------------------------------------------------------------------------------------------
    # CYCLE FOR BLOCKS EXTRACTION and ANALYSIS
    print('\n\n')
    print('*** Start Structure Tensor analysis... ')

    t_start = time.time()

    # create empty Result matrix
    R, shape_R = create_R(shape_V, shape_P)

    # estimate sigma of blurring for isotropic resolution
    sigma_blur = sigma_for_uniform_resolution(
        FWHM_xy=parameters['fwhm_xy'],
        FWHM_z=parameters['fwhm_z'],
        px_size_xy=parameters['px_size_xy'])
    perc = 0
    count = 0  # count iteration
    tot = np.prod(shape_R)
    print(' > Expected iterations : ', tot)

    for z in range(shape_R[2]):
        if _verbose: print('\n\n')
        print('{0:0.1f} % - z: {1:3}'.format(perc, z))
        for r in range(shape_R[0]):
            for c in range(shape_R[1]):

                start_coord = create_coord_by_iter(r, c, z, shape_P)
                slice_coord = create_slice_coordinate(start_coord, shape_P)

                perc = 100 * (count / tot)
                if _verbose: print('\n')

                # save init info in R
                R[r, c, z]['id_block'] = count
                R[r, c, z]['init_coord'] = start_coord

                # extract parallelepiped
                parall = volume[slice_coord]

                # check dimension (if iteration is on border of volume, add zero_pad)
                parall = pad_dimension(parall, shape_P)

                # If it's not all black...
                if np.max(parall) != 0:

                    # analysis of parallelepiped extracted
                    there_is_cell, there_is_info, results = block_analysis(
                        parall, shape_P, parameters, sigma_blur, _verbose)

                    # save info in R[r, c, z]
                    if there_is_cell: R[r, c, z]['cell_info'] = True
                    if there_is_info: R[r, c, z]['orient_info'] = True

                    # save results in R
                    if _verbose: print(' saved in R:  ')
                    for key in results.keys():
                        R[r, c, z][key] = results[key]
                        if _verbose:
                            print(' > {} : {}'.format(key, R[r, c, z][key]))

                else:
                    if _verbose: print('   block rejected   ')
                    print()

                count += 1

    block_with_cell = np.count_nonzero(R['cell_info'])
    block_with_info = np.count_nonzero(R['orient_info'])
    p_rejec_cell = 100 * (1 - (block_with_cell / count))
    p_rejec_info_tot = 100 * (1 - (block_with_info / count))
    p_rejec_info = 100 * (1 - (block_with_info / block_with_cell))

    t_process = time.time() - t_start

    mess_strings.append('\n\n*** Results of Orientation analysis:')
    mess_strings.append(' > Expected iterations : {}'.format(np.prod(shape_R)))
    mess_strings.append(' > total_ iteration : {}'.format(count))
    mess_strings.append(' > Time elapsed: {0:.3f} s'.format(t_process))
    mess_strings.append('\n > Total blocks: {}'.format(count))
    mess_strings.append(
        ' > block with cell : {0}, rejected from total: {1} ({2:0.1f}%)'.
        format(block_with_cell, count - block_with_cell, p_rejec_cell))
    mess_strings.append(
        ' > block with gradient information : {}'.format(block_with_info))
    mess_strings.append(' > rejected from total: {0} ({1:0.1f}%)'.format(
        count - block_with_info, p_rejec_info_tot))
    mess_strings.append(
        ' > rejected from block with cell: {0} ({1:0.1f}%)'.format(
            block_with_cell - block_with_info, p_rejec_info))

    # print and write into .txt
    write_on_txt(mess_strings, txt_path, _print=True)
    # clear list of strings
    mess_strings.clear()

    # 3 ----------------------------------------------------------------------------------------------------
    # Disarray estimation

    # the function estimate local disarrays and write these values also inside R
    matrix_of_disarrays, shape_G, R = estimate_local_disarry(R,
                                                             parameters,
                                                             ev_index=2,
                                                             _verb=True,
                                                             _verb_deep=False)

    # extract only valid disarray values
    disarray_values = matrix_of_disarrays[matrix_of_disarrays != -1]

    # 4 ----------------------------------------------------------------------------------------------------
    # WRITE RESULTS AND SAVE

    # create result matrix (R) filename:
    R_filename = 'R_' + stack_prefix + '_' + str(
        int(parameters['roi_xy_pix'] * parameters['px_size_xy'])) + 'um.npy'
    R_prefix = R_filename.split('.')[0]
    R_filepath = os.path.join(base_path, process_folder, R_filename)

    # Save Results in R.npy
    np.save(R_filepath, R)
    mess_strings.append('\n > R matrix saved in: {}'.format(
        os.path.dirname(source_path)))
    mess_strings.append(' > with name: {}'.format(R_filename))

    mess_strings.append('\n > Results .txt file saved in: {}'.format(
        os.path.dirname(txt_path)))
    mess_strings.append(' > with name: {}'.format(txt_filename))

    # create filename of numpy.file where save disarray matrix
    disarray_numpy_filename = 'MatrixDisarray_{}_G({},{},{})_limNeig{}.npy'.format(
        R_prefix, int(shape_G[0]), int(shape_G[1]), int(shape_G[2]),
        int(parameters['neighbours_lim']))

    mess_strings.append('\n> Matrix of Disarray saved in:')
    mess_strings.append(os.path.join(base_path, process_folder))
    mess_strings.append(' > with name: \n{}'.format(disarray_numpy_filename))

    # save numpy file
    np.save(os.path.join(base_path, process_folder, disarray_numpy_filename),
            matrix_of_disarrays)

    # create results strings
    mess_strings.append(
        '\n\n*** Results of statistical analysis of Disarray on accepted points. \n'
    )
    mess_strings.append('> Disarray (%):= 100 * (1 - alignment)\n')
    mess_strings.append('> Matrix of disarray shape: {}'.format(
        matrix_of_disarrays.shape))
    mess_strings.append('> Valid disarray values: {}'.format(
        disarray_values.shape))
    mess_strings.append('\n> Disarray mean: {0:0.2f}%'.format(
        np.mean(disarray_values)))
    mess_strings.append('> Disarray std: {0:0.2f}% '.format(
        np.std(disarray_values)))
    mess_strings.append('> Disarray (min, MAX)%: ({0:0.2f}, {1:0.2f})'.format(
        np.min(disarray_values), np.max(disarray_values)))

    # create results.txt filename and filepath
    disarray_results_filename = 'results_disarray_by_{}_G({},{},{})_limNeig{}.txt'.format(
        R_prefix, int(shape_G[0]), int(shape_G[1]), int(shape_G[2]),
        int(parameters['neighbours_lim']))

    disarray_txt_filepath = os.path.join(base_path, process_folder,
                                         disarray_results_filename)

    if _save_csv:
        disarray_csv_filename = disarray_results_filename.split(
            '.')[0] + '.csv'
        np.savetxt(os.path.join(base_path, process_folder,
                                disarray_csv_filename),
                   disarray_values,
                   delimiter=",",
                   fmt='%f')

    # print and write into .txt
    write_on_txt(mess_strings, disarray_txt_filepath, _print=True)