dehazer_web_logo

Dehazer C++ library

v5.0.0

Table of contents

Overview

Dehazer C++ library improves the local contrast of video frames (video defog / dehaze) to reduce the effect of haze and to increase the visibility of low-contrast objects. The library is implemented in C++ (C++17 standard). This library is suitable for various types of cameras (daylight, SWIR, MWIR, and LWIR) and provides a simple programming interface. Each instance of the Dehazer C++ class object performs frame-by-frame processing of a video data stream, processing each video frame independently. The library includes optimized SIMD kernels for x86-64 (AVX2 and SSE2), for AArch64 (NEON) and a portable C++ implementation for other architectures. The kernels are selected at run time from the features of the CPU and all of them give identical results (see Portability). The library depends on the VFilter library (provides interface as well as defines data structures for various video filters implementation), which uses Frame and ConfigReader libraries. Demo application and example additionally depend on the SimpleUI library (draws on video frames and displays them in a window) and VSourceFile library (captures and decodes video from files). Demo application also depends on the SimpleFileDialog (provides dialog to open files) and VideoRecorderMp4 library (records video to MP4 files). The libraries are not included in the repository (see Build and connect to your project).

Dehazer is general-purpose software: it is not tied to any sector or application, and the developer of a program decides what the program does with it.

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 11.12.2023 First version.
2.0.0 21.02.2024 - Library updated with VFilter interface.
- Added second algorithm type.
2.0.1 24.03.2024 - VFilter class updated.
- SimpleFileDialog class updated.
- Documentation updated.
2.0.2 21.05.2024 - Submodules updated.
- Documentation updated.
2.0.3 19.07.2024 - CMake updated.
- Files structure updated.
3.0.0 01.04.2026 - OpenCV dependency removed from core library.
- Own implementation with AVX2/NEON/scalar paths.
- Added CUSTOM_1 and CUSTOM_2 (thread count) parameters.
- Performance optimizations.
3.1.0 22.07.2026 - Added third algorithm type (TYPE=2).
- Uniform areas and smooth gradients without banding artifacts.
- Added CUSTOM_3 parameter for TYPE=2 (32/64/128, default 64).
- Added fourth algorithm type (TYPE=3).
4.0.0 31.07.2026 - Performance optimization.
- Algorithm changed for TYPE=2.
- Parameters usage simplified.
4.0.1 02.07.2026 - Added number of thread validity control.
4.0.2 10.09.2026 - Performance optimization.
4.1.0 21.09.2026 - Accelerated processing performance by >30% across multiple frame sizes.
- Clean compilation with zero warnings under /W4 (MSVC) and -Wall -Wextra -Wpedantic (GCC/Clang).
4.1.1 29.09.2026 - Update VFilter to 1.1.5 version.
- Update SimpleDialog to 1.1.0 version.
4.1.2 01.10.2026 - Removed OpenCV from demo and example applications.
4.1.3 12.10.2026 - Submodules updated.
4.1.4 18.10.2026 - Git submodules removed: the libraries that Dehazer depends on are cloned next to the repository (stand alone build) or added by the top-level project.
- SIMD kernels are built into separate files and selected at run time (AVX2, SSE2, NEON, portable): the instruction set of the whole library is not raised by compiler flags, the library runs on CPUs without AVX2. The output is bit-identical to version 4.1.3.
- Build hardening and strict warning flags for all targets.
- Frame and mask data size is checked against the width, height and pixel format; NaN, infinity and out of range parameter values are rejected; initVFilter changes nothing if it fails; the working memory is zeroed, an allocation failure does not throw an exception.
- Fixed undefined behavior: left shift of negative numbers, float to int conversion of invalid values, unaligned memory access.
- Example, demo application and benchmark check the results of all calls and do not wait endlessly.
- Documentation updated.
5.0.0 27.10.2026 - Processing changed: the output differs from version 4.1.4 for LEVEL from 1.5625 to below 100; for LEVEL 100 and below 1.5625 it is identical. The interface, the processing time and the memory size are unchanged.
- Uniform areas that are not part of the image (for example black borders) are changed differently at high LEVEL values: they can be excluded with the mask (see setMask).
- Documentation updated.

Library files

The library is supplied as source code only. The user is provided with a set of files in the form of a CMake project (repository). The repository structure is shown below:

CMakeLists.txt ------------ Main CMake file of the library.
src ----------------------- Folder with library source code.
    CMakeLists.txt -------- CMake file of the library.
    Dehazer.h ------------- Main library header file.
    DehazerVersion.h ------ Header file with library version.
    DehazerVersion.h.in --- Service CMake file to generate version header.
    Dehazer.cpp ----------- C++ implementation file.
    impl ------------------ Folder with the SIMD levels of the algorithm.
        DehazerKernels.h -- Declarations shared by the levels.
        DehazerBody.h ----- Code shared by all levels.
        DehazerDispatch.cpp  Run-time selection of the level.
        DehazerPortable.cpp  Portable level (plain C++).
        DehazerSse2.cpp --- SSE2 level (x86-64).
        DehazerAvx2.cpp --- AVX2 level (x86-64).
        DehazerNeon.cpp --- NEON level (AArch64).
demo ---------------------- Folder for demo application files.
    CMakeLists.txt -------- CMake file for demo application.
    main.cpp -------------- Source C++ file of demo application.
example ------------------- Folder for simple example.
    CMakeLists.txt -------- CMake file of example.
    main.cpp -------------- Source C++ file of example.
benchmark ----------------- Folder with test application (benchmark).
    CMakeLists.txt -------- CMake file of test application (benchmark).
    main.cpp -------------- Source C++ file of test application.
static -------------------- Images used by this document.

Key features and capabilities

Table 2 - Key features and capabilities.

Parameter and feature Description
Programming language C++ (standard C++17). The core library depends only on the VFilter library (and on the Frame and ConfigReader libraries used by VFilter). OpenMP is optional: the library is linked with it if CMake finds it (see Portability).
Supported OS Compatible with any operating system that supports the C++ compiler (C++17 standard).
Processing Local contrast enhancement of the intensity (Y) channel; the chroma data is not changed. LEVEL 0 returns the frame unchanged, LEVEL 100 gives the strongest effect.
Memory All working memory is allocated as a single block of 9 306 624 bytes (~8.9 MiB) by the class constructor and freed by the destructor. The size does not depend on the frame resolution and there are no per-frame allocations. Additionally each worker thread uses up to ~10 KB of stack during processFrame().
Supported pixel formats GRAY, YUV24, YUYV, UYVY, NV12, NV21, YV12, YU12. The library uses pixels intensity for video processing. If the pixel format of the image doesn’t include intensity channel it should be converted to proper format before applying dehazing.
Maximum and minimum video frame size The intended size range is from 16x16 to 8192x8192 pixels. Frames wider or taller than 8192 pixels are rejected (processFrame returns FALSE); frames smaller than 16x16 are accepted, but the result is not meaningful. The size field of the frame must cover the intensity data (see processFrame). The size of the video frames to be processed has a significant impact on the computation speed.
Calculation speed The processing time per video frame depends on the computing platform used. The processing time per video frame can be estimated with the benchmark application.

Supported pixel formats

Frame library, which Dehazer library depends on, contains Fourcc enum, which defines supported pixel formats (Frame.h file). Dehazer library supports intensity channel included pixel formats only (GRAY, YUV24, YUYV, UYVY, NV12, NV21, YV12, YU12). The library utilizes the intensity channel for video processing. Fourcc enum declaration:

enum class Fourcc
{
    /// RGB 24bit pixel format.
    RGB24 = MAKE_FOURCC_CODE('R', 'G', 'B', '3'),
    /// BGR 24bit pixel format.
    BGR24 = MAKE_FOURCC_CODE('B', 'G', 'R', '3'),
    /// YUYV 16bits per pixel format.
    YUYV  = MAKE_FOURCC_CODE('Y', 'U', 'Y', 'V'),
    /// UYVY 16bits per pixel format.
    UYVY  = MAKE_FOURCC_CODE('U', 'Y', 'V', 'Y'),
    /// Grayscale 8bit.
    GRAY  = MAKE_FOURCC_CODE('G', 'R', 'A', 'Y'),
    /// YUV 24bit per pixel format.
    YUV24  = MAKE_FOURCC_CODE('Y', 'U', 'V', '3'),
    /// NV12 pixel format.
    NV12  = MAKE_FOURCC_CODE('N', 'V', '1', '2'),
    /// NV21 pixel format.
    NV21  = MAKE_FOURCC_CODE('N', 'V', '2', '1'),
    /// YU12 (YUV420) - Planar pixel format.
    YU12 = MAKE_FOURCC_CODE('Y', 'U', '1', '2'),
    /// YV12 (YVU420) - Planar pixel format.
    YV12 = MAKE_FOURCC_CODE('Y', 'V', '1', '2'),
    /// JPEG compressed format.
    JPEG  = MAKE_FOURCC_CODE('J', 'P', 'E', 'G'),
    /// H264 compressed format.
    H264  = MAKE_FOURCC_CODE('H', '2', '6', '4'),
    /// HEVC compressed format.
    HEVC  = MAKE_FOURCC_CODE('H', 'E', 'V', 'C')
};

Table 3 - Bytes layout of supported RAW pixel formats. Example of 4x4 pixels image.

yuvYUV24 grayGRAY
yuyvYUYV uyvyUYVY
nv12NV12 nv21NV21
yu12YU12 yv12YV12

Library principles

The library is available as a source code only. To utilize the library as source code, developers have to incorporate the library files into their project. The usage sequence for the library is following:

  1. Include the library and the libraries it depends on in the project (see Build and connect to your project).
  2. Create an instance of the Dehazer C++ class. If you need multiple parallel cameras processing you have to create multiple Dehazer C++ class instances. The working memory (9 306 624 bytes ≈ 8.9 MiB per instance, independent of the frame size) is allocated once by the constructor; there are no per-frame allocations.
  3. If necessary, modify the default library parameters (MODE, LEVEL, CUSTOM_2 = number of threads) using the setParam(…) method.
  4. Create Frame class object for input frame.
  5. Call the processFrame(…) method for frame processing.
  6. The algorithm applies changes on the input original frame (taking into account mask if necessary). The method returns FALSE and does not change the frame if the frame is not supported.

Dehazer class description

Dehazer class declaration

Dehazer.h file contains Dehazer class declaration.

namespace rapidpixel
{
class Dehazer : public VFilter
{
public:

    /// Class constructor. Allocates the working buffers.
    Dehazer() noexcept;

    /// Class destructor. Frees the working buffers.
    ~Dehazer() override;

    /// Copy and move operations are deleted.
    Dehazer(const Dehazer&) = delete;
    Dehazer(Dehazer&&) = delete;
    Dehazer& operator= (const Dehazer&) = delete;
    Dehazer& operator= (Dehazer&&) = delete;

    /// Get string of current library version.
    static std::string getVersion();

    /// Initialize video filter.
    bool initVFilter(VFilterParams& params) override;

    /// Set parameter.
    bool setParam(VFilterParam id, float value) override;

    /// Get parameter value.
    float getParam(VFilterParam id) override;

    /// Get all library parameters.
    void getParams(VFilterParams& params) override;

    /// Execute action command.
    bool executeCommand(VFilterCommand id) override;

    /// Process frame.
    bool processFrame(Frame& frame) override;

    /// Set mask for the filter.
    bool setMask(rapidpixel::Frame mask) override;

    /// Decode and execute command.
    bool decodeAndExecuteCommand(uint8_t* data, int size) override;
};
}

The constructor does not throw an exception: if the memory for the working buffers is not available the object is created in a failed state: setMask(…) and processFrame(…) return FALSE (processFrame(…) still returns TRUE while the mode is 0).

getVersion method

The getVersion() method returns string of current version of Dehazer class. Method declaration:

static std::string getVersion();

Method can be used without Dehazer class instance. Example:

std::cout << "Dehazer version: " << rapidpixel::Dehazer::getVersion() << std::endl;

Console output:

Dehazer version: 5.0.0

initVFilter method

The initVFilter(…) method initializes the filter with the parameters of the VFilterParams structure: mode, level and custom2 (number of threads). All values are checked first and applied only if all of them are valid: if the method returns FALSE the filter is not changed. Method declaration:

bool initVFilter(VFilterParams& params) override;
Parameter Value
params VFilterParams class object. Used fields: mode (0 - off, any other value - on; the structure default is 1, a new Dehazer object starts disabled), level (from 0 to 100, structure default 0.0) and custom2 (number of threads, integer from 1 to 32; a value below 1, including the structure default 0, leaves the current thread count unchanged). The other fields are ignored.

Returns: TRUE if the dehazer initialized or FALSE if level or custom2 is not a number, level is out of the range from 0 to 100 or custom2 is not below 1 and out of the range from 1 to 32.

setParam method

The setParam(…) method sets Dehazer parameter value. setParam(…) is a thread-safe method. This means that the setParam(…) method can be safely called from any thread. Method declaration:

bool setParam(VFilterParam id, float value) override;
Parameter Description
id Parameter ID according to VFilterParam enum. The library supports parameters: MODE, LEVEL and CUSTOM_2. For other parameters the method will return FALSE.
value Parameter value. Value depends on parameter ID (see Table 5). NaN and infinity are rejected for all parameters.

Returns: TRUE if the parameter was set or FALSE if not (if the parameter is not supported or read only, or the value is out of range or is not a finite number). The parameter is not changed if FALSE is returned.

getParam method

The getParam(…) method returns Dehazer parameter value. getParam(…) is a thread-safe method. This means that the getParam(…) method can be safely called from any thread. Method declaration:

float getParam(VFilterParam id) override;
Parameter Description
id Parameter ID according to VFilterParam enum. The library supports parameters: MODE, LEVEL, PROCESSING_TIME_MCSEC and CUSTOM_2. For other parameters the method will return -1.

Returns: parameter value or -1, if the parameter ID is not supported.

getParams method

The getParams(…) method is designed to obtain params structure and it is thread-safe. This means that the getParams(…) method can be safely called from any thread. Method declaration:

void getParams(VFilterParams& params) override;
Parameter Description
params Reference to VFilterParams object to store params. The fields type, custom1 and custom3 are set to 0, -1 and -1 (not supported by Dehazer).

executeCommand method

The executeCommand(…) method executes library action command and it is thread-safe. This means that the executeCommand(…) method can be safely called from any thread. Method declaration:

bool executeCommand(VFilterCommand id) override;
Parameter Description
id Command ID according to VFilterCommand enum. The library supports commands ON and OFF.

Returns: TRUE if the command executed or FALSE if not (the command is not supported or unknown).

processFrame method

The processFrame(..) method processes the given frame. The method can be called from any thread, but calls of processFrame(…) and setMask(…) are serialized: a call waits until the previous call is finished. If the mode is set to 0 (disabled), the method will skip the frame and return true without inspecting it. If LEVEL is 0 the frame is also returned untouched and the method returns true, but the pixel format, the frame size and the frame data are validated first. If the method returns FALSE the frame is not changed.

bool processFrame(rapidpixel::Frame& frame) override;
Parameter Description
frame Video Frame for processing. Implemented algorithm takes frame as a source and process original video frame. Dehazer processes only GRAY, YUV24, YUYV, UYVY, NV12, NV21, YV12, YU12 formats. The library utilizes pixels intensity for video processing: only the intensity data is read and changed, the chroma data stays untouched. If the pixel format of the image doesn’t include intensity channel it should be converted to proper format before dehazing. The data pointer must not be nullptr, the width and height must be from 1 to 8192 and the size field of the frame must be at least the size of the intensity data: width x height for GRAY and the planar formats (NV12, NV21, YV12, YU12), 2 x width x height for YUYV and UYVY, 3 x width x height for YUV24. The library can not check that the data pointer is valid for the size of the frame: this is the contract of the Frame class. The intended frame size range is from 16x16 to 8192x8192 pixels: frames smaller than 16x16 are accepted but produce a meaningless result.

Returns: TRUE if frame was successfully processed or skipped, FALSE if the pixel format, the size or the data of the frame is not supported (the frame is not changed).

setMask method

The setMask(…) method designed to set filtration mask. The user can disable dehazing in any area of the video frame. For this purpose the user can create an image of any size with GRAY, NV12, NV21, YV12 or YU12 pixel format (only the Y plane is read). Mask image pixel values equal to 0 prohibit video filtration in the corresponding area of video frames. Any mask pixel value different than 0 allows to apply filter in this area. Areas that are not part of the image, for example black borders around it, can be excluded with the mask: the library leaves them unchanged. The method can be called either before video frame processing or during video frame processing (calls of setMask(…) and processFrame(…) are serialized). Method declaration:

bool setMask(rapidpixel::Frame mask) override;
Parameter Description
mask Filtration mask in form of Frame object with GRAY, NV12, NV21, YV12 or YU12 pixel format (only the Y plane is read; other formats are rejected). The data pointer must not be nullptr, the width and height must be positive and the size field of the frame must be at least width x height. Dehazer omits image segments, where filtration mask pixel values equal 0. Mask can have any resolution: the library stores it internally at a fixed 512x512 resolution (nearest-neighbor resampled) and maps it to the processed video resolution.

Returns: TRUE if the mask is accepted or FALSE if not (not valid pixel format, no data or the data is smaller than the width x height). The mask is not changed if FALSE is returned.

encodeSetParamCommand method of VFilter class

The encodeSetParamCommand(…) static method encodes command to change any VFilter parameter value. To control any video filter remotely, the developer has to design his own protocol and according to it encode the command and deliver it over the communication channel. To simplify this, the VFilter class contains static methods for encoding the control command. The VFilter class provides two types of commands: a parameter change command (SET_PARAM) and an action command (COMMAND). encodeSetParamCommand(…) designed to encode SET_PARAM command. Method declaration:

static void encodeSetParamCommand(uint8_t* data, int& size, VFilterParam id, float value);
Parameter Description
data Pointer to data buffer for encoded command. Must not be nullptr and must be valid for 11 bytes.
size Size of encoded data. Will be 11 bytes (0 if data is nullptr: nothing is written).
id Parameter ID according to VFilterParam enum. The ID is not checked by the method: decodeCommand(…) rejects an ID which is not in the enum.
value Parameter value. The value is not checked by the method.

encodeSetParamCommand(…) is static and used without VFilter class instance. This method used on client side (control system). Command encoding example:

// Buffer for encoded data.
uint8_t data[11];
// Size of encoded data.
int size = 0;
// Parameter value.
float outValue = 10.0f;
// Encode command.
rapidpixel::VFilter::encodeSetParamCommand(data, size, rapidpixel::VFilterParam::LEVEL, outValue);

encodeCommand method of VFilter class

The encodeCommand(…) static method encodes command for VFilter remote control. To control any video filter remotely, the developer has to design his own protocol and according to it encode the command and deliver it over the communication channel. To simplify this, the VFilter class contains static methods for encoding the control command. The VFilter class provides two types of commands: a parameter change command (SET_PARAM) and an action command (COMMAND). encodeCommand(…) designed to encode COMMAND command (action command). Method declaration:

static void encodeCommand(uint8_t* data, int& size, VFilterCommand id);
Parameter Description
data Pointer to data buffer for encoded command. Must not be nullptr and must be valid for 7 bytes.
size Size of encoded data. Will be 7 bytes (0 if data is nullptr: nothing is written).
id Command ID according to VFilterCommand enum. The ID is not checked by the method: decodeCommand(…) rejects an ID which is not in the enum.

encodeCommand(…) is static and used without VFilter class instance. This method used on client side (control system). Command encoding example:

// Buffer for encoded data.
uint8_t data[7];
// Size of encoded data.
int size = 0;
// Encode command.
rapidpixel::VFilter::encodeCommand(data, size, rapidpixel::VFilterCommand::ON);

decodeCommand method of VFilter class

The decodeCommand(…) static method decodes command on image filter side. Method declaration:

static int decodeCommand(uint8_t* data, int size, VFilterParam& paramId, VFilterCommand& commandId, float& value);
Parameter Description
data Pointer to input command. Must not be nullptr and must be valid for size bytes.
size Size of command. Must be exactly 11 bytes for SET_PARAM and exactly 7 bytes for COMMAND.
paramId VFilter parameter ID according to VFilterParam enum. After decoding SET_PARAM command the method will return parameter ID.
commandId VFilter command ID according to VFilterCommand enum. After decoding COMMAND the method will return command ID.
value VFilter parameter value (after decoding SET_PARAM command). Set to 0 after decoding COMMAND.

Returns: 0 - in case decoding COMMAND, 1 - in case decoding SET_PARAM command or -1 in case errors (null pointer, wrong size, version, command type or ID). The output arguments are changed only if the command is decoded.

decodeAndExecuteCommand method

decodeAndExecuteCommand(…) method decodes and executes command encoded on video filter side. The decodeAndExecuteCommand(…) method can be safely called from any thread. Method declaration:

bool decodeAndExecuteCommand(uint8_t* data, int size) override;
Parameter Description
data Pointer to input command. Must not be nullptr.
size Size of command. Should be 11 bytes for SET_PARAM or 7 bytes for COMMAND.

Returns: TRUE if command decoded (SET_PARAM or COMMAND) and executed (action command or set param command) or FALSE if not (invalid command, unsupported or unknown command or parameter, invalid parameter value).

Data structures

VFilterCommand enum

Enum declaration:

enum class VFilterCommand
{
    /// Reset video filter algorithm.
    RESET = 1,
    /// Enable filter.
    ON,
    /// Disable filter.
    OFF
};

Table 4 - Action commands description.

Command Description
RESET Not supported by Dehazer class.
ON Enable video filter.
OFF Disable video filter.

VFilterParam enum

Enum declaration:

enum class VFilterParam
{
    /// Filter mode: 0 - off, 1 - on. Depends on implementation.
    MODE = 1,
    /// Enhancement level for particular filter, as a percentage from
    /// 0% to 100%. May have another meaning depends on implementation.
    LEVEL,
    /// Processing time in microseconds. Read only parameter.
    PROCESSING_TIME_MCSEC,
    /// Type of the filter. Depends on the implementation.
    TYPE,
    /// VFilter custom parameter. Custom parameters used when particular image
    /// filter has specific unusual parameter.
    CUSTOM_1,
    /// VFilter custom parameter. Custom parameters used when particular image
    /// filter has specific unusual parameter.
    CUSTOM_2,
    /// VFilter custom parameter. Custom parameters used when particular image
    /// filter has specific unusual parameter.
    CUSTOM_3
};

Table 5 - Params description.

Parameter Access Description
MODE read / write Current filter mode: 0 - off, 1 - on (any non-zero number enables the filter, NaN and infinity are rejected). When the mode is set to 0, processFrame just forwards frame without processing and returns true. Default value: 0.
LEVEL read / write Enhancement level as a percentage in range from 0% to 100%: 0 - no enhancement at all (the frame is returned untouched, see processFrame), values below about 10 have almost no effect, higher values give a stronger local contrast enhancement and also amplify the noise more, 100 - strongest enhancement. Values out of range, NaN and infinity are rejected (setParam returns FALSE). Default value: 0.
PROCESSING_TIME_MCSEC read only Processing time in microseconds. Read only parameter. Used to control performance of video filter.
TYPE not supported Not supported: the library implements a single algorithm. setParam returns FALSE, getParam returns -1.
CUSTOM_1 not supported Custom parameter. Not supported by Dehazer. setParam returns FALSE, getParam returns -1.
CUSTOM_2 read / write Number of threads for parallel processing. Valid range: integer from 1 to 32 inclusive. Default value: 1 (sequential processing). The fractional part of the value is discarded. Any value outside this range, NaN and infinity will be rejected (setParam returns FALSE). Multi-threading requires the library to be compiled with OpenMP support (_OPENMP defined); without OpenMP the parameter is accepted but processing remains single-threaded. At run time the value is additionally clamped to the number of available hardware threads. Recommendation for Linux (libgomp): when the thread count approaches the machine’s hardware thread count, run the process with OMP_WAIT_POLICY=passive — the default (active) policy makes idle threads spin at barriers and can collapse multi-threaded performance when every hardware thread is occupied; on hybrid P/E-core CPUs the best throughput is usually reached with the thread count close to the number of physical performance cores.
CUSTOM_3 not supported Custom parameter. Not supported by Dehazer. setParam returns FALSE, getParam returns -1.

VFilterParams class description

VFilterParams class declaration

VFilterParams class is used to provide video filter parameters structure. Also VFilterParams provides possibility to write/read params from JSON files (JSON_READABLE macro) and provides methods to encode and decode params. VFilterParams interface class declared in VFilter.h file. Class declaration:

class VFilterParams
{
public:
    /// Filter mode: 0 - off, 1 - on. Depends on implementation.
    int mode{1};
    /// Enhancement level for particular filter, as a percentage from
    /// 0% to 100%. May have another meaning depends on implementation.
    float level{0.0f};
    /// Processing time in microseconds. Read only parameter.
    int processingTimeMcSec{0};
    /// Type of the filter. Depends on the implementation.
    int type{0};
    /// VFilter custom parameter. Custom parameters used when particular image
    /// filter has specific unusual parameter.
    float custom1{0.0f};
    /// VFilter custom parameter. Custom parameters used when particular image
    /// filter has specific unusual parameter.
    float custom2{0.0f};
    /// VFilter custom parameter. Custom parameters used when particular image
    /// filter has specific unusual parameter.
    float custom3{0.0f};

    /// Macro from ConfigReader to make params readable / writable from JSON.
    JSON_READABLE(VFilterParams, mode, level, type, custom1, custom2, custom3)

    /// Encode (serialize) params.
    bool encode(uint8_t* data, int bufferSize, int& size,
                VFilterParamsMask* mask = nullptr);

    /// Decode (deserialize) params.
    bool decode(uint8_t* data, int dataSize);
};

The values of the fields are only transported by the VFilterParams class (JSON, serialization): they are not checked against any range. The video filter implementation validates the values (see initVFilter).

Table 6 - VFilterParams class fields description is related to VFilterParam enum description.

Field type Description
mode int Current filter mode, 0 - off, 1 - on. When the mode is set to 0, processFrame just forwards frame without processing and returns true.
level float Enhancement level as a percentage in range from 0% to 100%. 0 - the frame is returned untouched, 100 - strongest enhancement.
processingTimeMcSec int Processing time in microseconds. Read only parameter. Used to control performance of video filter.
type int Not used: the library implements a single algorithm. getParams() always returns 0.
custom1 float Not supported by Dehazer. getParams() always returns -1.
custom2 float Number of threads for parallel processing. Valid range: integer from 1 to 32 inclusive. Default value of the library: 1 (sequential processing); the structure default 0 leaves the current thread count unchanged in initVFilter. Multi-threading requires the library to be compiled with OpenMP support (_OPENMP defined).
custom3 float Not supported by Dehazer. getParams() always returns -1.

Note: VFilterParams class fields listed in Table 6 have to reflect params set/get by methods setParam(…) and getParam(…).

Serialize VFilter params

VFilterParams class provides method encode(…) to serialize VFilter params. Serialization of VFilterParams is necessary in case when video filter parameters have to be sent via communication channels. Method provides options to exclude particular parameters from serialization. To do this method inserts binary mask (1 byte) where each bit represents particular parameter and decode(…) method recognizes it. Method declaration:

bool encode(uint8_t* data, int bufferSize, int& size, VFilterParamsMask* mask = nullptr);
Parameter Value
data Pointer to data buffer. Must not be nullptr. The buffer must be valid for bufferSize bytes.
bufferSize Data buffer size. Buffer size must be >= 32 bytes.
size Size of encoded data: 4 bytes of header plus 4 bytes for each encoded field (not more than bufferSize). Set to 0 if the method fails.
mask Parameters mask - pointer to VFilterParamsMask structure. VFilterParamsMask (declared in VFilter.h file) determines flags for each field (parameter) declared in VFilterParams class. If user wants to exclude any parameters from serialization, he can put a pointer to the mask. If the user wants to exclude a particular parameter from serialization, he should set the corresponding flag in the VFilterParamsMask structure.

Returns: TRUE if params encoded (serialized) or FALSE if not (data is nullptr or buffer size < 32). Nothing is written to the buffer if FALSE is returned. Integer and float fields are stored as raw 4 bytes values in the byte order of the host.

VFilterParamsMask structure declaration:

struct VFilterParamsMask
{
    /// Serialize mode field.
    bool mode{true};
    /// Serialize level field.
    bool level{true};
    /// Serialize processingTimeMcSec field.
    bool processingTimeMcSec{true};
    /// Serialize type field.
    bool type{true};
    /// Serialize custom1 field.
    bool custom1{true};
    /// Serialize custom2 field.
    bool custom2{true};
    /// Serialize custom3 field.
    bool custom3{true};
};

Example without parameters mask:

// Prepare parameters.
rapidpixel::VFilterParams params;
params.level = 80.0f;

// Encode (serialize) params.
int bufferSize = 128;
uint8_t buffer[128];
int size = 0;
if (!params.encode(buffer, bufferSize, size))
{
    std::cout << "Can't encode params" << std::endl;
}

Example with parameters mask:

// Prepare parameters.
rapidpixel::VFilterParams params;
params.level = 80.0f;

// Prepare mask.
rapidpixel::VFilterParamsMask mask;
// Exclude level.
mask.level = false;

// Encode (serialize) params.
int bufferSize = 128;
uint8_t buffer[128];
int size = 0;
if (!params.encode(buffer, bufferSize, size, &mask))
{
    std::cout << "Can't encode params" << std::endl;
}

Deserialize VFilter params

VFilterParams class provides method decode(…) to deserialize params. Deserialization of VFilterParams is necessary in case when it is needed to receive params via communication channels. Method automatically recognizes which parameters were serialized by encode(…) method. Method declaration:

bool decode(uint8_t* data, int dataSize);
Parameter Value
data Pointer to data buffer with serialized params. Must not be nullptr. The buffer must be valid for dataSize bytes.
dataSize Size of serialized params, bytes.

Returns: TRUE if params decoded (deserialized) or FALSE if not (data is nullptr, wrong data type, version or size, unknown flag in the mask). If FALSE is returned the object is not changed. Fields that are not selected by the mask in the data are set to 0.

Example:

// Prepare parameters.
rapidpixel::VFilterParams params1;
params1.level = 80;

// Encode (serialize) params.
int bufferSize = 128;
uint8_t buffer[128];
int size = 0;
if (!params1.encode(buffer, bufferSize, size))
{
    std::cout << "Can't encode params" << std::endl;
}

// Decode (deserialize) params.
rapidpixel::VFilterParams params2;
if (!params2.decode(buffer, size))
{
    std::cout << "Can't decode params" << std::endl;
}

Read params from JSON file and write to JSON file

VFilter depends on the ConfigReader library which provides method to read params from JSON file and to write params to JSON file. Example of writing and reading params to JSON file:

// Prepare params.
rapidpixel::VFilterParams params1;
params1.mode = 1;
params1.level = 10.1f;
params1.processingTimeMcSec = 10;
params1.type = 2;
params1.custom1 = 22.3f;
params1.custom2 = 23.4f;
params1.custom3 = 24.5f;

// Save to JSON.
rapidpixel::ConfigReader configReader1;
if (!configReader1.set(params1, "Params"))
{
    std::cout << "Can't set params" << std::endl;
}
if (!configReader1.writeToFile("VFilterParams.json"))
{
    std::cout << "Can't write to file" << std::endl;
}

// Read params from file.
rapidpixel::ConfigReader configReader2;
if (!configReader2.readFromFile("VFilterParams.json"))
{
    std::cout << "Can't read from file" << std::endl;
}
rapidpixel::VFilterParams params2;
if (!configReader2.get(params2, "Params"))
{
    std::cout << "Can't get params" << std::endl;
}

VFilterParams.json will look like:

{
    "Params": {
        "mode": 1,
        "level": 10.100000381469727,
        "type": 2,
        "custom1": 22.299999237060547,
        "custom2": 23.399999618530273,
        "custom3": 24.5
    }
}

Build and connect to your project

Build stand alone

The repository does not contain sub-repositories. The libraries Dehazer depends on (Frame, ConfigReader and VFilter) must be cloned into the same folder as Dehazer. The example and the demo application additionally need the libraries SimpleUI, VSource, VCodec, VDecoderH264, VDecoderH265, VDecoderJPEG, FormatConverter and VSourceFile, and the demo application needs SimpleFileDialog, VEncoderJPEG and VideoRecorderMp4 too. The folder structure must be:

<work folder>
    Dehazer
    Frame
    ConfigReader
    VFilter
    SimpleUI
    SimpleFileDialog
    VSource
    VCodec
    VDecoderH264
    VDecoderH265
    VDecoderJPEG
    FormatConverter
    VSourceFile
    VEncoderJPEG
    VideoRecorderMp4

Typical commands to build the library, the example, the demo application and the benchmark (use the URLs of the repositories provided to you):

git clone <Dehazer repository URL>
git clone <Frame repository URL>
git clone <ConfigReader repository URL>
git clone <VFilter repository URL>
git clone <SimpleUI repository URL>
git clone <SimpleFileDialog repository URL>
git clone <VSource repository URL>
git clone <VCodec repository URL>
git clone <VDecoderH264 repository URL>
git clone <VDecoderH265 repository URL>
git clone <VDecoderJPEG repository URL>
git clone <FormatConverter repository URL>
git clone <VSourceFile repository URL>
git clone <VEncoderJPEG repository URL>
git clone <VideoRecorderMp4 repository URL>
cd Dehazer
mkdir build
cd build
cmake ..
make

On Windows use cmake --build . --config Release instead of make. If Dehazer is the top-level CMake project, the main CMakeLists.txt file adds the folders ../Frame, ../ConfigReader and ../VFilter (in this order) before the library itself, and the folders of the libraries of the example and the demo application (in the order of their mutual dependencies) before the example and the demo application. If a folder does not exist CMake stops with an error. To build the library only, switch the example, the demo application and the benchmark off (see CMake options):

cmake -D_SUBMODULE_CACHE_OVERWRITE=OFF -D_DEHAZER_DEMO=OFF -D_DEHAZER_EXAMPLE=OFF -D_DEHAZER_BENCHMARK=OFF ..

On Linux the window of the demo application and example uses X11: install the libx11-dev and libxext-dev packages before configuring CMake, otherwise SimpleUI library builds a headless backend without window.

CMake options

Table 7 - CMake options of the repository. In the options below ${PARENT} is the prefix defined by the top-level project (empty if Dehazer is the top-level project).

Option Default Description
${PARENT}_DEHAZER ON Build the library.
${PARENT}_DEHAZER_DEMO ON (stand alone), OFF (included in other project) Build the demo application.
${PARENT}_DEHAZER_EXAMPLE ON (stand alone), OFF (included in other project) Build the example.
${PARENT}_DEHAZER_BENCHMARK ON (stand alone), OFF (included in other project) Build the benchmark.
${PARENT}_SUBMODULE_CACHE_OVERWRITE ON Forced rewriting of the options above in the CMake cache. The top-level project sets it to OFF when it includes Dehazer as a sub-repository.
CMAKE_BUILD_TYPE Release (if not specified) Build type.
DEHAZER_ENABLE_SIMD ON Build the SIMD kernels (AVX2, SSE2, NEON). If OFF, the library is built with the portable kernels only (macro DEHAZER_NO_SIMD, see Portability).

Connect to your project

If you want to connect Dehazer library to your CMake project as source code, you can do the following. For example, if your repository has structure:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp

Create folder 3rdparty in your repository folder and copy the repositories of Frame, ConfigReader, VFilter and Dehazer there (Dehazer does not add the libraries it depends on when it is included in another project: they must be added by your project). The new structure of your repository will be as follows:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    Frame
    ConfigReader
    VFilter
    Dehazer

Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should be containing:

cmake_minimum_required(VERSION 3.13)

################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)

################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# To inherit the top-level architecture when the project is used as a submodule.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
# Disable self-overwriting of parameters inside included subdirectories.
SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE)

################################################################################
## CONFIGURATION
## 3rd-party submodules configuration
################################################################################
SET(${PARENT}_SUBMODULE_FRAME                           ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_FRAME)
    SET(${PARENT}_FRAME                                 ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_FRAME_TEST                            OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_CONFIG_READER                   ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_CONFIG_READER)
    SET(${PARENT}_CONFIG_READER                         ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_CONFIG_READER_EXAMPLES                OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_CONFIG_READER_TESTS                   OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_VFILTER                         ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_VFILTER)
    SET(${PARENT}_VFILTER                               ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_VFILTER_TEST                          OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_VFILTER_EXAMPLE                       OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_DEHAZER                         ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_DEHAZER)
    SET(${PARENT}_DEHAZER                               ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_DEHAZER_DEMO                          OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_DEHAZER_EXAMPLE                       OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_DEHAZER_BENCHMARK                     OFF CACHE BOOL "" FORCE)
endif()

################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_FRAME)
    add_subdirectory(Frame)
endif()

if (${PARENT}_SUBMODULE_CONFIG_READER)
    add_subdirectory(ConfigReader)
endif()

if (${PARENT}_SUBMODULE_VFILTER)
    add_subdirectory(VFilter)
endif()

if (${PARENT}_SUBMODULE_DEHAZER)
    add_subdirectory(Dehazer)
endif()

File 3rdparty/CMakeLists.txt adds the folders Frame, ConfigReader, VFilter and Dehazer to your project (in the order of their mutual dependencies) and excludes the demo application, the example and the benchmark from compiling (by default they are excluded from compiling if Dehazer is included as sub-repository). The new structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    Frame
    ConfigReader
    VFilter
    Dehazer

Next, you need to include the ‘3rdparty’ folder in the main CMakeLists.txt file of your repository. Add the following string at the end of your main CMakeLists.txt:

add_subdirectory(3rdparty)

Next, you have to include Dehazer library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} Dehazer)

If your project does not add the libraries that Dehazer depends on, the compiler stops with an error such as Frame.h: No such file or directory.

Done!

Portability

The library is written in standard C++17 and does not use compiler extensions. It processes 8-bit pixel data and does not depend on the byte order of the host. The library does not use the instruction set of the build machine: compiler options such as -march=native are not used.

The algorithm has four implementations (levels) with bit-identical results. The level is selected once per process from the features of the CPU, without any environment variable or user setting:

Level CPU Notes
AVX2 x86-64 CPU with AVX2 and an OS that saves the YMM registers Built in its own file with the AVX2 compiler option (-mavx2 or /arch:AVX2): this option is not applied to the other files of the library.
SSE2 x86-64 CPU (SSE2 is part of the x86-64 baseline) Used if the CPU has no AVX2.
NEON AArch64 CPU (NEON is part of the AArch64 baseline) GCC and Clang.
Portable any CPU Plain C++, used on other architectures and if the SIMD kernels are switched off.

The SIMD kernels are switched by the CMake option DEHAZER_ENABLE_SIMD (see CMake options) and by the macros that CMake defines for the library target (a project that compiles the source files without the CMake file of the library can define them too):

Macro Description
DEHAZER_NO_SIMD Build the portable level only. CMake defines it if DEHAZER_ENABLE_SIMD is OFF or the compiler can not build the CPU feature detection.
DEHAZER_NO_AVX2 Do not build the AVX2 level (the SSE2 level is used on x86-64). CMake defines it if the compiler does not support the AVX2 option, and for universal macOS binaries.

The file impl/DehazerAvx2.cpp must be compiled with the AVX2 compiler option if the macro DEHAZER_NO_AVX2 is not defined, the CMake file of the library does it.

Multi-threaded processing uses OpenMP if the compiler supports it (see CUSTOM_2 in Table 5); the result does not depend on the number of threads. OpenMP is optional (find_package(OpenMP)): if CMake finds it the library links OpenMP::OpenMP_CXX, so a program that links the library also needs the OpenMP runtime of the compiler (for example libgomp and pthread with GCC); CMake adds it to targets that link Dehazer.

Built and run with identical results of all levels: GCC 12 and 13 and Clang 15 and 18 on x86-64 Linux (AVX2, SSE2 and portable levels; the SSE2 level was also run on an emulated CPU without AVX2), GCC 13 on AArch64 (NEON and portable levels, emulated). Version 4.1.4 was also checked with MSVC 2022 (x64 and x86).

Example

An example shows how to use the Dehazer library. The application captures video frames from the “test.mp4” file in YUV24 format with VSourceFile library, performs dehazing and displays the results in a window with SimpleUI library. The video file is played in a loop. To exit, press ESC or close the window. The example stops with an error message if the video source does not open, if no frame arrives 10 times in a row or if a frame is not processed.

#include <exception>
#include <iostream>
#include <string>
#include "Dehazer.h"
#include "SimpleUi.h"
#include "VSourceFile.h"



/// Video file that the example plays in a loop.
constexpr const char* VIDEO_FILE{"test.mp4"};

/// Timeout of getting a video frame, milliseconds.
constexpr int FRAME_TIMEOUT_MS{1000};

/// Number of failed attempts in a row to get a video frame after which the
/// example stops: it does not wait endlessly for a source that has no frames.
constexpr int MAX_FRAME_FAILURES{10};

/// Key code of ESC.
constexpr int KEY_ESC{27};



int main()
{
    try
    {
        // Open video source.
        std::string initString{VIDEO_FILE};
        rapidpixel::VSourceFile source;
        if (!source.openVSource(initString))
        {
            std::cout << "ERROR: video source is not open" << std::endl;
            return -1;
        }

        // Init dehazer.
        rapidpixel::Dehazer dehazer;
        if (!dehazer.setParam(rapidpixel::VFilterParam::MODE, 1.0f) ||
            !dehazer.setParam(rapidpixel::VFilterParam::LEVEL, 50.0f))
        {
            std::cout << "ERROR: dehazer params are not set" << std::endl;
            return -1;
        }

        // Get frame size.
        rapidpixel::VSourceParams params;
        source.getParams(params);

        // Init frame. Video source gives YUV24 frames, dehazer processes them
        // and window renders them without conversion. A frame with an invalid
        // size has no memory.
        rapidpixel::Frame frame(params.width, params.height,
                                rapidpixel::Fourcc::YUV24);
        if (frame.data == nullptr)
        {
            std::cout << "ERROR: invalid frame size" << std::endl;
            return -1;
        }

        // Create window.
        rapidpixel::SimpleUiWindow window("Result");
        window.show();

        // Main loop. Video source loops video file and keeps its FPS.
        int failures{0};
        while (window.isOpen())
        {
            // Capture next video frame.
            if (!source.getFrame(frame, FRAME_TIMEOUT_MS))
            {
                ++failures;
                if (failures >= MAX_FRAME_FAILURES)
                {
                    std::cout << "ERROR: no video frames" << std::endl;
                    return -1;
                }
                continue;
            }
            failures = 0;

            // Dehaze.
            if (!dehazer.processFrame(frame))
            {
                std::cout << "ERROR: frame is not processed" << std::endl;
                return -1;
            }

            // Show results.
            window.render(frame);
            if (window.getKey(1) == KEY_ESC)
            {
                return 0;
            }
        }
        return 0;
    }
    catch (const std::exception& e)
    {
        std::cout << "ERROR: " << e.what() << std::endl;
        return -1;
    }
}

Benchmark

Benchmark allows you to evaluate the speed of the algorithm on different platforms. This is a console application that allows you to enter initial parameters after startup (frame width and height from 16 to 8192, number of threads from 1 to 32). After entering the parameters, the application will create one synthetic YUV24 frame and process it 100 times with the level 50, then calculate and show statistics (minimum / average / maximum processing time per frame) and repeat until it is stopped (Ctrl+C). The application stops with an error message if a parameter is out of range or a frame is not processed. Benchmark console interface:

--------------------------------------------------
Dehazer v5.0.0 benchmark
Benchmark will process 100 frames and will show
processing time per frame
--------------------------------------------------

Set frame width: 1920
Set frame height: 1080
Set number of threads for processing: 4

Processing time: 0.667 -> 0.878 -> 1.17 msec

Table 8 - Performance benchmark across resolutions and thread configurations (Core i7-13700H, AVX2 level, MSVC 2022 x64, YUV24, level 50, minimum processing time of a frame).

Resolution Threads Processing Time (ms) Frame Rate (FPS)
640x480 (VGA) 1 1.17 ms 854 FPS
640x480 (VGA) 4 0.37 ms 2713 FPS
1280x720 (HD) 1 1.66 ms 602 FPS
1280x720 (HD) 4 0.51 ms 1953 FPS
1920x1080 (FullHD) 1 2.89 ms 345 FPS
1920x1080 (FullHD) 4 0.77 ms 1296 FPS
3840x2160 (4K) 1 8.90 ms 112 FPS
3840x2160 (4K) 4 2.42 ms 414 FPS

Demo application

Demo application overview

The demo application is intended to evaluate the performance of the Dehazer C++ library. The application allows you to evaluate the operation of the algorithm with different dehazing levels and thread counts. The video source is selected in the console, the video is shown in a window. The application serves as an example of using the Dehazer library. The application uses the VSourceFile library for capturing video from files, the VideoRecorderMp4 library for recording video and the SimpleUI library for displaying video and forming a simple user interface. Video frames are processed in YUV24 format without conversion to other pixel formats. The frame width and height must be from 16 to 8192 pixels. The application stops with an error message if the video source does not open, if no frame arrives 30 times in a row or if a frame is not processed.

Launch and user interface

The demo application does not require installation. The demo application is compiled for Windows OS x64 (Windows 10 and newer) as well as for Linux OS (several distros; to get the demo application for a specific Linux distro, please send us a request). Table 9 shows the list of files of the demo application.

Table 9 - List of files of demo application (example for Windows OS).

File Description
DehazerDemo.exe Demo application executable file for Windows OS.
VC_redist.x64 Installer of necessary system libraries for Windows x64 (use if required).
src Folder with application source code.

To launch the demo application run the DehazerDemo.exe executable file on Windows x64 OS or run commands on Linux:

sudo chmod +x DehazerDemo
./DehazerDemo

If a message about missing system libraries appears (on Windows OS) when launching the application, you must install the VC_redist.x64.exe program, which will install the system libraries required for operation.

After starting the application (running the executable file) the user should select the video file in the dialog box (if typed “y” to run file dialog). After that the user will see the user interface as shown below. The image at the top is the source image and the image at the bottom shows the results of the algorithm.

dehazer_demo_interface

Control

To control the application, make sure the main video display window is active (in focus), and the English keyboard layout is selected without CapsLock enabled. The program is controlled by the keyboard and mouse.

Table 10 - Control buttons.

Button Description
ESC Exit the application. If video recording is active, it will be stopped. Closing the window also exits the application.
SPACE Enable / disable dehazing algorithm.
2 Cycle thread count: 1 → 2 → 4 → 8 → 1 … Default - 1.
R Start/stop video recording. When video recording is enabled, files dst_[date and time].mp4 (result video) and mix_[date and time].mp4 (combined source and result video) are created in the current directory of the application. The recording frame rate is the frame rate of the video source (30 frames per second if the source does not report it). To stop the recording, press the R key again. During video recording, the application shows the message “RECORDING: R to stop”.
Level Dehazing algorithm enhancement value slider in the panel above the video, can be set by dragging slider with a mouse or by mouse wheel over it.

The user can set the filtration mask (mark a rectangular area where dehazing will be applied). In order to set a rectangular filtration area it is necessary to draw a line with the mouse with the left button pressed from the top-left corner of the required area to the bottom-right corner. The filtration area will be marked in cyan color as shown in the image. To reset the filtration area (apply dehazing to the whole frame), click the left mouse button without dragging.


Table of contents