Пример #1
0
def batch_evaluate_image_quality(path, options):
    """
    Batch calculate quality features for images in a directory
    :param options: options for the quality ranking scripts, as in miplib/ui/image_quality_options.py
    :parame path:   directory that contains the images to be analyzed
    """

    df = pd.DataFrame(columns=["Filename", "tEntropy", "tBrenner", "fMoments", "fMean", "fSTD", "fEntropy",
               "fTh", "fMaxPw", "Skew", "Kurtosis", "MeanBin", "Resolution"])

    for idx, image_name in enumerate(os.listdir(path)):
        if options.file_filter is None or options.file_filter in image_name:
            real_path = os.path.join(path, image_name)
            # Only process images
            if not os.path.isfile(real_path) or not real_path.endswith((".jpg", ".tif", ".tiff", ".tif")):
                continue
            # ImageJ files have particular TIFF tags that can be processed correctly
            # with the options.imagej switch
            image = read.get_image(real_path, channel=options.rgb_channel)

            # Only grayscale images are processed. If the input is an RGB image,
            # a channel can be chosen for processing.
            results = evaluate_image_quality(image, options)
            results.insert(0, real_path)

            # Add resolution value to the end
            results.append(frc.calculate_single_image_frc(image, options).resolution["resolution"])

            df.loc[idx] = results

            print ("Done analyzing {}".format(image_name))

    return df
        
Пример #2
0
def read_tiff_sequence(path, detectors=25, channels=1):
    """
    Construct ArrayDetectorData from a series of TIF images on disk. The images
    should be named in a way that the detector channels are in a correct order
    ((det_0, channel_0), (det_0, channel_1),  (det_1, channel_0), (det_1, channel_1))
    after a simple sort.

    :param path: the directory that contains the images
    :param detectors: number of pixels in the array detectors
    :param channels: number of channels. Can denote photodetectors (pixel time split),
    color channels, time-points etc.

    :return: the ArrayDetectorData object that cotnains the imported data
    """

    files = sorted(filter(lambda x: x.endswith(".tif"), os.listdir(path)))
    if len(files) != detectors * channels:
        raise RuntimeError(
            "The number of images does not match the data definition.")

    data = ArrayDetectorData(detectors, channels)
    steps = itertools.product(range(channels), range(detectors))
    for idx, (channel, detector) in enumerate(steps):
        image = imread.get_image(os.path.join(path, files[idx]),
                                 bioformats=False)
        data[detector, channel] = image

    return data
def batch_evaluate_frc(path, options):
    """
    Batch calculate FRC resolution for files placed in a directory
    :param options: options for the FRC
    :parame path:   directory that contains the images to be analyzed
    """
    assert os.path.isdir(path)

    measures = FourierCorrelationDataCollection()
    image_names = []

    for idx, image_name in enumerate(sorted(os.listdir(path))):

        real_path = os.path.join(path, image_name)
        # Only process images. The bioformats reader can actually do many more file formats
        # but I was a little lazy here, as we usually have tiffs.
        if not os.path.isfile(real_path) or not real_path.endswith(
            (".tiff", ".tif")):
            continue
        # ImageJ files have particular TIFF tags that can be processed correctly
        # with the options.imagej switch
        image = imread.get_image(real_path)

        # Only grayscale images are processed. If the input is an RGB image,
        # a channel can be chosen for processing.
        measures[idx] = calculate_single_image_frc(image, options)

        image_names.append(image_name)

    return measures, image_names
Пример #4
0
def main():
    path = sys.argv[1]
    assert os.path.isdir(path), path

    # Create output directory
    output_dir = os.path.join(path, "Subtracted")
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    for image_name in os.listdir(path):
        real_path = os.path.join(path, image_name)
        if not os.path.isfile(real_path) or not real_path.endswith(".jpg"):
            continue
        image_red = read.get_image(real_path, channel=0)
        image_green = read.get_image(real_path, channel=1)

        image_sub = image_green - image_red

        save_name = "sub_" + image_name
        save_path = os.path.join(path, output_dir)
        save_path = os.path.join(save_path, save_name)
        write.image(image_sub, save_path)
        print("Saved %s to %s" % (save_name, save_path))
Пример #5
0
def main():
    options = miplib_entry_point_options.get_power_script_options(sys.argv[1:])
    path = options.working_directory

    assert os.path.isdir(path)

    # Create output directory
    output_dir = datetime.datetime.now().strftime("%Y-%m-%d") + '_PyIQ_output'
    output_dir = os.path.join(options.working_directory, output_dir)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # Create output file
    date_now = datetime.datetime.now().strftime("%H-%M-%S")
    file_name = date_now + '_PyIQ_power_spectra' + '.csv'
    file_path = os.path.join(output_dir, file_name)

    csv_data = pandas.DataFrame()

    # Scan through images
    for image_in in os.listdir(path):
        if not image_in.endswith((".jpg", ".tif", ".tiff", ".png")):
            continue
        path_in = os.path.join(path, image_in)

        # Get image
        image = read.get_image(path_in, channel=options.rgb_channel)
        image = improc.crop_to_rectangle(image)

        for dim in image.shape:
            if dim != options.image_size:
                image = improc.resize(image, options.image_size)
                break

        task = filters.FrequencyQuality(image, options)
        task.calculate_power_spectrum()
        task.calculate_summed_power()

        power_spectrum = task.get_power_spectrum()

        csv_data[image_in] = power_spectrum[1]

    csv_data.insert(0, "Power", numpy.linspace(0, 1, num=len(csv_data)))
    csv_data.to_csv(file_path, index=False)
Пример #6
0
def main():
    path = sys.argv[1]
    assert os.path.isdir(path), path
    channel = sys.argv[2]

    # Create output directory
    output_dir = os.path.join(path, "Extracted")
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    for image_name in os.listdir(path):
        real_path = os.path.join(path, image_name)
        if not os.path.isfile(real_path):
            continue
        image = read.get_image(real_path, channel=channel)

        save_name = "channel_" + channel + "_" + image_name
        save_path = os.path.join(path, output_dir)
        save_path = os.path.join(save_path, save_name)
        write.image(image, save_path)
        print("Saved %s to %s" % (save_name, save_path))
Пример #7
0
def main():
    args = options.get_deconvolve_script_options(sys.argv[1:])

    # Load image
    image_name = args.image
    dir = args.working_directory
    image_path = os.path.join(dir, image_name)

    # Figure out an image type and use the correct loader.
    if image_path.endswith('.tif'):
        image = imread.get_image(image_path)
    elif image_path.endswith('.mat'):
        image = imread.get_image(image_path)[args.carma_gate_idx,
                                             args.carma_det_idx]
    else:
        raise AttributeError("Unknown image type %s" %
                             image_path.split('.')[-1])

    # Load/Generate PSF
    if args.psf == "estimate":
        psf = psfgen.PSF(psftype=args.psf_type,
                         shape=args.psf_shape,
                         dims=args.psf_size,
                         ex_wavelen=args.ex_wl,
                         em_wavelen=args.em_wl,
                         num_aperture=args.na,
                         refr_index=args.refractive_index,
                         magnification=args.magnification,
                         pinhole_radius=args.pinhole_radius)

        if args.sted_psf:
            psf.sted_correction(args.sted_phi, args.sted_sigma)

            psf_image = psf.volume()
            psf_spacing = psf.dims['um'][0] / psf.shape[0]

            if image.get_dimensions()[0] == 1:
                psf_image = psf_image[psf_image.shape / 2]

            psf = image.Image(psf_image, psf_spacing)

    else:
        psf = image.Image.get_image_from_imagej_tiff(args.psf_path)

    # Start deconvolution
    if cuda_compatible():
        print("Found a compatible GPU. The image deconvolution will be run with " \
              "hardware acceleration.")
        task = deconvolve_cuda.DeconvolutionRLCuda(image, psf, args)
    else:
        task = deconvolve.DeconvolutionRL(image, psf, args)

    begin = time.time()
    task.execute()
    end = time.time()
    result = task.get_result()

    print("Deconvolution complete.")
    print("The deconvolution process with %i iterations " \
          "took %s (H:M:S) to complete." % (args.max_nof_iterations,
                                            ops_output.format_time_string(
                                                end - begin)))
    if uiutils.get_user_input("Do you want to save the result to TIFF? "):
        file_path = os.path.join(args.working_directory, "fusion_result.tif")
        result.save_to_tiff(file_path)
Пример #8
0
def main():
    options = miplib_entry_point_options.get_import_script_options(sys.argv[1:])
    directory = options.data_dir_path

    # Create a new HDF5 file. If a file exists, new data will be appended.
    file_name = input("Give a name for the HDF5 file: ")
    file_name += ".hdf5"
    data_path = os.path.join(directory, file_name)
    data = image_data.ImageData(data_path)

    # Add image files that have been named according to the correct format
    for image_name in os.listdir(directory):
        full_path = os.path.join(directory, image_name)

        if full_path.endswith((".tiff", ".tif", ".mhd", ".mha")):
            images = read.get_image(full_path)
            spacing = images.spacing
        else:
            continue

        if options.normalize_inputs:
            images = (images * (255.0/images.max())).astype(numpy.uint8)

        if not all(x in image_name for x in params_c) or not any(x in image_name for x in image_types_c):
            print("Unrecognized image name %s. Skipping it." % image_name)
            continue

        image_type = image_name.split("_scale")[0]
        scale = image_name.split("scale_")[-1].split("_index")[0]
        index = image_name.split("index_")[-1].split("_channel")[0]
        channel = image_name.split("channel_")[-1].split("_angle")[0]
        angle = image_name.split("angle_")[-1].split(".")[0]

        assert all(x.isdigit() for x in (scale, index, channel, angle))
        # data, angle, spacing, index, scale, channel, chunk_size=None

        if image_type == "original":
            data.add_original_image(images, scale, index, channel, angle, spacing)
        elif image_type == "registered":
            data.add_registered_image(images, scale, index, channel, angle, spacing)
        elif image_type == "psf":
            data.add_psf(images, scale, index, channel, angle, spacing)

    # Calculate resampled images
    if options.scales is not None:
        for scale in options.scales:
            print("Creating %s percent downsampled versions of the original images" % scale)
            data.create_rescaled_images("original", scale)

    # Add transforms for registered images.
    for transform_name in os.listdir(directory):
        if not transform_name.endswith(".txt"):
            continue

        if not all(x in transform_name for x in params_c) or not "transform" in transform_name:
            print("Unrecognized transform name %s. Skipping it." % transform_name)
            continue

        scale = transform_name.split("scale_")[-1].split("_index")[0]
        index = transform_name.split("index_")[-1].split("_channel")[0]
        channel = transform_name.split("channel_")[-1].split("_angle")[0]
        angle = transform_name.split("angle_")[-1].split(".")[0]

        full_path = os.path.join(directory, transform_name)

        # First calculate registered image if not in the data structure
        if not data.check_if_exists("registered", index, channel, scale):
            print("Resampling registered image for image nr. ", index)
            data.set_active_image(0, channel, scale, "original")
            reference = data.get_itk_image()
            data.set_active_image(index, channel, scale, "original")
            moving = data.get_itk_image()

            transform = read.__itk_transform(full_path, return_itk=True)

            registered = itkutils.resample_image(moving, transform, reference=reference)
            registered = itkutils.convert_from_itk_image(registered)
            spacing = registered.spacing

            data.add_registered_image(registered, scale, index, channel, angle, spacing)

        # The add it's transform
        transform_type, params, fixed_params = read.__itk_transform(full_path)
        data.add_transform(scale, index, channel, params, fixed_params, transform_type)

    # Calculate missing PSFs
    if options.calculate_psfs:
        data.calculate_missing_psfs()

    if options.copy_registration_result != -1:
        from_scale = options.copy_registration_result[0]
        to_scale = options.copy_registration_result[1]
        data.copy_registration_result(from_scale, to_scale)

    data.close()
Пример #9
0
def main():
    options = supertomo_options.get_transform_script_options(sys.argv[1:])
    fixed_image = None
    moving_image = None
    transform = None

    if options.hdf:
        raise NotImplementedError("Only single image files are supported "
                                  "currently")
    else:
        # CHECK FILES
        # Check that the fixed image exists
        fixed_image_path = os.path.join(
            options.working_directory,
            options.fixed_image
        )

        if not os.path.isfile(fixed_image_path):
            raise ValueError('No such file: %s' % options.fixed_image)

        # Check that the EM image exists
        moving_image_path = os.path.join(
            options.working_directory,
            options.moving_image)

        if not os.path.isfile(moving_image_path):
            raise ValueError('No such file: %s' % options.moving_image)

        transform_path = os.path.join(
            options.working_directory,
            options.transform)

        if not os.path.isfile(transform_path):
            raise ValueError('No such file: %s' % options.transform)

        # READ FILES
        fixed_image = ioutils.get_image(fixed_image_path, return_itk=True)
        moving_image = ioutils.get_image(moving_image_path, return_itk=True)
        transform = ioutils.__itk_transform(transform_path, return_itk=True)

        transformed_image = itkutils.resample_image(moving_image,
                                                    transform,
                                                    fixed_image)

        # OUTPUT
        ##########################################################################
        output_dir = datetime.datetime.now().strftime("%Y-%m-%d") + '_supertomo_out'
        output_dir = os.path.join(options.working_directory, output_dir)

        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        # Files are named according to current time (the date will be
        # in the folder name)
        date_now = datetime.datetime.now().strftime("%H-%M-%S")

        file_name = date_now + '_transformed.mha'

        rgb_image = itkutils.make_composite_rgb_image(fixed_image,
                                                               transformed_image)

        image_path = os.path.join(output_dir, file_name)
        sitk.WriteImage(rgb_image, image_path)
Пример #10
0
def main():
    """
    The Main program of the PyImageQualityRanking software.
    """
    options = miplib_entry_point_options.get_quality_script_options(
        sys.argv[1:])
    path = options.working_directory
    file_path = None
    csv_data = None

    print("Mode option is %s" % options.mode)

    if "file" in options.mode:
        # In "file" mode a single file is analyzed and the various parameter
        # values are printed on screen. This functionality is provided mainly
        # for debugging purposes.
        assert options.file is not None, "You have to specify a file with a " \
                                         "--file option"
        path = os.path.join(path, options.file)
        assert os.path.isfile(path)
        image = read.get_image(path, channel=options.rgb_channel)

        print("The shape is %s" % str(image.shape))

        task = filters.LocalImageQuality(image, options)
        task.set_smoothing_kernel_size(100)
        entropy = task.calculate_image_quality()
        task2 = filters.FrequencyQuality(image, options)
        finfo = task2.analyze_power_spectrum()

        print("SPATIAL MEASURES:")
        print("The entropy value of %s is %f" % (path, entropy))
        print("ANALYSIS OF THE POWER SPECTRUM TAIL")
        print("The mean is: %e" % finfo[0])
        print("The std is: %e" % finfo[1])
        print("The entropy is %e" % finfo[2])
        print("The threshold frequency is %f Hz" % finfo[3])
        print("Power at high frequencies %e" % finfo[4])
        print("The skewness is %f" % finfo[5])
        print("The kurtosis is %f" % finfo[6])

    if "directory" in options.mode:
        # In directory mode every image in a given directory is analyzed in a
        # single run. The analysis results are saved into a csv file.

        assert os.path.isdir(path), path

        # Create output directory
        output_dir = datetime.datetime.now().strftime(
            "%Y-%m-%d") + '_PyIQ_output'
        output_dir = os.path.join(options.working_directory, output_dir)
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
        # Create output file
        date_now = datetime.datetime.now().strftime("%H-%M-%S")
        file_name = date_now + '_PyIQ_out' + '.csv'
        file_path = os.path.join(output_dir, file_name)
        output_file = open(file_path, 'wt')
        output_writer = csv.writer(output_file,
                                   quoting=csv.QUOTE_NONNUMERIC,
                                   delimiter=",")
        output_writer.writerow(
            ("Filename", "tEntropy", "tBrenner", "fMoments", "fMean", "fSTD",
             "fEntropy", "fTh", "fMaxPw", "Skew", "Kurtosis", "MeanBin"))

        for image_name in os.listdir(path):
            if options.file_filter is None or options.file_filter in image_name:
                real_path = os.path.join(path, image_name)
                # Only process images
                if not os.path.isfile(real_path) or not real_path.endswith(
                    (".jpg", ".tif", ".tiff", ".png")):
                    continue
                # ImageJ files have particular TIFF tags that can be processed correctly
                # with the options.imagej switch
                image = read.get_image(path, channel=options.rgb_channel)

                # Only grayscale images are processed. If the input is an RGB image,
                # a channel can be chosen for processing.

                # Time series sometimes contain images of very different content: the start
                # of the series may show nearly empty (black) images, whereas at the end
                # of the series the whole field-of-view may be full of cells. Ranking such
                # dataset in a single piece may be challenging. Therefore the beginning of
                # the dataset can be separated from the end, by selecting a minimum value
                # for average grayscale pixel value here.
                if options.average_filter > 0 and image.average(
                ) < options.average_filter:
                    continue

                # Run spatial domain analysis
                task = filters.LocalImageQuality(image, options)
                task.set_smoothing_kernel_size(100)
                entropy = task.calculate_image_quality()
                # Run frequency domain analysis
                task2 = filters.FrequencyQuality(image, options)
                results = task2.analyze_power_spectrum()

                task3 = filters.SpectralMoments(image, options)
                moments = task3.calculate_spectral_moments()

                task4 = filters.BrennerImageQuality(image, options)
                brenner = task4.calculate_brenner_quality()

                # Save results
                results.insert(0, moments)
                results.insert(0, brenner)
                results.insert(0, entropy)
                results.insert(0, os.path.join(path, image_name))
                output_writer.writerow(results)

                print("Done analyzing %s" % image_name)

        output_file.close()
        print("The results were saved to %s" % file_path)

    if "analyze" in options.mode:
        # In analyze mode the previously created quality ranking variables are
        # normalized to the highest value of every given variable. In addition
        # some new parameters are calculated. The results are saved into a new
        # csv file.
        if file_path is None:
            assert options.file is not None, "You have to specify a data file" \
                                             "with the --file option"
            path = os.path.join(options.working_directory, options.file)
            print(path)
            file_path = path
            assert os.path.isfile(path), "Not a valid file %s" % path
            assert path.endswith(
                ".csv"), "Unknown suffix %s" % path.split(".")[-1]

        csv_data = pandas.read_csv(file_path)
        csv_data["cv"] = csv_data.fSTD / csv_data.fMean
        csv_data["SpatEntNorm"] = csv_data.tEntropy / csv_data.tEntropy.max()
        csv_data["SpectMean"] = csv_data.fMean / csv_data.fMean.max()
        csv_data["SpectSTDNorm"] = csv_data.fSTD / csv_data.fSTD.max()
        csv_data["InvSpectSTDNorm"] = 1 - csv_data.SpectSTDNorm
        csv_data["SpectEntNorm"] = csv_data.fEntropy / csv_data.fEntropy.max()
        csv_data["SkewNorm"] = 1 - abs(csv_data.Skew) / abs(
            csv_data.Skew).max()
        csv_data["KurtosisNorm"] = abs(csv_data.Kurtosis) / abs(
            csv_data.Kurtosis).max()
        csv_data["SpectHighPowerNorm"] = csv_data.fMaxPw / csv_data.fMaxPw.max(
        )
        csv_data["MeanBinNorm"] = csv_data.MeanBin / csv_data.MeanBin.max()
        csv_data["BrennerNorm"] = csv_data.tBrenner / csv_data.tBrenner.max()
        csv_data[
            "SpectMomentsNorm"] = csv_data.fMoments / csv_data.fMoments.max()

        # Create output directory
        output_dir = datetime.datetime.now().strftime(
            "%Y-%m-%d") + '_PyIQ_output'
        output_dir = os.path.join(options.working_directory, output_dir)
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
        date_now = datetime.datetime.now().strftime("%H-%M-%S")
        file_name = date_now + '_PyIQ_analyze_out' + '.csv'
        file_path = os.path.join(output_dir, file_name)

        csv_data.to_csv(file_path)
        print("The results were saved to %s" % file_path)

    if "plot" in options.mode:
        # With the plot option the dataset is sorted according to the desired ranking variable.
        # The changes are saved to the original csv file. In addition a plot is created to show
        # a subset of highest and lowest ranked images (the amount of images to show is
        # controlled by the options.npics parameter
        if csv_data is None:
            file_path = os.path.join(options.working_directory, options.file)
            assert os.path.isfile(file_path), "Not a valid file %s" % path
            assert path.endswith(
                ".csv"), "Unknown suffix %s" % path.split(".")[-1]
            csv_data = pandas.read_csv(file_path)
        if options.result == "average":
            csv_data["Average"] = csv_data[["InvSpectSTDNorm",
                                            "SpatEntNorm"]].mean(axis=1)
            csv_data.sort(columns="Average", ascending=False, inplace=True)
        elif options.result == "fskew":
            csv_data.sort(columns="SkewNorm", ascending=False, inplace=True)
        elif options.result == "fentropy":
            csv_data.sort(columns="SpectEntNorm",
                          ascending=False,
                          inplace=True)
        elif options.result == "ientropy":
            csv_data.sort(columns="SpatEntNorm", ascending=False, inplace=True)
        elif options.result == "icv":
            csv_data.sort(columns="SpatEntNorm", ascending=False, inplace=True)
        elif options.result == "fstd":
            csv_data.sort(columns="SpectSTDNorm",
                          ascending=False,
                          inplace=True)
        elif options.result == "fkurtosis":
            csv_data.sort(columns="KurtosisNorm",
                          ascending=False,
                          inplace=True)
        elif options.result == "fpw":
            csv_data.sort(columns="SpectHighPowerNorm",
                          ascending=False,
                          inplace=True)
        elif options.result == "fmean":
            csv_data.sort(columns="SpectHighPowerNorm",
                          ascending=False,
                          inplace=True)
        elif options.result == "meanbin":
            csv_data.sort(columns="MeanBinNorm", ascending=False, inplace=True)
        else:
            print("Unknown results sorting method %s" % options.result)
            sys.exit()

        best_pics = csv_data["Filename"].head(options.npics).as_matrix()
        worst_pics = csv_data["Filename"].tail(options.npics).as_matrix()
        show_pics_from_disk(best_pics, title="BEST PICS")
        show_pics_from_disk(worst_pics, title="WORST PICS")

        csv_data.to_csv(file_path, index=False)
Пример #11
0
def main():

    # Get input arguments
    args = options.get_frc_script_options(sys.argv[1:])
    path = args.directory

    # Create output directory
    output_dir = args.directory
    date_now = datetime.datetime.now().strftime("%H-%M-%S")

    filename = "{}_miplib_{}_frc_results.csv".format(date_now, args.frc_mode)
    filename = os.path.join(output_dir, filename)

    # Get image file names, sort in alphabetic order and complete.
    files_list = list(i for i in os.listdir(path)
                      if i.endswith((".jpg", ".tif", ".tiff", ".png")))
    files_list.sort()
    print('Number of images to analyze: {}'.format(len(files_list)))

    #df_main = pandas.DataFrame(0, index=np.arange(len(files_list)), columns=["Image", "Depth", "Kind", "Resolution"])
    df_main = pandas.DataFrame(0,
                               index=np.arange(len(files_list)),
                               columns=["Image", "Resolution"])

    if args.frc_mode == "two-image":

        def pairwise(iterable):
            a = iter(iterable)
            return zip(a, a)

        for idx, im1, im2 in enumerate(pairwise(files_list)):
            # Read images
            image1 = imread.get_image(os.path.join(path, im1))
            image2 = imread.get_image(os.path.join(path, im2))

            result = frc.calculate_two_image_frc(image1, image2, args)
            title = strutils.common_start(im1, im2)

            resolution = result.resolution['resolution']
            df_main.iloc[idx] = title, resolution

    elif args.frc_mode == "one-image":
        for idx, im in enumerate(files_list):
            image = imread.get_image(os.path.join(path, im))

            print("Analyzing image {}".format(im))

            result = frc.calculate_single_image_frc(image, args)

            title = im.split('.')[0]

            # I left these snippets here to show how one can add additional info
            # to the dataframes in particular use cases.

            #depth = title.split('um_')[0].split("_")[-1]

            # kind = None
            # for x in ("apr_ism", "apr_ism_bplus", "closed", "open", "static_ism", "ism_sim"):
            #     if x in title:
            #         kind = x
            # if kind is None:
            #     raise RuntimeError("Unknown image: {}".format(title))
            resolution = result.resolution['resolution']
            #df_main.iloc[idx] = title, depth, kind, resolution
            df_main.iloc[idx] = title, resolution

    else:
        raise NotImplementedError()

    df_main.index = list(range(len(df_main)))
    df_main.to_csv(filename)