OpenCV4X学习-图像金字塔、图像形态、视频相关
图像金字塔与形态学操作摘要: 图像金字塔提供多尺度图像表示,包括高斯金字塔(通过连续下采样构建)和拉普拉斯金字塔(保存尺度间细节信息)。形态学操作基于结构元素处理图像,包括膨胀(扩张物体)、腐蚀(收缩物体)、开运算(去噪)、闭运算(填充空洞)等。代码示例展示了如何构建两种金字塔,以及实现各类形态学操作(如提取水平/垂直线)。这些技术在目标检测、图像融合和特征提取中具有重要应用价值。
图像金字塔
定义:图像金字塔是一种多尺度的图像表示方法,通过对图像进行一系列的下采样和上采样操作,生成不同分辨率的图像集合。它在计算机视觉中广泛应用于图像特征提取、目标检测、图像融合等领域。
作用:不同尺度的图像可以提供不同层次的信息,有助于处理不同大小的目标或细节。例如,在目标检测中,大尺度图像可以用于检测大目标,小尺度图像可以用于检测小目标。
高斯金字塔
原理:高斯金字塔通过对图像进行连续的高斯模糊和下采样操作构建。具体步骤为:首先对原始图像应用高斯核进行模糊处理,然后将图像尺寸缩小为原来的一半(去除偶数行和列)。重复这个过程,得到一系列尺寸逐渐减小的图像,构成高斯金字塔。
用途:常用于图像的多尺度表示,为其他基于多尺度的算法提供基础,如尺度不变特征变换(SIFT)算法中的尺度空间构建。
拉普拉斯金字塔
原理:拉普拉斯金字塔基于高斯金字塔构建,它表示的是图像在不同尺度间的细节信息。通过将高斯金字塔中某一层图像上采样后与上一层高斯图像相减得到。具体来说,假设高斯金字塔有 G0,G1,G2,⋯ 等层,拉普拉斯金字塔的第 i 层 Li=Gi−pyrUp(Gi+1),其中 pyrUp 是上采样操作。
用途:在图像压缩、图像融合等方面有重要应用。例如在图像融合中,拉普拉斯金字塔可以保留不同尺度下图像的细节信息,使得融合后的图像更自然。
代码示例

#include <QCoreApplication>
#include <QDebug>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
void displayPyramid(const std::vector<cv::Mat>& pyramid, const std::string& title) {
int numLevels = pyramid.size();
for (int i = 0; i < numLevels; ++i) {
cv::Mat resized;
// 调整图像大小以适应显示
cv::resize(pyramid[i], resized, cv::Size(), 0.5, 0.5);
std::string levelTitle = title + " Level " + std::to_string(i);
cv::imshow(levelTitle, resized);
}
cv::waitKey(0);
cv::destroyAllWindows();
}
int main() {
// 读取图像
cv::Mat image = cv::imread("1.png");
if (image.empty()) {
std::cerr << "Could not open or find the image" << std::endl;
return -1;
}
// 将图像从BGR转换为RGB
cv::Mat rgbImage;
cv::cvtColor(image, rgbImage, cv::COLOR_BGR2RGB);
// 构建高斯金字塔
int num_levels = 5;
std::vector<cv::Mat> gaussian_pyramid;
gaussian_pyramid.push_back(rgbImage.clone()); // 使用clone确保是深拷贝
for (int i = 0; i < num_levels - 1; ++i) {
cv::Mat downsampled;
cv::pyrDown(gaussian_pyramid.back(), downsampled);
gaussian_pyramid.push_back(downsampled);
}
// 构建拉普拉斯金字塔
std::vector<cv::Mat> laplacian_pyramid;
// 对于除最后一级外的所有高斯金字塔层
for (int i = 0; i < num_levels - 1; ++i) {
cv::Mat expanded;
cv::pyrUp(gaussian_pyramid[i + 1], expanded, gaussian_pyramid[i].size());
cv::Mat laplacian;
cv::subtract(gaussian_pyramid[i], expanded, laplacian);
laplacian_pyramid.push_back(laplacian);
}
// 添加高斯金字塔的最后一级到拉普拉斯金字塔
laplacian_pyramid.push_back(gaussian_pyramid.back().clone());
// 显示高斯金字塔
displayPyramid(gaussian_pyramid, "Gaussian Pyramid");
// 显示拉普拉斯金字塔
displayPyramid(laplacian_pyramid, "Laplacian Pyramid");
return 0;
}
图像形态学
结构元素:
结构元素是一个小的矩阵或核,用于在图像上滑动并与图像像素进行比较和操作。它决定了形态学操作的形状和大小,常见的形状有矩形、圆形、十字形等。例如,一个 3×3 的矩形结构元素可以表示为 111111111,在形态学操作中,这个结构元素会覆盖图像上对应的 3×3 区域进行处理。
膨胀:
膨胀操作是将与结构元素有重叠的像素点置为前景(通常是白色,值为 255)。其效果是使图像中的物体区域扩张,填补一些空洞或连接临近的物体。数学上,对于二值图像 A 和结构元素 B,膨胀操作定义为 A⊕B={z∣(B^)z∩A=∅},其中 B^ 是 B 关于原点的反射。
腐蚀:
腐蚀操作与膨胀相反,它将结构元素完全覆盖的前景像素保留,否则置为背景(通常是黑色,值为 0)。其效果是使图像中的物体区域收缩,去除一些小的噪声点。数学上,对于二值图像 A 和结构元素 B,腐蚀操作定义为 A⊖B={z∣(B)z⊆A}。
开运算:
开运算是先腐蚀后膨胀的操作。它可以去除图像中的小物体,平滑较大物体的边界,同时保持物体的整体位置和形状基本不变。数学上,对于二值图像 A 和结构元素 B,开运算定义为 A∘B=(A⊖B)⊕B。
闭运算:
闭运算是先膨胀后腐蚀的操作。它可以填充物体内的小空洞,连接临近的物体,同样能保持物体的整体位置和形状基本不变。数学上,对于二值图像 A 和结构元素 B,闭运算定义为 A⋅B=(A⊕B)⊖B。
顶帽:
顶帽运算定义为原图像与开运算结果的差值。它可以突出图像中的微小细节,如在一幅有光照不均匀的图像中,顶帽运算可以增强光照变化较小区域的细节。数学上,对于二值图像 A 和结构元素 B,顶帽运算定义为 A−(A∘B)。
黑帽:
黑帽运算定义为闭运算结果与原图像的差值。它可以突出图像中比周围暗的区域,常用于检测图像中的暗点或凹陷部分。数学上,对于二值图像 A 和结构元素 B,黑帽运算定义为 (A⋅B)−A。
击中击不中:
击中击不中变换用于在图像中寻找与结构元素完全匹配的部分。它同时考虑结构元素在前景和背景中的匹配情况,对于目标检测和形状识别有重要应用。数学上,对于二值图像 A 和结构元素 B,击中击不中变换定义为 (A⊖B1)∩((A)⊖B2),其中 B=B1∪B2,B1 对应前景部分,B2 对应背景部分,A 是 A 的补集。
代码示例
#include <QCoreApplication>
#include <QDebug>
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 读取图像
cv::Mat image = cv::imread("D:/ljl/FPGA/QT_openCV_pro/pro_01/untitled_pro_01/src/1.png", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Could not open or find the image" << std::endl;
return -1;
}
// 定义结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 膨胀
cv::Mat dilated;
cv::dilate(image, dilated, kernel);
// 腐蚀
cv::Mat eroded;
cv::erode(image, eroded, kernel);
// 开运算
cv::Mat opened;
cv::morphologyEx(image, opened, cv::MORPH_OPEN, kernel);
// 闭运算
cv::Mat closed;
cv::morphologyEx(image, closed, cv::MORPH_CLOSE, kernel);
// 顶帽
cv::Mat tophat;
cv::morphologyEx(image, tophat, cv::MORPH_TOPHAT, kernel);
// 黑帽
cv::Mat blackhat;
cv::morphologyEx(image, blackhat, cv::MORPH_BLACKHAT, kernel);
// 显示结果
cv::imshow("Original Image", image);
cv::imshow("Dilated Image", dilated);
cv::imshow("Eroded Image", eroded);
cv::imshow("Opened Image", opened);
cv::imshow("Closed Image", closed);
cv::imshow("Tophat Image", tophat);
cv::imshow("Blackhat Image", blackhat);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
利用形态学提取水平线和垂直线
#include <QCoreApplication>
#include <QDebug>
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 读取图像
cv::Mat image = cv::imread("D:/ljl/FPGA/QT_openCV_pro/pro_01/untitled_pro_01/src/1.png", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Could not open or find the image" << std::endl;
return -1;
}
// 提取水平线
cv::Mat horizontalKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(image.cols / 16, 1));
cv::Mat horizontalLines;
cv::morphologyEx(image, horizontalLines, cv::MORPH_OPEN, horizontalKernel);
// 提取垂直线
cv::Mat verticalKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, image.rows / 16));
cv::Mat verticalLines;
cv::morphologyEx(image, verticalLines, cv::MORPH_OPEN, verticalKernel);
// 显示结果
cv::imshow("Original Image", image);
cv::imshow("Horizontal Lines", horizontalLines);
cv::imshow("Vertical Lines", verticalLines);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
cv::VideoCapture
cv::VideoCapture是 OpenCV 中用于捕获视频的类,它可以从摄像头或视频文件中读取帧。
从摄像头捕获视频:创建 cv::VideoCapture 对象并传入参数 0 以连接默认摄像头。在循环中不断读取帧并显示,按下 ‘q’ 键退出。
从视频文件中读取视频:创建 cv::VideoCapture 对象并传入视频文件路径。同样在循环中读取帧并显示,按下 ‘q’ 键退出。
获取和设置视频属性:使用 cap.get 方法获取视频的宽度、高度和帧率等属性,并使用 cap.set 方法尝试设置视频的亮度属性。实际应用中,不同的视频源和设备对属性的支持情况可能不同。
从摄像头捕获视频
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 创建VideoCapture对象,参数0表示默认摄像头
cv::VideoCapture cap(0);
// 检查摄像头是否成功打开
if (!cap.isOpened()) {
std::cerr << "无法打开摄像头" << std::endl;
return -1;
}
cv::Mat frame;
while (true) {
// 从摄像头读取一帧
cap >> frame;
// 检查是否成功读取帧
if (frame.empty()) {
std::cerr << "无法读取帧" << std::endl;
break;
}
// 显示帧
cv::imshow("摄像头视频", frame);
// 等待按键事件,按下 'q' 键退出
if (cv::waitKey(1) == 'q') {
break;
}
}
// 释放VideoCapture对象
cap.release();
// 关闭所有OpenCV窗口
cv::destroyAllWindows();
return 0;
}
从视频文件中读取视频
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 创建VideoCapture对象,参数为视频文件路径
cv::VideoCapture cap("your_video_file.mp4");
// 检查视频文件是否成功打开
if (!cap.isOpened()) {
std::cerr << "无法打开视频文件" << std::endl;
return -1;
}
cv::Mat frame;
while (true) {
// 从视频文件中读取一帧
cap >> frame;
// 检查是否成功读取帧
if (frame.empty()) {
std::cerr << "视频结束或无法读取帧" << std::endl;
break;
}
// 显示帧
cv::imshow("视频播放", frame);
// 等待按键事件,按下 'q' 键退出
if (cv::waitKey(25) == 'q') {
break;
}
}
// 释放VideoCapture对象
cap.release();
// 关闭所有OpenCV窗口
cv::destroyAllWindows();
return 0;
}
获取和设置视频属性
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::VideoCapture cap("your_video_file.mp4");
if (!cap.isOpened()) {
std::cerr << "无法打开视频文件" << std::endl;
return -1;
}
// 获取视频的宽度、高度和帧率
double width = cap.get(cv::CAP_PROP_FRAME_WIDTH);
double height = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
double fps = cap.get(cv::CAP_PROP_FPS);
std::cout << "视频宽度: " << width << std::endl;
std::cout << "视频高度: " << height << std::endl;
std::cout << "帧率: " << fps << std::endl;
// 设置视频的亮度(这里仅作示例,某些属性可能因设备或视频格式不支持)
cap.set(cv::CAP_PROP_BRIGHTNESS, 0.5);
cv::Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) {
break;
}
cv::imshow("视频", frame);
if (cv::waitKey(25) == 'q') {
break;
}
}
cap.release();
cv::destroyAllWindows();
return 0;
}
cv::VideoWriter
cv::VideoWriter 是 OpenCV 中用于将视频帧写入文件的类。
基本的视频写入示例:从摄像头捕获视频,并将其写入名为 output.avi 的文件中。cv::VideoWriter 对象使用 XVID 编码格式,帧率和尺寸与摄像头捕获的相同。
写入特定格式的视频:读取一个 MP4 视频文件,并将其以 H264 编码格式写入另一个 MP4 文件 output.mp4 中。
写入处理后的视频帧:读取一个视频文件,将每一帧转换为灰度图后写入 output.avi 文件中。## 基本的视频写入
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 打开摄像头
cv::VideoCapture cap(0);
if (!cap.isOpened()) {
std::cerr << "无法打开摄像头" << std::endl;
return -1;
}
// 获取摄像头的帧率、宽度和高度
double fps = cap.get(cv::CAP_PROP_FPS);
int width = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int height = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
// 创建VideoWriter对象
cv::VideoWriter writer("output.avi", cv::VideoWriter::fourcc('X', 'V', 'I', 'D'), fps, cv::Size(width, height));
if (!writer.isOpened()) {
std::cerr << "无法创建VideoWriter对象" << std::endl;
cap.release();
return -1;
}
cv::Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) {
std::cerr << "无法读取帧" << std::endl;
break;
}
// 将帧写入视频文件
writer.write(frame);
// 显示帧
cv::imshow("摄像头视频", frame);
// 等待按键事件,按下 'q' 键退出
if (cv::waitKey(1) == 'q') {
break;
}
}
// 释放资源
cap.release();
writer.release();
cv::destroyAllWindows();
return 0;
}
写入特定格式的视频
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 读取现有视频
cv::VideoCapture cap("input.mp4");
if (!cap.isOpened()) {
std::cerr << "无法打开输入视频" << std::endl;
return -1;
}
// 获取视频的帧率、宽度和高度
double fps = cap.get(cv::CAP_PROP_FPS);
int width = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int height = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
// 创建VideoWriter对象,这里写入为MP4格式
cv::VideoWriter writer("output.mp4", cv::VideoWriter::fourcc('H', '2', '6', '4'), fps, cv::Size(width, height));
if (!writer.isOpened()) {
std::cerr << "无法创建VideoWriter对象" << std::endl;
cap.release();
return -1;
}
cv::Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) {
std::cerr << "视频结束或无法读取帧" << std::endl;
break;
}
// 将帧写入视频文件
writer.write(frame);
// 显示帧
cv::imshow("视频处理", frame);
// 等待按键事件,按下 'q' 键退出
if (cv::waitKey(1) == 'q') {
break;
}
}
// 释放资源
cap.release();
writer.release();
cv::destroyAllWindows();
return 0;
}
写入处理后的视频帧
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 读取现有视频
cv::VideoCapture cap("input.mp4");
if (!cap.isOpened()) {
std::cerr << "无法打开输入视频" << std::endl;
return -1;
}
// 获取视频的帧率、宽度和高度
double fps = cap.get(cv::CAP_PROP_FPS);
int width = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int height = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
// 创建VideoWriter对象
cv::VideoWriter writer("output.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, cv::Size(width, height));
if (!writer.isOpened()) {
std::cerr << "无法创建VideoWriter对象" << std::endl;
cap.release();
return -1;
}
cv::Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) {
std::cerr << "视频结束或无法读取帧" << std::endl;
break;
}
// 对帧进行处理,例如转换为灰度图
cv::Mat grayFrame;
cv::cvtColor(frame, grayFrame, cv::COLOR_BGR2GRAY);
// 将处理后的帧写入视频文件
writer.write(grayFrame);
// 显示处理后的帧
cv::imshow("处理后的视频", grayFrame);
// 等待按键事件,按下 'q' 键退出
if (cv::waitKey(1) == 'q') {
break;
}
}
// 释放资源
cap.release();
writer.release();
cv::destroyAllWindows();
return 0;
}
更多推荐

所有评论(0)