Skip to content

Latest commit

 

History

History
504 lines (341 loc) · 19 KB

File metadata and controls

504 lines (341 loc) · 19 KB

QualCompare Developer Guide

Purpose and research constraints

QualCompare is a research application built during a PhD project on perceptual quality assessment of 3D content. The desktop GUI remains Windows-only, while the companion CLI and native patchify core are being aligned for cross-platform use.

This repository is not production software. The main priority is to preserve reproducibility, backward compatibility, and the existing experimental workflow. Small safe changes are preferred over broad refactors.

For user-facing setup and platform-specific usage, see:

The current end-to-end workflow is:

  1. Select a dataset folder.
  2. Discover supported 3D files recursively.
  3. Copy each object and its dependencies to an SSD-backed temporary input cache.
  4. Launch Blender in background mode with the current render arguments.
  5. Generate views and masks.
  6. Copy rendered outputs back to the final output folder.
  7. Run patch extraction on the rendered images.

When editing the project, assume that output layout, Blender command-line arguments, and patchify conventions are already consumed by existing datasets, papers, and downstream scripts.

Repository map

  • QualCompare/ WPF desktop application. It contains the UI, render queue, configuration bootstrap, path handling, output path generation, and the managed entry point.

  • obj2png/ Blender-side Python pipeline. The active script is render_single.py, supported by positions.py for camera sampling.

  • patchify/ Native C++ patch extraction logic. It reads rendered images and sibling masks, then writes CSV outputs used downstream.

  • PatchifyWrapper/ C++/CLI bridge used by the WPF application to call the native patchify code from C#.

  • installer/ Inno Setup packaging for the installable Windows release. It packages the application output, bundled scripts, and resources, but not Blender itself.

  • docs/ Maintained technical documentation covering architecture, datasets, rendering, installer validation, known issues, release process, and short-term priorities.

  • QualCompareCLI/ Cross-platform .NET 8 console application for batch rendering without UI. It reads JSON configuration files and executes rendering on Windows, Linux, or macOS.

Historical naming still exists internally. The product name is now QualCompare, but older names remain in some solution-level identifiers, namespaces, paths, comments, and documentation fragments. Treat those as technical debt rather than a reason to rename things aggressively.

Architecture and design choices

Why Blender is launched externally

Rendering is delegated to Blender and Python instead of being reimplemented in C#. This keeps the GUI focused on orchestration while the rendering logic stays close to Blender's API. The current WPF application constructs a Blender command line in QualCompare/RenderQueue.cs and launches Blender in background mode with:

blender --background --python render_single.py -- <arguments>

That split is intentional:

  • WPF handles datasets, queueing, settings, and user workflow.
  • Blender handles import, normalization, camera placement, lighting, rendering, and mask generation.
  • Python remains the source of truth for rendering semantics.

Why SSD temp input and output roots exist

The application stages data into temporary input and output folders before copying results back to the final destination. This is done to reduce repeated reads from slower dataset storage, keep per-object processing isolated, and reduce I/O bottlenecks during rendering.

The relevant runtime paths are stored in JSON config as:

  • TempInputRoot
  • TempOutputRoot

Why the queue is sequential but rendering inside a job is parallel

The render queue processes one job at a time, but objects inside that job are rendered in parallel. This is the current compromise between throughput and disk pressure:

  • queue level: sequential
  • object level inside one job: parallel

The code currently treats storage I/O as the main bottleneck. HDD reads are gated during prefetch, SSD staging is used deliberately, and parallelism defaults to a fraction of CPU count rather than full saturation.

Why output layout is a contract

Rendered output layout is part of the workflow contract, not just a cosmetic convention. Current per-object output is:

object_name/
    views/
    masks/

Expected filenames are:

views/view_1.png
masks/mask_1.png

This must stay stable because:

  • patchify derives mask paths from rendered image paths
  • CSV outputs assume the current naming scheme
  • downstream experiments rely on stable folder conventions
  • the WPF app can also build higher-level experiment directories from dataset metadata

Why there are two persistence layers

The application currently uses two persistence systems:

  • legacy UI state in QualCompare/Properties/Settings.settings
  • runtime JSON config in %AppData%/QualCompare/settings.json

The legacy settings mainly preserve last-used UI values such as folders, selected method, or patchify mode. The JSON config stores runtime-critical settings such as:

  • BlenderPath
  • RenderScriptPath
  • TempInputRoot
  • TempOutputRoot
  • DefaultOutputRoot
  • render parameters
  • patchify parameters
  • up-axis and PLY render options

This split is real and contributors should preserve it unless they are intentionally consolidating configuration with a migration plan.

Development environment and dependencies

Required environment

  • Windows 10 or Windows 11
  • Visual Studio 2022
  • Visual Studio workloads for:
    • .NET desktop development
    • Desktop development with C++
  • .NET Framework 4.8.1 targeting pack
  • MSVC v143 toolset
  • Blender 4.x installed separately
  • OpenCV native installation for patchify/ and PatchifyWrapper/

Managed dependencies

The C# project restores packages from QualCompare/packages.config. Current managed dependencies include:

  • Newtonsoft.Json
  • System.Reactive
  • System.Reactive.Windows.Forms
  • System.Runtime.CompilerServices.Unsafe
  • System.Threading.Tasks.Extensions

Native dependencies

Both patchify/patchify.vcxproj and PatchifyWrapper/PatchifyWrapper.vcxproj expect OpenCV headers and libraries to be discoverable through either:

  • OPENCV_DIR
  • standard installation paths such as C:\Program Files\opencv\build

The current project files look for OpenCV include and lib folders and attempt to resolve opencv_world automatically.

Blender Python dependencies

obj2png/render_single.py imports:

  • bpy
  • cv2
  • numpy
  • mathutils

bpy and mathutils come from Blender. cv2 and numpy must be available in Blender's Python environment for the render script to work reliably.

Platform note

The solution is effectively x64 in practice. The C# project uses Any CPU solution entries, but the project itself targets x64 output, and both native projects are configured around x64 builds for the active workflow. Treat x64 as the expected developer target unless you are explicitly working on platform support.

Starting a new development session

Use this checklist when starting on a fresh workstation or after a long pause.

  1. Open QualCompare/QualCompare.sln in Visual Studio 2022.
  2. Restore NuGet packages for the C# project if Visual Studio did not do it automatically.
  3. Confirm that OpenCV is installed and that the native projects can resolve headers and libraries.
  4. Select Debug|x64 for normal development, or Release|x64 when validating packaging.
  5. Build the full solution so QualCompare, PatchifyWrapper, and patchify stay aligned.
  6. Set QualCompare as the startup project and launch it from Visual Studio.
  7. On first run, let the application create %AppData%/QualCompare/settings.json.
  8. In the application settings, verify or update:
    • BlenderPath
    • RenderScriptPath
    • TempInputRoot
    • TempOutputRoot
    • DefaultOutputRoot
  9. Confirm that the bundled script path resolves to scripts/render_single.py from the application output.
  10. Run a small OBJ render smoke test.
  • Use --no-patchify if you only want to validate rendering.
  1. Run a patchify smoke test on a rendered result if you want to validate the post-render extraction step separately.

Suggested smoke test

For a minimal validation pass:

  1. Pick a small dataset folder with a known-good OBJ.
  2. Render a low number of views using the default method.
  3. Confirm output folders contain both views/ and masks/.
  4. Run patchify on one rendered image or on the rendered folder.

Patchify: Native cross-platform build and C API

As of this update, the native patchify core has been modernized for cross-platform builds and consumption:

CMake-based build

The patchify/ folder now includes a CMakeLists.txt that enables:

  • Cross-platform compilation on Windows, Linux, and macOS
  • Decoupling from Visual Studio, allowing non-Windows developers to build the native core independently
  • OpenCV discovery via standard CMake module search paths or explicit OPENCV_DIR hints
  • Three build targets:
    • patchify – static library (core algorithm)
    • patchify_c – shared library exposing a stable C ABI
    • patchify_cli – standalone CLI for quick testing

C API boundary

Instead of direct C++ linkage, the library now exposes two stable C functions:

int patchify_process_image(const char* imagePath);
int patchify_process_folder(const char* folderPath);

Benefits:

  • Decouples application code from C++ symbol name mangling
  • Enables cross-platform shared library loading (.dll on Windows, .so on Linux, .dylib on macOS)
  • Simplifies .NET P/Invoke interop for QualCompareCLI
  • Allows alternative language bindings (Python, Rust, etc.) in the future

Building patchify with CMake

Prerequisites:

  • CMake 3.20 or later
  • C++ compiler with C++20 support (MSVC 2022, GCC 10+, or Clang 12+)
  • OpenCV development headers and libraries

Commands:

# Configure (from repository root)
# By default patchify targets are built. To skip building the native
# patchify binaries (for example on machines without OpenCV or when you
# only need the managed projects), pass -DBUILD_PATCHIFY=OFF.
cmake -S patchify -B patchify/build

# To disable building patchify targets:
cmake -S patchify -B patchify/build -DBUILD_PATCHIFY=OFF

# Build all targets (Debug)
cmake --build patchify/build --config Debug

# Build specific target
cmake --build patchify/build --config Debug --target patchify_cli

# Install to system or custom location (optional)
cmake --install patchify/build --config Debug --prefix /opt/patchify

# Notes:
- By default the CMake configuration builds `patchify`, `patchify_c` (shared
  C API) and `patchify_cli`. If you disable `BUILD_PATCHIFY`, the shared
  library and CLI are skipped and only the core static `patchify` target is
  configured (useful for minimal native-only work or dependency-limited
  environments).

# After installing, ensure the runtime library is discoverable by the
# application (copy next to the CLI executable or set `LD_LIBRARY_PATH` /
# `PATH` / `DYLD_LIBRARY_PATH` accordingly).

Windows with OpenCV:

If OpenCV is installed at C:\Program Files\opencv, the CMakeLists.txt will attempt auto-discovery. Otherwise, set the environment variable:

Set-Item -Path Env:OpenCV_DIR -Value "C:\Program Files\opencv\build\x64\vc16\lib"

Linux/macOS:

export OpenCV_DIR=/opt/opencv/lib/cmake/opencv4
cmake -S patchify -B patchify/build

Patchify CLI

After building, patchify_cli is available for testing:

# Process a single image
patchify_cli /path/to/image.png --image

# Process a folder of rendered images
patchify_cli /path/to/rendered_object/ --folder

# Help
patchify_cli --help

The CLI returns 0 on success, non-zero on error.

Integration with QualCompareCLI

The .NET CLI (QualCompareCLI) includes a --patchify mode that discovers the native patchify_c library, calls the C API through P/Invoke, and reports success or failure through the CLI.

dotnet run --project QualCompareCLI -- --patchify /path/to/rendered_object

See QualCompareCLI/README.md for full usage and deployment details.

Windows-only legacy bridge

The existing PatchifyWrapper/ C++/CLI bridge remains the path used by the WPF desktop application on Windows. It is still supported, but new cross-platform work should prefer the C API instead of the C++/CLI wrapper.

When validating patch extraction changes, confirm that CSV outputs are generated without wrapper or native DLL errors.

What to check when startup fails

  • blender.exe is missing or the configured BlenderPath is invalid.
  • scripts/render_single.py is missing from the application output or the configured RenderScriptPath is stale.
  • OpenCV headers or libs are not being resolved for patchify or PatchifyWrapper.
  • Blender's Python environment cannot import cv2.
  • A stale %AppData%/QualCompare/settings.json points to old development paths.

Runtime contracts contributors must preserve

These points are part of the active runtime contract and should not be changed casually.

  • Preserve the Blender CLI argument names and semantics assembled in QualCompare/RenderQueue.cs and parsed by obj2png/render_single.py.
  • Preserve per-object output layout:
object_name/views/view_N.png
object_name/masks/mask_N.png
  • Preserve patchify's assumption that masks live in the sibling masks/ folder with matching mask_N names.
  • Preserve dataset compatibility and the current source/distorted behavior unless you are intentionally redesigning that logic.
  • Preserve the current Python CLI spelling polyedric. Older docs may say polyhedral, but the active parser and WPF command building depend on polyedric.
  • Preserve current Blender invocation style and output copy-back behavior unless the full downstream workflow is revalidated.

Cross-platform CLI (QualCompareCLI)

The QualCompareCLI project provides cross-platform batch rendering without UI and reuses the same Blender argument contract as the WPF app.

For usage, schema, Linux/WSL setup, and troubleshooting, use the canonical documentation in:

Practical contributor workflows

Working on CLI

Primary area:

  • QualCompareCLI/

Main files:

  • Program.cs - CLI entry point and argument parsing
  • RenderConfig.cs - JSON configuration schema
  • BlenderRenderService.cs - Blender process execution

Main tasks:

  • Adding configuration options
  • Improving error messages
  • Adding validation logic
  • Handling edge cases

Important: Keep argument construction in sync with RenderQueue.cs. If you change Blender arguments, update both locations.

Working on WPF and UI behavior

Primary area:

  • QualCompare/

Main responsibilities in the current app:

  • UI event handling
  • render job creation
  • queue orchestration
  • settings bootstrap
  • output path generation
  • patchify launch

Main risk:

QualCompare/MainWindow.xaml.cs is highly centralized. Small changes can affect configuration, rendering, patchify, and logging at once. Prefer focused edits and verify the full user workflow after touching this area.

Working on rendering and Blender pipeline

Primary area:

  • obj2png/

Main files:

  • obj2png/render_single.py
  • obj2png/positions.py

Main regression risks:

  • breaking CLI argument compatibility
  • changing output naming
  • changing mask generation behavior
  • changing supported position names
  • changing PLY rendering semantics without updating the GUI assumptions

When changing rendering behavior, validate at least one OBJ workflow and one mask generation pass.

Working on patchify and native code

Primary areas:

  • patchify/
  • PatchifyWrapper/

Current setup:

  • patchify/ now has a CMake build and a small native CLI so the core patch extraction code can be built and exercised on Linux, macOS, and Windows.
  • OpenCV is still an external dependency; if CMake cannot find it automatically, point OPENCV_DIR or OpenCV_DIR at the folder containing OpenCVConfig.cmake.
  • patchify/ now also exposes a stable C API in patchify_c for language bindings and other native consumers.
  • QualCompareCLI can invoke that native API through a --patchify mode.
  • PatchifyWrapper/ remains Windows-only because it is C++/CLI and is still the path used by the WPF application.
  • The full repository is therefore not 100 percent cross-platform yet because the WPF GUI and C++/CLI bridge remain Windows-only.

Main regression risks:

  • OpenCV linkage failures
  • C++/CLI wrapper load failures
  • CSV format drift
  • mask path derivation breakage
  • path separator assumptions

When changing patch extraction behavior, validate the native CMake build, the shared C API, and the CLI first, then validate wrapper loading and a real patchify run from the WPF application, not just native compilation.

Validating installer changes

Primary area:

  • installer/

Reference documentation:

  • docs/installer_validation.md
  • docs/release_process.md

Current installer scope:

  • packages the built application output
  • includes bundled scripts and resources
  • does not bundle Blender

Main regression risks:

  • packaged script paths still pointing to development locations
  • missing Python scripts or CSV resources in the install directory
  • first-run config bootstrap not creating usable defaults
  • PatchifyWrapper.dll failing after installation

Use the documented installer validation protocol when touching packaging or path resolution.

Near-term directives

The following items are strong candidates for the next development iterations:

  • Execute and record the fresh-machine installer validation workflow from docs/installer_validation.md.
  • Clean up and clarify the exact Blender Python dependency installation process, especially for cv2.
  • Keep docs aligned with code after every substantial change to reduce documentation drift.
  • Replace or harden the current source/distorted heuristic so it is less dependent on path keywords.
  • Gradually move orchestration logic out of QualCompare/MainWindow.xaml.cs without changing behavior.
  • Review whether ModelCsvFilePath is still required by the active runtime workflow.
  • Improve logging around SSD prefetch, Blender failures, and output copy-back.

References for contributors

Useful repo documents for future sessions:

The code remains the final source of truth when documentation and implementation diverge.