Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finder #31

Merged
merged 33 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e37d2a2
add finder class.
hstewart93 Jun 6, 2024
3526b1c
add post processing class to processing,py.
hstewart93 Jun 7, 2024
b0bda16
fix bug in threhsolding.
hstewart93 Jun 7, 2024
47fbd61
Merge branch 'main' into finder
hstewart93 Jun 18, 2024
2b2e3dc
add command line colours.
hstewart93 Jun 18, 2024
e28e172
remove todo.
hstewart93 Jun 18, 2024
5b4ab57
Update version to 0.0.3
hstewart93 Jun 18, 2024
56d78b7
set trained model path using importlib.resources
hstewart93 Jun 18, 2024
a73391e
update to v0.0.4
hstewart93 Jun 18, 2024
9599d35
fix bug with importlib
hstewart93 Jun 18, 2024
82533ed
update v0.0.5
hstewart93 Jun 18, 2024
56f6021
add trained_model.h5 to package data in pyproject.toml
hstewart93 Jun 19, 2024
f23f392
fix package data bug and bump version to 0.0.6
hstewart93 Jun 19, 2024
44af464
update pyproject.toml
hstewart93 Jun 19, 2024
b88a720
bump version 0.0.7.
hstewart93 Jun 19, 2024
854536e
update pyproject.toml, add image and network packages.
hstewart93 Jun 20, 2024
bbe8ba2
add tests for postprocessing.
hstewart93 Jun 20, 2024
96e1c5e
update readme.
hstewart93 Jun 21, 2024
442b9e0
add parameter description table to readme
hstewart93 Jun 21, 2024
656963a
add parameters to docstrings.
hstewart93 Jun 21, 2024
d67562e
add pytest actions config.
hstewart93 Jun 21, 2024
b4abf3a
add run tests to github action.
hstewart93 Jun 21, 2024
03ef0db
add pytest to ci dependencies pyproject.toml
hstewart93 Jun 21, 2024
1fad6c8
fix syntax error.
hstewart93 Jun 21, 2024
b0d6bed
update readme, add pytest badge and feature development information.
hstewart93 Jun 21, 2024
171512a
update example fits file.
hstewart93 Jun 21, 2024
19ed670
add new line at eof pytest.yml
hstewart93 Jul 1, 2024
8b009db
update readme.
hstewart93 Jul 1, 2024
5b50bb8
update readme.
hstewart93 Jul 1, 2024
887d12b
add user example notebook.
hstewart93 Jul 2, 2024
6f61632
update path.
hstewart93 Jul 2, 2024
39f1e47
fix test.
hstewart93 Jul 2, 2024
f807c49
update example notebook.
hstewart93 Jul 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,3 @@ jobs:
files: dist/*
name: ${{ steps.branch_info.outputs.TAG }}
tag_name: ${{ steps.branch_info.outputs.TAG }}


24 changes: 24 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Pytest

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
pytest:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[ci]
- name: Run tests
run: |
pytest
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include continunet/network/trained_model.h5
110 changes: 103 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
# ContinUNet
[![Pytest](https://github.com/hstewart93/continunet/actions/workflows/pytest.yml/badge.svg)](https://github.com/hstewart93/continunet/actions/workflows/pytest.yml)

Source finding package for radio continuum data powered by U-Net segmentation algorithm.

- [Paper](https://academic.oup.com/rasti/article/3/1/315/7685538?utm_source=advanceaccess&utm_campaign=rasti&utm_medium=email#supplementary-data)
- [Installation](#installation)
- [Developer Installation](#developer-installation)
- [Quickstart](#quickstart)
- [Example Notebook](https://github.com/hstewart93/continunet/tree/finder/continunet/user_example.ipynb)
- [Training Dataset](https://www.kaggle.com/datasets/harrietstewart/continunet)
- [Next Release](#development)

## Installation
The project is available on [PyPI](https://pypi.org/project/continunet/), to install latest stable release use:

```pip install continunet```
```bash
pip install continunet
```

To install version in development, use:

```pip install git+https://github.com/hstewart93/continunet```
```bash
pip install git+https://github.com/hstewart93/continunet
```

**ContinUNet has a `Python 3.9` minimum requirement.**

## Developer Installation
If you want to contribute to the repository, install as follows:

Once you have cloned down this repository using `git clone` cd into the app directory eg.
Once you have cloned down this repository using `git clone`, cd into the app directory:

```
```bash
git clone [email protected]:hstewart93/continunet.git
cd continunet
```

Create a virtual environment for development, if you are using bash:

```
```bash
python3 -m venv venv
source venv/bin/activate
pip install -e .[dev,ci]
Expand All @@ -31,10 +48,89 @@ To exit the virtual environment use `deactivate`.

This project used the black auto formatter which can be run on git commit along with flake8 if you install pre-commit. To do this run the following in your terminal from within your virtual environment.

```
```bash
pre-commit install
```

Now pre-commit hooks should run on `git commit`.

The run the test suite use `pytest`.
To run the test suite use `pytest`.

## Quickstart
The package currently support `.FITS` type images. To perform source finding you can import the `finder` module:

```python
from continunet.finder import Finder
```

Load your image file:

```python
finder = Finder("<filepath>")
```

To produce a source catalogue and populate the `Finder` instance:

```python
sources = finder.find()
```

If you want to calculate the model map and residuals image as part of source finding, use the `Finder.find()` method with `generate_maps=True`:

```python
sources = finder.find(generate_maps=True)
model_map = finder.model_map
residuals = finder.residuals
```

Alternatively, manually calculate model map and residual images using:

```python
model_map = finder.get_model_map()
residuals = finder.get_residuals()
```

Useful available attributes of the `Finder` object are:
```python
finder.sources # cleaned source catalogue
finder.reconstructed_image # predicted image reconstructed by unet module
finder.segmentation_map # predicted segmentation map
finder.model_map # model map of cleaned predicted sources
finder.residuals # residual image as numpy array
finder.raw_sources # sources from labelled segmentation map before cleaning
```

Export source catalogue using `finder.export_sources` as `.csv` by default or `.FITS` by setting `export_fits=True`:

```python
finder.export_sources("<filepath>", export_fits=<Boolean>)
```

Source parameters extracted are:

| **Parameter** | **Description** |
|----------------------------|--------------------------------------------------------------------------------------------------------|
| `x_location_original` | x coordinate of the source from the cutout used for inference |
| `y_location_original` | y coordinate of the source from the cutout used for inference |
| `orientation` | orientation of source ellipse in radians |
| `major_axis` | major axis of source ellipse |
| `minor_axis` | minor axis of source ellipse |
| `flux_density_uncorrected` | total intensity of the segmented source region before beam corrections applied |
| `label` | class label in predicted segmentation map |
| `x_location` | x coordinate of the source in the original input image dimensions |
| `y_location` | y coordinate of the source in the original input image dimensions |
| `right_ascension` | RA coordinate of the source in the original input image dimensions |
| `declination` | Dec coordinate of the source in the original input image dimensions |
| `area` | area of source ellipse |
| `position_angle` | position angle of source ellipse in degrees |
| `correction_factor` | correction factor applied to flux density measurement to account for undersampling of synthesised beam |
| `flux_density` | corrected flux density |

## Development
ContinUNet is subject to ongoing development. To see the backlog of features and bug fixes please go to the [project board](https://github.com/users/hstewart93/projects/4/views/1). Please raise any feature requests or bugs as [issues](https://github.com/hstewart93/continunet/issues).

The following features will be added in the next release:

1. Exporting processed images to `.npy` and `.FTIS` [(#33)](https://github.com/hstewart93/continunet/issues/33)
2. Inference for non-square images [(#27)](https://github.com/hstewart93/continunet/issues/27)
3. Taking cutout of `ImageSquare` object before inference [(#28)](https://github.com/hstewart93/continunet/issues/28)
13 changes: 13 additions & 0 deletions continunet/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Constants for ContinuNet."""


TRAINED_MODEL = "continunet/network/trained_model.h5"

# ANSI escape sequences
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
RESET = "\033[0m"
Binary file modified continunet/example_image.fits
Binary file not shown.
87 changes: 87 additions & 0 deletions continunet/finder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Compile ContinUNet modules into Finder class for source finding."""

import importlib.resources
import time

from astropy.table import Table

from continunet.constants import GREEN, RESET
from continunet.image.fits import ImageSquare
from continunet.image.processing import PreProcessor, PostProcessor
from continunet.network.unet import Unet


class Finder:
"""Class for source finding in radio continuum images."""

def __init__(self, image: str, layers: int = 4):
"""Initialise the Finder class.

Parameters
----------
image : str
The path to the FITS image.
layers : int
The number of encoding and decoding layers in the U-Net model.
Layers is set by default to 4, and cannot currently be changed.
"""
if not image.endswith(".fits"):
raise ValueError("File must be a .fits file.")
self.image = image
if layers != 4:
raise ValueError("Number of layers must be 4.")
self.layers = layers
self.image_object = None
self.sources = None
self.reconstructed_image = None
self.post_processor = None
self.segmentation_map = None
self.model_map = None
self.residuals = None
self.raw_sources = None

def find(self, generate_maps=False, threshold="default"):
"""Find sources in a continuum image."""
start_time = time.time()
# Load image
self.image_object = ImageSquare(self.image)

# Pre-process image
pre_processor = PreProcessor(self.image_object, self.layers)
data = pre_processor.process()

# Run U-Net
with importlib.resources.path("continunet.network", "trained_model.h5") as path:
unet = Unet(data.shape[1:4], trained_model=path, image=data, layers=self.layers)
self.reconstructed_image = unet.decode_image()

# Post-process reconstructed image
self.post_processor = PostProcessor(unet.reconstructed, pre_processor, threshold=threshold)
self.sources = self.post_processor.get_sources()
self.segmentation_map = self.post_processor.segmentation_map
self.raw_sources = self.post_processor.raw_sources

end_time = time.time()
print(
f"{GREEN}ContinUNet found {len(self.sources)} sources "
f"in {(end_time - start_time):.2f} seconds.{RESET}"
)

if generate_maps:
self.model_map = self.post_processor.get_model_map()
self.residuals = self.post_processor.get_residuals()
self.segmentation_map = self.post_processor.segmentation_map

return self.sources

def export_sources(self, path: str, export_fits=False):
"""Export source catalogue to a directory. Use export_fits=True to save as FITS."""
if self.sources is None:
raise ValueError("No sources to export.")
if export_fits:
table = Table.from_pandas(self.sources)
table.write(path, format="fits", overwrite=True)
return self

self.sources.to_csv(path)
return self
3 changes: 3 additions & 0 deletions continunet/image/fits.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from astropy.io import fits
from astropy.wcs import WCS

from continunet.constants import CYAN, RESET


class FitsImage(ABC):
"""Abstract model for an image imported from FITS format."""
Expand All @@ -22,6 +24,7 @@ def __init__(self, path):

def load(self):
"""Load fits image from file and populate model args."""
print(f"{CYAN}Loading FITS image from {self.path}...{RESET}")
if not self.path:
raise ValueError("Path to FITS file not provided.")

Expand Down
61 changes: 0 additions & 61 deletions continunet/image/pre_processing.py

This file was deleted.

Loading