Skip to content

Commit 0a787f7

Browse files
author
J. Cappelletto
committed
Merge branch 'release/0.8.0'
* Complete implementation of enhacemente pipeline * Partial target restructure for new enhacement modules * Updates the corresponding documentation * General TODO and dead code maintenance
2 parents be33193 + 32bba19 commit 0a787f7

16 files changed

+742
-42
lines changed

CMakeLists.txt

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.18)
2-
project(videostrip VERSION 0.7.1 LANGUAGES CXX)
2+
project(videostrip VERSION 0.8.0 LANGUAGES CXX)
33

44
set(CMAKE_CXX_STANDARD 17)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -15,7 +15,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
1515
# Build options
1616
# ==========================================
1717
option(BUILD_TESTS "Build unit/integration tests" ON)
18-
option(BUILD_GUI "Build GUI module" OFF)
18+
# option(BUILD_GUI "Build GUI module" OFF)
19+
option(BUILD_MANPAGE "Generate manpage for CLI tool (requires help2man)" OFF)
1920

2021

2122
message(STATUS "--------------------")
@@ -54,21 +55,45 @@ message(STATUS "OpenCV version: ${OpenCV_VERSION}")
5455
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
5556

5657
# ==========================================
57-
# Global include path (so <videostrip_core/...> works everywhere)
58-
# ==========================================
59-
# All headers live in subfolders inside ${CMAKE_SOURCE_DIR}
60-
include_directories(${CMAKE_SOURCE_DIR})
61-
58+
# yaml-cpp detection. FetchContent fallback
6259
# ==========================================
63-
# Promoted to top-level as it is needed by multiple modules
64-
find_package(yaml-cpp QUIET)
65-
if(NOT yaml-cpp_FOUND)
66-
message(STATUS "yaml-cpp not found; fetching...")
60+
find_package(yaml-cpp CONFIG)
61+
62+
if (TARGET yaml-cpp)
63+
message(STATUS "yaml-cpp found (CONFIG package)")
64+
message(STATUS "yaml-cpp include dirs: ${yaml-cpp_INCLUDE_DIRS}")
65+
else()
66+
message(STATUS "yaml-cpp not found (CONFIG package)")
67+
message(STATUS ">>>> yaml-cpp not found; fetching source with FetchContent...")
6768
include(FetchContent)
68-
FetchContent_Declare(yaml_cpp GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git GIT_TAG 0.8.0)
69+
70+
# Keep the fetched build small & static
71+
set(YAML_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
72+
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
73+
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
74+
set(YAML_CPP_INSTALL OFF CACHE BOOL "" FORCE)
75+
# MSVC runtime per your project policy
76+
set(YAML_MSVC_SHARED_RT ON CACHE BOOL "" FORCE)
77+
78+
FetchContent_Declare(yaml_cpp
79+
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
80+
GIT_TAG 0.8.0
81+
)
6982
FetchContent_MakeAvailable(yaml_cpp)
7083
endif()
7184

85+
# At this point, a target named 'yaml-cpp' must exist (from either path).
86+
# If not, abort with error.
87+
if(NOT TARGET yaml-cpp)
88+
message(FATAL_ERROR "yaml-cpp target not found even after FetchContent; aborting.")
89+
endif()
90+
91+
# ==========================================
92+
# Global include path (so <videostrip_core/...> works everywhere)
93+
# ==========================================
94+
# All headers live in subfolders inside ${CMAKE_SOURCE_DIR}
95+
include_directories(${CMAKE_SOURCE_DIR})
96+
7297
# ==========================================
7398
# Add project modules (progressive build)
7499
# ==========================================
@@ -137,9 +162,6 @@ install(TARGETS videostrip_core
137162
LIBRARY DESTINATION lib
138163
)
139164

140-
# TODO: Minimal manpage (optional). We do not have one yet, generate with help2man later.
141-
# install(FILES packaging/videostrip.1 DESTINATION share/man/man1)
142-
143165
# Public headers (install only .hpp files from videostrip_core/)
144166
# If the headers live in subdirs (feature/, logging/), this will include them.
145167
install(DIRECTORY ${CMAKE_SOURCE_DIR}/videostrip_core/
@@ -154,6 +176,37 @@ install(FILES configs/sample_config.yaml
154176
install(FILES ${CMAKE_SOURCE_DIR}/README.md ${CMAKE_SOURCE_DIR}/LICENSE
155177
DESTINATION share/doc/videostrip
156178
)
179+
# -------------------------
180+
# Manpage generation (optional)
181+
# -------------------------
182+
if (BUILD_MANPAGE)
183+
message(STATUS "Manpage generation enabled")
184+
find_program(HELP2MAN_EXECUTABLE help2man)
185+
186+
if(HELP2MAN_EXECUTABLE)
187+
# Create packaging dir if it does not exist
188+
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/packaging)
189+
add_custom_command(
190+
OUTPUT ${CMAKE_BINARY_DIR}/packaging/videostrip.1
191+
COMMAND ${HELP2MAN_EXECUTABLE} --no-discard-stderr --output=${CMAKE_BINARY_DIR}/packaging/videostrip.1 $<TARGET_FILE:videostrip_cli>
192+
DEPENDS videostrip_cli
193+
COMMENT "Generating manpage for videostrip_cli"
194+
VERBATIM
195+
)
196+
197+
add_custom_target(manpage ALL
198+
DEPENDS ${CMAKE_BINARY_DIR}/packaging/videostrip.1
199+
)
200+
201+
install(FILES ${CMAKE_BINARY_DIR}/packaging/videostrip.1 DESTINATION share/man/man1)
202+
else()
203+
message(WARNING "help2man not found: manpage will not be generated automatically.")
204+
endif()
205+
206+
else()
207+
message(STATUS "Manpage generation disabled")
208+
endif()
209+
157210

158211
# -------------------------
159212
# CPack configuration
@@ -175,20 +228,17 @@ set(CPACK_PACKAGE_FILE_NAME
175228
# Source + binary archives
176229
set(CPACK_GENERATOR "TGZ;ZIP;DEB")
177230

178-
# Debian metadata (tune as you learn your shared libs)
231+
# Debian metadata (tune as we expand shared libs)
179232
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Jose Cappelletto") # required
180233
set(CPACK_DEBIAN_PACKAGE_SECTION "science")
181234
set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
182235

183236
# Runtime deps if linking dynamically; keep minimal first, then refine with ldd
184-
# Example:
185237
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libstdc++6, libgcc-s1")
186-
# TODO:
187-
# Add correct libraries from OpenCV
188-
# set(CPACK_DEBIAN_PACKAGE_DEPENDS "libopencv-core4.8, libopencv-imgcodecs4.8")
238+
# Automate depedencies based on linked shared libs
189239
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
190240
# Avoid the classic error: ensure CPack knows the project
191241
set(CPACK_PACKAGE_FILE_NAME "videostrip-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
192242

193243
include(CPack)
194-
# End of CPack configuration
244+
# End of CPack configuration

README.md

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ It produces **SfM-ready frame sets** and metadata for tools like **COLMAP, Meshr
1616
> * A **CLI application** for processing videos into frames + metadata.
1717
> * A **core library** exposing `VideoFrameExtractor` and related modules.
1818
> * A **metadata writer** for reproducible CSV + YAML outputs.
19-
> * CI/CD with unit tests for schema regression.
19+
> * CI/CD with unit tests for schema regression, running on Linux and Windows.
20+
> * A modular image enhacement pipeline (pre-export stage)
21+
> * Optional packaging system (DEB/TAR)
22+
> * Linux compatible documentation (manpages)
2023
2124
**Short-term focus**: Consolidate usability, error resilience, and schema compliance before expanding to advanced feature modes (grid, enhancement, optical flow).
2225

2326
---
2427

25-
## **Key Features (v0.4.0)**
28+
## **Key Features (v0.8.0)**
2629

2730
***CLI support** for video processing with YAML or CLI configs.
2831
***Frame extraction with stride or overlap-based selection**.
@@ -31,7 +34,7 @@ It produces **SfM-ready frame sets** and metadata for tools like **COLMAP, Meshr
3134
* `frames.csv` — per-frame metadata.
3235
* `summary.yaml` — run summary + configuration snapshot.
3336
***Deterministic file structure**: images/, features/, frames.csv, summary.yaml, run.log.
34-
* **On-export frame enhancement** (future).
37+
* **On-export frame enhancement** (future).
3538
***Grid-based feature density normalization** (future).
3639

3740
---
@@ -67,7 +70,7 @@ git clone https://github.com/cappelletto/videostrip.git
6770
cd videostrip
6871

6972
# Configure and build
70-
cmake -B build -DCMAKE_BUILD_TYPE=Release
73+
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_MANPAGE=OFF -DBUILD_TESTS=OFF
7174
cmake --build build -j4
7275
```
7376

@@ -129,6 +132,73 @@ processing:
129132
130133
---
131134
135+
## **Enhancement Pipeline (NEW)**
136+
TODO: extract this into a separate `docs/enhancement.md`.
137+
138+
Starting with **v0.7.x**, videostrip supports an optional **image enhancement stage** that preprocesses frames before feature extraction. This improves contrast and uniformity in underwater imagery.
139+
140+
### Supported enhancement steps
141+
142+
* **Contrast & offset**
143+
Linear transform per pixel: `alpha * I + beta`.
144+
*Params*: `alpha` (gain), `beta` (offset).
145+
146+
* **Gray-world white balance**
147+
Scales RGB channels so that their mean matches the global mean.
148+
*Params*: none.
149+
150+
* **Gamma correction**
151+
Applies a gamma LUT (`I_out = I_in^(1/gamma)`).
152+
*Params*: `value` (gamma > 0).
153+
154+
* **CLAHE (Contrast Limited Adaptive Histogram Equalization)**
155+
Adaptive histogram equalization on luminance channel.
156+
*Params*:
157+
158+
* `clip_limit` (float, default 2.0)
159+
* `tile_grid` (two-element array, default `[8,8]`)
160+
* `space` (one of `YCrCb`, `HSV`, `Lab`, `BGR`)
161+
Channel is chosen implicitly: Y (YCrCb), V (HSV), L (Lab), all channels (BGR).
162+
163+
### Minimal YAML configuration
164+
165+
Add an `enhance` block at the top level of the config file:
166+
167+
```yaml
168+
enhance:
169+
enable: true
170+
sequence:
171+
- type: contrast
172+
alpha: 1.10
173+
beta: -5
174+
- type: grayworld
175+
- type: gamma
176+
value: 1.05
177+
- type: clahe
178+
clip_limit: 2.0
179+
tile_grid: [8, 8]
180+
space: YCrCb
181+
```
182+
183+
If no `enhance:` block is given but the legacy flag
184+
185+
```yaml
186+
processing:
187+
apply_enhancement: true
188+
```
189+
190+
is set, a default sequence `{grayworld, clahe(YCrCb)}` will be applied.
191+
192+
### CLI override
193+
194+
You can also pass a shorthand string (for quick tests):
195+
196+
```bash
197+
./videostrip_cli --enhance.sequence "contrast(alpha=1.1,beta=-5); grayworld; gamma(1.05); clahe(clip=2.0,grid=8x8,space=YCrCb)"
198+
```
199+
200+
---
201+
132202
## **Schema v1 Contract**
133203

134204
* **frames.csv** — columns: `frame_idx,timestamp_ms,output_image,feature_count,quality_score,georef`
@@ -144,13 +214,13 @@ Schema is locked at `v1`. Future changes will bump schema_version.
144214
## **Roadmap**
145215

146216
Near-term milestones:
147-
1. Add **frame enhancement filters** (CLAHE, WB).
148-
2. Introduce **grid-based feature normalization**.
149-
3. Expand **Windows CI/CD** coverage.
217+
1. Introduce **grid-based feature normalization**.
218+
2. Performance release by adding multithreading and GPU support.
150219

151220
Long-term:
152221
* Optical flow / ECC overlap modes.
153-
* Batch manager & GUI front-end.
222+
* Cross-platform GUI for both pipeline configuration and dispatching.
223+
* Integration with Meshroom (node-base core library)
154224

155225
---
156226

configs/sample_config.yaml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
version: 1
22

33
input:
4-
video: ./data/example_video.avi
5-
4+
# video: ./data/21062025_Lipa_WFC_P1_Mosaic_StereoL.MOV
5+
#
66
output:
77
base_dir: ./_tmp_out
88
images_dir: images
@@ -12,11 +12,25 @@ output:
1212
log: run.log
1313

1414
processing:
15-
feature_type: ORB
15+
feature_type: KAZE
1616
image_format: png
1717
overlap_threshold: 0.75
18-
max_skipped_frames: 4
19-
apply_enhancement: false
18+
max_skipped_frames: 5
19+
apply_enhancement: true
2020
create_output_dirs: true
2121
enable_logging: true
2222
# overlap_mode: FEATURE # (optional, harmless for now)
23+
24+
enhance:
25+
enable: true
26+
sequence:
27+
- type: contrast
28+
alpha: 1.5
29+
beta: 10
30+
- type: grayworld
31+
- type: gamma
32+
value: 1.05
33+
- type: clahe
34+
clip_limit: 2.0
35+
tile_grid: [8, 8]
36+
space: YCrCb # YCrCb | HSV | Lab | BGR

videostrip_cli/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ add_executable(videostrip_cli
1313
# - Project external folder for args.hxx
1414
# - Core module for headers.hpp and helper.hpp
1515
target_include_directories(videostrip_cli
16-
PRIVATE
16+
PUBLIC
1717
${CMAKE_CURRENT_SOURCE_DIR}
1818
${CMAKE_SOURCE_DIR}/external
1919
${CMAKE_SOURCE_DIR}/videostrip_core

videostrip_cli/config_loader.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <yaml-cpp/yaml.h>
55

66
#include <videostrip_cli/config_loader.hpp>
7+
#include <videostrip_core/enhance_yaml.hpp>
78

89
namespace fs = std::filesystem;
910

@@ -98,6 +99,32 @@ bool load_yaml_config(const std::string& yaml_path,
9899
}
99100
normalize_output_paths(out, chosen_base);
100101

102+
// enhancement.* (authoritative block)
103+
// YAML::Node root["enhance"] is the standard way to access it.
104+
// We still delegate parsing to core to avoid duplicating schema rules. For that we use the parseEnhanceConfig function.
105+
std::string enhance_err;
106+
auto enhance_cfg = videostrip::parseEnhanceConfig(root, enhance_err); // return type std::optional<EnhanceConfig>
107+
if (!enhance_cfg.has_value()) {
108+
// Check if the node was present but malformed
109+
if (root["enhance"]) {
110+
err = "Failed to parse enhance config: " + enhance_err;
111+
return false;
112+
}
113+
}
114+
else{
115+
out.enhance = *enhance_cfg; //copy the content of the optional to the out config
116+
}
117+
118+
// Check if we received the legacy flag for apply_enhancement
119+
if (out.apply_enhancement && !root["enhance"]) {
120+
// If so, enable a default enhancement sequence (GrayWorldWB)
121+
out.enhance.enable = true;
122+
// clear the sequence if any (should be empty anyway)
123+
out.enhance.sequence.clear();
124+
out.enhance.sequence.push_back({EnhanceType::GrayWorldWB, GrayWorldParams{}});
125+
out.enhance.sequence.push_back({ EnhanceType::CLAHE, ClaheParams{2.0, {8,8}, ClaheSpace::YCrCb} });
126+
}
127+
101128
err.clear();
102129
return true;
103130
}
@@ -164,6 +191,7 @@ void merge_yaml_into(ExtractorConfig& dst, const ExtractorConfig& y)
164191
dst.apply_enhancement = y.apply_enhancement;
165192
dst.create_output_dirs = y.create_output_dirs;
166193
dst.enable_logging = y.enable_logging;
194+
dst.enhance = y.enhance;
167195

168196
// Optional future field:
169197
// if (!y.overlap_mode.empty()) dst.overlap_mode = y.overlap_mode;

videostrip_cli/videostrip_cli.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@ int main(int argc, char *argv[])
7171
if (config_file) {
7272
std::string err;
7373
ExtractorConfig y;
74+
// Show in console we are loading a config
75+
std::cout << "Loading config from " << args::get(config_file) << "\n";
7476
if (!videostrip::cli::load_yaml_config(args::get(config_file), y, err)) {
7577
std::cerr << "Config error: " << err << "\n";
7678
return 2;
7779
}
78-
videostrip::cli::merge_yaml_into(config, y);
80+
// TODO: Reevaluate either setting flags as optional (otherwise we can't tell if user set them or end using defaults)
81+
// Or just avoid merging and always let CLI override YAML if present
82+
videostrip::cli::merge_yaml_into(config, y); // Ensure 'config' is passed by reference in the function definition
7983
}
8084

8185
// Input (CLI > YAML)
@@ -202,7 +206,7 @@ int main(int argc, char *argv[])
202206
});
203207

204208
// Run
205-
const bool ok = extractor.run();
209+
const bool ok = extractor.run(); ///< this will run main videostrip_core loop: VideoFrameExtractor::run()
206210
std::cout << std::endl;
207211
if (!ok)
208212
{

0 commit comments

Comments
 (0)