Skip to content

Commit

Permalink
Merge branch 'dev-0.0.1a2' of https://github.com/KevinCortacero/Kartezio
Browse files Browse the repository at this point in the history
 into dev-0.0.1a2
  • Loading branch information
KevinCortacero committed Dec 20, 2024
2 parents cce0b67 + 4677f22 commit 4a1862f
Show file tree
Hide file tree
Showing 47 changed files with 1,595 additions and 1,596 deletions.
84 changes: 46 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,66 @@
<h2 align="center"> Kartezio </h2>
<h5 align="center"> A Darwinian Designer of Explainable Algorithms for Biomedical Image Segmentation </h5>
[![Discord Channel](https://dcbadge.limes.pink/api/server/uwFwHyRxub)](https://discord.gg/KnJ4XWdQMK)

## Package Description
[TODO]
<h1 align="center">Kartezio: Evolutionary design of explainable algorithms for biomedical image segmentation</h1>

## Installation

Tested on Windows, Ubuntu 18.04, Ubuntu 22.04.
**Kartezio** is a modular Cartesian Genetic Programming (CGP) framework that enables the automated design of fully interpretable image-processing pipelines, without the need for GPUs or extensive training datasets.
Built on top of [OpenCV](https://opencv.org/), Kartezio empowers researchers, engineers, and practitioners to discover novel computer vision (CV) solutions using only a handful of annotated samples and a single CPU core.

Tested with different versions of Python3: 3.7, 3.8, 3.9 and 3.10.
Originally developed for biomedical image segmentation, Kartezio has been successfully showcased in [Nature Communications](https://www.nature.com/articles/s41467-023-42664-x). Although it shines in medical and life science applications, Kartezio’s underlying principles are domain-agnostic.
Whether you’re working with industrial quality control, satellite imagery, embedded vision, or robotics, Kartezio helps you craft custom CV pipelines that are **transparent, fast, frugal and efficient**.

## Key Features

### Creation of a virtualenv is recommanded:
:nut_and_bolt: **Modular and Customizable**
Kartezio is built from interchangeable building blocks, called **Components**, that you can mix, match, or replace. Adapt the pipeline to your project’s unique requirements.

```bash
python3 -m pip install virtualenv
python3 -m venv <path/to/venv/venv_name>
source <path/to/venv/venv_name>/bin/activate
pip install --upgrade pip
```
:pencil2: **Few-Shot Learning**
Forget the need for massive, annotated datasets. Kartezio can evolve solutions from just a few annotated examples, saving both time and computational resources.

### Installation from Pypi
:white_check_mark: **Transparent and Certifiable**
Every pipeline produced is fully transparent. Inspect the exact operations used, understand their sequence, and trust the decisions made by your model.

```bash
(venv_name)$ pip install kartezio
```
:earth_africa: **Frugal and Local**
Run everything on a single CPU, without GPUs or massive compute clusters. This makes Kartezio ideal for edge devices, embedded systems, or scenarios with limited computational resources.

### Local installation using pip
:microscope: **Broad Applicability**
While proven in biomedical image segmentation, Kartezio’s methods readily extend to other fields—like industrial machine vision, space imaging, drone footage analysis, or any custom image-based problem.

```bash
(venv_name)$ git clone [TODO]
(venv_name)$ cd kartezio
(venv_name)$ python -m pip install -e .
```
## First steps
[TODO]
## Getting Started

## Reported Results
Kartezio was compared on the Cell Image Library dataset against the reported performance of Cellpose/Stardist/MRCNN (December, 2022) as reported in Stringer et al, Nature Methods, 2021 and published in Cortacero et al, Nature Communnications, 2023:
1. **Installation:**
```bash
pip install kartezio

| | Kartezio | Kartezio | Kartezio | Cellpose | Stardist | MRCNN |
|------------------|----------|----------|----------|----------|----------|-------|
| Training images | 8 | 50 | 89 | 89 | 89 | 89 |
| AP50 on test set | 0.838 (mean)| 0.849 (mean)| 0.858 (mean) | 0.91 (max) | 0.76 (max) | 0.80 (max |
2. **First steps**
[TODO]

An additional, but not published, comparison was performed against the reported performance of CPP-Net, on BBBC006v1 dataset reported in Chen et al, 2023 (July, 2023):

| | Kartezio-s1 | Kartezio-s2 | CPP-Net | Stardist |
|------------------|-------------|-------------|---------|----------|
| Training images | 20 | 20 | 538 | 538 |
| AP50 on test set | 0.822 | 0.879 | 0.9811 | 0.9757 |

## References and Citation
[TODO .bibtex]
If you use Kartezio in your research, please consider citing:
```
@article{cortacero2023evolutionary,
title={Evolutionary design of explainable algorithms for biomedical image segmentation},
author={Cortacero, K{\'e}vin and McKenzie, Brienne and M{\"u}ller, Sabina and Khazen, Roxana and Lafouresse, Fanny and Corsaut, Ga{\"e}lle and Van Acker, Nathalie and Frenois, Fran{\c{c}}ois-Xavier and Lamant, Laurence and Meyer, Nicolas and others},
journal={Nature Communications},
volume={14},
number={1},
pages={7112},
year={2023},
publisher={Nature Publishing Group UK London}
}
```
If you are using the multimodal version of Kartezio, please also cite:
```
@inproceedings{de2024multimodal,
title={Multimodal adaptive graph evolution},
author={De La Torre, Camilo and Cortacero, K{\'e}vin and Cussat-Blanc, Sylvain and Wilson, Dennis},
booktitle={Proceedings of the Genetic and Evolutionary Computation Conference Companion},
pages={499--502},
year={2024}
}
```
## Licensing
Expand Down
9 changes: 6 additions & 3 deletions examples/components/create_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from typing import Dict

import numpy as np
from kartezio.components.core import (

from kartezio.core.components import (
Components,
Endpoint,
dump_component,
load_component,
register,
)
from kartezio.components.endpoint import Endpoint
from kartezio.types import TypeArray


Expand Down Expand Up @@ -73,7 +74,9 @@ def main():
print(f"Output from my_endpoint: {output}")

# Instantiate the endpoint from the component registry and apply it
my_endpoint_2 = Components.instantiate("Endpoint", "my_endpoint", n_classes=3)
my_endpoint_2 = Components.instantiate(
"Endpoint", "my_endpoint", n_classes=3
)
output_2 = my_endpoint_2.call(inputs) # Expected Output: 0
print(f"Output from my_endpoint_2: {output_2}")

Expand Down
4 changes: 2 additions & 2 deletions examples/components/create_primitive.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import List

import numpy as np
from kartezio.components.core import Components, register
from kartezio.components.library import Primitive

from kartezio.core.components import Components, Primitive, register
from kartezio.types import TypeArray


Expand Down
12 changes: 7 additions & 5 deletions examples/training/advanced_trainer.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from kartezio.callback import CallbackVerbose
from kartezio.endpoint import EndpointThreshold
from kartezio.core.endpoints import EndpointThreshold
from kartezio.core.fitness import FitnessIOU
from kartezio.evolution.base import KartezioTrainer
from kartezio.fitness import FitnessIOU
from kartezio.libraries.array import create_array_lib
from kartezio.libraries.scalar import library_scalar
from kartezio.mutation.behavioral import AccumulateBehavior
from kartezio.mutation.decay import LinearDecay
from kartezio.mutation.edges import MutationEdgesNormal
from kartezio.mutation.effect import MutationNormal
from kartezio.primitives.array import create_array_lib
from kartezio.primitives.scalar import library_scalar
from kartezio.utils.dataset import one_cell_dataset


Expand Down Expand Up @@ -51,7 +51,9 @@ def main():
# model.summary() # Display the model summary

# Load training data
train_x, train_y = one_cell_dataset() # Use a simple one-cell dataset for training
train_x, train_y = (
one_cell_dataset()
) # Use a simple one-cell dataset for training

# trainer = KartezioTrainer(model)

Expand Down
10 changes: 6 additions & 4 deletions examples/training/basic_trainer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from kartezio.endpoint import EndpointThreshold
from kartezio.core.endpoints import EndpointThreshold
from kartezio.core.fitness import FitnessIOU
from kartezio.evolution.base import KartezioTrainer
from kartezio.fitness import FitnessIOU
from kartezio.libraries.array import create_array_lib
from kartezio.primitives.array import create_array_lib
from kartezio.utils.dataset import one_cell_dataset


Expand Down Expand Up @@ -30,7 +30,9 @@ def main():
model.set_mutation_rates(node_rate=0.05, out_rate=0.1)

# Load training data
train_x, train_y = one_cell_dataset() # Use a simple one-cell dataset for training
train_x, train_y = (
one_cell_dataset()
) # Use a simple one-cell dataset for training

# Train the model
elite, history = model.fit(100, train_x, train_y)
Expand Down
20 changes: 14 additions & 6 deletions src/kartezio/callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

import matplotlib.pyplot as plt
import numpy as np
from kartezio.components.genotype import Genotype

from kartezio.core.components import Genotype
from kartezio.enums import JSON_ELITE
from kartezio.evolution.decoder import Decoder
from kartezio.helpers import Observer
Expand All @@ -32,7 +33,9 @@ def eventid():


class Event:
def __init__(self, iteration: int, name: str, content: Dict, force: bool = False):
def __init__(
self, iteration: int, name: str, content: Dict, force: bool = False
):
self.iteration = iteration
self.name = name
self.content = content
Expand Down Expand Up @@ -106,7 +109,6 @@ def on_evolution_end(self, n, e_content):
print(verbose)



class CallbackSaveScores(Callback):
def __init__(self, filename, dataset, preprocessing, fitness):
super().__init__()
Expand Down Expand Up @@ -141,7 +143,9 @@ def on_new_parent(self, iteration, event_content):

def on_evolution_end(self, iteration, event_content):
self._add_new_line(iteration, event_content)
print(f"{self.filename} saved. {len(self.data)} lines, last = {self.data[-1]}")
print(
f"{self.filename} saved. {len(self.data)} lines, last = {self.data[-1]}"
)
data = np.array(self.data)
plt.figure()
plt.plot(data[:, 0], data[:, 1])
Expand All @@ -155,7 +159,9 @@ def __init__(self, filename, dataset, preprocessing, fitness):
self.filename = filename
self.dataset = dataset.__to_dict__()
self.decoder = None
self.preprocessing = preprocessing.__to_dict__() if preprocessing else None
self.preprocessing = (
preprocessing.__to_dict__() if preprocessing else None
)
self.fitness = fitness.__to_dict__()

def set_decoder(self, decoder: Decoder):
Expand All @@ -167,7 +173,9 @@ def on_new_parent(self, iteration, event_content):
"iteration": iteration,
"dataset": self.dataset,
"elite": {
"chromosomes": {k: v.tolist() for k, v in elite._chromosomes.items()}
"chromosomes": {
k: v.tolist() for k, v in elite._chromosomes.items()
}
},
"preprocessing": self.preprocessing,
"decoder": self.decoder,
Expand Down
3 changes: 2 additions & 1 deletion src/kartezio/cli/insight.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import cv2
import numpy as np

from kartezio.core.fitness import FitnessAP, FitnessIOU
from kartezio.core.registry import registry
from kartezio.easy import load_model, read_dataset
from kartezio.export import KartezioInsight
from kartezio.fitness import FitnessAP, FitnessIOU
from kartezio.inference import KartezioModel


Expand Down
15 changes: 9 additions & 6 deletions src/kartezio/cli/movie.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec
from numena.io.drive import Directory
from numena.io.image import imread_color
from numena.io.json import json_read

from kartezio.core.components import BaseGenotype
from kartezio.core.fitness import FitnessAP
from kartezio.data import read_dataset
from kartezio.fitness import FitnessAP
from kartezio.inference import KartezioModel
from kartezio.preprocessing import SelectChannels
from kartezio.utils.viewer import KartezioViewer
from matplotlib import gridspec
from numena.io.drive import Directory
from numena.io.image import imread_color
from numena.io.json import json_read

CHANNELS = [1, 2]
preprocessing = SelectChannels(CHANNELS)
Expand All @@ -36,7 +37,9 @@ def main():
args = parser.parse_args()

history_directory = Directory(args.history)
model = KartezioModel(f"{history_directory._path}/elite.json", fitness=FitnessAP())
model = KartezioModel(
f"{history_directory._path}/elite.json", fitness=FitnessAP()
)
viewer = KartezioViewer(
model._model.decoder.infos,
model._model.decoder.library,
Expand Down
21 changes: 14 additions & 7 deletions src/kartezio/cli/movie_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

import matplotlib.pyplot as plt
import numpy as np
from numena.io.drive import Directory
from numena.io.image import imread_color
from numena.io.json import json_read

from kartezio.core.components import BaseGenotype
from kartezio.core.fitness import FitnessAP
from kartezio.easy import read_dataset
from kartezio.fitness import FitnessAP
from kartezio.inference import KartezioModel
from kartezio.utils.viewer import KartezioViewer
from numena.io.drive import Directory
from numena.io.image import imread_color
from numena.io.json import json_read


def reformat_x(x):
Expand Down Expand Up @@ -51,7 +52,9 @@ def main():
args = parser.parse_args()

history_directory = Directory(args.history)
model = KartezioModel(f"{history_directory._path}/elite.json", fitness=FitnessAP())
model = KartezioModel(
f"{history_directory._path}/elite.json", fitness=FitnessAP()
)
viewer = KartezioViewer(
model._model.decoder.infos,
model._model.decoder.library,
Expand All @@ -61,7 +64,9 @@ def main():
cols_std = ["Parent", "Child", "Child"]
cols_first = ["Child", "Child", "Child"]

idx_to_frame = list(range(1, 201)) + list(range(575, 626)) + list(range(1575, 1626))
idx_to_frame = (
list(range(1, 201)) + list(range(575, 626)) + list(range(1575, 1626))
)
frame_name_count = 1
for i in idx_to_frame:
frame_name = f"frame_{frame_name_count:04}.png"
Expand All @@ -76,7 +81,9 @@ def main():
)
genome = BaseGenotype(sequence=sequence)
model._model.genome = genome
p, f, t = model.eval(dataset, subset="train", reformat_x=reformat_x)
p, f, t = model.eval(
dataset, subset="train", reformat_x=reformat_x
)
fitness.append(1.0 - f)
model_graph = viewer.get_graph(
model._model.genome,
Expand Down
Loading

0 comments on commit 4a1862f

Please sign in to comment.