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 三个通道的数值取平均:
Gray = (R + G + B) / 3
加权平均法(更符合人眼感知)
人眼对绿色更敏感,因此绿色通道权重更高:
Gray = 0.299*R + 0.587*G + 0.114*B
这也是 OpenCV 默认的转换公式(CV_BGR2GRAY)
最大值法
使用 RGB 中的最大值作为灰度值:
Gray = max(R, G, B)
4.1.2 灰度处理的效果与场景
效果
- 减少数据量:从三通道变为单通道,内存占用减少 1/3。
- 突出形状特征:消除颜色干扰,更专注于纹理、边缘等结构信息。
常见应用
- 人脸识别:预处理阶段常将图像转为灰度,降低计算复杂度。
- 边缘检测:灰度图像更适合 Canny、Sobel 等算子提取边缘。
- 模板匹配:灰度处理可提高匹配准确性。
- 图像压缩:灰度图像可使用更高效的压缩算法。
4.1.2 代码示例
#include #include 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枚举指定),以下是常用类型及效果:
4.2.2 常见颜色转换示例:
#include #include 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 图像增强原理
增强是通过像素值映射函数调整图像对比度或亮度:
- 线性增强:直接缩放像素值范围(如直方图均衡化)。
- 非线性增强:通过对数、指数等函数调整(如伽马校正)。
代码示例
#include #include 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 常见边缘检测算法对比:
4.4.2 代码示例
#include #include 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 代码示例:
#include #include 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;}