BRISQUE Refactoring Journal: C++ to C#
Document Information
Project
Luxoria BRISQUE Implementation
Original Language
C++ (OpenCV 4.10.0)
Target Language
C# (.NET 8.0)
Architecture
Native Interop via P/Invoke
License
Apache 2.0
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.
Project Goal
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 packagecommandNative 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
Performance
Native code via P/Invoke
CPU-intensive operations remain optimized
Distribution
Embedded native libraries
Zero external dependencies
API Surface
Simplified wrapper
Easy adoption for .NET developers
Platform
Windows-focused
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
Purpose
CLI tool
Library for Luxoria
OpenCV Access
Direct linking
Indirect via native DLL
Image Input
cv::Mat objects
File path strings
Memory Model
RAII with smart pointers
Managed + IDisposable
Distribution
Executable
NuGet package
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
No native dependencies
Requires reimplementing complex algorithms
True cross-platform
Performance penalty on CPU math
Simpler deployment
~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
Larger package size
Guaranteed algorithmic consistency
Native memory management
Proven implementation
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
Smaller package
Complex installation
Shared across apps
Version compatibility issues
PATH configuration required
Support burden
Option B: Embedded Resources (Selected)
Zero-config deployment
~14MB total package size
Version-locked
Cannot share OpenCV
Works everywhere
Temp directory usage
NuGet "just works"
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:
x86
4.2 MB
OpenCV core + quality (static)
x64
5.6 MB
OpenCV core + quality (static)
arm64
3.9 MB
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
C# Wrapper API
Design Changes Explained
cv::Mat → string imagePath
Avoids exposing OpenCV types to managed code
compute() → ComputeScore()
.NET naming conventions (PascalCase, descriptive)
Destructor → IDisposable
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
C++
luxoria::filter::algorithms
C#
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
Forgetting Dispose()
Recommend using statement
Double disposal
IntPtr.Zero check prevents double-free
Invalid instance
Constructor throws if creation fails
7. Platform Strategy
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
x86
Windows 32-bit
Supported
x64
Windows 64-bit
Supported
ARM64
Windows on ARM
Supported
7.4 Native Library Loading
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):
brisque_model_live.yml
555 KB
Pre-trained SVM model
brisque_range_live.yml
1.3 KB
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
Model not found
FileNotFoundException
"Model file not found: {path}"
Range not found
FileNotFoundException
"Range file not found: {path}"
Image not found
FileNotFoundException
"Image file not found: {path}"
Native creation failure
InvalidOperationException
"Failed to create BRISQUE algorithm instance."
Unsupported architecture
NotSupportedException
"Unsupported architecture"
9.3 Comparison with C++
Missing file
stderr + exit code
Structured exception
Invalid image
OpenCV exception
Pre-validated in managed code
Out of memory
std::bad_alloc
OutOfMemoryException
10. API Design
10.1 Side-by-Side Comparison
C++ Usage:
C# Usage:
10.2 API Improvements in C#
Simpler input
File path instead of cv::Mat handling
Automatic grayscale
Conversion happens in native layer
Using statement
Resource cleanup via IDisposable
Strong typing
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:
.NET 8.0 SDK
Packages native DLLs as embedded resources
Produces NuGet package
12. Lessons Learned
12.1 What Worked Well
P/Invoke Approach
Minimal code to maintain (~113 lines)
Leveraged battle-tested OpenCV implementation
Fast time to production
Embedded Resources
Seamless NuGet installation experience
No external dependency issues
Version consistency guaranteed
Static Linking
Single DLL per architecture
No dependency conflicts
Predictable deployment
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
Async API
Low
Add ComputeScoreAsync for UI responsiveness
Batch processing
Low
Process multiple images efficiently
13. Conclusion
13.1 Project Success Metrics
NuGet package for Visual Studio
Achieved
Zero-configuration deployment
Achieved
Integration with Luxoria
Achieved
Native performance preserved
Achieved
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
BrisqueAlgorithm.hpp
41
C++ wrapper class
main.cpp
60
C++ CLI entry point
CMakeLists.txt
37
C++ build config
BrisqueScore.cs
113
C# P/Invoke wrapper
.csproj
43
C# project config
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
Model Details
SVM Type
EPS_SVR
Kernel
RBF (Radial Basis Function)
C
1024
gamma
0.05
epsilon
0.001
Support Vectors
774
Features
36
Appendix C: Glossary
BRISQUE
Blind/Referenceless Image Spatial Quality Evaluator
P/Invoke
Platform Invocation Services - .NET native interop
RAII
Resource Acquisition Is Initialization (C++ pattern)
IQA
Image Quality Assessment
NSS
Natural Scene Statistics
MSCN
Mean Subtracted Contrast Normalized
SVM
Support Vector Machine
vcpkg
Microsoft C++ package manager
WinUI
Windows UI Library for modern Windows apps
Document Version: 1.0 Last Updated: January 2026 Repository: LuxoriaSoft/brisque_impl_netlib
Last updated