# PyArcFiRe
PyArcFiRe is a python port of [SpArcFiRe](https://github.com/waynebhayes/SpArcFiRe) which is written primarily in MatLab.
Like SpArcFiRe it can be used to detect spiral arcs in images, mostly for galaxy images however it perhaps may work in other contexts.
## Limitations
Note that this is currently a work in progress and the project may change greatly over time.
### Functionality
This port does not have all of the functionality and features of SpArcFiRe such as bar finding and fitting, automatic centering and deprojection, etc.
## Installation
You can install this package by simply using the command
```
$ pip install pyarcfire
```
## Interface
There are two main ways of using PyArcFiRe
1. As a python package to use in your own programs.
2. As a command line interface.
### Package
Create an instance of `SpiralFinder` and then call the `extract` method on a 2D array to run the spiral finding algorithm.
```python
from pyarcfire import SpiralFinder
import numpy as np
# Example: Load a grayscale image
image = ... # Replace with an actual 2D image array
# Create a SpiralFinder instance
finder = SpiralFinder()
# Extract spiral features
result = finder.extract(image)
```
Then to extract the spiral arm masks you can simply use the `mask` property to get a 2D array of integers. The non-zero values of the `mask` array is the cluster id of
the pixel.
For example, you can plot just the first cluster like this
```python
import matplotlib.pyplot as plt
fig = plt.figure()
axes = fig.add_subplot(111)
axes.imshow(result.mask == 1)
plt.show()
plt.close(fig)
```
The original image and the preprocessed image can be accessed as well.
```python
import matplotlib.pyplot as plt
fig = plt.figure()
original_axes = fig.add_subplot(121)
original_axes.imshow(result.original_image)
processed_axes = fig.add_subplot(122)
processed_axes.imshow(result.processed_image)
plt.show()
plt.close(fig)
```
Additionally, you can get the dominant chirality and overall pitch angle of the result like so
```python
print(result.get_dominant_chirality()) # Chirality.NONE | Chirality.CLOCKWISE | Chirality.COUNTER_CLOCKWISE
print(result.get_overall_pitch_angle()) # +- n degrees
```
or get the log spiral fits to each cluster and plot them like so
```python
import matplotlib.pyplot as plt
fig = plt.figure()
axes = fig.add_subplot(111)
for cluster_index in range(result.num_clusters):
fit = result.get_fit(cluster_index)
x, y = spiral_fit.calculate_cartesian_coordinates(100, pixel_to_distance=1, flip_y=False) # `pixel_to_distance` converts from pixel units to your desired distance units
axes.plot(x, y)
plt.show()
plt.close()
```
#### Algorithm Parameters
The spiral finding algorithm has many configurable parameters for each step.
When computing the orientation field the parameters are:
- `neighbour_distance (int)`: The distance in pixels between a cell and its neighbour when denoising the orientation field.
- `kernel_radius (int)`: The radius of the orientation filter kernel in pixels.
- `num_levels (int)`: The number of image rescalings to create orientation fields of and then join.
When computing the similarity matrix:
- `similarity_cutoff (float)`: The minimum allowed similarity between orientation field pixels.
When clustering pixels:
- `error_ratio_threshold (float)`: The maximum error ratio allowed for a merge between two clusters to be permitted.
- `merge_check_mininum_cluster_size (int)`: The maximum size of a cluster before merges with other clusters become checked.
- `minimum_cluster_size (int)`: The minimum cluster size allowed after all merges are completed.
- `remove_central_cluster (bool)`: A flag to remove remove the clusters that touch the center of the image.
When merging nearby clusters by checking their log spiral fits:
- `stop_threshold (float)`: The maximum merge error ratio before stopping merges.
These parameters can be configured by calling the associated methods on `SpiralFinder`
```python
from pyarcfire import SpiralFinder
# Create a SpiralFinder instance
finder = SpiralFinder()
# Adjust orientation field generation
finder = finder.with_orientation_field_settings(
neighbour_distance=4,
kernel_radius=None, # Set to `None` to keep the old value.
# num_levels=3, # Omit the parameter if you want to keep the old value as well.
)
# Adjust similarity matrix generation
finder = finder.with_similarity_matrix_settings(
cutoff=0.15,
)
# Adjust clustering
finder = finder.with_clustering_settings(
error_ratio_threshold=2.5,
merge_check_minimum_cluster_size=25,
minimum_cluster_size=120,
remove_central_cluster=False,
)
# Adjust merging by fit
finder = finder.with_merge_fit_settings(
stop_threshold=2.4,
)
```
#### Preprocessing
In order for an image to be ran through the algorithm, some preprocessing may be necessary. The requirements are that:
- The array is 2D.
- The array values are normalized in the range [0, 1].
- The array's height and width must be divisible by 2^N where N is the number of orientation field levels.
Therefore `SpiralFinder` will by default perform the following:
1. It will first check that the array is 2D. If not an exception will be raised.
2. It will normalize the values in the array using a linear scale.
3. It will resize the image so that the height and width are valid by finding the closest valid size.
Also as part of the algorithm, a contrast boosting step will be performed. By default this is an unsharp mask.
These preprocessing steps can be changed however and in fact can be turned entirely off.
```python
from pyarcfire import SpiralFinder
from pyarcfire.preprocess import ImageIdentityNormalizer
# Turn off all preprocessing
finder = SpiralFinder().with_normalizer(None).with_resizer(None).with_booster(None)
# Change the normalizer
finder = SpiralFinder().with_normalizer(ImageIdentityNormalizer())
```
You can create custom preprocessors as well by implementing the `ImageNormalizer`, `ImageResizer` and `ImageContrastBooster` protocols (see `pyarcfire.preprocess`).
### Command Line Interface
PyArcFiRe can also be interacted with through the command line interface via `python -m pyarcfire ...`. Currently this is a work in progress and is mainly
a way to drive debugging code.
Raw data
{
"_id": null,
"home_page": null,
"name": "pyarcfire",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10.15",
"maintainer_email": null,
"keywords": "arc, astronomy, finder, spiral",
"author": "Pavadol Yamsiri",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/21/29/22709b9a0922733fca400753262b5b024707f66198104fdc3a8fbfe33b55/pyarcfire-0.1.1.tar.gz",
"platform": null,
"description": "# PyArcFiRe\n\nPyArcFiRe is a python port of [SpArcFiRe](https://github.com/waynebhayes/SpArcFiRe) which is written primarily in MatLab.\nLike SpArcFiRe it can be used to detect spiral arcs in images, mostly for galaxy images however it perhaps may work in other contexts.\n\n## Limitations\n\nNote that this is currently a work in progress and the project may change greatly over time.\n\n### Functionality\n\nThis port does not have all of the functionality and features of SpArcFiRe such as bar finding and fitting, automatic centering and deprojection, etc.\n\n## Installation\n\nYou can install this package by simply using the command\n\n```\n$ pip install pyarcfire\n```\n\n## Interface\n\nThere are two main ways of using PyArcFiRe\n\n1. As a python package to use in your own programs.\n2. As a command line interface.\n\n### Package\n\nCreate an instance of `SpiralFinder` and then call the `extract` method on a 2D array to run the spiral finding algorithm.\n\n```python\nfrom pyarcfire import SpiralFinder\nimport numpy as np\n\n# Example: Load a grayscale image\nimage = ... # Replace with an actual 2D image array\n\n# Create a SpiralFinder instance\nfinder = SpiralFinder()\n\n# Extract spiral features\nresult = finder.extract(image)\n```\n\nThen to extract the spiral arm masks you can simply use the `mask` property to get a 2D array of integers. The non-zero values of the `mask` array is the cluster id of\nthe pixel.\n\nFor example, you can plot just the first cluster like this\n\n```python\nimport matplotlib.pyplot as plt\n\nfig = plt.figure()\naxes = fig.add_subplot(111)\naxes.imshow(result.mask == 1)\nplt.show()\nplt.close(fig)\n```\n\nThe original image and the preprocessed image can be accessed as well.\n\n```python\nimport matplotlib.pyplot as plt\n\nfig = plt.figure()\noriginal_axes = fig.add_subplot(121)\noriginal_axes.imshow(result.original_image)\nprocessed_axes = fig.add_subplot(122)\nprocessed_axes.imshow(result.processed_image)\nplt.show()\nplt.close(fig)\n```\n\nAdditionally, you can get the dominant chirality and overall pitch angle of the result like so\n\n```python\nprint(result.get_dominant_chirality()) # Chirality.NONE | Chirality.CLOCKWISE | Chirality.COUNTER_CLOCKWISE\nprint(result.get_overall_pitch_angle()) # +- n degrees\n```\n\nor get the log spiral fits to each cluster and plot them like so\n```python\nimport matplotlib.pyplot as plt\n\nfig = plt.figure()\naxes = fig.add_subplot(111)\n\nfor cluster_index in range(result.num_clusters):\n fit = result.get_fit(cluster_index)\n x, y = spiral_fit.calculate_cartesian_coordinates(100, pixel_to_distance=1, flip_y=False) # `pixel_to_distance` converts from pixel units to your desired distance units\n axes.plot(x, y)\nplt.show()\nplt.close()\n```\n\n#### Algorithm Parameters\n\nThe spiral finding algorithm has many configurable parameters for each step.\n\nWhen computing the orientation field the parameters are:\n\n- `neighbour_distance (int)`: The distance in pixels between a cell and its neighbour when denoising the orientation field.\n- `kernel_radius (int)`: The radius of the orientation filter kernel in pixels.\n- `num_levels (int)`: The number of image rescalings to create orientation fields of and then join.\n\nWhen computing the similarity matrix:\n\n- `similarity_cutoff (float)`: The minimum allowed similarity between orientation field pixels.\n\nWhen clustering pixels:\n\n- `error_ratio_threshold (float)`: The maximum error ratio allowed for a merge between two clusters to be permitted.\n- `merge_check_mininum_cluster_size (int)`: The maximum size of a cluster before merges with other clusters become checked.\n- `minimum_cluster_size (int)`: The minimum cluster size allowed after all merges are completed.\n- `remove_central_cluster (bool)`: A flag to remove remove the clusters that touch the center of the image.\n\nWhen merging nearby clusters by checking their log spiral fits:\n\n- `stop_threshold (float)`: The maximum merge error ratio before stopping merges.\n\nThese parameters can be configured by calling the associated methods on `SpiralFinder`\n\n```python\nfrom pyarcfire import SpiralFinder\n\n\n# Create a SpiralFinder instance\nfinder = SpiralFinder()\n\n# Adjust orientation field generation\nfinder = finder.with_orientation_field_settings(\n neighbour_distance=4,\n kernel_radius=None, # Set to `None` to keep the old value.\n # num_levels=3, # Omit the parameter if you want to keep the old value as well.\n)\n\n# Adjust similarity matrix generation\nfinder = finder.with_similarity_matrix_settings(\n cutoff=0.15,\n)\n\n# Adjust clustering\nfinder = finder.with_clustering_settings(\n error_ratio_threshold=2.5,\n merge_check_minimum_cluster_size=25,\n minimum_cluster_size=120,\n remove_central_cluster=False,\n)\n\n# Adjust merging by fit\nfinder = finder.with_merge_fit_settings(\n stop_threshold=2.4,\n)\n```\n\n#### Preprocessing\n\nIn order for an image to be ran through the algorithm, some preprocessing may be necessary. The requirements are that:\n\n- The array is 2D.\n- The array values are normalized in the range [0, 1].\n- The array's height and width must be divisible by 2^N where N is the number of orientation field levels.\n\nTherefore `SpiralFinder` will by default perform the following:\n\n1. It will first check that the array is 2D. If not an exception will be raised.\n2. It will normalize the values in the array using a linear scale.\n3. It will resize the image so that the height and width are valid by finding the closest valid size.\n\nAlso as part of the algorithm, a contrast boosting step will be performed. By default this is an unsharp mask.\n\nThese preprocessing steps can be changed however and in fact can be turned entirely off.\n\n```python\nfrom pyarcfire import SpiralFinder\nfrom pyarcfire.preprocess import ImageIdentityNormalizer\n\n# Turn off all preprocessing\nfinder = SpiralFinder().with_normalizer(None).with_resizer(None).with_booster(None)\n\n# Change the normalizer\nfinder = SpiralFinder().with_normalizer(ImageIdentityNormalizer())\n```\n\nYou can create custom preprocessors as well by implementing the `ImageNormalizer`, `ImageResizer` and `ImageContrastBooster` protocols (see `pyarcfire.preprocess`).\n\n\n### Command Line Interface\n\nPyArcFiRe can also be interacted with through the command line interface via `python -m pyarcfire ...`. Currently this is a work in progress and is mainly\na way to drive debugging code.\n",
"bugtrack_url": null,
"license": "BSD 3-Clause License",
"summary": "A port of SpArcFiRe, a spiral arc finder",
"version": "0.1.1",
"project_urls": {
"Issues": "https://github.com/pavyamsiri/pyarcfire/issues",
"Source": "https://github.com/pavyamsiri/pyarcfire.git"
},
"split_keywords": [
"arc",
" astronomy",
" finder",
" spiral"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4e5be63866c95293a9cfadaf69b8272d9c429fefe5cfa100d06281802ea63e40",
"md5": "069a36359a8c2ec67e646bdbb410e9b3",
"sha256": "a4b5a1d9e4f77d3674daca1f4956aaa7aba2db94de28f39365d05a03226ee220"
},
"downloads": -1,
"filename": "pyarcfire-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "069a36359a8c2ec67e646bdbb410e9b3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10.15",
"size": 49294,
"upload_time": "2024-11-21T07:16:21",
"upload_time_iso_8601": "2024-11-21T07:16:21.870166Z",
"url": "https://files.pythonhosted.org/packages/4e/5b/e63866c95293a9cfadaf69b8272d9c429fefe5cfa100d06281802ea63e40/pyarcfire-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "212922709b9a0922733fca400753262b5b024707f66198104fdc3a8fbfe33b55",
"md5": "7fd15aeb206ac5ebf2a9f1a29827eb57",
"sha256": "13601190a77dddb55eaad8503f10efefba5ddb963f17bdbd94506f3d360c7c6a"
},
"downloads": -1,
"filename": "pyarcfire-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "7fd15aeb206ac5ebf2a9f1a29827eb57",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10.15",
"size": 79821,
"upload_time": "2024-11-21T07:16:25",
"upload_time_iso_8601": "2024-11-21T07:16:25.474288Z",
"url": "https://files.pythonhosted.org/packages/21/29/22709b9a0922733fca400753262b5b024707f66198104fdc3a8fbfe33b55/pyarcfire-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-21 07:16:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "pavyamsiri",
"github_project": "pyarcfire",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pyarcfire"
}