
ImageFlip C++ library
v2.0.4
Table of contents
- Overview
- Versions
- Library files
- ImageFlip class description
- ImageFlip class declaration
- getVersion method
- initVFilter method
- setParam method
- getParam method
- getParams method
- executeCommand method
- processFrame method
- setMask method
- encodeSetParamCommand method of VFilter class
- encodeCommand method of VFilter class
- decodeCommand method of VFilter class
- decodeAndExecuteCommand method
- Data structures
- VFilterParams class description
- Build and connect to your project
- Portability
- Benchmark application
Overview
ImageFlip C++ library provides image flip functions with a custom SIMD-optimized implementation. The library is compatible with the VFilter interface for integration into video processing pipelines. It supports three flip modes: left-right, up-down and both (180-degree rotation). The frame is flipped in place. Supported pixel formats: BGR24, RGB24 and YUV24. The library selects the SIMD instruction set at run time: AVX2 or SSSE3 on x86/x64, NEON on AArch64 or a portable C++ implementation on other CPUs (see Portability). All implementations produce identical results. The library uses C++17 standard.
ImageFlip 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 | 27.02.2024 | First version of the library. |
| 1.0.1 | 24.03.2024 | - Submodules updated. - Documentation updated. |
| 1.0.2 | 21.05.2024 | - Submodules updated. - Documentation updated. |
| 1.0.3 | 23.07.2024 | - Update CMake structure. |
| 2.0.0 | 03.04.2026 | - Removed OpenCV dependency. - Custom SIMD implementation (AVX2, SSE, NEON). - Automatic platform detection. |
| 2.0.1 | 01.10.2026 | - Removed OpenCV from the test application. - Converted the test application into a benchmark application. - Updated VFilter to 1.1.5. |
| 2.0.2 | 12.10.2026 | - Submodules updated. |
| 2.0.3 | 19.10.2026 | - Sub-repositories removed: the dependencies (Frame, ConfigReader, VFilter) are expected next to the library repository (see Build and connect to your project). - SIMD kernels are built in separate source files with the flag of their instruction set only and are selected at run time (the library no longer requires AVX2 on the CPU). Option IMAGE_FLIP_ENABLE_SIMD added. The flags -Ofast, -march=native, -mtune=native, /arch:AVX2 and /fp:fast are removed. - Fixed reading of one byte after the end of the last row of the frame by the AVX2 and SSSE3 kernels. - processFrame(…) checks the data pointer and the data size of the frame, the mode is read once per call, the processing time is measured with a monotonic clock, the memory of the temporary buffers is released on all paths. - setParam(…) rejects NaN, infinity and values outside the range of int (before the conversion). - NEON kernel is built for AArch64 only. - Compiler warnings fixed, hardening flags added to the CMake files, CMAKE_CXX_EXTENSIONS is OFF. - Documentation corrected, subsections Build stand alone, CMake options and section Portability added. |
| 2.0.4 | 25.10.2026 | - Link-time optimization removed from the Release build (-flto, MSVC /GL and /LTCG): the static library contains ordinary object files, so programs built with another compiler version can link it. The speed is unchanged. |
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 does not contain sub-repositories: the libraries ImageFlip depends on are described in Build and connect to your project. The repository structure is shown below:
CMakeLists.txt ------------- Main CMake file of the library.
src ------------------------ Folder with source code of the library.
CMakeLists.txt --------- CMake file of the library.
ImageFlip.cpp ---------- C++ implementation file.
ImageFlip.h ------------ Header file which includes ImageFlip class declaration.
ImageFlipVersion.h ----- Header file which includes version of the library.
ImageFlipVersion.h.in -- CMake service file to generate version file.
impl ------------------- Folder with the row kernels (internal, not a part of the API).
RowKernels.h ------- Declaration of the row kernels and of the run time selection.
RowKernels.cpp ----- Portable kernel and run time selection of the SIMD level.
SimdConfig.h ------- Macros of the SIMD levels that are built.
x86 ---------------- Folder with the x86 kernels.
RowKernelsSsse3.cpp -- SSSE3 kernel.
RowKernelsAvx2.cpp --- AVX2 kernel.
arm ---------------- Folder with the ARM kernels.
RowKernelsNeon.cpp --- NEON kernel (AArch64).
benchmark ------------------ Folder for benchmark application.
CMakeLists.txt --------- CMake file for benchmark application.
main.cpp --------------- Source code file of benchmark application.
ImageFlip class description
ImageFlip class declaration
The ImageFlip class declared in ImageFlip.h file. Class declaration:
namespace rapidpixel
{
class ImageFlip : public VFilter
{
public:
/// Class constructor.
ImageFlip() = default;
/// Class destructor.
~ImageFlip() override = default;
/// Copy constructor. The class is not copyable.
ImageFlip(const ImageFlip&) = delete;
/// Move constructor. The class is not movable.
ImageFlip(ImageFlip&&) = delete;
/// Copy assignment operator. The class is not copyable.
ImageFlip& operator= (const ImageFlip&) = delete;
/// Move assignment operator. The class is not movable.
ImageFlip& operator= (ImageFlip&&) = delete;
/// Get the version of the ImageFlip class.
static std::string getVersion();
/// Initialize image flip.
bool initVFilter(VFilterParams& params) override;
/// Set VFilter parameter.
bool setParam(VFilterParam id, float value) override;
/// Get the value of a specific VFilter parameter.
float getParam(VFilterParam id) override;
/// Get the structure containing all VFilter parameters.
void getParams(VFilterParams& params) override;
/// Execute a VFilter action command. Not supported.
bool executeCommand(VFilterCommand id) override;
/// Process frame: flip the pixels of the frame in place.
bool processFrame(rapidpixel::Frame& frame) override;
/// Set mask for filter. Not supported: the mask is ignored.
bool setMask(rapidpixel::Frame mask) override;
/// Decode and execute command.
bool decodeAndExecuteCommand(uint8_t* data, int size) override;
};
}
The class is thread-safe: all methods can be called from any thread (a frame must not be used by other threads while processFrame(…) processes it).
getVersion method
The getVersion() static method returns string of current class version. Method declaration:
static std::string getVersion();
Method can be used without ImageFlip class instance:
std::cout << "ImageFlip version: " << rapidpixel::ImageFlip::getVersion();
Console output:
ImageFlip version: 2.0.4
initVFilter method
The initVFilter(…) method initializes image flip. Method uses only parameter mode from VFilterParams class. Method declaration:
bool initVFilter(VFilterParams& params) override;
| Parameter | Value |
|---|---|
| params | VFilterParams class object. Method uses only parameter mode from VFilterParams class. Mode: 0 - Off. 1 - Left-Right. 2 - Up-Down. 3 - Both. |
Returns: TRUE if the video filter initialized or FALSE if the mode is not valid (the mode is not changed in this case).
setParam method
The setParam (…) method sets new parameters value. The library provides thread-safe setParam(…) method call. 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. Method supports only parameters: MODE. Mode: 0 - Off. 1 - Left-Right. 2 - Up-Down. 3 - Both. |
| value | Mode value. The value is converted to int by truncation, so values from the range (-1, 4) are valid (for example, 1.5 sets mode 1). |
Returns: TRUE if the parameter was set or FALSE if the parameter is not supported or the value is not valid, including NaN and infinity (the parameter is not changed in this case).
getParam method
The getParam(…) method returns parameter value. The library provides thread-safe getParam(…) method call. 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. Method supports only parameters: MODE and PROCESSING_TIME_MCSEC. For other parameters the method will return -1. |
Returns: parameter value or -1 if the parameters doesn’t exist (not supported in particular implementation).
getParams method
The getParams(…) method is designed to obtain all image flip params. The library provides thread-safe getParams(…) method call. 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. Method will fill parameters: mode and processingTimeMcSec. Other parameters will be set to -1.0. |
executeCommand method
The executeCommand(…) method executes video filter action command. Not supported by ImageFlip class. Method declaration:
bool executeCommand(VFilterCommand id) override;
| Parameter | Description |
|---|---|
| id | Command ID according to VFilterCommand enum. Not supported by ImageFlip class. |
Returns: always FALSE.
processFrame method
The processFrame(…) method designed to process frame (image flip): the pixels of the frame are flipped in place. The library provides thread-safe processFrame(…) method call. This means that the processFrame(…) method can be safely called from any thread. A frame must not be used by other threads while it is processed. The method allocates temporary buffers of one or two rows. Method declaration:
bool processFrame(rapidpixel::Frame& frame) override;
| Parameter | Description |
|---|---|
| frame | Reference to Frame object. If the mode is not 0 (off), the frame must have pixel format BGR24, RGB24 or YUV24, size not less than 16x16 pixels, data pointer which is not nullptr and data size (field size of the frame) not less than width x height x 3 bytes. Width, height, pixel format, frame ID and source ID of the frame are not changed. |
Returns: TRUE if frame processed (image flip) or FALSE if not: the pixel format is not supported, the frame is smaller than 16x16 pixels, the data pointer is nullptr, the data size is less than width x height x 3 bytes or the memory for the temporary buffers can not be allocated. If FALSE is returned the data of the frame is not changed. If the mode is 0 (off) the method returns TRUE without checking or processing the frame. Every call updates the processing time (parameter PROCESSING_TIME_MCSEC).
setMask method
The setMask(…) method designed to set video filter mask. Not supported by ImageFlip class. Method declaration:
bool setMask(rapidpixel::Frame mask) override;
| Parameter | Description |
|---|---|
| mask | Filter mask in form of Frame object with GRAY pixel format. Mask not supported by ImageFlip class. |
Returns: TRUE in any cases (the mask is ignored).
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, the buffer must have size >= 11. |
| size | Size of encoded data. Will be 11 bytes (0 if data is nullptr, nothing is written). |
| id | Parameter ID according to VFilterParam enum. |
| value | Parameter value. |
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: flip left-right.
const float outValue = 1.0f;
// Encode command.
rapidpixel::VFilter::encodeSetParamCommand(
data, size, rapidpixel::VFilterParam::MODE, 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, the buffer must have size >= 7. |
| size | Size of encoded data. Will be 7 bytes (0 if data is nullptr, nothing is written). |
| id | Command ID according to VFilterCommand 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::RESET);
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. |
| size | Size of command. Must be exactly 11 bytes for SET_PARAM and 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). |
The method checks the pointer, the size, the version, the data type and the ID (it must be in the enum). The output arguments are changed only if the command is decoded.
Returns: 0 - in case decoding COMMAND, 1 - in case decoding SET_PARAM command or -1 in case errors.
decodeAndExecuteCommand method
The decodeAndExecuteCommand(…) method decodes and executes command encoded by encodeSetParamCommand(…) and encodeCommand(…) methods on video filter side. The library provides thread-safe decodeAndExecuteCommand(…) method call. This means that 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. Must be exactly 11 bytes for SET_PARAM or 7 bytes for COMMAND. |
Returns: TRUE if the command is a valid SET_PARAM command for the MODE parameter with a valid value and it was executed. FALSE if the data is not a valid command (see decodeCommand(…)), if the command is an action command (not supported by ImageFlip class), if the parameter is not supported by ImageFlip class or if the value is not valid.
Data structures
VFilterCommand enum
Enum declaration:
enum class VFilterCommand
{
/// Reset video filter algorithm.
RESET = 1,
/// Enable filter.
ON,
/// Disable filter.
OFF
};
OFF is the last value: VFilter::decodeCommand(…) accepts the IDs from RESET to OFF.
Table 2 - Action commands description.
| Command | Description |
|---|---|
| RESET | Not supported by ImageFlip class. |
| ON | Not supported by ImageFlip class. |
| OFF | Not supported by ImageFlip class. |
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
};
CUSTOM_3 is the last value: VFilter::decodeCommand(…) accepts the IDs from MODE to CUSTOM_3.
Table 3 - Params description.
| Parameter | Access | Description |
|---|---|---|
| MODE | read / write | Image flip mode: 0 - Off. 1 - Left-Right. 2 - Up-Down. 3 - Both. |
| LEVEL | read / write | Not supported by ImageFlip class. |
| PROCESSING_TIME_MCSEC | read only | Processing time in microseconds. Read only parameter. Used to check performance of image flip. |
| TYPE | read / write | Not supported by ImageFlip class. |
| CUSTOM_1 | read / write | Not supported by ImageFlip class. |
| CUSTOM_2 | read / write | Not supported by ImageFlip class. |
| CUSTOM_3 | read / write | Not supported by ImageFlip class. |
VFilterParams class description
VFilterParams class declaration
The 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 class (JSON, serialization): they are not checked against any range. The video filter implementation must validate the values before use. The class is not thread-safe: an object must not be accessed from several threads at the same time without external synchronization.
Table 4 - VFilterParams class fields description is related to VFilterParam enum description.
| Field | type | Description |
|---|---|---|
| mode | int | Image flip mode: 0 - Off. 1 - Left-Right. 2 - Up-Down. 3 - Both. |
| level | float | Not supported by ImageFlip class. |
| processingTimeMcSec | int | Processing time in microseconds. Read only parameter. Used to check performance of image flip. |
| type | int | Not supported by ImageFlip class. |
| custom1 | float | Not supported by ImageFlip class. |
| custom2 | float | Not supported by ImageFlip class. |
| custom3 | float | Not supported by ImageFlip class. |
Note: VFilterParams class fields listed in Table 4 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 image 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. Integer and float fields are stored as raw 4 bytes values in the byte order of the host. The size of the encoded data is 4 bytes of header plus 4 bytes for each field selected by the mask (32 bytes if all fields are selected). 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. Buffer size must be >= 32 bytes. |
| bufferSize | Data buffer size. Buffer size must be >= 32 bytes. |
| size | Size of encoded data. 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 the 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 data is nullptr or bufferSize is less than 32 (nothing is written to the buffer in this case).
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.mode = 1;
// Encode (serialize) params.
const int bufferSize = 128;
uint8_t buffer[bufferSize];
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.mode = 1;
// Prepare mask.
rapidpixel::VFilterParamsMask mask;
// Exclude level.
mask.level = false;
// Encode (serialize) params.
const int bufferSize = 128;
uint8_t buffer[bufferSize];
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. |
| dataSize | Size of serialized params, bytes. |
The method checks the data type, the version (major and minor) and the size of the data (it must be equal to the size defined by the mask in the data) before reading each field. Fields not selected by the mask in the data are set to zero. Values of the fields are not checked against any range.
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.
Example:
// Prepare parameters.
rapidpixel::VFilterParams params1;
params1.level = 80.0f;
// Encode (serialize) params.
const int bufferSize = 128;
uint8_t buffer[bufferSize];
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:
// Write params to file.
rapidpixel::ConfigReader inConfig;
rapidpixel::VFilterParams in;
in.mode = 2;
if (!inConfig.set(in, "vFilterParams") ||
!inConfig.writeToFile("VFilterParams.json"))
{
std::cout << "Can't write config file" << std::endl;
}
// Read params from file.
rapidpixel::ConfigReader outConfig;
rapidpixel::VFilterParams out;
if (!outConfig.readFromFile("VFilterParams.json") ||
!outConfig.get(out, "vFilterParams"))
{
std::cout << "Can't read config file" << std::endl;
}
VFilterParams.json will look like:
{
"vFilterParams": {
"mode": 2,
"level": 0.0,
"type": 0,
"custom1": 0.0,
"custom2": 0.0,
"custom3": 0.0
}
}
Build and connect to your project
Build stand alone
The repository does not contain sub-repositories. The libraries ImageFlip depends on (Frame, ConfigReader and VFilter) must be cloned into the same folder as ImageFlip. The folder structure must be:
<work folder>
ImageFlip
Frame
ConfigReader
VFilter
Typical commands to build the library and the benchmark application (use the URLs of the repositories provided to you):
git clone <ImageFlip repository URL>
git clone <Frame repository URL>
git clone <ConfigReader repository URL>
git clone <VFilter repository URL>
cd ImageFlip
mkdir build
cd build
cmake ..
make
On Windows use cmake --build . --config Release instead of make. If ImageFlip 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. If a folder does not exist CMake stops with an error.
CMake options
Table 5 - CMake options of the repository. In the options below ${PARENT} is the prefix defined by the top-level project (empty if ImageFlip is the top-level project).
| Option | Default | Description |
|---|---|---|
| ${PARENT}_IMAGE_FLIP | ON | Build the library. |
| ${PARENT}_IMAGE_FLIP_BENCHMARK | ON (stand alone), OFF (included in other project) | Build the benchmark application. |
| ${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 ImageFlip as a sub-repository. |
| CMAKE_BUILD_TYPE | Release (if not specified) | Build type. |
| IMAGE_FLIP_ENABLE_SIMD | ON | Build the SIMD kernels (see Portability). If OFF, only the portable code is built (the macro IMAGE_FLIP_NO_SIMD is defined for the library). |
Connect to your project
If you want connect ImageFlip to your CMake project as source code you can make follow. For example, if your repository has structure:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
Create folder 3rdparty in your repository and copy the repositories Frame, ConfigReader, VFilter and ImageFlip there (each of them as a separate sub-repository of your repository). The structure of your repository after that:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
Frame
ConfigReader
VFilter
ImageFlip
Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should contain (the libraries are added in the order of their mutual dependencies):
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_IMAGE_FLIP ON CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_IMAGE_FLIP)
SET(${PARENT}_IMAGE_FLIP ON CACHE BOOL "" FORCE)
SET(${PARENT}_IMAGE_FLIP_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_IMAGE_FLIP)
add_subdirectory(ImageFlip)
endif()
File 3rdparty/CMakeLists.txt adds the folders of the libraries to your project and excludes the tests, the examples and the benchmark application from compiling (they are excluded from compiling by default if a library is included as sub-repository). The structure of your repository after that:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
Frame
ConfigReader
VFilter
ImageFlip
Next you need include folder 3rdparty in main CMakeLists.txt file of your repository. Add string at the end of your main CMakeLists.txt:
add_subdirectory(3rdparty)
Next you have to include ImageFlip library in your src/CMakeLists.txt file:
target_link_libraries(${PROJECT_NAME} ImageFlip)
When ImageFlip is included in another project, it does not add its dependencies: Frame, ConfigReader and VFilter must be added by your project, each of them once. If a dependency is missing the compilation stops with the error “VFilter.h: No such file or directory” (or “Frame.h: No such file or directory”, “ConfigReader.h: No such file or directory”).
Done!
Portability
The library is written in standard C++17. The pixel data is processed as bytes: the library does not read or write multi-byte values of the frame, so the result does not depend on the byte order of the CPU. The library works with frames of 3 bytes per pixel (BGR24, RGB24, YUV24).
The SIMD level is selected at run time, once per process, according to the CPU that runs the program. All levels produce identical results. The portable level runs on every CPU.
Table 6 - SIMD levels.
| Level | CPU | Selection |
|---|---|---|
| AVX2 | x86 / x64 CPU with AVX2 (the OS must save the state of the YMM registers) | CPUID and XGETBV instructions. |
| SSSE3 | x86 / x64 CPU with SSSE3 | CPUID instruction. |
| NEON | AArch64 | NEON is a part of the AArch64 baseline, no check is needed. |
| Portable | Any other CPU, including 32-bit ARM | Always available. |
Each x86 kernel is built in its own source file with the compiler flag of its instruction set (-mssse3, -mavx2, /arch:AVX2 for MSVC). The rest of the library is built without any flag of an instruction set, so the library runs on every x86-64 CPU. A level is left out of the build if the option IMAGE_FLIP_ENABLE_SIMD is OFF (all levels, macro IMAGE_FLIP_NO_SIMD) or if the compiler can not build it (macros IMAGE_FLIP_NO_SSSE3 and IMAGE_FLIP_NO_AVX2, CMake prints the message “x86 kernels left out”). Universal macOS binaries (several entries in CMAKE_OSX_ARCHITECTURES) are built without the x86 kernels.
In the Release configuration the library is compiled with -O3 and -funroll-loops (GCC and Clang) or /O2, /Ob3, /Qpar and /Gy (MSVC). Link-time optimization (-flto, /GL) is not used: the static library contains ordinary object files, so the program that links the library does not need link-time optimization support and can be built with another compiler version.
Verified compilers: GCC 9, 11 and 12 for x86-64 Linux (the levels AVX2, SSSE3 and portable) and GCC 11 for AArch64 (NEON and portable level, run under qemu). The SSSE3 and portable levels are also verified on the CPU models Conroe, Penryn, Nehalem, SandyBridge and qemu64 of qemu.
Benchmark application
The benchmark application measures the performance of the ImageFlip library. For each flip mode (left-right, up-down, both) and resolution (640x480, 1280x720, 1920x1080) the application creates a BGR24 frame filled with a deterministic test pattern, runs 10 warm-up iterations and then 1000 measured iterations of processFrame(…) method, and prints the average execution time of one call in microseconds. The results are printed as a Markdown table, so they can be copied directly into documentation or reports. The application returns 0 if all measurements are done and 1 if processFrame(…) fails.
The benchmark application is built by default when ImageFlip is built as a standalone project and is excluded from compiling when ImageFlip is included as a sub-repository. This is controlled by the CMake option ${PARENT}_IMAGE_FLIP_BENCHMARK (see CMake options). Build the library in Release mode to get representative results (Debug builds are much slower). Typical commands to build and run the benchmark application (the folder structure is described in Build stand alone):
cd ImageFlip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
./bin/ImageFlipBenchmark
Benchmark application source code:
#include <chrono>
#include <cstdint>
#include <exception>
#include <iomanip>
#include <iostream>
#include "ImageFlip.h"
namespace
{
/// Number of iterations for benchmarking.
constexpr int NUM_ITERATIONS{1000};
/// Number of iterations before the measurement.
constexpr int WARM_UP_ITERATIONS{10};
/// Modulus of the values of the test pattern (the values of a byte).
constexpr int PATTERN_MODULUS{256};
/// Test resolution.
struct Resolution
{
/// Frame width, pixels.
int width;
/// Frame height, pixels.
int height;
/// Name of the resolution.
const char* name;
};
/// Test flip mode.
struct Mode
{
/// Value of the MODE parameter.
int id;
/// Name of the mode.
const char* name;
};
/// Fill frame data with a test pattern.
void fillTestPattern(uint8_t* data, int width, int height)
{
const int stride = width * 3;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
const int idx = y * stride + x * 3;
data[idx + 0] = static_cast<uint8_t>((x + y) % PATTERN_MODULUS);
data[idx + 1] = static_cast<uint8_t>(
(x * 2 + y) % PATTERN_MODULUS);
data[idx + 2] = static_cast<uint8_t>(
(x + y * 2) % PATTERN_MODULUS);
}
}
}
/// Benchmark the flip. Returns false if the flip failed, otherwise the
/// average time of one frame in microseconds is returned in averageUs.
bool benchmarkCustom(int mode, int width, int height, double& averageUs)
{
rapidpixel::ImageFlip imageFlip;
if (!imageFlip.setParam(rapidpixel::VFilterParam::MODE,
static_cast<float>(mode)))
{
return false;
}
rapidpixel::Frame frame(width, height, rapidpixel::Fourcc::BGR24);
fillTestPattern(frame.data, width, height);
// Warm up.
for (int i = 0; i < WARM_UP_ITERATIONS; ++i)
{
if (!imageFlip.processFrame(frame))
{
return false;
}
}
const auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < NUM_ITERATIONS; ++i)
{
if (!imageFlip.processFrame(frame))
{
return false;
}
}
const auto end = std::chrono::high_resolution_clock::now();
const double totalUs = static_cast<double>(
std::chrono::duration_cast<std::chrono::microseconds>(
end - start).count());
averageUs = totalUs / NUM_ITERATIONS;
return true;
}
}
int main()
{
std::cout << "ImageFlip v" <<
rapidpixel::ImageFlip::getVersion() << std::endl;
std::cout << "========================================" << std::endl;
const Resolution resolutions[] = {
{640, 480, "640x480"},
{1280, 720, "1280x720"},
{1920, 1080, "1920x1080"},
};
const Mode modes[] = {
{1, "LEFT-RIGHT"},
{2, "UP-DOWN"},
{3, "BOTH"},
};
// Performance benchmarks.
std::cout << std::endl << "## Performance benchmarks "
<< "(" << NUM_ITERATIONS << " iterations)" << std::endl
<< std::endl;
std::cout << "| Resolution | Mode | Custom (us) |" << std::endl;
std::cout << "|------------|------------|------------:|" << std::endl;
try
{
for (const Resolution& res : resolutions)
{
for (const Mode& mode : modes)
{
double customUs = 0.0;
if (!benchmarkCustom(mode.id, res.width, res.height, customUs))
{
std::cout << "Benchmark failed: " << res.name << ", "
<< mode.name << std::endl;
return 1;
}
std::cout << "| " << std::left << std::setw(10) << res.name
<< " | " << std::setw(10) << mode.name << " | "
<< std::right << std::setw(11) << std::fixed
<< std::setprecision(1) << customUs << " |"
<< std::endl;
}
}
}
catch (const std::exception& e)
{
std::cout << "Benchmark failed: " << e.what() << std::endl;
return 1;
}
std::cout << std::endl << "Custom (us) shows the average time taken by "
<< "the custom implementation." << std::endl;
return 0;
}
Example console output (Release build). The results depend on the CPU and on the SIMD level selected at run time:
ImageFlip v2.0.4
========================================
## Performance benchmarks (1000 iterations)
| Resolution | Mode | Custom (us) |
|------------|------------|------------:|
| 640x480 | LEFT-RIGHT | 22.5 |
| 640x480 | UP-DOWN | 10.1 |
| 640x480 | BOTH | 22.0 |
| 1280x720 | LEFT-RIGHT | 69.9 |
| 1280x720 | UP-DOWN | 45.0 |
| 1280x720 | BOTH | 69.1 |
| 1920x1080 | LEFT-RIGHT | 163.3 |
| 1920x1080 | UP-DOWN | 122.6 |
| 1920x1080 | BOTH | 162.8 |
Custom (us) shows the average time taken by the custom implementation.