-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b232713
Showing
31 changed files
with
2,612 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
|
||
|
||
 | ||
|
||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
165
banding_pattern_extraction/banding_pattern_extraction.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
60 changes: 60 additions & 0 deletions
60
banding_pattern_extraction/banding_pattern_extraction_from_folder.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |
Oops, something went wrong.