C++视觉开发 三.缺陷检测

目录

一.距离变换

1.概念和功能

2.函数语法

1. cv::distanceTransform 

2.cv::normalize 

3.fore.convertTo

二.缺陷检测

1.cv::minEnclosingCircle 最小外接圆

2.完整代码示例


一.距离变换

1.概念和功能

距离变换是一种图像处理技术,用于计算图像中每个像素到最近的零像素(背景像素)的距离。它常用于图像分割、形态学操作和形状分析等领域。它计算图像中每个像素到最近的零像素(背景像素)的距离。这个距离可以是欧氏距离、曼哈顿距离等。距离变换后的图像中,前景像素的值代表它们到最近的背景像素的距离。

具体来说,距离变换在缺陷检测中有以下几个重要作用:

1.前景与背景的分离: 距离变换能够突出前景区域,使其与背景更明显地分离开。前景区域的像素值会表示其到最近背景像素的距离,这样可以更容易地识别和处理前景对象。

2.前景对象的中心检测: 距离变换的结果图像中,距离值最大的像素往往位于前景对象的中心位置。因此,通过距离变换,可以有效地检测出前景对象的中心点,为后续的形态学处理和轮廓检测提供可靠的信息。

3.分水岭算法的预处理: 距离变换常用于分水岭算法的预处理步骤,通过距离变换可以生成标记图像,这些标记可以帮助分水岭算法准确地分割图像中的各个对象。

4.形态学重建: 距离变换可以结合形态学操作进行形态学重建,用于去除图像中的小噪声和伪影,使前景对象更加清晰。

2.函数语法

示例:

// 距离变换
cv::Mat dist_transform;
cv::distanceTransform(op, dist_transform, cv::DIST_L2, 3);
cv::normalize(dist_transform, dist_transform, 0, 1.0, cv::NORM_MINMAX);

// 阈值处理
cv::Mat fore;
cv::threshold(dist_transform, fore, 0.3, 1.0, cv::THRESH_BINARY);
fore.convertTo(fore, CV_8U);

1. cv::distanceTransform 

功能:用于计算二值图像中每个前景像素到最近背景像素的距离。

函数语法:

void cv::distanceTransform(
    InputArray src, 
    OutputArray dst, 
    int distanceType, 
    int maskSize, 
    int dstType = CV_32F);

参数含义
src

输入图像,通常是一个二值图像。

其中非零像素被视为前景,零像素被视为背景。

dst

输出图像,包含每个像素到最近背景像素的距离。

默认情况下,输出图像是一个32位浮点图像(CV_32F)。

distanceType

距离类型,指定距离的计算方式。常用的类型有:

cv::DIST_L1:曼哈顿距离(每个像素的邻居为上下左右四个方向)。

cv::DIST_L2:欧氏距离(每个像素的邻居为周围八个方向)。

cv::DIST_C:棋盘距离(类似于曼哈顿距离,但允许对角线方向移动)

maskSize 掩码大小,影响距离计算的精度。可以是3、5或cv::DIST_MASK_PRECISE(在精确距离变换中使用)。常用值为3。
dstType

输出图像的类型。默认值为CV_32F,表示32位浮点图像。

可以更改为其他类型,如CV_8U(8位无符号整数)等。

2.cv::normalize 

功能:用于将数组的值归一化到指定的范围内。它可以应用于图像处理中的多种场景,例如将距离变换的结果归一化到 [0, 1] 或 [0, 255],从而便于可视化和后续处理。

函数语法:

void cv::normalize(
InputArray src, 
OutputArray dst, 
double alpha, 
double beta, 
int norm_type = cv::NORM_L2, 
int dtype = -1, 
InputArray mask = noArray()
);
参数含义
src 输入数组或图像
dst 输出数组或图像,与输入数组具有相同的大小和类型,或者具有指定的类型。
alpha

归一化后数组中最小值的目标值。

如果 norm_type 是 cv::NORM_MINMAX,这个参数表示归一化后的最小值。

beta

归一化后数组中最大值的目标值。

如果 norm_type 是 cv::NORM_MINMAX,这个参数表示归一化后的最大值。

norm_type

归一化类型。可以是以下之一:

cv::NORM_INF:归一化为无穷范数(最大绝对值)。

cv::NORM_L1:归一化为 L1 范数(绝对值之和)。

cv::NORM_L2:归一化为 L2 范数(平方和的平方根)。

cv::NORM_MINMAX:线性变换,将数组值线性缩放到 [alpha, beta] 范围。

dtype (可选)输出数组的类型。如果为 -1,则输出数组的类型与输入数组相同。
mask (可选)可选的操作掩码。仅对掩码内的元素进行归一化处理。

           只用前5个就够了

3.fore.convertTo

功能:将矩阵转换为另一种数据类型的函数。此函数通常用于图像处理中的数据类型转换,以确保图像处理操作使用正确的数据类型。

函数语法:

void cv::Mat::convertTo(OutputArray m, int rtype, double alpha , double beta)
参数含义
m 输出矩阵,类型由 rtype 指定。
rtype 输出矩阵的类型。可以是 CV_8U(8 位无符号整数)、CV_32F(32 位浮点数)等。
alpha 可选的比例因子,默认值为 1。输出矩阵的每个元素是输入矩阵相应元素乘以 alpha
beta 可选的加数,默认值为 0。输出矩阵的每个元素是输入矩阵相应元素乘以 alpha 再加上 beta

二.缺陷检测

通过计算轮廓面积和最小外接圆的面积比值来判断是否存在缺陷

1.cv::minEnclosingCircle 最小外接圆

功能:找到一个最小的圆,使得圆能够完全包围给定的轮廓。

函数语法:

void cv::minEnclosingCircle(InputArray points, Point2f& center, float& radius);
参数含义
points 输入的点集,可以可以是一个点的向量或Mat。
center 输出参数,存储最小外接圆的圆心坐标。
radius 输出参数,存储最小外接圆的半径。

2.完整代码示例

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    // 读取图像
    cv::Mat o = cv::imread("pill3.jpg", cv::IMREAD_GRAYSCALE);
    if (o.empty()) {
        std::cerr << "Could not open or find the image!" << std::endl;
        return -1;
    }

    // 二值化处理
    cv::Mat binary;
    cv::threshold(o, binary, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);

    // 形态学开运算
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
    cv::Mat op;
    cv::morphologyEx(binary, op, cv::MORPH_OPEN, kernel, cv::Point(-1, -1), 1);

    // 距离变换
    cv::Mat dist_transform;
    cv::distanceTransform(op, dist_transform, cv::DIST_L2, 3);
    cv::normalize(dist_transform, dist_transform, 0, 1.0, cv::NORM_MINMAX);

    // 阈值处理
    cv::Mat fore;
    cv::threshold(dist_transform, fore, 0.3, 1.0, cv::THRESH_BINARY);
    fore.convertTo(fore, CV_8U);

    // 形态学去噪
    cv::Mat kernel2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
    cv::Mat op2;
    cv::morphologyEx(fore, op2, cv::MORPH_OPEN, kernel2, cv::Point(-1, -1), 1);

    // 提取轮廓
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(op2, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

    // 缺陷检测
    int count = 0;
    cv::Mat img = o.clone();
    for (size_t i = 0; i < contours.size(); i++) {
        cv::Point2f center;
        float radius;
        cv::minEnclosingCircle(contours[i], center, radius);

        double area = cv::contourArea(contours[i]);
        double area_circle = 3.14 * radius * radius;
        if (area / area_circle >= 0.5) {
            cv::putText(img, "OK", center, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255), 2);
        }
        else {
            cv::putText(img, "bad", center, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255), 2);
        }
        count++;
    }
    cv::putText(img, "sum=" + std::to_string(count), cv::Point(20, 30), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255));

    // 显示结果
    cv::imshow("result", img);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}

结果如图:

上一篇:求职刷题力扣 DAY38动态规划 part04


下一篇:概率论中:二项分布和泊松分布有什么区别