Luxoria BRISQUE Implementation
C++ (OpenCV 4.10.0) with contrib modules
Native Interop via P/Invoke
For more details about OpenCV configuration (which includes OpenCV Core + OpenCV Contrib Modules with quality feature, check it out at : https://github.com/LuxoriaSoft/brisque_impl/blob/35fee77d24e5c9388c40e02639778e9e85fc2709/.github/workflows/build.yml#L27)
The inclusion of "contrib" is needed for bringing "quality" in, quality module sets between core and contrib modules.
For more details related to Brisque, check these links out :
https://learnopencv.com/image-quality-assessment-brisque/.
https://docs.opencv.org/4.x/d8/d99/classcv_1_1quality_1_1QualityBRISQUE.html
Table of Contents
1. Executive Summary
This document records the technical decisions and architectural choices made during the port of the BRISQUE (Blind/Referenceless Image Spatial Quality Evaluator) algorithm from a native C++ implementation to a .NET C# library.
Create a NuGet package for seamless integration into Luxoria, our C# WinUI desktop application, while preserving the performance characteristics of the native OpenCV implementation.
Key Achievements
Zero-configuration deployment: Single dotnet add package command
Native performance preserved: P/Invoke wrapper around optimized C++ code
Multi-architecture support: x86, x64, and ARM64 Windows
Minimal footprint: Strict as possible, one goal, one task to perform
Trade-off Summary
CPU-intensive operations remain optimized
Embedded native libraries
Zero external dependencies
Easy adoption for .NET developers
Optimal Visual Studio/NuGet integration
2. Project Context
2.1 What is BRISQUE?
BRISQUE (Blind/Referenceless Image Spatial Quality Evaluator) is a no-reference image quality assessment algorithm that:
Requires no reference image for comparison
Uses natural scene statistics (NSS) in the spatial domain
Extracts 36 features from locally normalized luminance coefficients
Employs SVM regression to predict perceptual quality scores (0-100, lower is better)
2.2 Why This Refactoring?
The Luxoria application needed a reliable image quality assessment solution. Rather than reimplementing a complex algorithm from scratch, we chose to wrap the battle-tested OpenCV implementation, gaining:
Proven accuracy: OpenCV's BRISQUE is validated against academic benchmarks
Optimized performance: Years of C++ optimization work
Reduced risk: No algorithmic implementation bugs
Faster delivery: Wrapper approach vs full reimplementation
2.3 Original Implementation (C++)
2.4 Target Implementation (C#)
3. Architecture Comparison
3.1 C++ Architecture
3.2 C# Architecture
3.3 Key Architectural Differences
4. Technical Trade-offs Analysis
4.1 Interop Strategy: P/Invoke vs Pure Managed
We evaluated two approaches for bringing BRISQUE to .NET:
Option A: Pure C# Reimplementation
Requires reimplementing complex algorithms
Performance penalty on CPU math
~2000+ lines of code to maintain
Risk of algorithmic discrepancies
Option B: P/Invoke Wrapper (Selected)
Leverages optimized OpenCV code
Platform-specific binaries
Only ~113 lines to maintain
Guaranteed algorithmic consistency
Decision: P/Invoke was the clear winner. BRISQUE involves complex mathematical operations (DCT, SVD, SVM) that OpenCV has spent years optimizing. The wrapper approach reduced development effort by approximately 90% while preserving performance.
4.2 Library Embedding vs External Dependencies
Option A: External DLL Reference
Version compatibility issues
PATH configuration required
Option B: Embedded Resources (Selected)
Decision: Developer experience trumps package size. When someone adds our package, it should work immediately without additional setup steps.
4.3 Static vs Dynamic Linking (Native Side)
The native DLLs statically link OpenCV:
Architecture
DLL Size
Contents
OpenCV core + quality (static)
OpenCV core + quality (static)
OpenCV core + quality (static)
Trade-off: Larger binaries but guaranteed compatibility. No DLL hell.
5. Design Decisions
5.1 API Surface Design
C++ Original API
Design Changes Explained
cv::Mat → string imagePath
Avoids exposing OpenCV types to managed code
compute() → ComputeScore()
.NET naming conventions (PascalCase, descriptive)
Standard .NET pattern for unmanaged resources
5.2 Native Function Signatures
Three clean P/Invoke entry points:
Design Rationale:
Cdecl convention: Standard for C++ exports
IntPtr for instances: Opaque handle hides implementation details
Explicit release: Required because GC doesn't manage native memory
5.3 Namespace Design
luxoria::filter::algorithms
Luxoria.Algorithm.BrisqueScore
The C# namespace was simplified while maintaining consistency with Luxoria's naming conventions.
6. Memory Management
6.1 C++ Memory Model
6.2 C# Memory Model
6.3 Memory Ownership
6.4 Safety Measures
Recommend using statement
IntPtr.Zero check prevents double-free
Constructor throws if creation fails
7.1 Windows-Focused Design
The library targets Windows exclusively because:
Luxoria Integration: Our main application (Luxoria) is a C# WinUI desktop app targeting Windows
Visual Studio Ecosystem: Primary development environment for the team
NuGet Distribution: Seamless package management in Visual Studio
Enterprise Target: Most enterprise deployments are Windows-based
7.2 Architecture Detection
7.3 Supported Configurations
Architecture
Platform
Status
7.4 Native Library Loading
This wrapper automatically selects and loads the correct native DLL (x86, x64, or arm64) based on the current process architecture.
In the original version, this selection was manual, meaning that callers had to choose which executable to run for each architecture, which partially negated the simplification goal because additional operations were required and not forgetting not-supported architecture.
8. Distribution Strategy
8.1 NuGet Package Structure
8.2 Installation
8.3 Usage in Luxoria
8.4 Model Files
Model files are distributed separately (not in the NuGet package):
Feature normalization ranges
Rationale: Users may want custom-trained models. Keeping models separate follows separation of concerns.
9. Error Handling
9.1 Validation Layer
The C# wrapper provides comprehensive input validation before calling native code:
9.2 Exception Types
Scenario
Exception
Message Pattern
"Model file not found: {path}"
"Range file not found: {path}"
"Image file not found: {path}"
InvalidOperationException
"Failed to create BRISQUE algorithm instance."
"Unsupported architecture"
9.3 Comparison with C++
Error Type
C++ Behavior
C# Behavior
Pre-validated in managed code
10.1 Side-by-Side Comparison
C++ Usage:
C# Usage:
10.2 API Improvements in C#
File path instead of cv::Mat handling
Conversion happens in native layer
Resource cleanup via IDisposable
No need to handle OpenCV types
11. Build System
11.1 C++ Build (CMake + vcpkg)
11.2 C# Build (MSBuild)
11.3 CI/CD Pipeline
C++ Build Matrix (GitHub Actions):
Builds for x86, x64, arm64 on Windows
Uses vcpkg for OpenCV dependency
Outputs architecture-specific DLLs
C# Build:
Packages native DLLs as embedded resources
12. Lessons Learned
12.1 What Worked Well
P/Invoke Approach
Minimal code to maintain (~113 lines)
Leveraged battle-tested OpenCV implementation
Embedded Resources
Seamless NuGet installation experience
No external dependency issues
Version consistency guaranteed
Static Linking
Single DLL per architecture
Comprehensive Validation
Pre-flight checks catch errors early
Clear, actionable error messages
Prevents crashes in native code
12.2 Challenges Overcome
vcpkg Integration
Initial path configuration required iteration
Cross-architecture builds needed separate CI jobs
Solution: Documented workflow in CI/CD
Resource Naming
Assembly resource names must match exactly
Solution: Explicit LogicalName in .csproj
Model File Distribution
Initially confusing for users
Solution: Clear documentation and examples
12.3 TODO / Technical Debt
Add ComputeScoreAsync for UI responsiveness
Process multiple images efficiently
13.1 Project Success Metrics
NuGet package for Visual Studio
Zero-configuration deployment
Native performance preserved
13.2 Final Architecture
The refactoring successfully transformed a C++ CLI tool into a polished .NET library:
13.3 Key Takeaways
Wrap, don't rewrite: When mature native libraries exist, wrapping is often the better choice
Prioritize developer experience: Zero-config installation pays dividends
Validate early: Catch errors in managed code before they reach native code
Document decisions: This journal captures the "why" for future maintainers
Appendix A: File Reference
Total Implementation: ~250 lines of production code
Appendix B: BRISQUE Algorithm Overview
Algorithm Pipeline
Preprocessing: Convert to grayscale, compute local mean/variance
MSCN Coefficients: Mean Subtracted Contrast Normalized values
Feature Extraction: 36 features from GGD fitting
Normalization: Scale using pre-computed ranges
Prediction: SVM regression outputs quality score
RBF (Radial Basis Function)
Appendix C: Glossary
Blind/Referenceless Image Spatial Quality Evaluator
Platform Invocation Services - .NET native interop
Resource Acquisition Is Initialization (C++ pattern)
Mean Subtracted Contrast Normalized
Microsoft C++ package manager
Windows UI Library for modern Windows apps
Document Version: 1.0 Last Updated: January 2026 Repository: LuxoriaSoft/brisque_impl_netlib