OpenCV学习探秘之二 :数字图像的矩阵原理,OpenCV图像类与常用函数接口说明,及其常见操作核心技术详解

一、图像处理基础概念​

1.1数字图像的矩阵

如下图,这是我们看到的 Lena 的头像,但是计算机看来,这副图像只是一堆亮度各异的点。一副尺寸为 M × N 的图像可以用一个 M × N 的矩阵来表示,矩阵元素的值表示这个位置上的像素的亮度,一般来说像素值越大表示该点越亮。

一般来说,灰度图用 2 维矩阵表示;彩色(多通道)图像用 3 维矩阵(M × N × 3)表示。对于图像显示来说,目前大部分设备都是用无符号 8 位整数(类型为 CV_8U)表示像素亮度。

1.2数字图像的本质​

图像在计算机中本质是多维矩阵​:

  • 彩色图像:三维矩阵(高度×宽度×3),通道顺序为BGR(非RGB)
  • 灰度图像:二维矩阵(高度×宽度),像素值范围0-255(0黑→255白)
  • 像素关系:相邻像素通过4-邻域、8-邻域空间连接,构成连通域

1.3​图像处理的意义​

​ - 降噪增强​:提升图像质量(如医疗影像去噪)

  • 特征提取:识别关键信息(如边缘、角点)
  • 数据压缩:减少存储与计算量(如灰度化降75%内存);

二、OpenCV图像类

2.1 Mat类结构

Mat类是 OpenCV 用于存储和操作图像 / 矩阵数据的核心结构,其设计兼顾了内存效率与操作灵活性。从结构上看,Mat由头部(Header) 和数据块(Data Block) 两部分组成。

2.2 Mat类构造

opencv最核心的Mat类,Mat 是一个非常优秀的图像类,它同时也是一个通用的矩阵类,可以用来创建和操作多维矩阵。有多种方法创建一个 Mat 对象。常用的构造函数有:

  • Mat::Mat()
    无参数构造方法;
  • Mat::Mat(int rows, int cols, int type)
    创建行数为 rows,列数为 col,类型为 type 的图像;
  • Mat::Mat(Size size, int type)
    创建大小为 size,类型为 type 的图像;
  • Mat::Mat(int rows, int cols, int type, const Scalar& s)
    创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始化为值 s;
  • Mat::Mat(Size size, int type, const Scalar& s)
    创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s;
  • Mat::Mat(const Mat& m)
    将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m 和新对象共用图像数据;
  • Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
    创建行数为 rows,列数为 col,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step指定。
  • Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
    创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定。
  • Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
    创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据;
  • Mat::Mat(const Mat& m, const Rect& roi)
    创建的新图像为 m 的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据。
    这些构造函数中,很多都涉及到类型type。type可以是CV_8UC1,CV_16SC1,...,CV_64FC4 等。里面的 8U 表示 8 位无符号整数,16S 表示 16 位有符号整数,64F表示 64 位浮点数(即 double 类型);C 后面的数表示通道数,例如 C1 表示一个通道的图像,C4 表示 4 个通道的图像,以此类推。

三 常用接口函数

3.1 图像读写与显示

3.1.1 读取图像
  • imread(const string& filename, int flags=IMREAD_COLOR)
    读取图像文件,flags可选:
    IMREAD_COLOR:加载彩色图(默认);
    IMREAD_GRAYSCALE:加载灰度图;
    IMREAD_UNCHANGED:加载包含 Alpha 通道的图像;
3.1.2 保存图像
  • imwrite(const string& filename, InputArray img)
    保存图像到文件,支持格式:JPEG、PNG、BMP 等。
3.1.3 显示图像
  • namedWindow(const string& winname, int flags=WINDOW_AUTOSIZE)
    创建窗口,flags可选:
    WINDOW_AUTOSIZE:窗口大小自适应图像;
    WINDOW_NORMAL:窗口可调整大小;
  • imshow(const string& winname, InputArray mat)
    在指定窗口显示图像。搭配waitKey(int delay=0)等待按键事件,delay为毫秒数,0 表示无限等待。

3.2 图像操作颜色空间转换

3.2.1 颜色空间转换
  • cvtColor(InputArray src, OutputArray dst, int code)
    转换颜色空间。
3.2.2 图像缩放
  • resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)
    调整图像大小,interpolation可选:
    INTER_LINEAR:双线性插值(默认);
    INTER_NEAREST:最近邻插值;
    INTER_CUBIC:双三次插值(放大更清晰);
3.2.3 图像翻转与旋转
  • flip(InputArray src, OutputArray dst, int flipCode)
    翻转图像,flipCode:
    0:上下翻转
    1:左右翻转
    -1:上下 + 左右翻转
  • rotate(InputArray src, OutputArray dst, int rotateCode)
    旋转图像,rotateCode:
    ROTATE_90_CLOCKWISE:顺时针 90 度;
    ROTATE_180:180 度;
    ROTATE_90_COUNTERCLOCKWISE:逆时针 90 度;

3.3 图像滤波与增强

3.3.1 平滑滤波
  • blur(InputArray src, OutputArray dst, Size ksize)
    均值滤波,ksize为核大小(如(3, 3))。
  • GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0)
    高斯滤波,sigmaX为 X 方向标准差。
  • medianBlur(InputArray src, OutputArray dst, int ksize)
    中值滤波,ksize为核大小(奇数)。
3.3.2 边缘检测
  • Canny(InputArray image, OutputArray edges, double threshold1, double threshold2)
    Canny 边缘检测,threshold1和threshold2为双阈值。
3.3.3 直方图均衡化
  • equalizeHist(InputArray src, OutputArray dst)
    增强图像对比度(仅适用于灰度图)。

3.4 形态学操作

  • erode(InputArray src, OutputArray dst, InputArray kernel)
    腐蚀操作,缩小前景物体。
  • dilate(InputArray src, OutputArray dst, InputArray kernel)
    膨胀操作,扩大前景物体。
  • morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel)
    形态学高级操作,op可选:
    MORPH_OPEN:开运算(先腐蚀后膨胀)
    MORPH_CLOSE:闭运算(先膨胀后腐蚀)
    MORPH_GRADIENT:形态学梯度(膨胀 - 腐蚀)

3.5 图像算术与逻辑运算

  • add(InputArray src1, InputArray src2, OutputArray dst)
    图像加法(支持带权重的加法)。
  • subtract(InputArray src1, InputArray src2, OutputArray dst)
    图像减法。
  • bitwise_and(InputArray src1, InputArray src2, OutputArray dst)
    按位与(用于掩码操作)。
  • bitwise_or(InputArray src1, InputArray src2, OutputArray dst)
    按位或。
  • bitwise_not(InputArray src, OutputArray dst)
    按位取反。

3.6 几何变换

3.6.1 仿射变换
  • warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize)
    应用仿射变换,M为 2×3 变换矩阵。
  • getRotationMatrix2D(Point2f center, double angle, double scale)
    获取旋转矩阵(用于warpAffine)。
3.6.2 透视变换
  • warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize)
    应用透视变换,M为 3×3 变换矩阵。
  • getPerspectiveTransform(const Point2f src[], const Point2f dst[])
    计算透视变换矩阵。

3.7 特征提取

  • findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method)
    查找图像轮廓,mode为轮廓检索模式,method为轮廓近似方法。
  • HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold)
    Hough 直线检测。
  • HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist)
    Hough 圆检测。

3.8 其他常用操作

  • split(const Mat& src, Mat* mvbegin)
    将多通道图像分割为单通道(例如 BGR→B、G、R)。
  • merge(const Mat* mv, size_t count, OutputArray dst)
    将多个单通道图像合并为多通道。
  • copyTo(InputArray src, OutputArray dst, InputArray mask)
    带掩码的图像复制。
  • setTo(InputOutputArray dst, const Scalar& value, InputArray mask=noArray())
    将图像或 ROI 设置为指定值。

四 常见操作原理说明

4.1 灰度处理原理与效果

在计算机视觉中,灰度图像是指每个像素仅由一个数值表示其亮度的图像,数值范围通常为 0(黑色)到 255(白色)。将彩色图像转换为灰度图像的过程称为灰度处理,其核心是通过加权平均或特定算法将 RGB 三个通道的信息合并为单通道。

4.1.1 灰度处理原理

常见的灰度转换的数学原理算法有以下几种:

平均值法

将 RGB 三个通道的数值取平均:

cpp 复制代码
Gray = (R + G + B) / 3
加权平均法(更符合人眼感知)

人眼对绿色更敏感,因此绿色通道权重更高:

cpp 复制代码
Gray = 0.299*R + 0.587*G + 0.114*B

这也是 OpenCV 默认的转换公式(CV_BGR2GRAY)

最大值法

使用 RGB 中的最大值作为灰度值:

cpp 复制代码
Gray = max(R, G, B)
4.1.2 灰度处理的效果与场景
效果
  • 减少数据量:从三通道变为单通道,内存占用减少 1/3。
  • 突出形状特征:消除颜色干扰,更专注于纹理、边缘等结构信息。
常见应用
  • 人脸识别:预处理阶段常将图像转为灰度,降低计算复杂度。
  • 边缘检测:灰度图像更适合 Canny、Sobel 等算子提取边缘。
  • 模板匹配:灰度处理可提高匹配准确性。
  • 图像压缩:灰度图像可使用更高效的压缩算法。
4.1.2 代码示例
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取彩色图像
    Mat colorImage = imread("input.jpg", IMREAD_COLOR);
    if (colorImage.empty()) {
        cout << "无法读取图像!" << endl;
        return -1;
    }

    // 创建用于存储灰度图像的Mat对象
    Mat grayImage;

    // 方法1: 使用OpenCV内置函数进行灰度转换
    cvtColor(colorImage, grayImage, COLOR_BGR2GRAY);

    // 方法2: 手动实现加权平均法
    Mat manualGray = Mat::zeros(colorImage.size(), CV_8UC1);
    for (int i = 0; i < colorImage.rows; i++) {
        for (int j = 0; j < colorImage.cols; j++) {
            Vec3b pixel = colorImage.at<Vec3b>(i, j);
            // 注意: OpenCV中颜色顺序为BGR而非RGB
            uchar gray = 0.299 * pixel[2] + 0.587 * pixel[1] + 0.114 * pixel[0];
            manualGray.at<uchar>(i, j) = gray;
        }
    }

    // 显示原图和灰度图
    imshow("彩色图像", colorImage);
    imshow("OpenCV灰度转换", grayImage);
    imshow("手动灰度转换", manualGray);

    // 保存灰度图像
    imwrite("output_gray.jpg", grayImage);

    // 等待按键退出
    waitKey(0);
    return 0;
}

4.2 颜色空间转换

颜色空间是描述颜色的数学模型,不同场景下需选择合适的颜色空间(如 RGB 适合显示,HSV 适合颜色分割)。OpenCV 通过cvtColor函数实现颜色空间转换,核心是基于数学公式的通道数值映射。

4.2.1 核心原理

颜色空间转换的本质是 通道数值的数学变换:通过预设公式将原颜色空间的通道值(如 RGB 的 R、G、B)转换为目标空间的通道值(如 HSV 的 H、S、V)。

OpenCV 支持很多种转换(通过ColorConversionCodes枚举指定),以下是常用类型及效果:

转换类型 适用场景 效果特点
COLOR_BGR2RGB 图像显示(OpenCV 默认 BGR 存储) 交换 R 和 B 通道,解决 OpenCV 与其他库(如 Qt)的显示颜色偏差。
COLOR_BGR2GRAY 预处理(减少计算量) 转为单通道灰度图,保留亮度信息,消除颜色干扰。
COLOR_BGR2HSV 颜色分割(如目标检测) H 通道单独表示颜色,可通过阈值快速提取特定颜色(如红色物体)。
COLOR_BGR2YCrCb 肤色检测 Y 通道为亮度,Cr、Cb 通道对肤色敏感,适合提取人脸区域。
COLOR_BGR2Lab 光照不变性场景 Lab 空间对光照变化不敏感,适合光照不均时的颜色分析。
4.2.2 常见颜色转换示例:
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 1. 读取图像(OpenCV默认以BGR格式加载)
    Mat bgr_img = imread("test.jpg");
    if (bgr_img.empty()) {
        cout << "无法读取图像!" << endl;
        return -1;
    }

    // 2. 定义目标图像
    Mat rgb_img, gray_img, hsv_img, ycrcb_img;

    // 3. 颜色空间转换(核心函数:cvtColor)
    cvtColor(bgr_img, rgb_img, COLOR_BGR2RGB);       // BGR→RGB(用于正确显示)
    cvtColor(bgr_img, gray_img, COLOR_BGR2GRAY);     // BGR→灰度
    cvtColor(bgr_img, hsv_img, COLOR_BGR2HSV);       // BGR→HSV
    cvtColor(bgr_img, ycrcb_img, COLOR_BGR2YCrCb);   // BGR→YCrCb

    // 4. 显示结果
    imshow("原图(BGR)", bgr_img);
    imshow("RGB(用于显示校正)", rgb_img);
    imshow("灰度图(减少干扰)", gray_img);
    imshow("HSV(颜色分割友好)", hsv_img);
    imshow("YCrCb(肤色检测)", ycrcb_img);

    // 5. 保存结果
    imwrite("hsv_result.jpg", hsv_img);
    imwrite("gray_result.jpg", gray_img);

    // 等待按键退出
    waitKey(0);
    destroyAllWindows();
    return 0;
}

4.3 图像滤波与增强

4.3.1 图像滤波

图像滤波与增强是计算机视觉中,用于改善图像质量、突出特征或抑制噪声。OpenCV 提供了丰富的滤波与增强函数,核心是通过卷积操作或像素值映射实现。滤波是通过 卷积核(Kernel) 对图像进行邻域操作的过程。

  • 线性滤波:输出像素是邻域像素的加权和(如均值滤波、高斯滤波)。
  • 非线性滤波:输出像素由邻域像素的排序或统计值决定(如中值滤波、双边滤波)。

数学表达:输出像素值 = 卷积核 × 邻域像素矩阵(逐元素相乘后求和)。

常见滤波方法及效果

滤波类型 原理 效果与应用场景
均值滤波 用邻域平均值替代中心像素 模糊图像、降噪,但会丢失细节
高斯滤波 用高斯函数加权邻域像素 平滑图像,保留更多细节,常用于预处理
中值滤波 用邻域像素的中值替代中心像素 有效去除椒盐噪声,保留边缘
双边滤波 同时考虑空间距离和像素值差异 平滑图像的同时保留边缘,适合美颜等场景
4.3.2 图像增强原理

增强是通过像素值映射函数调整图像对比度或亮度:

  • 线性增强:直接缩放像素值范围(如直方图均衡化)。
  • 非线性增强:通过对数、指数等函数调整(如伽马校正)。
增强类型 原理 效果与场景
直方图均衡化 拉伸像素值分布,使直方图更均匀 增强整体对比度,适合低对比度图像
伽马校正 通过幂函数调整亮度(γ>1 变暗,γ<1 变亮) 局部细节增强,适合调整过曝或过暗图像

代码示例

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 1. 读取图像(可替换为带噪声的图像)
    Mat src = imread("test.jpg", IMREAD_COLOR);
    if (src.empty()) {
        cout << "无法读取图像!" << endl;
        return -1;
    }

    // 2. 定义输出图像
    Mat blur_img, gaussian_img, median_img, bilateral_img;
    Mat equalized_img, gamma_corrected_img;

    // 3. 图像滤波
    blur(src, blur_img, Size(5, 5));                   // 均值滤波(5×5核)
    GaussianBlur(src, gaussian_img, Size(5, 5), 0);    // 高斯滤波
    medianBlur(src, median_img, 5);                    // 中值滤波(核大小必须为奇数)
    bilateralFilter(src, bilateral_img, 9, 75, 75);    // 双边滤波

    // 4. 图像增强
    // 4.1 直方图均衡化(需先转为灰度图)
    Mat gray, equalized_gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    equalizeHist(gray, equalized_gray);
    cvtColor(equalized_gray, equalized_img, COLOR_GRAY2BGR);

    // 4.2 伽马校正
    Mat src_f;
    src.convertTo(src_f, CV_32F, 1.0/255.0);  // 归一化到[0,1]
    pow(src_f, 0.5, gamma_corrected_img);     // γ=0.5(变亮)
    gamma_corrected_img.convertTo(gamma_corrected_img, CV_8U, 255.0);

    // 5. 显示结果
    imshow("原图", src);
    imshow("均值滤波", blur_img);
    imshow("高斯滤波", gaussian_img);
    imshow("中值滤波", median_img);
    imshow("双边滤波", bilateral_img);
    imshow("直方图均衡化", equalized_img);
    imshow("伽马校正(γ=0.5)", gamma_corrected_img);

    // 6. 保存结果
    imwrite("gaussian_result.jpg", gaussian_img);
    imwrite("equalized_result.jpg", equalized_img);

    waitKey(0);
    return 0;
}

4.4 边缘检测

边缘检测是计算机视觉中,用于识别图像中亮度突变的区域(即边缘),广泛应用于目标分割、特征提取和场景理解。OpenCV 提供了多种边缘检测算法,核心是基于一阶或二阶导数计算像素值的变化率。

图像边缘在对应像素值的梯度突变,数学上通过卷积核计算一阶导数(如 Sobel、Prewitt 算子)或二阶导数(如 Laplacian 算子)来检测这种变化。

4.4.1 常见边缘检测算法对比:
算法 原理 效果 适用场景
Sobel 一阶导数算子 计算水平和垂直梯度 边缘较粗,定位精度中等 快速检测、预处理
Canny 多阶段优化(非极大值抑制 + 双阈值) 边缘细、定位准、假边缘少 高精度场景(如工业检测)
Laplacian 二阶导数算子,检测梯度的零交叉点 对边缘方向不敏感,可能产生双边缘 简单场景、快速原型
Prewitt 类似 Sobel,但权重均匀 计算简单,对噪声敏感 教学或低计算资源场景
4.4.2 代码示例
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 1. 读取图像并转为灰度图
    Mat src = imread("test.jpg", IMREAD_COLOR);
    if (src.empty()) {
        cout << "无法读取图像!" << endl;
        return -1;
    }
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);

    // 2. 高斯平滑(减少噪声)
    Mat blurred;
    GaussianBlur(gray, blurred, Size(3, 3), 0);

    // 3. 定义输出图像
    Mat sobelx, sobely, sobel_edges;
    Mat laplacian_edges;
    Mat canny_edges;
    Mat prewitt_edges;

    // 4. Sobel边缘检测
    Sobel(blurred, sobelx, CV_64F, 1, 0, 3);  // x方向梯度
    Sobel(blurred, sobely, CV_64F, 0, 1, 3);  // y方向梯度
    convertScaleAbs(sobelx, sobelx);           // 转换为8位无符号整数
    convertScaleAbs(sobely, sobely);
    addWeighted(sobelx, 0.5, sobely, 0.5, 0, sobel_edges);  // 合并梯度

    // 5. Laplacian边缘检测
    Laplacian(blurred, laplacian_edges, CV_8U, 3);

    // 6. Canny边缘检测(关键参数:阈值1和阈值2)
    Canny(blurred, canny_edges, 50, 150);

    // 7. Prewitt边缘检测(手动实现)
    Mat prewitt_x = (Mat_<int>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
    Mat prewitt_y = (Mat_<int>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
    Mat grad_x, grad_y;
    filter2D(blurred, grad_x, CV_16S, prewitt_x);
    filter2D(blurred, grad_y, CV_16S, prewitt_y);
    convertScaleAbs(grad_x, grad_x);
    convertScaleAbs(grad_y, grad_y);
    addWeighted(grad_x, 0.5, grad_y, 0.5, 0, prewitt_edges);

    // 8. 显示结果
    imshow("原图", src);
    imshow("Sobel边缘", sobel_edges);
    imshow("Laplacian边缘", laplacian_edges);
    imshow("Canny边缘", canny_edges);
    imshow("Prewitt边缘", prewitt_edges);

    // 9. 保存结果
    imwrite("canny_result.jpg", canny_edges);

    waitKey(0);
    return 0;
}

4.5 腐蚀与膨胀

腐蚀(Erosion)与膨胀(Dilation)是形态学图像处理的操作中,用于改变图像中物体的形状和大小。它们通过 结构元素(Structuring Element)对图像进行逐像素扫描,实现对前景物体的收缩或扩张。腐蚀和膨胀本质上是对图像中 "前景像素(通常为白色)" 的操作。

4.5.1 腐蚀(Erosion)

原理:结构元素在图像上滑动,若结构元素完全包含于前景区域,则保留中心像素,否则删除。

效果:前景物体收缩,孔洞扩大,细小连接被断开。

4.5.2 膨胀(Dilation)

原理:结构元素在图像上滑动,若结构元素与前景区域有交集,则中心像素被设为前景。

效果:前景物体扩张,孔洞缩小,断裂部分被连接。

4.5.3 应用对比
操作 典型应用
腐蚀 去除小噪声点;分离相连物体;细化轮廓;
膨胀 连接断裂部分;增强轮廓
4.5.4 代码示例:
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 1. 读取图像并转为二值图(简化演示)
    Mat src = imread("test.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) {
        cout << "无法读取图像!" << endl;
        return -1;
    }
    
    // 二值化处理(阈值操作)
    Mat binary;
    threshold(src, binary, 127, 255, THRESH_BINARY);

    // 2. 定义结构元素(可使用矩形、椭圆或十字形)
    Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));

    // 3. 腐蚀和膨胀操作
    Mat eroded, dilated;
    erode(binary, eroded, kernel);       // 腐蚀
    dilate(binary, dilated, kernel);     // 膨胀

    // 4. 高级形态学操作(开运算和闭运算)
    Mat opened, closed;
    morphologyEx(binary, opened, MORPH_OPEN, kernel);    // 开运算 = 腐蚀+膨胀
    morphologyEx(binary, closed, MORPH_CLOSE, kernel);  // 闭运算 = 膨胀+腐蚀

    // 5. 显示结果
    imshow("原图", src);
    imshow("二值图", binary);
    imshow("腐蚀", eroded);
    imshow("膨胀", dilated);
    imshow("开运算", opened);
    imshow("闭运算", closed);

    // 6. 保存结果
    imwrite("eroded.jpg", eroded);
    imwrite("dilated.jpg", dilated);

    waitKey(0);
    return 0;
}
相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习