diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8dc7a80aed8e2edc3aaab019625bba2d1ca23723..fbd34e880e57933bd7295675d178d01cefa1dfd6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -112,4 +112,4 @@ bump-python-deps:
     - schedules
 
   script:
-    - devops python-deps requirements.txt test-requirements.txt training-requirements.txt
+    - devops python-deps pyproject.toml
diff --git a/Dockerfile b/Dockerfile
index ff0251ce762b1fac5bcfa605299d500a538ea50b..e905be9512107e40ee90ce5a2a1081a8e6d730a0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,9 +3,10 @@ FROM nvidia/cuda:12.2.0-base-ubuntu22.04
 # Add python3 in cuda image
 ENV DEBIAN_FRONTEND=non-interactive
 RUN apt-get update -q -y && apt-get install -q -y --no-install-recommends python3-pip
+RUN pip3 install --upgrade pip
 
 # Setup doc-ufcn library
 WORKDIR /src
-COPY requirements.txt training-requirements.txt LICENSE setup.py MANIFEST.in README.md VERSION /src/
+COPY LICENSE README.md pyproject.toml /src/
 COPY doc_ufcn /src/doc_ufcn
 RUN pip3 install .[training] --no-cache-dir
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 66f7d50c40f431c57b7e6ca8e15f68805cb3ec88..0000000000000000000000000000000000000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-include requirements.txt
-include training-requirements.txt
-include VERSION
diff --git a/Makefile b/Makefile
index b215f312f67c9751b8c67c6702d06782e7804b7e..3cd57d501465235e9af8a35f231573516b6761ae 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,10 @@
 .PHONY: release
 
 release:
-	$(eval version:=$(shell cat VERSION))
-	git commit VERSION -m "Version $(version)"
+	# Grep the version from pyproject.toml, squeeze multiple spaces, delete double and single quotes, get 3rd val.
+	# This command tolerates multiple whitespace sequences around the version number.
+	$(eval version:=$(shell grep -m 1 version pyproject.toml | tr -s ' ' | tr -d '"' | tr -d "'" | cut -d' ' -f3))
+	echo Releasing version $(version)
+	git commit pyproject.toml -m "Version $(version)"
 	git tag $(version)
 	git push origin main $(version)
diff --git a/VERSION b/VERSION
deleted file mode 100644
index e8c6c270548998e243ca03dd99c370af8d4a6b3e..0000000000000000000000000000000000000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.2.0rc2
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..8efc830e4b9630878fc8082565f8aa5725a72e48
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,55 @@
+[build-system]
+requires = ["setuptools >= 61.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "doc-ufcn"
+version = "0.2.0rc2"
+description = "Doc-UFCN"
+readme = { file = "README.md", content-type = "text/markdown" }
+requires-python = ">= 3.10, < 3.11"
+authors = [
+    { name = "Mélodie Boillet", email = "boillet@teklia.com" },
+]
+dependencies = [
+    "huggingface-hub==0.20.2",
+    "numpy==1.26.2",
+    "opencv-python-headless==4.7.0.72",
+    "pyyaml==6.0.1",
+    "requests>=2,<3",
+    "teklia_toolbox==0.1.3",
+    "torch==2.1.0",
+]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    # Specify the Python versions you support here.
+    "Programming Language :: Python :: 3 :: Only",
+    "Programming Language :: Python :: 3.10",
+    # Topics
+    "Topic :: Scientific/Engineering :: Artificial Intelligence",
+    "Topic :: Scientific/Engineering :: Image Recognition",
+]
+
+[project.urls]
+Source = "https://gitlab.teklia.com/dla/doc-ufcn/"
+Tracker = "https://gitlab.teklia.com/dla/doc-ufcn/issues/"
+
+[project.optional-dependencies]
+training = [
+    "imageio==2.27.0",
+    "matplotlib==3.8.1",
+    "mlflow==2.2.1",
+    "Shapely==2.0.3",
+    "teklia-toolbox==0.1.3",
+    "tensorboard==2.15.1",
+    "torchvision==0.16.0",
+    "tqdm==4.66.1",
+]
+test = [
+    "scikit-image==0.22.0",
+    "pytest==8.1.1",
+    "pytest-lazy-fixtures==1.0.7",
+]
+
+[tool.setuptools.packages]
+find = {}
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index a00ac1a2af9fafddb6580e9daf41df8eef8cb4e8..0000000000000000000000000000000000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-huggingface-hub==0.20.2
-numpy==1.26.2
-opencv-python-headless==4.7.0.72
-pyyaml==6.0.1
-requests>=2,<3
-teklia_toolbox==0.1.3
-torch==2.1.0
diff --git a/setup.py b/setup.py
deleted file mode 100755
index 1ad450888f5d58f4ac3260cb58e074c031c295d6..0000000000000000000000000000000000000000
--- a/setup.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from pathlib import Path
-
-from setuptools import find_packages, setup
-
-
-def parse_requirements(name):
-    path = Path(__file__).parent.resolve() / name
-    assert path.exists(), f"Missing requirements: {path}"
-    return list(map(str.strip, path.read_text().splitlines()))
-
-
-setup(
-    name="doc-ufcn",
-    version=open("VERSION").read(),
-    description="Doc-UFCN",
-    long_description=open("README.md").read(),
-    long_description_content_type="text/markdown",
-    author="Mélodie Boillet",
-    author_email="boillet@teklia.com",
-    project_urls={
-        "Source": "https://gitlab.teklia.com/dla/doc-ufcn/",
-        "Tracker": "https://gitlab.teklia.com/dla/doc-ufcn/issues/",
-    },
-    url="https://gitlab.teklia.com/dla/doc-ufcn",
-    install_requires=parse_requirements("requirements.txt"),
-    extras_require={
-        "training": parse_requirements("training-requirements.txt"),
-    },
-    packages=find_packages(exclude=["tests"]),
-    python_requires=">= 3.10, < 3.11",
-    classifiers=[
-        "Development Status :: 5 - Production/Stable",
-        # Specify the Python versions you support here.
-        "Programming Language :: Python :: 3 :: Only",
-        "Programming Language :: Python :: 3.10",
-        # Topics
-        "Topic :: Scientific/Engineering :: Artificial Intelligence",
-        "Topic :: Scientific/Engineering :: Image Recognition",
-    ],
-)
diff --git a/test-requirements.txt b/test-requirements.txt
deleted file mode 100644
index 3c33d6cde15a2dffd09337e28afbcc07ed417813..0000000000000000000000000000000000000000
--- a/test-requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-scikit-image==0.22.0
diff --git a/tox.ini b/tox.ini
index 7b9cee3390aa055b128f6571e58dae02ffe228ac..a465525c8dde8bc90f586177f8dbce0d438061df 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,12 +2,9 @@
 envlist = py310
 
 [testenv]
+extras =
+  training
+  test
+
 commands =
   pytest {posargs}
-
-deps =
-  pytest
-  pytest-lazy-fixtures
-  -rrequirements.txt
-  -rtraining-requirements.txt
-  -rtest-requirements.txt
diff --git a/training-requirements.txt b/training-requirements.txt
deleted file mode 100644
index cfde6309fd67d4407214810a63e163eb1a4d5a91..0000000000000000000000000000000000000000
--- a/training-requirements.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-imageio==2.27.0
-matplotlib==3.8.1
-mlflow==2.2.1
-Shapely==2.0.3
-teklia-toolbox==0.1.3
-tensorboard==2.15.1
-torchvision==0.16.0
-tqdm==4.66.1