音视频学习笔记十七——图像处理之OpenCV进阶

88 阅读6分钟

题记:前文介绍OpenCV的基本操作,本节会继续介绍OpenCV的更多效果。图像处理需要用到很多专业的算法,本人业余学习略知皮毛,只是庶竭驽钝叙其所得,在音视频学习Demo有一些的示例。文章或代码若有错误,也希望大佬不吝赐教。

一、滤波

编码原理中介绍了滤波效果,前文中是Python代码,C++代码类似,高通滤波如下

    cv::Mat gray;
    cv::cvtColor(mat, gray, cv::COLOR_BGR2GRAY);

    // 强制对齐到偶数尺寸
    int evenRows = (gray.rows + 1) & -2;
    int evenCols = (gray.cols + 1) & -2;

    cv::Mat channelPadded;
    cv::copyMakeBorder(gray, channelPadded,
                     0, evenRows - gray.rows,
                     0, evenCols - gray.cols,
                     cv::BORDER_CONSTANT, cv::Scalar::all(0));
    cv::Mat filter = cv::Mat::ones(channelPadded.size(), CV_32F);
    for(int i=0; i<100; i++) {
        for(int j=0; j<100; j++) {
            filter.at<float>(i,j) = 0.0;
        }
    }

    // DCT变换处理
    cv::Mat dctMat;
    channelPadded.convertTo(dctMat, CV_32F);
    
    // 前向DCT变换
    cv::dct(dctMat, dctMat);
    
    // 应用DCT域滤波器
    cv::multiply(dctMat, filter, dctMat);
    
    // 逆向DCT变换
    cv::idct(dctMat, dctMat);
    
    // 转换为8位并裁剪
    cv::Mat result;
    dctMat.convertTo(result, CV_8U);
    result = result(cv::Rect(0, 0, gray.cols, gray.rows));
    cv::bitwise_not(result, result);
    return result;
原图opencv原图.jpg
高通opencv高通.jpg
低通opencv低通.jpg

二、直方图均衡

2.1 直方图均衡

直方图均衡,可以先看效果,再说原理,均衡代码如图:

cv::Mat gray, equalized;
cv::cvtColor(mat, gray, cv::COLOR_BGR2GRAY);
cv::equalizeHist(gray, equalized);

效果如图,上图是原图,下图是均衡化效果:

opencv直方图.jpg

由上图可见,在偏暗或者偏亮的环境下,色值会集中在一部分。如右边是直方图的分布,横坐标是0-255的色值,纵坐标是统计值。均衡化的目标是将直方图的分布均匀一些,如下方的图。

cv::Mat origHist, eqHist;
int histSize = 256;
float range[] = {0,256};
const float* histRange = {range};
cv::calcHist(&gray, 1, 0, cv::Mat(), origHist, 1, &histSize, &histRange);
cv::calcHist(&equalized, 1, 0, cv::Mat(), eqHist, 1, &histSize, &histRange);

2.2 均衡化原理

基本思想是把图像的直方图分布变得更均匀,图像的对比度就会提高,效果如上图。

说明公式
统计图像中每个灰度级image.png
每个灰度级的累积分布image.png
将CDF线性映射到0-255范围,生成像素值的映射表:M×N为图像总像素数,image.png
应用映射表--

上述公式其实解释起来很简单,就是把色值转换为累计分布,如下图0-4最后映射的结果。密度大的色值范围会变大,密度小的色值范围变小,从而达到均衡的目的。

原始值色01234
概率0.30.30.20.10.1
累积概率0.30.60.80.91.0
映射1.22.43.23.64.0

2.3 自适应均衡化

上述属于全图均衡化,但图片如果偏大的情况下,自适应均衡化效果会更好一些。自适应实际上是对图块划分为格子,如下代码中的 8x8大小格子。对每个格子进行均衡化处理。最后,格子之间的分界进行线性插值得到最后结果。

cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
clahe->setClipLimit(4.0);
clahe->setTilesGridSize(cv::Size(8, 8));

自适应均衡化效果:

opencv自适应均衡.jpg

三、特征匹配

3.1 特征匹配

本文主要介绍一下SIFT算法,其他改进算法还有SURF,此外还有ORB和AKAZE。SIFT广泛应用在图像匹配与拼接、物体识别与跟踪等。SIFT在目前版本已经免费可用(专利过期),opencv需要包含opencv_contrib模块,pod支持的版本不包含,需要自己编译一下。

git clone https://github.com/opencv/opencv.git # 切换到合适版本
git clone https://github.com/opencv/opencv_contrib.git # 切换到合适版本
cd opencv/platforms/ios
python build_framework.py ios --contrib ../../opencv_contrib --disable-swift

特征匹配效果(SIFT特征+暴力匹配),相关代码音视频学习Demo

特征匹配.jpg

相关代码如下:

// 初始化SIFT检测器
 auto detector = cv::SIFT::create();

 // 检测关键点和计算描述子
 std::vector<cv::KeyPoint> queryKpts, sceneKpts;
 cv::Mat queryDesc, sceneDesc;
 detector->detectAndCompute(queryMat, cv::noArray(), queryKpts, queryDesc);
 detector->detectAndCompute(sceneMat, cv::noArray(), sceneKpts, sceneDesc);

 // 特征匹配
 auto matcher = cv::BFMatcher::create(cv::NORM_L2);
 std::vector<cv::DMatch> matches;
 matcher->match(queryDesc, sceneDesc, matches);

 // 筛选最佳匹配
 std::sort(matches.begin(), matches.end());
 const int keepMatches = matches.size() * 0.05;
 std::vector<cv::DMatch> goodMatches(matches.begin(), matches.begin() + keepMatches);

 // 可视化匹配结果
 cv::Mat resultMat;

 cv::drawMatches(queryMat, queryKpts,
                sceneMat, sceneKpts,
                goodMatches, resultMat,
                cv::Scalar::all(-1), cv::Scalar::all(-1),
                std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

 return resultMat;

SIFT(Scale-Invariant Feature Transform)是计算机视觉中经典的局部特征检测与描述算法。其核心优势在于对尺度缩放、旋转、光照变化、视角变化等具有强鲁棒性。

  • 关键点检测的核心思想通过把图片模糊、缩放得到几组图片,然后通过差值得到能够识别图片特征的点。 DOG.jpg

  • 利用三维泰勒展开调整极值点位置,提高定位精度。

  • 在关键点所在尺度的高斯图像中,计算邻域像素的梯度幅值和方向。生成36-bin直方图(每10°一bin),使用σ=1.5×关键点尺度的高斯窗口加权梯度幅值。主方向为最高峰,辅方向为超过主峰80%的次峰。

  • 将坐标轴旋转至主方向,确保描述符与方向无关。这一步是方向不变的关键

  • 区域划分,16×16窗口分为4×4子区域,每个子区域计算8方向梯度直方图,形成128维向量。

3.2 匹配

关于匹配方法,一般下列几种:

3.2.1 暴力匹配(Brute-Force Matcher)

  • 原理:遍历第一幅图像的所有特征描述符,与第二幅图像的每个描述符计算距离,选择最优匹配。

  • 用途:小规模数据集、精确匹配。

  • 代码示例

    import cv2
    
    # 创建BFMatcher对象(使用汉明距离,适用于ORB、BRISK等)
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    
    # 匹配描述符
    matches = bf.match(des1, des2)
    
    # 按距离排序
    matches = sorted(matches, key=lambda x: x.distance)
    

3.2.2 快速近似最近邻(FLANN)

  • 原理:基于高维数据的近似最近邻搜索,通过树结构加速匹配。

  • 用途:大规模数据集、实时性要求高的场景。

  • 代码示例

    # 设置FLANN参数(适用于SIFT、SURF等)
    FLANN_INDEX_KDTREE = 1
    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params = dict(checks=50)  # 搜索次数
    
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)
    

3.2.3 比率测试(Ratio Test)

  • 原理:对每个关键点的两个最佳匹配计算距离比值(如0.7),过滤模糊匹配。

  • 用途:去除错误匹配,提高匹配质量。

  • 代码示例

    good_matches = []
    for m, n in matches:
        if m.distance < 0.7 * n.distance:
            good_matches.append(m)
    

3.2.4 基于几何约束的筛选(RANSAC)

  • 原理:通过随机采样一致性算法(RANSAC)估计基础矩阵或单应性矩阵,剔除不符合几何约束的匹配。

  • 用途:图像拼接、相机位置估计。

  • 代码示例

    src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1,1,2)
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1,1,2)
    
    # 使用RANSAC计算单应性矩阵
    H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    
    # 保留内点(符合几何约束的匹配)
    inlier_matches = [m for i, m in enumerate(good_matches) if mask[i]]
    

3.2.5 总结

方法优点缺点适用场景
暴力匹配精确度高计算复杂度高,速度慢小规模数据、精确匹配需求
FLANN速度快,适合高维数据近似匹配,可能存在误差大规模数据、实时性要求高
比率测试有效过滤模糊匹配可能丢失部分正确匹配提高匹配质量
RANSAC鲁棒性强,抗噪声依赖初始匹配的准确性几何模型估计(如单应性)