一、模板匹配技术

1.1 技术概述

模板匹配(Template Matching)是一种在大图像中定位特定子图像(模板) 的核心技术。该技术通过将模板图像在目标图像上滑动,逐像素计算模板与目标图像局部区域的相似度,从而确定最佳匹配位置。模板匹配广泛应用于目标检测、工业零件定位及视频监控中的运动物体跟踪等场景。

1.2 工作原理

模板匹配的核心流程包含三个关键阶段:

  1. 输入准备
    • 目标图像(Source Image):待搜索的大尺寸图像

    • 模板图像(Template Image):需定位的子图像

  2. 相似度计算
    • 模板在目标图像上以滑动窗口方式移动

    • 每个位置计算模板与目标局部区域的相似度

    • 常用相似度度量方法:

      方法类型 最佳匹配指示
      平方差匹配(TM_SQDIFF) 最小值
      归一化平方差匹配 最小值
      相关匹配(TM_CCORR) 最大值
      相关系数匹配(TM_CCOEFF) 最大值
  3. 结果输出
    • 生成结果矩阵

    • 通过极值定位(minMaxLoc)确定最佳匹配位置

代码示例
void templateMatching(Mat src, Mat templ, Mat result, int method)
{
    // 1. 先定义输出的结果矩阵
    // 高=目标的高-模板的高+1
    // 宽=目标的宽-模板的宽+1
    int h = src.rows - templ.rows + 1;
    int w = src.cols - templ.cols + 1;
    Mat matchResult = Mat::zeros(h, w, CV_32FC1);

    // 2.做模板匹配
    matchTemplate(src, templ, matchResult, method);

    // 3. 寻找最大值和最小值(获取最佳匹配位置)
    double minValue, maxValue;
    Point minLoc, maxLoc;
    minMaxLoc(matchResult, &minValue, &maxValue, &minLoc, &maxLoc);

    rectangle(src, minLoc, Point(minLoc.x + templ.cols, minLoc.y + templ.rows), Scalar(0, 0, 255), 2, 8, 0);
    // 4. 绘制矩形
    rectangle(src, maxLoc, Point(maxLoc.x + templ.cols, maxLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0);
    // 5. 显示结果
    imshow("匹配结果", src);
}

1.3 代码实现

OpenCV提供matchTemplate()函数实现核心功能:

void matchTemplate(
    InputArray image,         // 目标图像
    InputArray templ,         // 模板图像
    OutputArray result,       // 匹配结果矩阵(CV_32FC1)
    int method,               // 匹配方法枚举值
    InputArray mask = noArray() // 掩码(部分方法有效)
);
完整实现流程
void runTemplateMatching(Mat& targetImage, Mat& templateImage) {
    // 1. 创建结果矩阵
    int cols = targetImage.cols - templateImage.cols + 1;
    int rows = targetImage.rows - templateImage.rows + 1;
    Mat result(Size(cols, rows), CV_32FC1);

    // 2. 执行模板匹配(归一化相关系数法)
    matchTemplate(targetImage, templateImage, result, TM_CCOEFF_NORMED);

    // 3. 定位最佳匹配位置
    double minVal, maxVal;
    Point minLoc, maxLoc;
    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);

    // 4. 绘制矩形标记匹配区域
    rectangle(targetImage, 
              maxLoc, 
              Point(maxLoc.x + templateImage.cols, maxLoc.y + templateImage.rows),
              Scalar(0, 255, 0), 2);

    // 5. 显示结果
    imshow("Template", templateImage);
    imshow("Matching Result", targetImage);
}

二、轮廓检测技术

2.1 技术概述

轮廓检测(Contour Detection)旨在提取图像中物体的精确边界,通过分析像素连通性生成连续边缘点集合。该技术是形状分析、目标跟踪和字符识别的基础,广泛应用于工业检测、医学成像和地理信息系统等领域。

2.2 实现原理

轮廓检测包含四个关键阶段:

  1. 图像预处理
    • 灰度转换:cvtColor(src, gray, COLOR_BGR2GRAY)

    • 二值化处理:threshold(gray, binary, 0, 255, THRESH_OTSU | THRESH_BINARY_INV)

    • 边缘增强(可选):Canny/Sobel算子

  2. 轮廓提取
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
  3. 轮廓后处理
    • 噪声过滤:基于面积/周长阈值

    • 轮廓近似:减少多边形点数

    • 特征计算:面积/周长/几何中心

  4. 结果可视化
    drawContours(result, contours, -1, Scalar(0,255,0), 2);

2.3 进阶应用

2.3.1 轮廓特征计算
// 计算轮廓面积
double area = contourArea(contour); 

// 计算轮廓周长
double perimeter = arcLength(contour, true);

// 按周长过滤轮廓示例
for (size_t i=0; i<contours.size(); ++i) {
    if(arcLength(contours[i], true) > 400) {
        drawContours(result, contours, i, Scalar(0,255,0), 2);
    }
}
2.3.2 轮廓近似
vector<vector<Point>> approxContours(contours.size());
for (size_t i=0; i<contours.size(); ++i) {
    // 精度=轮廓周长×0.02
    double epsilon = 0.02 * arcLength(contours[i], true); 
    approxPolyDP(contours[i], approxContours[i], epsilon, true);
}
2.3.3 几何特征计算
// 最小外接矩形
RotatedRect minRect = minAreaRect(contours[i]);
Point2f vertices[4];
minRect.points(vertices);
for(int j=0; j<4; j++) {
    line(src, vertices[j], vertices[(j+1)%4], Scalar(0,0,255), 2);
}

// 最小外接圆
Point2f center;
float radius;
minEnclosingCircle(contours[i], center, radius);
circle(src, center, radius, Scalar(0,0,255), 2);
2.3.4 代码实现
void findContours(Mat src, Mat templ, Mat result, int mode, int method)
{
    // 1.转为灰度图像
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    // 2.转为二值图像
    Mat binary;
    threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
    // 3.在二值图像查找图像的点
    vector<vector<Point>> contours;                                          // 用来装轮廓的点
    vector<Vec4i> hierarchy;                                                 // 用来装轮廓的索引
    findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); // 轮廓发现函数
    // 4. 打印轮廓的面积与周长
    for (int i = 0; i < contours.size(); i++)
    {
        // 1.3 轮廓面积与周长
        //  1. 计算轮廓的面积
        double area = contourArea(contours[i]);
        // 2. 计算轮廓的周长
        double length = arcLength(contours[i], true);
        cout << "第" << i << "个轮廓的面积为:" << area << "周长为:" << length << endl;
        // 只绘制面积>1000的轮廓
        if (area > 1000)
        {
            drawContours(src, contours, i, Scalar(0, 255, 0), -1);
        }
    }

    // 4.绘制轮廓
    // drawContours(src, contours, -1, Scalar(0, 255, 0), -1);
    // 5.展示
    imshow("轮廓检测", src);
}

三、图像分割技术

3.1 技术分类与应用

图像分割将图像划分为语义一致的独立区域,是计算机视觉的基础任务:

分割类型 原理 最佳应用场景
阈值分割 基于像素灰度分布 文档扫描、OCR
边缘分割 利用梯度不连续性 物体轮廓提取
区域分割 相似性区域合并 医学图像分析
分水岭算法 模拟水漫地形过程 重叠物体分离
深度学习分割 卷积神经网络特征学习 复杂场景理解

3.2 阈值分割技术

3.2.1 全局阈值法
double threshold(
    InputArray src,   // 输入图像(8UC1)
    OutputArray dst,  // 输出图像
    double thresh,    // 阈值
    double maxval,    // 最大值(通常255)
    int type          // 阈值类型
);
阈值类型对比
类型 效果
THRESH_BINARY 标准二值化
THRESH_BINARY_INV 反相二值化
THRESH_TRUNC 截断处理
THRESH_OTSU 双峰图像分割
3.2.2 自适应阈值
void adaptiveThreshold(
    InputArray src,     // 输入图像(8UC1)
    OutputArray dst,    // 输出图像
    double maxValue,    // 满足条件的像素值
    int adaptiveMethod, // ADAPTIVE_THRESH_MEAN_C/ADAPTIVE_THRESH_GAUSSIAN_C
    int thresholdType,  // THRESH_BINARY/THRESH_BINARY_INV
    int blockSize,      // 邻域大小(奇数)
    double C            // 偏移常数
);
void adaptiveThreshold(Mat src, Mat templ, Mat result, int method)
{
    Mat gray, gray2;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    cvtColor(src, gray2, COLOR_BGR2GRAY);
    // 2. 自适应域阈值分割
    adaptiveThreshold(gray, gray, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 3, 5);
    adaptiveThreshold(gray2, gray2, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, 5);
    imshow("自适应域阈值分割(均值法)", gray2);
    imshow("自适应域阈值分割(高斯法)", gray);
}

3.3 边缘分割技术

基于Canny边缘检测实现:

Mat gray, edges, segmented;
cvtColor(src, gray, COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(5,5), 1.5);
Canny(gray, edges, 50, 150);

// 使用边缘作为掩码进行分割
src.copyTo(segmented, edges);
void Canny(Mat src, Mat templ, Mat result, int method)
{
    Mat gray;
    // 高斯模糊-降噪
    GaussianBlur(src, gray, Size(3, 3), 0);
    // 灰度转换-二值化
    cvtColor(gray, gray, COLOR_BGR2GRAY);
    // Canny算子-边缘检测
    Canny(gray, gray, 50, 150);
    imshow("边缘检测", gray);
    // 将边缘检测的结果作为掩膜
    Mat dst = Mat::zeros(gray.size(), gray.type());
    gray.copyTo(dst, gray);
    // 3.2 模板匹配
    matchTemplate(src, templ, result, method);
    imshow("模板匹配", result);
}

3.4 分水岭算法

3.4.1 算法原理

分水岭算法将图像视为地形表面

  1. 低灰度区域:山谷(集水盆地)

  2. 高灰度区域:山峰(分水岭)

  3. 水漫过程

    • 从局部最小值开始注水

    • 水位上升时不同集水盆地汇合处形成分水岭

    • 分水岭线即分割边界

3.4.2 实现步骤
void watershedSegmentation(Mat src, Mat &dst)
{
    // 检查输入图像有效性
    if (src.empty())
    {
        cerr << "[错误] 输入图像为空!" << endl;
        return;
    }

    // 1. 图像预处理: 转为灰度图
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);

    // 2. 二值化处理: 使用OTSU自动阈值分割
    Mat binary;
    threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
    // 添加形态学操作去除噪声
    Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
    morphologyEx(binary, binary, MORPH_OPEN, kernel, Point(-1, -1), 2);

    // 3. 距离变换: 计算前景物体的距离图
    Mat dist;
    distanceTransform(binary, dist, DIST_L2, 3); // L2距离,3x3掩码
    normalize(dist, dist, 0, 1.0, NORM_MINMAX);  // 归一化到[0,1]范围

    // 4. 生成种子点: 基于距离图阈值化
    Mat distThresh;
    double maxVal;
    minMaxLoc(dist, nullptr, &maxVal);                             // 找到距离图最大值
    threshold(dist, distThresh, 0.7 * maxVal, 1.0, THRESH_BINARY); // 保留70%最大值以上区域
    distThresh.convertTo(distThresh, CV_8U);                       // 转为8位无符号整数

    // 5. 标记连通组件作为分水岭种子
    vector<vector<Point>> contours;
    findContours(distThresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);  

    // 创建标记矩阵 (必须是32位有符号整数类型)
    Mat markers = Mat::zeros(src.size(), CV_32S);
    for (int i = 0; i < contours.size(); i++)
    {
        // 用不同的正整数标记不同的前景物体
        drawContours(markers, contours, i, Scalar(i + 1), -1);
    }
    // 用255标记背景区域
    markers.setTo(255, binary == 0);

    // 6. 执行分水岭算法
    Mat markers8u;
    markers.convertTo(markers8u, CV_8U, 1.0); // 转换为8位以便显示
    imshow("标记图像", markers8u);

    // 应用分水岭算法 (输入图像需为彩色图)
    watershed(src, markers);

    // 7. 生成分割结果
    dst = Mat::zeros(src.size(), CV_8UC3);
    vector<Vec3b> colors;
    RNG rng(12345);
    for (int i = 0; i < contours.size() + 1; i++)
    {
        // 为每个区域生成随机颜色
        Vec3b color = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
        colors.push_back(color);
    }

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐