Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasuz committed Sep 27, 2021
0 parents commit b232713
Show file tree
Hide file tree
Showing 31 changed files with 2,612 additions and 0 deletions.
437 changes: 437 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@


![Chromsome Extraction Header](/home/lukas/Downloads/header.png)

# Banding-Pattern-Extraction

A package that offers multiple functionalities regarding banding pattern extraction of stained banded chromosome images. The extraction method is based on [0], but yields improvements such as angle interpolation in between perpendicular lines (see (d)). You can further generate banding pattern segmentation masks (see (h)) and impose Perlin noise banding patterns onto chromosome shapes. For more information check out our paper [paper](https://arxiv.org/abs/2109.09702).

If this work helps your research, please cite our paper (citation below).



### Installing this module

You can install this package globally on your device via pip:

`$ pip3 install path/to/Banding-Pattern-Extraction`



### Importing this module

Import the package in your python script via:

```python
import banding_pattern_extraction
```



### Interface

This package offers a programmatic python interface, as well as some stand-alone functionalities that can be called via the shell.



#### Command Line Interface

- `$ banding_pattern_extraction/banding_pattern_extraction.py`
- Show the extracted banding pattern and debugging information for one banding pattern.
- `$ banding_pattern_extraction/banding_pattern_extraction_from_folder.py`
- Extracts multiple banding patterns from a folder of images and saves them as binary vectors in a csv.
- `$ banding_pattern_extraction/banding_pattern_statistics.py`
- Extract statistics banding pattern statistics (mean, max length) from a folder of chromosome images and saves them in a csv.
- `$ banding_pattern_extraction/chromosome_segmentation.py`
- Creates a banding pattern segmentation mask of a stained chromosome image
- `$ banding_pattern_extraction/chromosome_segmentation_from_folder.py`
- Creates banding pattern segmentation masks of a stained chromosome images and saves them into a folder.
- `$ banding_pattern_extraction/impose_random_banding_pattern.py`
- Imposes a random Perlin banding pattern onto a chromosome image.
- `$ banding_pattern_extraction/impose_random_banding_pattern_from_folder.py`
- Imposes a random Perlin banding patterns onto chromosome images and saves them into a folder.

Find more details for each function by calling `$ python3 <script> -h`.



**Examples:**

A synthetic chromosome image is included to test out the functionalities, run for example:

*Extraction:*

```shell
python3 banding_pattern_extraction/banding_pattern_extraction.py -p banding_pattern_extraction/imgs/synthetic_chromosome.png
```
*Segmentation:*

```shell
python3 banding_pattern_extraction/chromosome_segmentation.py -p banding_pattern_extraction/imgs/synthetic_chromosome.png
```
*Impose a random banding pattern:*

```shell
python3 banding_pattern_extraction/impose_random_banding_pattern.py -p banding_pattern_extraction/imgs/synthetic_chromosome.png
```



#### Python3 Interface

Here some short examples on how to call the interface. For more descriptions look into the function comments.

```python
import banding_pattern_extraction as BPE

"""Banding pattern extraction"""
# Extract a banding pattern from a single image
result = BPE.get_banding_pattern(...)

# Extract a batch of banding patterns in a multiprocessing fashing
results = BPE.get_banding_pattern_multi_process(...)

# Get the banding pattern segmentation mask of a chromosome
segmented_chromosome = BPE.get_segmented_chromosome(...)

# Imposes a random banding pattern onto a chromosome shape
imposed_bp_img = BPE.impose_random_bp(...)

"""Visualisation utils"""
# Creates a 2D visualisation of a banding pattern vector
mat = BPE.binary_vector_to_bp_image(...)

# Create a matplotlib comparison image of input vs. output banding patterns
fig = BPE.binary_vector_comparison_img(...)

"""Utilitiy functions"""
# Generate a batch of fake banding patterns based on perlin noise
fake_bps = BPE.generate_random_banding_pattern(...)

# Clip a banding pattern symmetrically
bp = BPE.clip_bp(...)

# Pad a banding pattern symmetrically
bp = BPE.pad_bp(...)

# One hot encode a banding pattern
one_hot_bp = BPE.one_hot_encode(...)

```



## Citation

```latex
@misc{uzolas2021deep,
title={Deep Anomaly Generation: An Image Translation Approach of Synthesizing Abnormal Banded Chromosome Images},
author={Lukas Uzolas and Javier Rico and Pierrick Coupé and Juan C. SanMiguel and György Cserey},
year={2021},
eprint={2109.09702},
archivePrefix={arXiv},
primaryClass={eess.IV}
}
```



## References:

[0]: Wang, X., Zheng, B., Li, S., Mulvihill, J.J. and Liu, H., 2008. A rule-based computer scheme for centromere identification and polarity assignment of metaphase chromosomes. *computer methods and programs in biomedicine*, *89*(1), pp.33-42.



## License:

Shield: [![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]

This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa].

[![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]

[cc-by-nc-sa]: http://creativecommons.org/licenses/by-nc-sa/4.0/
[cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
[cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
10 changes: 10 additions & 0 deletions banding_pattern_extraction/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
""" Main interface """
from .scripts.banding_pattern_extraction import get_banding_pattern, get_banding_pattern_multi_process
from .scripts.banding_pattern_extraction_from_folder import folder_to_bp_csv as banding_pattern_extraction_from_folder_to_csv
from .scripts.chromosome_segmentation import get_segmented_chromosome
from .scripts.impose_random_banding_pattern import impose_random_bp

""" Some utility functions """
from .scripts.lib.visualisation_utils import binary_vector_to_bp_image, binary_vector_comparison_img
from .scripts.lib.utils import generate_random_banding_patterns, one_hot_encode, pad_bp, clip_bp
from .scripts.lib.banding_pattern_utils import resize_banding_pattern, cluster_1D
165 changes: 165 additions & 0 deletions banding_pattern_extraction/banding_pattern_extraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
from time import time
import matplotlib.pyplot as plt
import cv2 as cv
import numpy as np
import argparse
import traceback

from scripts.banding_pattern_extraction import get_banding_pattern
from scripts.lib.banding_pattern_utils import *

if __name__ == "__main__":
from scripts.lib.visualisation_utils import *

parser = argparse.ArgumentParser(description='Extract the banding pattern from a chromosome image.')
parser.add_argument('-p', '--path', help='input image', required=True)
parser.add_argument('--pixel_sampling', help='Sub sampling rate, i.e. keep every x-th pixel from the interpolated skeleton', type=int, nargs='?', default=8)
parser.add_argument('--pixel_sigma', help='Sigma for Gaussian filter that is applied on the skeleton pixels pixel prior sub sampling', type=int, nargs='?', default=2)
parser.add_argument('--density_sigma', help='Sigma for Gaussian filter of the density profiles', type=int, nargs='?', default=2)
parser.add_argument('--threshold', help='Segmentation threshold', type=int, nargs='?', default=254)
parser.add_argument('--size', help='Size of the banding pattern, extraction will be resized accordingly', type=int, nargs='?', default=None)

args = parser.parse_args()
img_path = args.path
pixel_sampling = args.pixel_sampling
pixel_sigma = args.pixel_sigma
density_sigma = args.density_sigma
chromsome_threshold = args.threshold
size = args.size
step_vector = 1

t1 = time()

## Load img
img = cv.imread(img_path, 0)

# A lot of results for visualisation purposes
try:
t1 = time()
results = get_banding_pattern(img, pixel_sampling, pixel_sigma, density_sigma, step_vector, chromsome_threshold=254, size=size)
print("Time: ", time() - t1)

if results["error"]:
print(results["error_message"])
print(results["stack_trace"])

except Exception:
traceback.print_exc()
exit()


binarized_banding_pattern = results['binarized_banding_pattern']
banding_pattern_filtered = results['banding_pattern_filtered']
banding_pattern_smooth = results['banding_pattern_smooth']
banding_points = results['banding_points']
banding_pattern = results['banding_pattern']
r = results['r']
c = results['c']
r_interpolated = results['r_interpolated']
c_interpolated = results['c_interpolated']
r_sampled = results['r_sampled']
c_sampled = results['c_sampled']
paths = results['paths']
longest_path = results['longest_path']
skeleton = results['skeleton']
blobs = results['blobs']
num_blobs = results['num_blobs']

print("Length of banding pattern:", len(binarized_banding_pattern))
print("Number of clusters: ", num_blobs)

t2 = time()
print("Seconds taken:", t2 - t1)

# Plot unfiltered density profile
fig = plt.figure()
x = np.arange(len(banding_pattern))
plt.bar(x, banding_pattern)
plt.title("Density Profile")

# Plot filtered density profile
fig = plt.figure()
x = np.arange(len(banding_pattern))
plt.bar(x, banding_pattern_filtered)
plt.title("Filtered Density Profile")

# Plot Banding Pattern
fig = plt.figure()
bp_img = binary_vector_to_bp_image(binarized_banding_pattern)
plt.imshow(bp_img, cmap=plt.cm.gray)
plt.title("Banding Pattern")

# Combined
fig, axes = plt.subplots(1, 4, figsize=(8, 8), sharex=True, sharey=True)
ax = axes.ravel()

ax[0].imshow(img, cmap=plt.cm.gray)
ax[0].set_title('original')
ax[0].axis('off')

ax[1].imshow(skeleton, cmap=plt.cm.gray)
ax[1].set_title("Skeleton")
ax[1].axis('off')

path_img = path_to_mat(longest_path, skeleton.shape)
ax[2].imshow(path_img)
ax[2].contour(blobs, cmap=plt.cm.gray)
ax[2].scatter(c, r, s=5, c='r')
ax[2].set_title('Longest Path')
ax[2].axis('off')

ax[3].imshow(blobs, cmap=plt.cm.gray)
ax[3].plot(c_interpolated, r_interpolated)
ax[3].scatter(c_sampled, r_sampled, s=3, c='r')
ax[3].scatter(c_interpolated, r_interpolated, s=3, c='g')
for i in range(len(banding_points)):
banding_pattern_line = np.array(banding_points[i])
try:
ax[3].plot(banding_pattern_line[:,1], banding_pattern_line[:,0])
except Exception:
pass
ax[3].scatter(c, r, s=20, c='b')
ax[3].set_title('Sampled')
ax[3].axis('off')

# Plot paths etc.
amount = int(np.ceil(len(paths) / 2))
fig, axes = plt.subplots(amount, 5, figsize=(8, 8), sharex=True, sharey=True)
ax = axes.ravel()

ax[0].imshow(img, cmap=plt.cm.gray)
ax[0].set_title('original')
ax[0].axis('off')

ax[1].imshow(blobs, cmap=plt.cm.gray)
ax[1].plot(c_interpolated, r_interpolated)
ax[1].scatter(c_sampled, r_sampled, s=3, c='r')
ax[1].scatter(c_interpolated, r_interpolated, s=3, c='g')
for i in range(len(banding_points)):
banding_pattern_line = np.array(banding_points[i])
try:
ax[1].plot(banding_pattern_line[:,1], banding_pattern_line[:,0])
except Exception:
pass
ax[1].scatter(c, r, s=20, c='b')
ax[1].set_title('Interpolated')
ax[1].axis('off')

path_img = path_to_mat(longest_path, skeleton.shape)
ax[2].imshow(path_img)
ax[2].contour(blobs, cmap=plt.cm.gray)
ax[2].scatter(c, r, s=5, c='r')
ax[2].set_title('Longest Path')
ax[2].axis('off')

for i in range(len(paths)):
path = paths[i]
path_img = path_to_mat(path, skeleton.shape)

ax[i+3].imshow(path_img, cmap='magma')
ax[i+3].contour(blobs, [0.5], colors='w')
ax[i+3].set_title('path: ' + str(i))
ax[i+3].axis('off')

fig.tight_layout()
plt.show()
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import argparse
import os
import csv
import traceback

from scripts.banding_pattern_extraction import get_banding_pattern

if __name__ == "__main__":

parser = argparse.ArgumentParser(description='Extracts the banding patterns from chromosome images from a folder.')
parser.add_argument('-s', '--source_path', help='source path', required=True)
parser.add_argument('-d', '--destination_path', help='destination path', required=True)
parser.add_argument('--pixel_sampling', help='Sub sampling rate, i.e. keep every x-th pixel from the interpolated skeleton', type=int, nargs='?', default=5)
parser.add_argument('--pixel_sigma', help='Sigma for Gaussian filter that is applied on the skeleton pixels pixel prior sub sampling', type=int, nargs='?', default=2)
parser.add_argument('--density_sigma', help='Sigma for Gaussian filter of the density profiles', type=int, nargs='?', default=2)
parser.add_argument('--threshold', help='Segmentation threshold', type=int, nargs='?', default=254)

args = parser.parse_args()
source_path = args.source_path
destination_path = args.destination_path
pixel_sampling = args.pixel_sampling
pixel_sigma = args.pixel_sigma
density_sigma = args.density_sigma
chromsome_threshold = args.threshold
step_vector = 1

file_list = os.listdir(source_path)
banding_patterns = {}

amount = len(file_list)

# Extract all patterns, if possible, and save into dict
for i in range(amount):
file_name = file_list[i]
if (i+1) % 100 == 0:
print("Finished:", i / amount)
file_path = os.path.join(source_path, file_name)
img = cv.imread(file_path, 0)

try:
bp = get_banding_pattern(img, pixel_sampling, pixel_sigma, density_sigma, step_vector, chromsome_threshold=chromsome_threshold, reject_multiple_blobs=False)
if not bp['error']:
banding_patterns[file_name] = bp['binarized_banding_pattern']
else:
raise bp["error_message"]
except Exception as e:
print("Extraction of '{0}' failed, due to: {1}".format(file_name, e))
traceback.print_exc()

# Save dict with banding patterns
source_file = os.path.join(destination_path, 'banding_patterns.csv')
with open(source_file, 'w') as f:
writer = csv.writer(f)

# header
writer.writerow(["file_name", "banding_pattern"])
for file_name, bp in banding_patterns.items():
bp_string = [str(x) for x in bp]
bp_string = " ".join(bp_string)
writer.writerow([file_name, bp_string])
Loading

0 comments on commit b232713

Please sign in to comment.