# -*- coding: utf-8 -*-
from setuptools import setup

package_dir = \
{'': 'src'}

packages = \
['analyzefrc']

package_data = \
{'': ['*']}

install_requires = \
['Pillow>=8.3.2,<9.0.0',
 'deco>=0.6.3,<0.7.0',
 'frc>=0.1.0,<0.2.0',
 'loess>=2.1.1,<3.0.0',
 'matplotlib>=3.4,<4.0',
 'readlif>=0.6.5,<0.7.0']

setup_kwargs = {
    'name': 'analyzefrc',
    'version': '0.1.4',
    'description': 'Plots, analysis and resolution measurement of microscopy images using Fourier Ring Correlation (FRC).',
    'long_description': '# AnalyzeFRC\n\n[![PyPI version](https://badge.fury.io/py/analyzefrc.svg)](https://badge.fury.io/py/analyzefrc)\n\n*Developed at the Department of Imaging Physics (ImPhys), Faculty of Applied Sciences, TU Delft.*\n\nPlots, analysis and resolution measurement of microscopy images using Fourier Ring Correlation (FRC).\n\nAnalyzeFRC has native support for .lif files and can also easily read single images in formats supported by Pillow (PIL). Other formats require converting that image into a NumPy array and using that to instantiate AnalyzeFRC\'s native objects.\n\nAnalyzeFRC provides a lot of default options and convenience functions for a specific use case. However, its core functionality, the `measure_frc` function in `analyzefrc.process` can be adapted in other workflows. You can also directly use the [frc library](https://github.com/tmtenbrink/frc), on which this library is built.\n\n### Defaults (please read)\n\n- By default, when using `frc_process`, `preprocess` is set to True. It ensures that each input image is cropped into square form and that a Tukey window is applied. Supply `proprocess=False` to disable this behavior.\n- By default, when using `frc_process`, `concurrency` is set to True. This leverages the `deco` package to leverage more cores for a 1.5x+ speedup (not higher because the most resource-intensive computations are already parallelized). !! However, please run the program inside a `if __name__ == \'__main__\':` block when concurrency is enabled! Otherwise it will fail! You can also disable concurrency instead by passing `concurrency=False` to `process_frc`.\n- By default, if an `FRCMeasurement` is processed without any preset `CurveTask` and has two images, it sets the method to `2FRC`. Otherwise, `1FRC` is used.\n- By default, plots are grouped by `measures`, i.e. every measurement will be plotted separately. Use the `group_<grouping>`. Other available groupings include `all` (all curves in one plot, use this only to retrieve them to use custom groupings), `sets` (all curves in the same set name in one plot) and `curves` (one plot per curve).\n- By default, 1FRC curves are computed 5 times and averaged, this can be overriden by passing `override_n` to process_frc.\n\n### Installation\n\n#### With (Ana)conda\n\nIf you already have Anaconda installed (or miniconda), it is easiest to create a new Python 3.9 environment (here \'envanalyze\' can be any environment name you like). Open the Anaconda/miniconda3 prompt and write:\n\n```shell\nconda create -n \'envanalyze\' python=3.9\n```\n\nThis package depends on a number of PyPI-only packages, some also with compiled extensions, which are difficult to port to conda. For this reason, it is recommended to have a seperate environment with only this package and then install using pip:\n\n```shell\nconda activate envanalyze\npip install analyzefrc\n```\n\nYou now have an environment called \'envanalyze\' with analyzefrc installed. Configure your favorite IDE to use the newly created environment and you\'re good to go!  See the usage examples for more details on how to use this package.\n\n\n#### Without conda\n\nCurrently, this library only works on Python 3.9. Ensure you have a working installation. You can use tools like [pyenv](https://github.com/pyenv/pyenv) for managing Python versions. \n\nIt is recommended to install this library into a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/). Many tools exist for this today (most IDEs can do it for you), but I recommend [Poetry](https://github.com/python-poetry/poetry).\n\nInstall using:\n\n```shell\npip install analyzefrc\n```\n\nIf using Poetry:\n\n```shell\npoetry add analyzefrc\n```\n\nThis library indirectly (through the `frc` library) depends on [rustfrc](https://github.com/tmtenbrink/rustfrc) (Rust extension) and [diplib](https://github.com/diplib) (C++ extension). These compiled extensions can sometimes cause issues, so refer to their pages as well.\n\n\n### Usage\n\nTo simply compute the 1FRC of all channels of a .lif dataset and plot the results, you can do the following:\n\n```python\nimport analyzefrc as afrc\n\n# This if-statement is required because concurrency is enabled\nif __name__ == \'__main__\':\n    # ./ means relative to the current folder\n    frc_sets = afrc.lif_read(\'./data/sted/2021_10_05_XSTED_NileRed_variation_excitation_power_MLampe.lif\')\n    plot_curves = afrc.process_frc("XSTED_NileRed", frc_sets, preprocess=True, concurrency=True)\n    afrc.plot_all(plot_curves)\n```\n\nIf instead you want to plot each image inside a .lif file in a single plot, do the following:\n\n```python\n... # imports and processing\n\nplot_curves = afrc.process_frc("XSTED_NileRed", frc_sets, grouping=\'sets\', preprocess=True, concurrency=False)\nafrc.plot_all(plot_curves)\n```\nOr if you already computed the curves with the default grouping (\'all\'):\n\n```python\n... # imports and processing\n\nfrc_per_set_sets = afrc.group_sets(plot_curves)\nplot_all(frc_per_set_sets)\n```\n\nIf you don\'t want to plot the results (in the case of many images the IDE plot buffer can easily be exceeded), but instead save them:\n\n```python\n... # imports and processing\nimport analyzefrc as afrc\n\n# Will save to \'./results/<timestamp>-XSTED_NileRed\'\nsave_folder = afrc.create_save(\'./results\', \'XSTED_NileRed\', add_timestamp=True)\nafrc.plot_all(plot_curves, show=False, save=True, save_directory=save_folder, dpi=180)\n\n```\n\nA slightly more complex example: If you have a sample .tiff file and you want to compare the performance of 1FRC vs 2FRC, you could do the following:\n\n```python\nimport numpy as np\nimport diplib as dip\nimport frc.utility as frcu\nimport analyzefrc as afrc\nfrom analyzefrc import FRCMeasurement, FRCSet\n\ndata_array: np.ndarray = afrc.get_image(\'./data/siemens.tiff\')\n# Blur the image (to create a frequency band)\ndata_array = frcu.gaussf(data_array, 30)\ndata_dip = dip.Image(data_array)\nhalf_set_1 = np.array(dip.PoissonNoise(data_dip / 2))\nhalf_set_2 = np.array(dip.PoissonNoise(data_dip / 2))\nfull_set = np.array(dip.PoissonNoise(data_dip))\n\n# Create seperate measurement objects\nfrc_2: FRCMeasurement = afrc.frc_measure(half_set_1, half_set_2, set_name=\'2FRC\')\nfrc_1: FRCMeasurement = afrc.frc_measure(full_set, set_name=\'1FRC\')\n# Combine in one set so they can be plot together\nfrc_set: FRCSet = afrc.frc_set(frc_1, frc_2, name=\'2FRC vs 1FRC\')\nplot_curve = afrc.process_frc("2FRC vs 1FRC", frc_set, concurrency=False)\nafrc.plot_all(plot_curve)\n```\n\n### Details\n\nThe three operations of setting up the measurements, computing the curves and plotting them are all decoupled and each have their Python module (`analyzefrc.read`, `analyzefrc.process`, `analyzefrc.plot`, respectively). Furthermore, actual file reading convenience functions can be found in `analyzefrc.file_read`.\n\n#### FRCSet, FRCMeasurement and FRCMeasureSettings\n\nFor setting up the measurements in preparation of processing, these three classes are essential. `FRCSet`-objects can be completely unrelated, they share no information. As such, if doing batch processing of different datasets, they can be divided over `FRCSet`-objects.\nWithin an `FRCSet`, there can be an arbitrary number of `FRCMeasurement`-objects, which should have similar image dimensions and should, in theory, be able to be sensibly plotted in a single figure.\n\n`FRCMeasurement` is the main data container class. It can be instantiated using an `FRCMeasureSettings`-object, which contains important parameters that are the same across all images within the measurement (such as the objective\'s NA value). If these differ across the images, multiple measurements should be used.\n\n#### Changing default curves\n\nBy default, when processing, a single `CurveTask` will be generated for each `FRCMeasurement`, meaning a single curve will be generated for each measurement. However, if a different threshold (other than the 1/7) is desired, or multiple curves per figure are wanted, a `CurveTask` can be created beforehand and given to the `FRCMeasurement`.\n\nExample:\n\n```python\n... # see .tiff example\nfrom analyzefrc import CurveTask\n\n# Create seperate measurement objects\n# For example we want a smoothed curve for the 1FRC, as well as a non-smoothed curve\nfrc1_task_smooth = CurveTask(key=\'smooth_curve\', smooth=True, avg_n=3, threshold=\'half_bit\')\nfrc1_task = CurveTask(key=\'standard_curve\', avg_n=3, threshold=\'half_bit\')\n\nfrc_2: FRCMeasurement = afrc.frc_measure(half_set_1, half_set_2, set_name=\'2FRC\')\nfrc_1: FRCMeasurement = afrc.frc_measure(full_set, set_name=\'1FRC\', curve_tasks=[frc1_task, frc1_task_smooth])\n\n... # process and plot\n```\n\n#### Changing default processing\n\nIf other measurement-based processings are desired, they can be added in two ways. Arbitrary functions (of the type `MeasureProcessing = Callable[[FRCMeasurement], FRCMeasurement]`) can be run for each measurement by passing them as a list to the `extra_processings`-argument for `process_frc`, or by populating the `FRCMeasurement`-objects\' `extra_processings` attribute.\n\nNote: each processing is performed in list order after the optional `preprocessing` step, with global extras performed before the measurement-defined extra processing tasks.\n\nThis can be useful when using a convenience file loading function. For example, to flip every image and apply a different window functon:\n\n```python\n... # .lif example\nfrom analyzefrc import FRCMeasurement\nimport numpy as np\nfrom scipy.signal import windows as wins\n\ndef flip_window_data(measure: FRCMeasurement) -> FRCMeasurement:\n    measure.image = np.flip(measure.image)\n    size = measure.image.shape[0]\n    assert size == measure.image.shape[1]\n    \n    cosine = wins.tukey(size)\n    cosine_square = np.ones((size, size)) * cosine.reshape((size, 1)) * cosine.reshape((1, size))\n    measure.image = cosine_square * measure.image\n    \n    return measure\n\nplot_curves = afrc.process_frc("XSTED_NileRed", frc_sets, preprocess=False, extra_processings=[flip_window_data], concurrency=False)\n\n... # plot\n```\n\n#### Other internal details\n\nThe general processing flow is as follows:\n\n1. (`read`/`read_file`) Create `FRCMeasureSettings` based on data acquisition parameters\n2. (`read`/`read_file`) Create `FRCMeasurement` using the previous step.\n3. (Optionally) create custom `CurveTask`-objects for the `FRCMeasurement`. Created by default in the `process` step if not provided.\n4. (`read`/`read_file`) Create `FRCSet` using multiple `FRCMeasurement`-objects.\n5. (`process`) Compute `Curve`-objects using `measure_frc`.\n6. (`process`) Sort/group the `Curve`-objects into a dictionary with lists of `Curve`-objects as entries.\n7. (`plot`) Plot the `list[Curve]`-dictionary, where each entry becomes a single figure.\n\nAll steps besides the `measure_frc`-step can be implemented in a custom way quite trivially. In a way, all steps except step 5 are for your convenience. Step 5, which is the only step that involves actually processing all the data using the `frc` library, forms the core of this package.\n\n### Performance\n\nProcessing 32 measurements of 1024x1024 pixels takes about thirty seconds to read from a .lif file, process (computing each curve 5 times) and plot on my i7-8750H laptop CPU (which is decently performant even by today\'s standards). \n\nOver 80% of the time is spent processing, i.e. performing the binomial splitting and computing the FRCs (with the latter taking significantly longer). All these functions are implemented through Rust (rustfrc), C++ (diplib) or C (numpy) extensions, meaning they are as fast as can be.\n\n10-15% of the time is spent plotting using matplotlib, meaning the overhead of this library is only 5-10%. \n',
    'author': 'Tip ten Brink',
    'author_email': 'T.M.tenBrink@student.tudelft.nl',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'package_dir': package_dir,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.9,<3.10',
}


setup(**setup_kwargs)
