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:
- Select a dataset folder.
- Discover supported 3D files recursively.
- Copy each object and its dependencies to an SSD-backed temporary input cache.
- Launch Blender in background mode with the current render arguments.
- Generate views and masks.
- Copy rendered outputs back to the final output folder.
- 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.
-
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 isrender_single.py, supported bypositions.pyfor 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.
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.
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:
TempInputRootTempOutputRoot
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.
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
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:
BlenderPathRenderScriptPathTempInputRootTempOutputRootDefaultOutputRoot- 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.
- 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/andPatchifyWrapper/
The C# project restores packages from QualCompare/packages.config. Current managed dependencies include:
Newtonsoft.JsonSystem.ReactiveSystem.Reactive.Windows.FormsSystem.Runtime.CompilerServices.UnsafeSystem.Threading.Tasks.Extensions
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.
obj2png/render_single.py imports:
bpycv2numpymathutils
bpy and mathutils come from Blender. cv2 and numpy must be available in Blender's Python environment for the render script to work reliably.
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.
Use this checklist when starting on a fresh workstation or after a long pause.
- Open
QualCompare/QualCompare.slnin Visual Studio 2022. - Restore NuGet packages for the C# project if Visual Studio did not do it automatically.
- Confirm that OpenCV is installed and that the native projects can resolve headers and libraries.
- Select
Debug|x64for normal development, orRelease|x64when validating packaging. - Build the full solution so
QualCompare,PatchifyWrapper, andpatchifystay aligned. - Set
QualCompareas the startup project and launch it from Visual Studio. - On first run, let the application create
%AppData%/QualCompare/settings.json. - In the application settings, verify or update:
BlenderPathRenderScriptPathTempInputRootTempOutputRootDefaultOutputRoot
- Confirm that the bundled script path resolves to
scripts/render_single.pyfrom the application output. - Run a small OBJ render smoke test.
- Use
--no-patchifyif you only want to validate rendering.
- Run a patchify smoke test on a rendered result if you want to validate the post-render extraction step separately.
For a minimal validation pass:
- Pick a small dataset folder with a known-good OBJ.
- Render a low number of views using the default method.
- Confirm output folders contain both
views/andmasks/. - Run patchify on one rendered image or on the rendered folder.
As of this update, the native patchify core has been modernized for cross-platform builds and consumption:
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_DIRhints - Three build targets:
patchify– static library (core algorithm)patchify_c– shared library exposing a stable C ABIpatchify_cli– standalone CLI for quick testing
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 (
.dllon Windows,.soon Linux,.dylibon macOS) - Simplifies .NET P/Invoke interop for QualCompareCLI
- Allows alternative language bindings (Python, Rust, etc.) in the future
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/buildAfter 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 --helpThe CLI returns 0 on success, non-zero on error.
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_objectSee QualCompareCLI/README.md for full usage and deployment details.
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.
blender.exeis missing or the configuredBlenderPathis invalid.scripts/render_single.pyis missing from the application output or the configuredRenderScriptPathis stale.- OpenCV headers or libs are not being resolved for
patchifyorPatchifyWrapper. - Blender's Python environment cannot import
cv2. - A stale
%AppData%/QualCompare/settings.jsonpoints to old development paths.
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.csand parsed byobj2png/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 matchingmask_Nnames. - 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 saypolyhedral, but the active parser and WPF command building depend onpolyedric. - Preserve current Blender invocation style and output copy-back behavior unless the full downstream workflow is revalidated.
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:
Primary area:
QualCompareCLI/
Main files:
Program.cs- CLI entry point and argument parsingRenderConfig.cs- JSON configuration schemaBlenderRenderService.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.
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.
Primary area:
obj2png/
Main files:
obj2png/render_single.pyobj2png/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.
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_DIRorOpenCV_DIRat the folder containingOpenCVConfig.cmake. patchify/now also exposes a stable C API inpatchify_cfor language bindings and other native consumers.QualCompareCLIcan invoke that native API through a--patchifymode.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.
Primary area:
installer/
Reference documentation:
docs/installer_validation.mddocs/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.dllfailing after installation
Use the documented installer validation protocol when touching packaging or path resolution.
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.cswithout changing behavior. - Review whether
ModelCsvFilePathis still required by the active runtime workflow. - Improve logging around SSD prefetch, Blender failures, and output copy-back.
Useful repo documents for future sessions:
- QualCompare/README.md
- QualCompareCLI/README.md
- RELEASE_NOTES.md
- docs/project_overview.md
- docs/current_architecture.md
- docs/rendering_pipeline.md
- docs/datasets_and_protocols.md
- docs/known_issues.md
- docs/todo.md
- docs/installer_validation.md
- docs/release_process.md
The code remains the final source of truth when documentation and implementation diverge.