Skip to content

Commit 119951c

Browse files
opencv-mark: add vision parity comparison mode (#20)
* opencv-mark: add vision parity comparison mode Co-authored-by: Cursor <cursoragent@cursor.com> * Bump openvx-mark version to 1.1.0 Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f8bab4f commit 119951c

12 files changed

Lines changed: 166 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66

77
## [Unreleased]
88

9+
## [1.1.0] — OpenCV parity comparisons
10+
911
### Added — CI fairness, accuracy & timing audit
1012

1113
A single PR that closes the headline credibility gap surfaced when

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ if(NOT DEFINED CMAKE_CXX_COMPILER AND EXISTS "${ROCM_PATH}/lib/llvm/bin/amdclang
4242
set(CMAKE_CXX_COMPILER ${ROCM_PATH}/lib/llvm/bin/amdclang++)
4343
endif()
4444

45-
project(openvx-mark VERSION 1.0.0)
45+
project(openvx-mark VERSION 1.1.0)
4646

4747
# Git commit SHA for version tracking
4848
execute_process(

include/benchmark_config.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,29 @@ inline const std::map<std::string, Resolution>& getResolutionPresets() {
2323
return presets;
2424
}
2525

26+
inline const std::vector<std::string>& getVisionParityKernels() {
27+
static const std::vector<std::string> kernels = {
28+
// Pixel-wise
29+
"And", "Or", "Xor", "Not", "AbsDiff", "Add", "Subtract", "Multiply",
30+
// Filters
31+
"Box3x3", "Gaussian3x3", "Median3x3", "Erode3x3", "Dilate3x3",
32+
"Sobel3x3", "CustomConvolution", "NonLinearFilter",
33+
// Color
34+
"ColorConvert_RGB2IYUV", "ChannelExtract", "ChannelCombine", "ConvertDepth",
35+
// Geometric
36+
"ScaleImage_Half", "WarpAffine", "WarpPerspective", "Remap",
37+
// Statistical
38+
"Histogram", "EqualizeHist", "MeanStdDev", "MinMaxLoc", "IntegralImage",
39+
// Multi-scale
40+
"GaussianPyramid", "LaplacianPyramid", "HalfScaleGaussian",
41+
// Features
42+
"CannyEdgeDetector", "HarrisCorners", "FastCorners", "OpticalFlowPyrLK",
43+
// Misc
44+
"Magnitude", "Phase", "TableLookup", "Threshold_Binary", "WeightedAverage",
45+
};
46+
return kernels;
47+
}
48+
2649
struct BenchmarkConfig {
2750
// Resolutions to test
2851
std::vector<Resolution> resolutions = {

include/system_info.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct SystemInfo {
2121
std::string vx_extensions;
2222

2323
// Benchmark version tracking
24+
std::string benchmark_tool; // e.g. "openvx-mark" or "opencv-mark"
2425
std::string benchmark_version;
2526
std::string benchmark_git_commit;
2627

opencv-mark/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
#
4747
################################################################################
4848

49-
find_package(OpenCV 4 QUIET COMPONENTS core imgproc features2d)
49+
find_package(OpenCV 4 QUIET COMPONENTS core imgproc features2d video)
5050
if(NOT OpenCV_FOUND)
5151
message(STATUS "opencv-mark: OpenCV 4 not found — skipping companion binary build.")
5252
message(STATUS " Install opencv4 (e.g. apt install libopencv-dev / brew install opencv) to enable.")

opencv-mark/include/opencv_runner.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ struct CaseBuffers {
5050
cv::Mat input_extra; // optional second input (e.g. warp matrix)
5151
cv::Mat output;
5252
cv::Mat output_extra; // optional second output (e.g. Sobel dy)
53+
std::vector<cv::Point2f> points_prev; // optional feature tracks
54+
std::vector<cv::Point2f> points_next;
55+
std::vector<unsigned char> status;
56+
std::vector<float> error;
5357
};
5458

5559
struct OpenCVBenchmarkCase {

opencv-mark/src/benchmarks/cv_feature.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@
2020
// dominant cost is the per-pixel detector loop; output extraction
2121
// is sub-dominant. We pass nonmaxSuppression=true to match
2222
// openvx-mark's vxFastCornersNode default.
23+
// * OpticalFlowPyrLK: cv::calcOpticalFlowPyrLK is timed with the same
24+
// 9x9 window, four pyramid levels, 5 iterations, and 0.01 epsilon
25+
// used by openvx-mark's vxOpticalFlowPyrLKNode case.
2326

27+
#include "benchmark_config.h"
2428
#include "opencv_runner.h"
2529
#include <opencv2/features2d.hpp>
2630
#include <opencv2/imgproc.hpp>
31+
#include <opencv2/video/tracking.hpp>
2732
#include <vector>
2833

2934
namespace opencv_mark {
@@ -121,6 +126,54 @@ std::vector<OpenCVBenchmarkCase> registerCvFeatureBenchmarks() {
121126
cases.push_back(bc);
122127
}
123128

129+
// OpticalFlowPyrLK — U8 image pair with 100 tracked points.
130+
{
131+
OpenCVBenchmarkCase bc;
132+
bc.name = "OpticalFlowPyrLK";
133+
bc.category = "feature";
134+
bc.feature_set = "vision";
135+
bc.setup_fn = [](uint32_t w, uint32_t h, OpenCVTestData& gen, CaseBuffers& bufs) -> bool {
136+
bufs.input = gen.makeU8(w, h);
137+
bufs.input_extra = gen.makeU8(w, h);
138+
bufs.points_prev.clear();
139+
bufs.points_prev.reserve(100);
140+
for (int i = 0; i < 100; ++i) {
141+
bufs.points_prev.emplace_back(static_cast<float>((i % 10) * (w / 10)),
142+
static_cast<float>((i / 10) * (h / 10)));
143+
}
144+
bufs.points_next.resize(bufs.points_prev.size());
145+
bufs.status.resize(bufs.points_prev.size());
146+
bufs.error.resize(bufs.points_prev.size());
147+
return true;
148+
};
149+
bc.run_fn = [](CaseBuffers& bufs) {
150+
cv::calcOpticalFlowPyrLK(
151+
bufs.input, bufs.input_extra, bufs.points_prev, bufs.points_next,
152+
bufs.status, bufs.error,
153+
cv::Size(DEFAULT_OPTFLOW_WINSIZE, DEFAULT_OPTFLOW_WINSIZE),
154+
DEFAULT_PYRAMID_LEVELS - 1,
155+
cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 5, 0.01));
156+
};
157+
bc.verify_fn = []() -> bool {
158+
cv::Mat img1(64, 64, CV_8UC1, cv::Scalar(100));
159+
cv::Mat img2(64, 64, CV_8UC1, cv::Scalar(100));
160+
std::vector<cv::Point2f> prev;
161+
for (int i = 0; i < 4; ++i) {
162+
prev.emplace_back(static_cast<float>(16 + (i % 2) * 32),
163+
static_cast<float>(16 + (i / 2) * 32));
164+
}
165+
std::vector<cv::Point2f> next;
166+
std::vector<unsigned char> status;
167+
std::vector<float> error;
168+
cv::calcOpticalFlowPyrLK(
169+
img1, img2, prev, next, status, error,
170+
cv::Size(5, 5), 1,
171+
cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 5, 0.01));
172+
return status.size() == prev.size();
173+
};
174+
cases.push_back(bc);
175+
}
176+
124177
return cases;
125178
}
126179

opencv-mark/src/benchmarks/cv_filters.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
// * `cv::filter2D` for CustomConvolution uses a 3x3 CV_16S kernel
2828
// identical to the one openvx-mark feeds vxConvolveNode, with
2929
// ddepth=CV_8U, anchor at centre, delta=0, BORDER_REPLICATE.
30+
// * `NonLinearFilter` is the OpenVX 1.1 median nonlinear-filter case
31+
// with a 3x3 all-ones mask. OpenCV's direct equivalent is another
32+
// `cv::medianBlur(..., ksize=3)` row, kept under the OpenVX kernel
33+
// name so the 41-kernel parity preset has a one-to-one join target.
3034

3135
#include "opencv_runner.h"
3236
#include "opencv_verify.h"
@@ -219,6 +223,29 @@ std::vector<OpenCVBenchmarkCase> registerCvFilterBenchmarks() {
219223
cases.push_back(bc);
220224
}
221225

226+
// NonLinearFilter — U8 in, U8 out, median mode with 3x3 all-ones mask.
227+
{
228+
OpenCVBenchmarkCase bc;
229+
bc.name = "NonLinearFilter";
230+
bc.category = "filters";
231+
bc.feature_set = "vision";
232+
bc.setup_fn = [](uint32_t w, uint32_t h, OpenCVTestData& gen, CaseBuffers& bufs) -> bool {
233+
bufs.input = gen.makeU8(w, h);
234+
bufs.output.create(static_cast<int>(h), static_cast<int>(w), CV_8UC1);
235+
return true;
236+
};
237+
bc.run_fn = [](CaseBuffers& bufs) {
238+
cv::medianBlur(bufs.input, bufs.output, /*ksize=*/3);
239+
};
240+
bc.verify_fn = []() -> bool {
241+
cv::Mat in(64, 64, CV_8UC1, cv::Scalar(100));
242+
cv::Mat out;
243+
cv::medianBlur(in, out, 3);
244+
return out.at<uint8_t>(32, 32) == 100;
245+
};
246+
cases.push_back(bc);
247+
}
248+
222249
return cases;
223250
}
224251

opencv-mark/src/main.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ void printUsage(const char* prog) {
3939
printf("Linked against OpenCV %s\n\n", OPENCV_VERSION_STRING);
4040

4141
printf("Benchmark Selection:\n");
42+
printf(" --vision-parity Run one apples-to-apples row for each of the\n");
43+
printf(" 41 OpenVX vision kernels\n");
4244
printf(" --feature-set SET[,SET,...] Currently only 'vision' is supported\n");
4345
printf(" --category CAT[,CAT,...] Filter by category (filters,color,geometric,\n");
4446
printf(" pixelwise,statistical,misc,multiscale,feature)\n");
@@ -103,6 +105,9 @@ bool parseArgs(int argc, char* argv[], BenchmarkConfig& config) {
103105
// parse failures (mapped to exit 1 by main).
104106
printUsage(argv[0]);
105107
std::exit(0);
108+
} else if (arg == "--vision-parity") {
109+
config.feature_sets = {"vision"};
110+
config.kernels = getVisionParityKernels();
106111
} else if (arg == "--feature-set" && i + 1 < argc) {
107112
// PR1 only supports the "vision" feature set on the OpenCV
108113
// side; "framework" and "enhanced_vision" are intentionally
@@ -277,6 +282,7 @@ int main(int argc, char* argv[]) {
277282
sys_info.vx_vendor_id = cv_ctx.vendorId();
278283
sys_info.vx_version = cv_ctx.version();
279284
sys_info.vx_extensions = cv_ctx.buildOptions();
285+
sys_info.benchmark_tool = "opencv-mark";
280286
sys_info.benchmark_version = OPENCV_MARK_VERSION;
281287
sys_info.benchmark_git_commit = GIT_COMMIT_SHA;
282288
// Build + threading provenance. cv::getNumThreads() is captured AFTER

scripts/compare_reports.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ def get_impl_name(report, path='unknown'):
4848
return report.get('openvx', {}).get('implementation', os.path.basename(path))
4949

5050

51+
def get_benchmark_tool(report):
52+
return report.get('benchmark', {}).get('tool', '')
53+
54+
55+
def is_opencv_report(report):
56+
tool = get_benchmark_tool(report).lower()
57+
impl = report.get('openvx', {}).get('implementation', '').lower()
58+
return tool == 'opencv-mark' or 'opencv' in impl
59+
60+
5161
def build_result_map(report):
5262
"""Build a dict keyed by (name, mode, resolution) -> result"""
5363
result_map = {}
@@ -89,6 +99,18 @@ def compare(reports, paths):
8999
return impl_names, result_maps, all_keys, system_infos
90100

91101

102+
def normalize_report_order(reports, paths):
103+
"""Use OpenCV as baseline when comparing exactly one OpenCV and one non-OpenCV report."""
104+
if len(reports) != 2:
105+
return reports, paths
106+
107+
first_is_opencv = is_opencv_report(reports[0])
108+
second_is_opencv = is_opencv_report(reports[1])
109+
if second_is_opencv and not first_is_opencv:
110+
return [reports[1], reports[0]], [paths[1], paths[0]]
111+
return reports, paths
112+
113+
92114
def write_markdown(impl_names, result_maps, all_keys, output_path, reports, system_infos=None):
93115
with open(output_path + '.md', 'w') as f:
94116
f.write('# OpenVX Benchmark Comparison\n\n')
@@ -562,6 +584,7 @@ def main():
562584
sys.exit(1)
563585
reports.append(load_report(path))
564586

587+
reports, args.reports = normalize_report_order(reports, args.reports)
565588
impl_names, result_maps, all_keys, system_infos = compare(reports, args.reports)
566589

567590
write_markdown(impl_names, result_maps, all_keys, args.output, reports, system_infos)

0 commit comments

Comments
 (0)