diff --git a/.gitignore b/.gitignore
index ed8ebf583f771da9150c35db3955987b7d757904..5363ae7c15dd829079a8bf8594b3a15389393041 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
-__pycache__
\ No newline at end of file
+__pycache__
+.ruff_cache
+.hypothesis
+.venv
+dist
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5c0439e214c839d0d65c47e5e6726579a4dfe8cc..14bed04f307d271c77c094bee9500edfc15f1472 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ image: ubuntu:latest
 
 stages:
   - code_embed
+  - ruff
 
 variables:
   GIT_STRATEGY: clone
@@ -9,7 +10,6 @@ variables:
 code_embedder:
   stage: code_embed
   script:
-    - echo "Personal a" 
     - cd code-embedder/
     - poetry install --only main
     - README_PATHS=$(find .. -maxdepth 1 -name "*.md" -print)
@@ -40,3 +40,13 @@ code_embedder:
     - merge_requests
     
 
+ruff_linter:
+  stage: ruff
+  script: 
+    - curl -LsSf https://astral.sh/uv/install.sh | sh
+    - source $HOME/.local/bin/env
+    - uv tool run ruff check
+  before_script:
+    - apt-get update && apt-get install -y curl
+  only:
+    - merge_requests
diff --git a/README.md b/README.md
index c9c3548abb373c4db0ec3f7e1b06528196bc5f65..8473b81d5c4bad23619df9f3d7a9ccb9a603cc9b 100644
--- a/README.md
+++ b/README.md
@@ -58,15 +58,17 @@ from thd_json.projection import get_projection_validator
 from pathlib import Path
 
 
-FILE = Path('./examples/thd_json/test_thd.json')
+FILE = Path("./examples/thd_json/test_thd.json")
 
 
 def main():
     projection_validator = get_projection_validator()
     projection_validator.file(FILE)
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()
+
 ```
 
 Python example to validate a folder of projections
@@ -92,31 +94,36 @@ from pathlib import Path
 import argparse
 
 
-def get_projection_validator(json_suffix: str = '*.json') -> Validator:
-    return Validator(Path(__file__).parent / Path('projection.json'), json_suffix)
+def get_projection_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(Path(__file__).parent / Path("projection.json"), json_suffix)
 
-def projection_cli():
-    parser = argparse.ArgumentParser(description='JSON THD Projection Validator CLI with uv.')
-    parser.add_argument('folder', help='Folder to check.', type=str)
-    parser.add_argument('suffix', help='Projection suffix.', default='*.json', type=Path, nargs='?')
+
+def projection_geometry_cli():
+    parser = argparse.ArgumentParser(
+        description="JSON THD Projection Geometry Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
     args = parser.parse_args()
 
     suffix = str(args.suffix)
-    if not suffix.startswith('*'):
+    if not suffix.startswith("*"):
         raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
 
-    validator = get_projection_validator(
-        suffix)
-    
+    validator = get_projection_validator(suffix)
+
     folder = Path(args.folder)
     if not folder.exists():
-        raise FileNotFoundError(f'Folder: {folder} does not exist')
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
 
     validator.folder(Path(args.folder))
 
 
 if __name__ == "__main__":
-    projection_cli()
+    projection_geometry_cli()
+
 ```
 - add the CLI script to the `pyproject.toml` 
 ```toml
diff --git a/examples/artist/projection.json b/examples/artist/projection.json
index 9e4637a3c68b109ba53b823bf73255fa884c6818..9c04612b6cd3c4a40ccd4c4941745ded1f45ebb0 100644
--- a/examples/artist/projection.json
+++ b/examples/artist/projection.json
@@ -1,28 +1,43 @@
 {
-    "focal_spot_position_mm": [
-        -10.0,
-        -10.0,
-        -10.0
-    ],
-    "focal_spot_orientation_quat": [
-        0.0,
-        0.0,
-        0.0,
-        1.0
-    ],
-    "detector_center_position_mm": [
-        990.0,
-        -10.0,
-        -10.0
-    ],
-    "detector_center_orientation_quat": [
-        0.0,
-        0.7071067811865475,
-        0.0,
-        0.7071067811865477
-    ],
-    "image_width_px": 2000,
-    "pixel_pitch_width_mm": 0.13899999856948853,
-    "image_height_px": 1800,
-    "pixel_pitch_height_mm": 0.13899999856948853
+    "header":
+    {
+        "uuid": "b83d81a1-333f-448e-af75-7c7db317fd96",
+        "timestamp": "2018-11-13T20:20:39+00:00"
+    },
+    "image":
+    {
+        "uuid": "b83d81a1-333f-448e-af75-7c7db317fd96",
+        "timestamp": "2018-11-13T20:20:39+00:00",
+        "image_path": "test_thd.tif"
+    },
+    "projection_geometry":{
+        "focal_spot_position_mm": [
+            -10.0,
+            -10.0,
+            -10.0
+        ],
+        "focal_spot_orientation_quat": [
+            0.0,
+            0.0,
+            0.0,
+            1.0
+        ],
+        "detector_center_position_mm": [
+            990.0,
+            -10.0,
+            -10.0
+        ],
+        "detector_center_orientation_quat": [
+            0.0,
+            0.7071067811865475,
+            0.0,
+            0.7071067811865477
+        ]
+    },
+    "detector": {
+        "image_width_px": 2000,
+        "pixel_pitch_width_mm": 0.13899999856948853,
+        "image_height_px": 1800,
+        "pixel_pitch_height_mm": 0.13899999856948853
+    }
 }
\ No newline at end of file
diff --git a/examples/generated/example_detector.json b/examples/generated/example_detector.json
new file mode 100644
index 0000000000000000000000000000000000000000..7708891bcd11793d633b3d0f2e82d069eb735b97
--- /dev/null
+++ b/examples/generated/example_detector.json
@@ -0,0 +1,6 @@
+{
+    "image_height_px": 1,
+    "image_width_px": 1,
+    "pixel_pitch_height_mm": 0.001,
+    "pixel_pitch_width_mm": 0.001
+}
\ No newline at end of file
diff --git a/examples/generated/example_header.json b/examples/generated/example_header.json
new file mode 100644
index 0000000000000000000000000000000000000000..4fbfdd4f1a2e12fcfde73a313b596ab3131a2e00
--- /dev/null
+++ b/examples/generated/example_header.json
@@ -0,0 +1,4 @@
+{
+    "timestamp": "2000-01-01T00:00:00Z",
+    "uuid": ""
+}
\ No newline at end of file
diff --git a/examples/generated/example_image.json b/examples/generated/example_image.json
new file mode 100644
index 0000000000000000000000000000000000000000..5f601cdb514c614546fc8658bec1f74d8220bb7c
--- /dev/null
+++ b/examples/generated/example_image.json
@@ -0,0 +1,5 @@
+{
+    "image_path": ".tif",
+    "timestamp": "2000-01-01T00:00:00Z",
+    "uuid": ""
+}
\ No newline at end of file
diff --git a/examples/generated/example_projection.json b/examples/generated/example_projection.json
new file mode 100644
index 0000000000000000000000000000000000000000..e6f19765015ef42bc11483de9bb2b77b0e79d5ad
--- /dev/null
+++ b/examples/generated/example_projection.json
@@ -0,0 +1,35 @@
+{
+    "detector": {
+        "image_height_px": 1,
+        "image_width_px": 1,
+        "pixel_pitch_height_mm": 0.001,
+        "pixel_pitch_width_mm": 0.001
+    },
+    "header": {
+        "timestamp": "2000-01-01T00:00:00Z",
+        "uuid": ""
+    },
+    "image": {
+        "image_path": ".tif",
+        "timestamp": "2000-01-01T00:00:00Z",
+        "uuid": ""
+    },
+    "projection_geometry": {
+        "detector_center_orientation_quat": [
+            0.0,
+            0.0,
+            0.0,
+            0.0
+        ],
+        "detector_center_position_mm": [
+            0.0,
+            0.0,
+            0.0
+        ],
+        "focal_spot_position_mm": [
+            0.0,
+            0.0,
+            0.0
+        ]
+    }
+}
\ No newline at end of file
diff --git a/examples/generated/example_source.json b/examples/generated/example_source.json
new file mode 100644
index 0000000000000000000000000000000000000000..206d614fcfa2963362528768937dd219d46541e2
--- /dev/null
+++ b/examples/generated/example_source.json
@@ -0,0 +1,4 @@
+{
+    "current_ma": 0.001,
+    "voltage_kv": 0.001
+}
\ No newline at end of file
diff --git a/examples/thd_json/test_thd.json b/examples/thd_json/test_thd.json
index 0a345d279d73bf3babac630e30714a6128db9b3c..d598300fe04037cfcb3d1d0c1204aec6df7b022e 100644
--- a/examples/thd_json/test_thd.json
+++ b/examples/thd_json/test_thd.json
@@ -4,54 +4,44 @@
         "uuid": "b83d81a1-333f-448e-af75-7c7db317fd96",
         "timestamp": "2018-11-13T20:20:39+00:00"
     },
-    "focal_spot_position_mm": [
-        2724.71682,
-        1690.5,
-        193.29345
-    ],
-    "detector_center_position_mm": [
-        1902.85619,
-        1690.5,
-        121.38996
-    ],
-    "detector_horizontal_vector": [
-        -0.08715574274765814,
-        0.0,
-        0.9961946980917458
-    ],
-    "detector_vertical_vector": [
-        -0.0,
-        -1.0000000000000002,
-        0.0
-    ],
-    "detector_center_orientation_quat": [
-        0.6755902076156602,
-        -0.0,
-        0.737277336810124,
-        0.0
-    ],
-    "pixel_pitch_width_mm": 0.07479989711934157,
-    "pixel_pitch_height_mm": 0.07479989711934157,
-    "image_width_px": 1944,
-    "image_height_px": 1536,
-    "projection_matrix_cera": [
-        [
-            -0.17494827015512399,
-            0.0,
-            0.988513847201303,
-            285.61124242325764
+    "image":
+    {
+        "uuid": "b83d81a1-333f-448e-af75-7c7db317fd96",
+        "timestamp": "2018-11-13T20:20:39+00:00",
+        "image_path": "test_thd.tif"
+    },
+    "projection_geometry": {
+        "focal_spot_position_mm": [
+            2724.71682,
+            1690.5,
+            193.29345
         ],
-        [
-            -0.06936693345179354,
-            -0.9999999999999998,
-            -0.006068820300589123,
-            1880.678313541253
+        "detector_center_position_mm": [
+            1902.85619,
+            1690.5,
+            121.38996
         ],
-        [
-            -9.032152793202284e-05,
+        "detector_horizontal_vector": [
+            -0.08715574274765814,
             0.0,
-            -7.902109766392087e-06,
-            0.24762801242350704
+            0.9961946980917458
+        ],
+        "detector_vertical_vector": [
+            -0.0,
+            -1.0000000000000002,
+            0.0
+        ],
+        "detector_center_orientation_quat": [
+            0.6755902076156602,
+            -0.0,
+            0.737277336810124,
+            0.0
         ]
-    ]
+    },
+    "detector": {
+        "pixel_pitch_width_mm": 0.07479989711934157,
+        "pixel_pitch_height_mm": 0.07479989711934157,
+        "image_width_px": 1944,
+        "image_height_px": 1536
+    }
 }
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 5dda9732d721e27cdd86f11c45e5a7fb453dd5b1..28157f8d23169d6f50f97a289b7bd8456514e9ff 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,6 +7,8 @@ authors = [
 ]
 requires-python = ">=3.10"
 dependencies = [
+    "hypothesis>=6.119.0",
+    "hypothesis-jsonschema>=0.23.1",
     "json-schema>=0.3",
     "jsonschema>=4.23.0",
 ]
@@ -14,6 +16,11 @@ dependencies = [
 [project.scripts]
 projection_validator = "thd_json.projection:projection_cli"
 header_validator = "thd_json.header:header_cli"
+generate_header = "thd_json.header:generate_header_cli"
+image_validator = "thd_json.image:image_cli"
+detector_validator = "thd_json.detector:detector_cli"
+source_validator = "thd_json.source:source_cli"
+projection_geometry_validator = "thd_json.projection_geometry:projection_geometry_cli"
 
 [build-system]
 requires = ["hatchling"]
diff --git a/scripts/generate_example_data.py b/scripts/generate_example_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..9dc224031186f916cd547e534a9ae9c357c6cfe6
--- /dev/null
+++ b/scripts/generate_example_data.py
@@ -0,0 +1,38 @@
+from hypothesis_jsonschema import from_schema
+from thd_json.projection import get_projection_validator
+from thd_json.header import get_header_validator
+from thd_json.source import get_source_validator
+from thd_json.detector import get_detector_validator
+from thd_json.image import get_image_validator
+from hypothesis import given, settings
+import json
+from pathlib import Path
+
+
+SAVE_FOLDER = Path("./examples/generated")
+
+
+def main():
+    for name, get_validator in [
+        ("projection", get_projection_validator),
+        ("header", get_header_validator),
+        ("source", get_source_validator),
+        ("detector", get_detector_validator),
+        ("image", get_image_validator),
+    ]:
+        validator = get_validator()
+        schema = validator.get_schema()
+        strategy = from_schema(schema)
+
+        @settings(max_examples=1)
+        @given(strategy)
+        def schema(data):
+            with open(SAVE_FOLDER / f"example_{name}.json", "w") as f:
+                json.dump(data, f, indent=4)
+            return
+
+        schema()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/validate_file.py b/scripts/validate_file.py
index dc79c7b6a511eb4d19dcd7e043113bc396f282fa..2b27d3a4eb32dcc8320a809e16a756fd6f218711 100644
--- a/scripts/validate_file.py
+++ b/scripts/validate_file.py
@@ -2,12 +2,13 @@ from thd_json.projection import get_projection_validator
 from pathlib import Path
 
 
-FILE = Path('./examples/thd_json/test_thd.json')
+FILE = Path("./examples/thd_json/test_thd.json")
 
 
 def main():
     projection_validator = get_projection_validator()
     projection_validator.file(FILE)
 
-if __name__ == '__main__':
-    main()
\ No newline at end of file
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/validate_folder.py b/scripts/validate_folder.py
index 0813bac1a93a79e13b63b482a45e8ecc5d7b554c..9517ec1b892dfb0a4496e387d4dbe5260af62ef8 100644
--- a/scripts/validate_folder.py
+++ b/scripts/validate_folder.py
@@ -2,12 +2,13 @@ from thd_json.projection import get_projection_validator
 from pathlib import Path
 
 
-FOLDER = Path('./examples/artist')
+FOLDER = Path("./examples/artist")
 
 
 def main():
     projection_validator = get_projection_validator()
     projection_validator.folder(FOLDER)
 
-if __name__ == '__main__':
-    main()
\ No newline at end of file
+
+if __name__ == "__main__":
+    main()
diff --git a/src/thd_json/__init__.py b/src/thd_json/__init__.py
index a95ecf83bfaa301dc2a1b9ff66d8032e43e852c5..9e05c3d0675746031424bac9f9e8c5fff21e0c4b 100644
--- a/src/thd_json/__init__.py
+++ b/src/thd_json/__init__.py
@@ -1 +1,3 @@
-from .validation import Validator
\ No newline at end of file
+from .validation import Validator
+
+__all__ = ["Validator"]
diff --git a/src/thd_json/detector/__init__.py b/src/thd_json/detector/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ca65f8876de6c3d4b67503c3d12f2434f00b693
--- /dev/null
+++ b/src/thd_json/detector/__init__.py
@@ -0,0 +1,35 @@
+from thd_json import Validator
+
+from pathlib import Path
+import argparse
+
+
+def get_detector_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(Path(__file__).parent / Path("detector.json"), json_suffix)
+
+
+def detector_cli():
+    parser = argparse.ArgumentParser(
+        description="JSON THD Detector Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
+    args = parser.parse_args()
+
+    suffix = str(args.suffix)
+    if not suffix.startswith("*"):
+        raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
+
+    validator = get_detector_validator(suffix)
+
+    folder = Path(args.folder)
+    if not folder.exists():
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
+
+    validator.folder(Path(args.folder))
+
+
+if __name__ == "__main__":
+    detector_cli()
diff --git a/src/thd_json/detector/detector.json b/src/thd_json/detector/detector.json
new file mode 100644
index 0000000000000000000000000000000000000000..14c555455f34fc34e1b87b1fc285f6c9cbf07bf2
--- /dev/null
+++ b/src/thd_json/detector/detector.json
@@ -0,0 +1,37 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "THD Detector File Schema",
+    "type": "object",
+    "version": 0.6,
+    "date": "19.11.2024",
+    "properties": {
+      "pixel_pitch_width_mm": {
+        "type": "number",
+        "description": "Pixel pitch in width dimension measured in millimeters.",
+        "minimum": 0.001
+      },
+      "pixel_pitch_height_mm": {
+        "type": "number",
+        "description": "Pixel pitch in height dimension measured in millimeters.",
+        "minimum": 0.001
+      },
+      "image_width_px": {
+        "type": "integer",
+        "description": "Width of the image in pixels.",
+        "minimum": 1
+      },
+      "image_height_px": {
+        "type": "integer",
+        "description": "Height of the image in pixels.",
+        "minimum": 1
+      }
+    },
+    "required": [
+      "pixel_pitch_height_mm",
+      "pixel_pitch_width_mm",
+      "image_width_px",
+      "image_height_px"
+    ],
+    "additionalProperties": false
+  }
+  
\ No newline at end of file
diff --git a/src/thd_json/header/__init__.py b/src/thd_json/header/__init__.py
index fcfc2d07132bd9d84ef0c1789896de554414a88f..4af07ce236d9cbf1f189f46cd4b2b77eb862c2b1 100644
--- a/src/thd_json/header/__init__.py
+++ b/src/thd_json/header/__init__.py
@@ -3,29 +3,55 @@ from thd_json import Validator
 from pathlib import Path
 import argparse
 
+from dataclasses import dataclass
+import uuid
+from datetime import datetime, timedelta
+
+
+@dataclass
+class JsonHeader:
+    uuid: str
+    timestamp: str
+
+
+def get_header_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(Path(__file__).parent / Path("header.json"), json_suffix)
 
-def get_header_validator(json_suffix: str = '*.json') -> Validator:
-    return Validator(Path(__file__).parent / Path('header.json'), json_suffix)
 
 def header_cli():
-    parser = argparse.ArgumentParser(description='JSON THD Header Validator CLI with uv.')
-    parser.add_argument('folder', help='Folder to check.', type=str)
-    parser.add_argument('suffix', help='Projection suffix.', default='*.json', type=Path, nargs='?')
+    parser = argparse.ArgumentParser(
+        description="JSON THD Header Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
     args = parser.parse_args()
 
     suffix = str(args.suffix)
-    if not suffix.startswith('*'):
+    if not suffix.startswith("*"):
         raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
 
-    validator = get_header_validator(
-        suffix)
-    
+    validator = get_header_validator(suffix)
+
     folder = Path(args.folder)
     if not folder.exists():
-        raise FileNotFoundError(f'Folder: {folder} does not exist')
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
 
     validator.folder(Path(args.folder))
 
 
+def generate_header() -> JsonHeader:
+    generated_uuid = uuid.uuid1()
+    timestamp = (generated_uuid.time - 0x01B21DD213814000) / 1e7
+    datetime_from_uuid = datetime(1970, 1, 1) + timedelta(seconds=timestamp)
+    return JsonHeader(str(generated_uuid), str(datetime_from_uuid))
+
+
+def generate_header_cli():
+    header = generate_header()
+    print(f"{header.uuid};{header.timestamp}")
+
+
 if __name__ == "__main__":
-    header_cli()
\ No newline at end of file
+    header_cli()
diff --git a/src/thd_json/header/header.json b/src/thd_json/header/header.json
index 27312ff1b4b0c53ac4c21949833fd92ae2e2dd08..73ef475a91efe60c4d8eae1a1fa5a93127befdbf 100644
--- a/src/thd_json/header/header.json
+++ b/src/thd_json/header/header.json
@@ -1,9 +1,9 @@
 {
     "$schema": "http://json-schema.org/draft-07/schema#",
-    "title": "THD Projection File Schema",
+    "title": "THD Data Metadata Header File Schema",
     "type": "object",
-    "version": 0.2,
-    "date": "14.11.2024",
+    "version": 0.6,
+    "date": "16.11.2024",
     "properties": {
         "timestamp": {
             "type": "string",
diff --git a/src/thd_json/image/__init__.py b/src/thd_json/image/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4dd5cb3706ea7d3f2c36811f61443543e135c302
--- /dev/null
+++ b/src/thd_json/image/__init__.py
@@ -0,0 +1,35 @@
+from thd_json import Validator
+
+from pathlib import Path
+import argparse
+
+
+def get_image_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(Path(__file__).parent / Path("image.json"), json_suffix)
+
+
+def image_cli():
+    parser = argparse.ArgumentParser(
+        description="JSON THD Image Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
+    args = parser.parse_args()
+
+    suffix = str(args.suffix)
+    if not suffix.startswith("*"):
+        raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
+
+    validator = get_image_validator(suffix)
+
+    folder = Path(args.folder)
+    if not folder.exists():
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
+
+    validator.folder(Path(args.folder))
+
+
+if __name__ == "__main__":
+    image_cli()
diff --git a/src/thd_json/image/image.json b/src/thd_json/image/image.json
new file mode 100644
index 0000000000000000000000000000000000000000..ff2f8822e69f5847f8fbdf15dc7bceb4519f9b4f
--- /dev/null
+++ b/src/thd_json/image/image.json
@@ -0,0 +1,30 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "THD Projection Image File Schema",
+    "type": "object",
+    "version": 0.6,
+    "date": "19.11.2024",
+    "properties": {
+        "timestamp": {
+            "type": "string",
+            "description": "Timestamp, e.g. 2018-11-13T20:20:39+00:00",
+            "format": "date-time"
+        },
+        "uuid": {
+            "type": "string",
+            "description": "Unique identifier",
+            "format": "uuid"
+        },
+        "image_path": {
+            "type": "string",
+            "description": "Path to TIFF file.",
+            "pattern": "^.*\\.(tif|tiff)$"
+        }
+    },
+    "required": [
+        "timestamp",
+        "uuid",
+        "image_path"
+    ],
+    "additionalProperties": false
+}
\ No newline at end of file
diff --git a/src/thd_json/projection/__init__.py b/src/thd_json/projection/__init__.py
index 8cdd6116f4382c4979dcab781f983f5ac0984ea7..071e623aa5d872034e58c9dcbd205c4cebcdea7a 100644
--- a/src/thd_json/projection/__init__.py
+++ b/src/thd_json/projection/__init__.py
@@ -4,28 +4,32 @@ from pathlib import Path
 import argparse
 
 
-def get_projection_validator(json_suffix: str = '*.json') -> Validator:
-    return Validator(Path(__file__).parent / Path('projection.json'), json_suffix)
-
-def projection_cli():
-    parser = argparse.ArgumentParser(description='JSON THD Projection Validator CLI with uv.')
-    parser.add_argument('folder', help='Folder to check.', type=str)
-    parser.add_argument('suffix', help='Projection suffix.', default='*.json', type=Path, nargs='?')
+def get_projection_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(Path(__file__).parent / Path("projection.json"), json_suffix)
+
+
+def projection_geometry_cli():
+    parser = argparse.ArgumentParser(
+        description="JSON THD Projection Geometry Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
     args = parser.parse_args()
 
     suffix = str(args.suffix)
-    if not suffix.startswith('*'):
+    if not suffix.startswith("*"):
         raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
 
-    validator = get_projection_validator(
-        suffix)
-    
+    validator = get_projection_validator(suffix)
+
     folder = Path(args.folder)
     if not folder.exists():
-        raise FileNotFoundError(f'Folder: {folder} does not exist')
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
 
     validator.folder(Path(args.folder))
 
 
 if __name__ == "__main__":
-    projection_cli()
\ No newline at end of file
+    projection_geometry_cli()
diff --git a/src/thd_json/projection/projection.json b/src/thd_json/projection/projection.json
index fcafe946b991747f4715d05e49a68ec6853b62f3..7868d0bdc4257927d2928d8aed3898dfa6c7cf17 100644
--- a/src/thd_json/projection/projection.json
+++ b/src/thd_json/projection/projection.json
@@ -2,110 +2,30 @@
     "$schema": "http://json-schema.org/draft-07/schema#",
     "title": "THD Projection File Schema",
     "type": "object",
-    "version": 0.3,
-    "date": "14.11.2024",
+    "version": 0.6,
+    "date": "19.11.2024",
     "properties": {
       "header": {
         "$ref": "./header/header.json"
       },
-      "focal_spot_position_mm": {
-        "type": "array",
-        "description": "Position of the focal spot in millimeters.",
-        "items": {
-          "type": "number"
-        },
-        "minItems": 3,
-        "maxItems": 3
+      "image": {
+        "$ref": "./image/image.json"
       },
-      "focal_spot_orientation_quat": {
-        "type": "array",
-        "description": "Quaternion representing the detector's center orientation.",
-        "items": {
-          "type": "number"
-        },
-        "minItems": 4,
-        "maxItems": 4
+      "projection_geometry": {
+        "$ref": "./projection_geometry/projection_geometry.json"
       },
-      "detector_center_position_mm": {
-        "type": "array",
-        "description": "Center position of the detector in millimeters.",
-        "items": {
-          "type": "number"
-        },
-        "minItems": 3,
-        "maxItems": 3
+      "detector": {
+        "$ref": "./detector/detector.json"
       },
-      "detector_horizontal_vector": {
-        "type": "array",
-        "description": "Horizontal orientation vector of the detector.",
-        "items": {
-          "type": "number"
-        },
-        "minItems": 3,
-        "maxItems": 3
-      },
-      "detector_vertical_vector": {
-        "type": "array",
-        "description": "Vertical orientation vector of the detector.",
-        "items": {
-          "type": "number"
-        },
-        "minItems": 3,
-        "maxItems": 3
-      },
-      "detector_center_orientation_quat": {
-        "type": "array",
-        "description": "Quaternion representing the detector's center orientation.",
-        "items": {
-          "type": "number"
-        },
-        "minItems": 4,
-        "maxItems": 4
-      },
-      "pixel_pitch_width_mm": {
-        "type": "number",
-        "description": "Pixel pitch in width dimension measured in millimeters.",
-        "minimum": 0.001
-      },
-      "pixel_pitch_height_mm": {
-        "type": "number",
-        "description": "Pixel pitch in height dimension measured in millimeters.",
-        "minimum": 0.001
-      },
-      "image_width_px": {
-        "type": "integer",
-        "description": "Width of the image in pixels.",
-        "minimum": 1
-      },
-      "image_height_px": {
-        "type": "integer",
-        "description": "Height of the image in pixels.",
-        "minimum": 1
-      },
-      "projection_matrix_cera": {
-        "type": "array",
-        "description": "Projection matrix for CERA, representing transformations.",
-        "items": {
-          "type": "array",
-          "items": {
-            "type": "number"
-          },
-          "minItems": 4,
-          "maxItems": 4
-        },
-        "minItems": 3,
-        "maxItems": 3
+      "source": {
+        "$ref": "./source/source.json"
       }
     },
     "required": [
-      "focal_spot_position_mm",
-      "detector_center_position_mm",
-      "detector_center_orientation_quat",
-      "pixel_pitch_height_mm",
-      "pixel_pitch_width_mm",
-      "image_width_px",
-      "image_height_px"
+      "header",
+      "projection_geometry",
+      "detector",
+      "image"
     ],
     "additionalProperties": false
   }
-  
\ No newline at end of file
diff --git a/src/thd_json/projection_geometry/__init__.py b/src/thd_json/projection_geometry/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5316ee932ef2b0cf791856f646d4e86f25d8adc
--- /dev/null
+++ b/src/thd_json/projection_geometry/__init__.py
@@ -0,0 +1,37 @@
+from thd_json import Validator
+
+from pathlib import Path
+import argparse
+
+
+def get_projection_geometry_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(
+        Path(__file__).parent / Path("projection_geometry.json"), json_suffix
+    )
+
+
+def projection_geometry_cli():
+    parser = argparse.ArgumentParser(
+        description="JSON THD Projection Geometry Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
+    args = parser.parse_args()
+
+    suffix = str(args.suffix)
+    if not suffix.startswith("*"):
+        raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
+
+    validator = get_projection_geometry_validator(suffix)
+
+    folder = Path(args.folder)
+    if not folder.exists():
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
+
+    validator.folder(Path(args.folder))
+
+
+if __name__ == "__main__":
+    projection_geometry_cli()
diff --git a/src/thd_json/projection_geometry/projection_geometry.json b/src/thd_json/projection_geometry/projection_geometry.json
new file mode 100644
index 0000000000000000000000000000000000000000..bc376b524918ea96328a70cdaf7e630892b431b9
--- /dev/null
+++ b/src/thd_json/projection_geometry/projection_geometry.json
@@ -0,0 +1,70 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "THD Projection Geometry File Schema",
+    "type": "object",
+    "version": 0.6,
+    "date": "19.11.2024",
+    "properties": {
+      "focal_spot_position_mm": {
+        "type": "array",
+        "description": "Position of the focal spot in millimeters.",
+        "items": {
+          "type": "number"
+        },
+        "minItems": 3,
+        "maxItems": 3
+      },
+      "focal_spot_orientation_quat": {
+        "type": "array",
+        "description": "Quaternion representing the detector's center orientation.",
+        "items": {
+          "type": "number"
+        },
+        "minItems": 4,
+        "maxItems": 4
+      },
+      "detector_center_position_mm": {
+        "type": "array",
+        "description": "Center position of the detector in millimeters.",
+        "items": {
+          "type": "number"
+        },
+        "minItems": 3,
+        "maxItems": 3
+      },
+      "detector_horizontal_vector": {
+        "type": "array",
+        "description": "Horizontal orientation vector of the detector.",
+        "items": {
+          "type": "number"
+        },
+        "minItems": 3,
+        "maxItems": 3
+      },
+      "detector_vertical_vector": {
+        "type": "array",
+        "description": "Vertical orientation vector of the detector.",
+        "items": {
+          "type": "number"
+        },
+        "minItems": 3,
+        "maxItems": 3
+      },
+      "detector_center_orientation_quat": {
+        "type": "array",
+        "description": "Quaternion representing the detector's center orientation.",
+        "items": {
+          "type": "number"
+        },
+        "minItems": 4,
+        "maxItems": 4
+      }
+    },
+    "required": [
+      "focal_spot_position_mm",
+      "detector_center_position_mm",
+      "detector_center_orientation_quat"
+    ],
+    "additionalProperties": false
+  }
+  
\ No newline at end of file
diff --git a/src/thd_json/projection_matrix/__init__.py b/src/thd_json/projection_matrix/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/thd_json/projection_matrix/projection_matrix.json b/src/thd_json/projection_matrix/projection_matrix.json
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/thd_json/source/__init__.py b/src/thd_json/source/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..62e56f51131dcba93fc66a69f0f8453df4ecfb58
--- /dev/null
+++ b/src/thd_json/source/__init__.py
@@ -0,0 +1,35 @@
+from thd_json import Validator
+
+from pathlib import Path
+import argparse
+
+
+def get_source_validator(json_suffix: str = "*.json") -> Validator:
+    return Validator(Path(__file__).parent / Path("source.json"), json_suffix)
+
+
+def source_cli():
+    parser = argparse.ArgumentParser(
+        description="JSON THD Source Validator CLI with uv."
+    )
+    parser.add_argument("folder", help="Folder to check.", type=str)
+    parser.add_argument(
+        "suffix", help="Projection suffix.", default="*.json", type=Path, nargs="?"
+    )
+    args = parser.parse_args()
+
+    suffix = str(args.suffix)
+    if not suffix.startswith("*"):
+        raise ValueError(f'The suffix must always start with: "*". \nIt is: {suffix}')
+
+    validator = get_source_validator(suffix)
+
+    folder = Path(args.folder)
+    if not folder.exists():
+        raise FileNotFoundError(f"Folder: {folder} does not exist")
+
+    validator.folder(Path(args.folder))
+
+
+if __name__ == "__main__":
+    source_cli()
diff --git a/src/thd_json/source/source.json b/src/thd_json/source/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..54feda7af7d77d262313ccff1e9b314cb9bf6a0e
--- /dev/null
+++ b/src/thd_json/source/source.json
@@ -0,0 +1,26 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "title": "THD X-Ray Source File Schema",
+    "type": "object",
+    "version": 0.6,
+    "date": "19.11.2024",
+    "properties": {
+      "voltage_kv": {
+        "type": "number",
+        "description": "Source voltage in kV.",
+        "minimum": 0.001
+      },
+      "current_ma": {
+        "type": "number",
+        "description": "Source current in µA.",
+        "minimum": 0.001
+      }
+    },
+    
+    "required": [
+      "voltage_kv",
+      "current_ma"
+    ],
+    "additionalProperties": false
+  }
+  
\ No newline at end of file
diff --git a/src/thd_json/validation.py b/src/thd_json/validation.py
index 754a534f6a5c0dd0bdbad11793540207f61644fc..40cf87505e900dbd893b464edf83a0648b16cfd7 100644
--- a/src/thd_json/validation.py
+++ b/src/thd_json/validation.py
@@ -1,37 +1,66 @@
-import os
 import json
 from pathlib import Path
 from jsonschema import ValidationError, RefResolver
 from jsonschema.validators import Draft202012Validator
-import os
 
 
-class Validator():
-    def __init__(self, json_schema: Path, json_suffix: str = '*.json') -> None:
+class Validator:
+    def __init__(self, json_schema: Path, json_suffix: str = "*.json") -> None:
         self.json_schema = json_schema
         self.json_suffix = json_suffix
         print(f"Validator for schema: {json_schema} \nSuffix: {json_suffix}\n")
 
     def file(self, file_path: Path) -> bool:
-        with open(str(self.json_schema)) as schema_file, open(str(file_path)) as data_file:
+        with open(str(self.json_schema)) as schema_file, open(
+            str(file_path)
+        ) as data_file:
             schema = json.load(schema_file)
             data = json.load(data_file)
+            schema_path = self.json_schema.parent.absolute().as_uri()
+            resolver = RefResolver(base_uri=schema_path, referrer=schema)
+
             try:
-                schema_path = self.json_schema.parent.absolute().as_uri()
-                resolver = RefResolver(base_uri=schema_path,
-                                       referrer=schema)
-                Draft202012Validator(schema=schema,
-                                     format_checker=Draft202012Validator.FORMAT_CHECKER,
-                                     resolver=resolver).validate(data)
+                Draft202012Validator(
+                    schema=schema,
+                    format_checker=Draft202012Validator.FORMAT_CHECKER,
+                    resolver=resolver,
+                ).validate(data)
                 print(f"Validate file: {file_path.parent} / {file_path.name}\n")
-                
+
             except ValidationError as e:
                 print("Validation error:", e)
 
         return True
-    
+
     def folder(self, folder_path: Path) -> bool:
         print(f"Validate folder: {folder_path}\n")
         for file in folder_path.glob(self.json_suffix):
             self.file(file)
-        return True
\ No newline at end of file
+        return True
+
+    def get_schema(self):
+        with open(str(self.json_schema)) as schema_file:
+            schema = json.load(schema_file)
+            schema_path = self.json_schema.parent.absolute().as_uri()
+            resolver = RefResolver(base_uri=schema_path, referrer=schema)
+
+            return self.resolve_ref(resolver, schema)
+
+    def resolve_ref(self, resolver, schema_part):
+        if isinstance(schema_part, dict) and "$ref" in schema_part:
+            # Resolve the reference
+            ref = schema_part["$ref"]
+            with resolver.resolving(ref) as resolved:
+                return self.resolve_ref(
+                    resolver, resolved
+                )  # Recursively resolve nested references
+        elif isinstance(schema_part, dict):
+            # Resolve each value in the dictionary
+            return {
+                key: self.resolve_ref(resolver, value)
+                for key, value in schema_part.items()
+            }
+        elif isinstance(schema_part, list):
+            # Resolve each item in the list
+            return [self.resolve_ref(resolver, item) for item in schema_part]
+        return schema_part
diff --git a/uv.lock b/uv.lock
index 0ec452f68313febb762afffbd35fcf7dfbfb7f8c..a6e03ff7697f2e5f859e742ce704884d91cc594b 100644
--- a/uv.lock
+++ b/uv.lock
@@ -10,6 +10,42 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 },
 ]
 
+[[package]]
+name = "exceptiongroup"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
+]
+
+[[package]]
+name = "hypothesis"
+version = "6.119.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "attrs" },
+    { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+    { name = "sortedcontainers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/f2/9f8aa06c692549cbe2c4d8993bd8563092ca88fc65a6a58c52a400c89d67/hypothesis-6.119.0.tar.gz", hash = "sha256:ca441c6ef55d17f27f642fa08657e80f9c13d9da7ae191c8ad58fbc2f16acd1b", size = 411964 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/45/be/fd7deddef49491c01375345b1a21a5867603b021460c3efae5a16285197c/hypothesis-6.119.0-py3-none-any.whl", hash = "sha256:99d660bfdab62cfd7c9966e3829a217697671e0b992f0372b5b43647b3414471", size = 473116 },
+]
+
+[[package]]
+name = "hypothesis-jsonschema"
+version = "0.23.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "hypothesis" },
+    { name = "jsonschema" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4f/ad/2073dd29d8463a92c243d0c298370e50e0d4082bc67f156dc613634d0ec4/hypothesis-jsonschema-0.23.1.tar.gz", hash = "sha256:f4ac032024342a4149a10253984f5a5736b82b3fe2afb0888f3834a31153f215", size = 42896 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/17/44/635a8d2add845c9a2d99a93a379df77f7e70829f0a1d7d5a6998b61f9d01/hypothesis_jsonschema-0.23.1-py3-none-any.whl", hash = "sha256:a4d74d9516dd2784fbbae82e009f62486c9104ac6f4e3397091d98a1d5ee94a2", size = 29200 },
+]
+
 [[package]]
 name = "json-schema"
 version = "0.3"
@@ -128,17 +164,30 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/66/86/6f72984a284d720d84fba5ee7b0d1b0d320978b516497cbfd6e335e95a3e/rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677", size = 219621 },
 ]
 
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 },
+]
+
 [[package]]
 name = "thd-json"
 version = "0.1.0"
 source = { editable = "." }
 dependencies = [
+    { name = "hypothesis" },
+    { name = "hypothesis-jsonschema" },
     { name = "json-schema" },
     { name = "jsonschema" },
 ]
 
 [package.metadata]
 requires-dist = [
+    { name = "hypothesis", specifier = ">=6.119.0" },
+    { name = "hypothesis-jsonschema", specifier = ">=0.23.1" },
     { name = "json-schema", specifier = ">=0.3" },
     { name = "jsonschema", specifier = ">=4.23.0" },
 ]