From 7ca2e9b5893529eb23dd8367ced7ebc5c617d24b Mon Sep 17 00:00:00 2001 From: aelmiger <40243985+aelmiger@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:23:43 +0100 Subject: [PATCH] Add initial integration test (#1) * Add initial integration test * Fix type issue with path * Change integration test job * Fix None to Path conversion * Fix test job name --------- Co-authored-by: Anton Elmiger --- .github/workflows/integration_test.yaml | 73 +++++++++ .../__example_assets__/test_job.syclops.yaml | 149 ++++++++++++++++++ syclops/cli.py | 13 ++ syclops/utility/setup_utils.py | 6 +- 4 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/integration_test.yaml create mode 100644 syclops/__example_assets__/test_job.syclops.yaml diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml new file mode 100644 index 0000000..c3b3661 --- /dev/null +++ b/.github/workflows/integration_test.yaml @@ -0,0 +1,73 @@ +name: Syclops Pipeline Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-test: + name: Syclops Pipeline Test + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt-get update -q + sudo apt-get install -y --no-install-recommends libgl1-mesa-glx libglib2.0-0 libsm6 libxrender1 libxext6 libxi6 libxkbcommon0 + - name: Set up Python 3.10 + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install package + run: | + pip install . + + - name: Run Syclops + run: | + syclops -if ./ --test-job -log + + # Check for expected output files and report any missing ones + - name: Check for output files + run: | + patterns=( + "./output/*/main_camera/rect/*.png" + "./output/*/main_camera/rect/*metadata.yaml" + "./output/*/main_camera/extrinsics/*.yaml" + "./output/*/main_camera/extrinsics/*metadata.yaml" + "./output/*/calibration/*intrinsics.yaml" + "./output/*/calibration/*metadata.yaml" + "./output/*/main_camera_annotations/bounding_box/*.txt" + "./output/*/main_camera_annotations/bounding_box/*metadata.yaml" + "./output/*/main_camera_annotations/depth/*.npz" + "./output/*/main_camera_annotations/depth/*metadata.yaml" + "./output/*/main_camera_annotations/instance_segmentation/*.npz" + "./output/*/main_camera_annotations/instance_segmentation/*metadata.yaml" + "./output/*/main_camera_annotations/semantic_segmentation/*.npz" + "./output/*/main_camera_annotations/semantic_segmentation/*metadata.yaml" + "./output/*/main_camera_annotations/pointcloud/*.npz" + "./output/*/main_camera_annotations/pointcloud/*metadata.yaml" + "./output/*/main_camera_annotations/object_volume/*.npz" + "./output/*/main_camera_annotations/object_volume/*metadata.yaml" + "./output/*/object_positions/*.json" + "./output/*/object_positions/*metadata.yaml" + ) + + missing=false + + for pattern in "${patterns[@]}"; do + files=$(find . -path "$pattern") + if [[ ! $files ]]; then + echo "MISSING: $pattern" + missing=true + fi + done + + if [ "$missing" = true ]; then + exit 1 + fi \ No newline at end of file diff --git a/syclops/__example_assets__/test_job.syclops.yaml b/syclops/__example_assets__/test_job.syclops.yaml new file mode 100644 index 0000000..94db738 --- /dev/null +++ b/syclops/__example_assets__/test_job.syclops.yaml @@ -0,0 +1,149 @@ +steps: 1 +seeds: + numpy: 42 + cycles: 42 +render_device: "CPU" +render_hardware: "CUDA" +denoising_enabled: False +denoising_algorithm: "OPENIMAGEDENOISE" + +# TRANSFORMATION CONFIG +transformations: + map: + location: [0, 0, 0] + rotation: [0, 0, 0] + children: + camera_link: + location: + linear: [[-20,0,2],[0.5,0,0]] + rotation: + normal: [[0.785398, 0, 0],[0.05,0.05,0.05]] + iso_object: + location: + uniform: [[-20,-20,0],[20,20,0]] + rotation: [0,0,0] + +textures: + plain_noise: + config: + image_size: [512, 512] + bit_depth: 16 + seed: 2 + num_textures: 2 + ops: + - perlin: + res: 4 + octaves: 4 + - math_expression: "((x-0.5) * 100 + 65535 / 2)/65535" + +# SCENE CONFIG +scene: + syclops_plugin_ground: + - name: "Ground" + size: 50 # m + texture: Example Assets/Muddy Dry Ground + # displacement_texture: Example Assets/Ground Displacement 1 + displacement_texture: Preprocessed Assets/plain_noise + class_id: 1 + + syclops_plugin_environment: + - type: hdri + environment_image: + random_selection: [Example Assets/Sunflower Field] + + syclops_plugin_scatter: + - name: "Corn Scatter" + models: [Example Assets/Corn] + floor_object: Ground + max_texture_size: 512 + density_max: 10 # per m^2 + distance_min: 0.05 # m + scale_standard_deviation: 0.5 + class_id: 2 + class_id_offset: + Stem: 1 + seed: 1 + clumps: + ratio: 0.3 + size: 3 + size_std: 2 + position_std: 0.02 + scale_std: 0.4 + - name: "Weed Scatter" + models: [Example Assets/Plain Weeds] + floor_object: Ground + max_texture_size: 512 + density_max: 15 # per m^2 + distance_min: 0.01 # m + scale_standard_deviation: 0.5 + class_id: 4 + seed: 2 + clumps: + ratio: 0.3 + size: 3 + size_std: 2 + position_std: 0.02 + scale_std: 0.4 + + syclops_plugin_object: + - name: iso_object + frame_id: iso_object + class_id: 5 + place_on_ground: true + models: [Example Assets/ISO Object] + floor_object: Ground + + +# SENSOR CONFIG +sensor: + syclops_sensor_camera: + - name: "main_camera" + # Location, rotation and velocity of camera is optional if frame_id is set + frame_id: "camera_link" + resolution: [1280, 960] + focal_length: 65 # mm + shutter_speed: 0.02 # s Currently only affects motion blur + sensor_width: 35 # mm + + exposure: 0.0 # Exposure (stops) shift of camera + gamma: 1.0 # Gamma correction applied to the image + + depth_of_field: + aperture: 8 # f-number (no influence on exposure) + autofocus: true + #focus_distance: 2 # m (disabled because of autofocus) + + motion_blur: + enabled: true + rolling_shutter: # Only top to bottom supported + enabled: true + duration: 0.03 # s (Scanline "exposure" time) + outputs: + syclops_output_rgb: + - samples: 16 + compositor: + chromatic_aberration: 0.007 #Strong aberration can cause shift between ground truth and rgb + bloom: + threshold: 0.99 # higher is less bloom + id: main_cam_rgb + syclops_output_object_positions: + - id: main_cam_object_positions + syclops_output_pixel_annotation: + - semantic_segmentation: + id: main_cam_semantic + instance_segmentation: + id: main_cam_instance + pointcloud: + id: main_cam_pointcloud + depth: + id: main_cam_depth + object_volume: + id: main_cam_object_volume + +postprocessing: + syclops_postprocessing_bounding_boxes: + - type: "YOLO" + classes_to_skip: [0,1] + id: yolo_bound_boxes + sources: ["main_cam_instance", "main_cam_semantic"] + multiple_bb_per_instance: False \ No newline at end of file diff --git a/syclops/cli.py b/syclops/cli.py index aaeeb72..0d77521 100644 --- a/syclops/cli.py +++ b/syclops/cli.py @@ -36,6 +36,11 @@ help="Use example job description file", action="store_true", ) +parser.add_argument( + "--test-job", + help="Run test job for an integration test", + action="store_true", +) parser.add_argument( "-log", "--show-logging", @@ -349,6 +354,14 @@ def main(): ) _run_syclops_job(args, install_folder, job_filepath) + elif args.test_job: + job_filepath = ( + get_module_path("syclops").parent + / "__example_assets__" + / "test_job.syclops.yaml" + ) + _run_syclops_job(args, install_folder, job_filepath) + elif args.texture_viewer: texture_viewer(args) diff --git a/syclops/utility/setup_utils.py b/syclops/utility/setup_utils.py index d04fcdf..c9cc2ef 100644 --- a/syclops/utility/setup_utils.py +++ b/syclops/utility/setup_utils.py @@ -95,12 +95,12 @@ def install_blender(version: str, install_dir: Path) -> None: # Clean up dest_file.unlink() -def get_or_create_install_folder(install_folder_path: Path) -> Path: +def get_or_create_install_folder(install_folder_path: str) -> Path: """ Get the install folder from the config file, or ask the user for a folder. Args: - install_folder_path (Path): The path to the install folder, if it exists. + install_folder_path (str): The path to the install folder, if it exists. Returns: Path: The path to the install folder. @@ -110,7 +110,7 @@ def get_or_create_install_folder(install_folder_path: Path) -> Path: def determine_folder(): if install_folder_path is not None: - return install_folder_path.resolve() + return Path(install_folder_path).resolve() return _ask_directory().resolve() # If 'install_folder' is not in the config or the saved folder doesn't exist