Files
2026-08-17 06:31:43 +09:00

547 lines
28 KiB
C++

#ifndef ZCPPWIN_ZCV_H
#define ZCPPWIN_ZCV_H
/*
* zcv.h
* -----------------------------------------------------------------------------
* 基于 OpenCV 4.x / opencv_contrib 4.x 的常用计算机视觉工具库声明文件。
*
* 设计目标:
* 1. 将 OpenCV 中高频使用的图像、视频、特征、DNN、二维码/条码、ArUco、标定等
* 操作整理成统一的 ZCV 命名空间,减少项目中的重复样板代码。
* 2. 保持“薄封装”原则:函数内部仍然使用 OpenCV 原生数据结构,如 cv::Mat、cv::Rect、
* cv::Point2f、cv::dnn::Net,便于和既有 OpenCV 代码互操作。
* 3. 尽量兼容 OpenCV 4.x 的不同小版本。对 contrib 中可能不存在或 API 变化较大的模块,
* 使用 __has_include 与版本宏做条件编译。
*
* 使用方式:
* #include "zcv.h"
* cv::Mat img = ZCV::readImage("test.jpg");
* cv::Mat gray = ZCV::ensureGray(img);
* cv::Mat edges = ZCV::canny(gray, 80, 160);
*
* 编译提示:
* CMake 中通常写法:
* find_package(OpenCV 4 REQUIRED)
* add_library(zcv zcv.cpp)
* target_include_directories(zcv PUBLIC ${OpenCV_INCLUDE_DIRS})
* target_link_libraries(zcv PUBLIC ${OpenCV_LIBS})
*/
#include <opencv2/core.hpp>
#include <opencv2/core/version.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio.hpp>
// #include <opencv2/video.hpp>
#include <opencv2/calib3d.hpp>
// #include <opencv2/features2d.hpp>
// #include <opencv2/objdetect.hpp>
#include <opencv2/dnn.hpp>
// #include <opencv2/photo.hpp>
// #include <algorithm>
#include <cstdint>
// #include <limits>
// #include <map>
// #include <stdexcept>
#include <string>
// #include <utility>
#include <vector>
// ---------------------------- 可选 contrib 模块检测 ----------------------------
// ArUco 在 OpenCV 4.x 的不同小版本中经历过头文件与 API 调整;优先使用传统 aruco.hpp,
// 若新版本只提供 objdetect 下的头文件,也尝试包含。
#if __has_include(<opencv2/aruco.hpp>)
#include <opencv2/aruco.hpp>
#define ZCV_HAS_ARUCO 1
#elif __has_include(<opencv2/objdetect/aruco_detector.hpp>) && __has_include(<opencv2/objdetect/aruco_dictionary.hpp>)
#include <opencv2/objdetect/aruco_detector.hpp>
#include <opencv2/objdetect/aruco_dictionary.hpp>
#define ZCV_HAS_ARUCO 1
#else
#define ZCV_HAS_ARUCO 0
#endif
// barcode 模块属于 contrib/objdetect 体系,不同版本头文件位置可能不同。
#if __has_include(<opencv2/objdetect/barcode.hpp>)
#include <opencv2/objdetect/barcode.hpp>
#define ZCV_HAS_BARCODE 1
#elif __has_include(<opencv2/barcode.hpp>)
#include <opencv2/barcode.hpp>
#define ZCV_HAS_BARCODE 1
#else
#define ZCV_HAS_BARCODE 0
#endif
// SIFT 在较新的 OpenCV 4.x 中位于 features2d;较旧版本可能在 xfeatures2d。
#if (CV_VERSION_MAJOR > 4) || (CV_VERSION_MAJOR == 4 && CV_VERSION_MINOR >= 4)
#define ZCV_HAS_SIFT 1
#elif __has_include(<opencv2/xfeatures2d.hpp>)
#include <opencv2/xfeatures2d.hpp>
#define ZCV_HAS_SIFT 1
#define ZCV_SIFT_IN_XFEATURES2D 1
#else
#define ZCV_HAS_SIFT 0
#endif
namespace ZCV {
// =============================================================================
// 1. 基础枚举与数据结构
// =============================================================================
/** 图像缩放模式。 */
enum class ResizeMode {
Stretch, ///< 直接拉伸到目标尺寸,可能改变宽高比。
KeepAspectFit, ///< 保持宽高比完整放入目标画布,不足区域填充颜色,类似 letterbox。
KeepAspectFill ///< 保持宽高比并填满目标尺寸,多余区域居中裁剪。
};
/** 常用形态学操作类型。 */
enum class MorphType {
Erode, ///< 腐蚀:缩小白色前景区域。
Dilate, ///< 膨胀:扩大白色前景区域。
Open, ///< 开运算:先腐蚀后膨胀,常用于去除小噪声。
Close, ///< 闭运算:先膨胀后腐蚀,常用于填补小孔洞。
Gradient, ///< 形态学梯度:突出目标边界。
TopHat, ///< 顶帽:原图 - 开运算结果,突出小亮区域。
BlackHat, ///< 黑帽:闭运算结果 - 原图,突出小暗区域。
HitMiss ///< 击中击不中,仅适用于二值单通道图像。
};
/** 90 度倍数旋转模式。 */
enum class RotateMode {
Clockwise90, ///< 顺时针旋转 90 度。
CounterClockwise90, ///< 逆时针旋转 90 度。
Rotate180 ///< 旋转 180 度。
};
/** 图像基本信息,便于调试日志输出。 */
struct ImageInfo {
bool empty = true; ///< cv::Mat 是否为空。
int width = 0; ///< 图像宽度,单位:像素。
int height = 0; ///< 图像高度,单位:像素。
int channels = 0; ///< 通道数。
int depth = -1; ///< OpenCV 深度,例如 CV_8U、CV_32F。
int type = -1; ///< OpenCV 完整类型,例如 CV_8UC3。
std::string typeString; ///< 便于阅读的类型字符串,例如 "CV_8UC3"。
};
/** letterbox 缩放的返回结果。 */
struct LetterboxResult {
cv::Mat image; ///< 输出图像,尺寸等于 targetSize。
double scale = 1.0; ///< 原图到缩放图的比例。
int padLeft = 0; ///< 左侧填充像素数。
int padTop = 0; ///< 顶部填充像素数。
cv::Rect contentRect; ///< 原图缩放后在目标画布中的区域。
};
/** 轮廓摘要信息。 */
struct ContourInfo {
std::vector<cv::Point> contour; ///< 原始轮廓点。
double area = 0.0; ///< 轮廓面积,来自 cv::contourArea。
double perimeter = 0.0; ///< 轮廓周长,来自 cv::arcLength。
cv::Rect boundingBox; ///< 水平外接矩形。
cv::RotatedRect rotatedBox; ///< 最小面积旋转外接矩形。
cv::Point2f center; ///< 根据矩计算出的质心;矩退化时取外接框中心。
float enclosingRadius = 0.0f; ///< 最小外接圆半径。
double circularity = 0.0; ///< 圆形度,4*pi*area/perimeter^2,越接近 1 越圆。
};
/** 连通域摘要信息。 */
struct ComponentInfo {
int label = -1; ///< 连通域标签,0 通常是背景。
int area = 0; ///< 像素面积。
cv::Rect boundingBox; ///< 外接矩形。
cv::Point2d centroid; ///< 质心。
};
/** 特征检测结果。 */
struct FeatureResult {
std::vector<cv::KeyPoint> keypoints; ///< 关键点列表。
cv::Mat descriptors; ///< 描述子矩阵。
};
/** 两幅图像特征匹配结果。 */
struct MatchResult {
FeatureResult first; ///< 第一幅图像特征。
FeatureResult second; ///< 第二幅图像特征。
std::vector<cv::DMatch> matches; ///< 通过筛选的匹配。
cv::Mat homography; ///< 若启用单应性估计,则保存 3x3 H 矩阵。
std::vector<char> inlierMask; ///< RANSAC 内点掩码,与 matches 一一对应。
int inlierCount = 0; ///< 内点数量。
};
/** 棋盘格角点检测结果。 */
struct ChessboardResult {
bool found = false; ///< 是否成功找到所有内角点。
cv::Size boardSize; ///< 棋盘格内角点数量,width x height。
std::vector<cv::Point2f> corners; ///< 角点坐标。
cv::Mat debugImage; ///< 绘制角点后的调试图。
};
/** 相机标定结果。 */
struct CalibrationResult {
bool ok = false; ///< 标定是否执行成功。
double reprojectionError = 0.0; ///< cv::calibrateCamera 返回的重投影误差。
cv::Mat cameraMatrix; ///< 3x3 内参矩阵。
cv::Mat distCoeffs; ///< 畸变系数。
std::vector<cv::Mat> rvecs; ///< 每张标定图的旋转向量。
std::vector<cv::Mat> tvecs; ///< 每张标定图的平移向量。
std::size_t usedImageCount = 0; ///< 实际参与标定的图片数量。
};
/** 视频文件或摄像头的摘要信息。 */
struct VideoInfo {
bool opened = false; ///< 是否成功打开。
int width = 0; ///< 帧宽。
int height = 0; ///< 帧高。
double fps = 0.0; ///< 帧率;摄像头或部分文件可能返回 0。
int frameCount = 0; ///< 总帧数;实时流可能返回 0 或不可靠值。
double durationSec = 0.0; ///< 估算时长,frameCount / fps。
std::string backendName; ///< OpenCV 使用的视频后端名称。
};
/** QRCodeDetector 单个二维码识别结果。 */
struct QRCodeResult {
bool success = false; ///< 是否检测并解码出非空字符串。
std::string text; ///< 解码文本,OpenCV 通常返回 UTF-8 字符串。
std::vector<cv::Point2f> points; ///< 二维码四个角点。
cv::Mat straight; ///< 透视校正后的二维码图像,可为空。
};
#if ZCV_HAS_ARUCO
/** ArUco marker 检测结果。 */
struct ArucoResult {
std::vector<int> ids; ///< marker ID 列表。
std::vector<std::vector<cv::Point2f>> corners; ///< 每个 marker 的四个角点。
std::vector<std::vector<cv::Point2f>> rejected; ///< 被拒绝的候选区域,便于调参。
};
#endif
#if ZCV_HAS_BARCODE
/** 条形码检测与解码结果。 */
struct BarcodeResult {
bool success = false; ///< 是否成功解码。
std::string text; ///< 解码文本。
std::string type; ///< 条码类型,如 EAN_13、CODE_128 等,具体取决于 OpenCV。
std::vector<cv::Point2f> points; ///< 条码旋转矩形四个点;可能因版本差异为空。
};
#endif
/** DNN 前处理参数。 */
struct DnnOptions {
cv::Size inputSize; ///< 网络输入尺寸;空尺寸表示不强制 resize。
double scale = 1.0; ///< 像素缩放因子,例如 1/255.0。
cv::Scalar mean = cv::Scalar(); ///< 均值,按 BGR 或 RGB 顺序取决于 swapRB。
bool swapRB = false; ///< 是否交换 R/B 通道,常用于 BGR -> RGB。
bool crop = false; ///< blobFromImage 是否中心裁剪。
int ddepth = CV_32F; ///< blob 数据深度,一般为 CV_32F。
int backend = cv::dnn::DNN_BACKEND_OPENCV; ///< 推理后端。
int target = cv::dnn::DNN_TARGET_CPU; ///< 推理目标设备。
std::string inputName; ///< setInput 的输入层名称,通常留空。
};
/** DNN 推理结果。 */
struct DnnForwardResult {
std::vector<cv::Mat> outputs; ///< 网络输出 blob 列表。
double inferenceMs = 0.0; ///< 单次 forward 耗时,单位毫秒。
};
/** 分类分数。 */
struct ClassScore {
int classId = -1; ///< 类别索引。
float score = 0.0f; ///< 置信度或概率。
};
/** 简单计时器,基于 cv::TickMeter。 */
class TickTimer {
public:
void start(); ///< 清零并开始计时。
double stopMs(); ///< 停止计时并返回毫秒数。
double elapsedMs() const; ///< 返回当前累计毫秒数。
private:
cv::TickMeter meter_;
};
// =============================================================================
// 2. 基础信息、类型转换与安全检查
// =============================================================================
std::string opencvVersionString();
std::string matTypeToString(int type);
ImageInfo getImageInfo(const cv::Mat& image);
bool isGray(const cv::Mat& image);
bool isBgrLike(const cv::Mat& image);
bool hasAlpha(const cv::Mat& image);
void assertNotEmpty(const cv::Mat& image, const std::string& name = "image");
cv::Mat ensureGray(const cv::Mat& image);
cv::Mat ensureBgr(const cv::Mat& image);
cv::Mat ensureBgra(const cv::Mat& image);
cv::Mat to8U(const cv::Mat& image, bool normalize = true, double alpha = 1.0, double beta = 0.0);
cv::Mat toFloat32(const cv::Mat& image, double scale = 1.0 / 255.0);
// =============================================================================
// 3. 图像读写、显示与基础矩阵操作
// =============================================================================
cv::Mat readImage(const std::string& path, int flags = cv::IMREAD_COLOR, bool throwIfEmpty = true);
bool saveImage(const std::string& path, const cv::Mat& image,
const std::vector<int>& params = std::vector<int>());
std::vector<cv::Mat> readImages(const std::vector<std::string>& paths,
int flags = cv::IMREAD_COLOR,
bool skipFailed = true);
void showImage(const std::string& windowName, const cv::Mat& image,
int delayMs = 0, int windowFlags = cv::WINDOW_AUTOSIZE,
bool destroyAfter = false);
cv::Rect clampRect(const cv::Rect& roi, const cv::Size& imageSize);
cv::Mat crop(const cv::Mat& image, const cv::Rect& roi, bool safe = true, bool clone = true);
cv::Mat centerCrop(const cv::Mat& image, const cv::Size& size, bool clone = true);
std::vector<cv::Mat> splitChannels(const cv::Mat& image);
cv::Mat mergeChannels(const std::vector<cv::Mat>& channels);
cv::Mat stackHorizontal(const std::vector<cv::Mat>& images, int gap = 0,
const cv::Scalar& background = cv::Scalar(0, 0, 0));
cv::Mat stackVertical(const std::vector<cv::Mat>& images, int gap = 0,
const cv::Scalar& background = cv::Scalar(0, 0, 0));
cv::Mat makeGrid(const std::vector<cv::Mat>& images, int columns,
cv::Size cellSize = cv::Size(), int gap = 0,
const cv::Scalar& background = cv::Scalar(0, 0, 0));
// =============================================================================
// 4. 几何变换
// =============================================================================
cv::Mat resizeTo(const cv::Mat& image, const cv::Size& targetSize,
ResizeMode mode = ResizeMode::Stretch,
int interpolation = cv::INTER_LINEAR,
const cv::Scalar& borderColor = cv::Scalar(114, 114, 114));
cv::Mat resizeScale(const cv::Mat& image, double fx, double fy = 0.0,
int interpolation = cv::INTER_LINEAR);
LetterboxResult letterbox(const cv::Mat& image, const cv::Size& targetSize,
const cv::Scalar& color = cv::Scalar(114, 114, 114),
bool allowScaleUp = true,
int interpolation = cv::INTER_LINEAR);
cv::Mat rotate90(const cv::Mat& image, RotateMode mode);
cv::Mat rotateAngle(const cv::Mat& image, double angleDeg, double scale = 1.0,
bool expand = true,
const cv::Scalar& borderValue = cv::Scalar(0, 0, 0));
cv::Mat translate(const cv::Mat& image, double dx, double dy,
const cv::Scalar& borderValue = cv::Scalar(0, 0, 0));
std::vector<cv::Point2f> orderQuadPoints(const std::vector<cv::Point2f>& points);
cv::Mat fourPointTransform(const cv::Mat& image, const std::vector<cv::Point2f>& points,
int interpolation = cv::INTER_LINEAR);
cv::Mat warpPerspectiveFromPoints(const cv::Mat& image,
const std::vector<cv::Point2f>& srcPoints,
const std::vector<cv::Point2f>& dstPoints,
const cv::Size& dstSize,
int interpolation = cv::INTER_LINEAR);
// =============================================================================
// 5. 图像增强与滤波
// =============================================================================
cv::Mat adjustBrightnessContrast(const cv::Mat& image, double alpha = 1.0, double beta = 0.0);
cv::Mat gammaCorrect(const cv::Mat& image, double gamma);
cv::Mat equalizeGray(const cv::Mat& image);
cv::Mat clahe(const cv::Mat& image, double clipLimit = 2.0,
cv::Size tileGridSize = cv::Size(8, 8));
cv::Mat normalizeMinMax(const cv::Mat& image, double alpha = 0.0, double beta = 255.0,
int dtype = -1);
cv::Mat grayWorldWhiteBalance(const cv::Mat& image);
cv::Mat sharpen(const cv::Mat& image);
cv::Mat unsharpMask(const cv::Mat& image, double amount = 1.0, double sigma = 1.0);
cv::Mat denoise(const cv::Mat& image, float h = 10.0f, float hColor = 10.0f,
int templateWindowSize = 7, int searchWindowSize = 21);
cv::Mat applyColorMapToGray(const cv::Mat& image, int colorMap = cv::COLORMAP_JET);
cv::Mat meanBlur(const cv::Mat& image, int ksize = 3);
cv::Mat gaussianBlur(const cv::Mat& image, int ksize = 5, double sigmaX = 0.0);
cv::Mat medianBlur(const cv::Mat& image, int ksize = 5);
cv::Mat bilateral(const cv::Mat& image, int d = 9, double sigmaColor = 75.0, double sigmaSpace = 75.0);
cv::Mat sobelEdges(const cv::Mat& image, int dx = 1, int dy = 0, int ksize = 3);
cv::Mat laplacianEdges(const cv::Mat& image, int ksize = 3);
cv::Mat calcGrayHist(const cv::Mat& image, int histSize = 256, float rangeMin = 0.0f, float rangeMax = 256.0f);
cv::Mat drawGrayHist(const cv::Mat& hist, cv::Size canvasSize = cv::Size(512, 300),
const cv::Scalar& color = cv::Scalar(255, 255, 255));
// =============================================================================
// 6. 二值化、边缘、形态学与距离变换
// =============================================================================
cv::Mat thresholdBinary(const cv::Mat& image, double thresh, double maxVal = 255.0,
bool inverse = false);
cv::Mat thresholdOtsu(const cv::Mat& image, double maxVal = 255.0, bool inverse = false);
cv::Mat adaptiveThresholdBinary(const cv::Mat& image, int blockSize = 31, double C = 5.0,
bool inverse = false,
int method = cv::ADAPTIVE_THRESH_GAUSSIAN_C);
cv::Mat canny(const cv::Mat& image, double threshold1, double threshold2,
int apertureSize = 3, bool L2gradient = false);
cv::Mat morph(const cv::Mat& image, MorphType type, int kernelSize = 3, int iterations = 1,
int shape = cv::MORPH_RECT);
cv::Mat erodeImage(const cv::Mat& image, int kernelSize = 3, int iterations = 1,
int shape = cv::MORPH_RECT);
cv::Mat dilateImage(const cv::Mat& image, int kernelSize = 3, int iterations = 1,
int shape = cv::MORPH_RECT);
cv::Mat fillHoles(const cv::Mat& binaryImage);
cv::Mat distanceTransformBinary(const cv::Mat& binaryImage, int distanceType = cv::DIST_L2,
int maskSize = 3);
// =============================================================================
// 7. 轮廓、连通域与几何度量
// =============================================================================
std::vector<ContourInfo> findContoursInfo(const cv::Mat& binaryImage,
int retrievalMode = cv::RETR_EXTERNAL,
int approximation = cv::CHAIN_APPROX_SIMPLE,
double minArea = 0.0,
bool sortByAreaDesc = true);
bool largestContour(const cv::Mat& binaryImage, ContourInfo& outInfo, double minArea = 0.0);
cv::Mat drawContourInfos(const cv::Mat& image, const std::vector<ContourInfo>& contours,
const cv::Scalar& color = cv::Scalar(0, 255, 0),
int thickness = 2, bool drawBoundingBox = true,
bool drawIndex = false);
cv::Mat contourMask(const cv::Size& size, const std::vector<cv::Point>& contour,
uchar fillValue = 255);
std::vector<ComponentInfo> connectedComponentsInfo(const cv::Mat& binaryImage,
int connectivity = 8,
int minArea = 1);
cv::Mat drawComponents(const cv::Mat& labels, int labelCount);
double intersectionOverUnion(const cv::Rect2f& a, const cv::Rect2f& b);
cv::Scalar randomColor(int seed, int minValue = 40, int maxValue = 255);
// =============================================================================
// 8. 特征点、描述子与匹配
// =============================================================================
FeatureResult detectORB(const cv::Mat& image, int nfeatures = 1000);
FeatureResult detectAKAZE(const cv::Mat& image);
#if ZCV_HAS_SIFT
FeatureResult detectSIFT(const cv::Mat& image, int nfeatures = 0);
#endif
std::vector<cv::DMatch> matchDescriptors(const cv::Mat& desc1, const cv::Mat& desc2,
int normType = -1,
bool crossCheck = false,
double ratio = 0.75,
int maxMatches = 0);
MatchResult matchORB(const cv::Mat& image1, const cv::Mat& image2,
int nfeatures = 1500, double ratio = 0.75,
bool estimateHomography = true,
double ransacReprojThreshold = 3.0);
cv::Mat drawMatchResult(const cv::Mat& image1, const cv::Mat& image2,
const MatchResult& result,
bool onlyInliers = true);
// =============================================================================
// 9. 相机标定、畸变校正与传统目标检测
// =============================================================================
ChessboardResult findChessboard(const cv::Mat& image, cv::Size boardSize,
bool refineSubPix = true,
int flags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE);
CalibrationResult calibrateCameraFromChessboards(const std::vector<cv::Mat>& images,
cv::Size boardSize,
float squareSize,
int flags = 0);
CalibrationResult calibrateCameraFromChessboardFiles(const std::vector<std::string>& imagePaths,
cv::Size boardSize,
float squareSize,
int flags = 0);
cv::Mat undistortImage(const cv::Mat& image, const cv::Mat& cameraMatrix,
const cv::Mat& distCoeffs, double alpha = 0.0);
std::vector<cv::Rect> detectHaar(const cv::Mat& image, const std::string& cascadePath,
double scaleFactor = 1.1, int minNeighbors = 3,
cv::Size minSize = cv::Size(), cv::Size maxSize = cv::Size());
cv::Mat drawRects(const cv::Mat& image, const std::vector<cv::Rect>& rects,
const cv::Scalar& color = cv::Scalar(0, 255, 0), int thickness = 2);
// =============================================================================
// 10. DNN:模型加载、blob 前处理、推理与后处理辅助
// =============================================================================
cv::dnn::Net loadDnnNet(const std::string& modelPath,
const std::string& configPath = std::string(),
const std::string& framework = std::string(),
int backend = cv::dnn::DNN_BACKEND_OPENCV,
int target = cv::dnn::DNN_TARGET_CPU);
cv::Mat makeBlob(const cv::Mat& image, const DnnOptions& options);
DnnForwardResult forwardDnn(cv::dnn::Net& net, const cv::Mat& image,
const DnnOptions& options,
const std::vector<std::string>& outputNames = std::vector<std::string>());
std::vector<ClassScore> topK(const cv::Mat& scores, int k = 5);
std::vector<int> nmsBoxes(const std::vector<cv::Rect>& boxes,
const std::vector<float>& confidences,
float scoreThreshold = 0.25f,
float nmsThreshold = 0.45f,
float eta = 1.0f,
int topK = 0);
// =============================================================================
// 11. 视频读取、写入与帧抽取
// =============================================================================
VideoInfo getCaptureInfo(cv::VideoCapture& cap);
cv::VideoCapture openVideo(const std::string& path, int apiPreference = cv::CAP_ANY,
bool throwIfFailed = true);
cv::VideoCapture openCamera(int index = 0, int width = 0, int height = 0,
double fps = 0.0, int apiPreference = cv::CAP_ANY,
bool throwIfFailed = true);
bool readFrameAt(const std::string& videoPath, int frameIndex, cv::Mat& frame,
int apiPreference = cv::CAP_ANY);
cv::VideoWriter createVideoWriter(const std::string& path, cv::Size frameSize, double fps,
const std::string& fourcc = "MJPG", bool isColor = true,
int apiPreference = cv::CAP_ANY);
bool writeVideoFromFrames(const std::string& path, const std::vector<cv::Mat>& frames,
double fps, const std::string& fourcc = "MJPG",
bool isColor = true);
std::vector<cv::Mat> readAllFrames(const std::string& videoPath, int maxFrames = -1,
int step = 1, int apiPreference = cv::CAP_ANY);
int extractFrames(const std::string& videoPath, const std::string& outputPattern,
int step = 1, int maxFrames = -1, int startFrame = 0,
int apiPreference = cv::CAP_ANY);
// =============================================================================
// 12. 二维码、条码、ArUco 与绘制辅助
// =============================================================================
QRCodeResult detectAndDecodeQRCode(const cv::Mat& image);
cv::Mat drawQRCodeResult(const cv::Mat& image, const QRCodeResult& result,
const cv::Scalar& color = cv::Scalar(0, 255, 0));
#if ZCV_HAS_BARCODE
std::vector<BarcodeResult> detectAndDecodeBarcodes(const cv::Mat& image,
const std::string& srPrototxt = std::string(),
const std::string& srModel = std::string());
cv::Mat drawBarcodeResults(const cv::Mat& image, const std::vector<BarcodeResult>& results,
const cv::Scalar& color = cv::Scalar(255, 0, 0));
#endif
#if ZCV_HAS_ARUCO
ArucoResult detectArucoMarkers(const cv::Mat& image,
int dictionaryId = cv::aruco::DICT_4X4_50,
bool refineCorners = true);
cv::Mat drawArucoResult(const cv::Mat& image, const ArucoResult& result,
const cv::Scalar& color = cv::Scalar(0, 255, 0));
cv::Mat generateArucoMarker(int markerId, int sidePixels = 300,
int dictionaryId = cv::aruco::DICT_4X4_50,
int borderBits = 1);
#endif
void drawLabel(cv::Mat& image, const std::string& text, cv::Point origin,
double fontScale = 0.6,
const cv::Scalar& textColor = cv::Scalar(255, 255, 255),
const cv::Scalar& bgColor = cv::Scalar(0, 0, 0),
int thickness = 1);
void drawRotatedRect(cv::Mat& image, const cv::RotatedRect& rect,
const cv::Scalar& color = cv::Scalar(0, 255, 0), int thickness = 2);
void drawCrosshair(cv::Mat& image, cv::Point center, int radius = 10,
const cv::Scalar& color = cv::Scalar(0, 0, 255), int thickness = 1);
cv::Mat drawGridOverlay(const cv::Mat& image, int gridSize = 50,
const cv::Scalar& color = cv::Scalar(80, 80, 80),
int thickness = 1);
} // namespace ZCV
#endif //ZCPPWIN_ZCV_H