opencv图像处理
- Ai
- 1天前
- 30热度
- 0评论
图像滤波
图像的滤波核心是使用一个小的矩阵(滤波器或卷积核)在图像上进行滑动卷积,将计算得到的结果作为目标像素的值。
均值滤波
cv::blur(InputArray src, OutputArray dst, Size ksize,
Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT);
- src:输入的原始图像,支持Mat格式
- dst:经过滤波处理的图像
- ksize:滤波器大小,通常为2D大小,表示滤波的宽度和高度。
- anchor:锚点指定窗口内的参考点,默认是Point(-1,-1)表示窗口中心点
- boarderType:边界类型,用来处理边界参数的外推方式。
cv::Mat src = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat dst;
cv::blur(src, dst, cv::Size(3, 3)); // 3x3的均值滤波
高斯滤波
void cv::GaussianBlur( const cv::Mat& src, cv::Mat& dst, cv::Size ksize,
double sigmaX, double sigmaY = 0, int borderType = cv::BORDER_DEFAULT );
- sigmaX:X方向的标准差,决定模糊的程度,值越大,模糊效果越强。
- sigmaY:Y方向的标准差,默认是0,表示与X方向相同。
cv::GaussianBlur(src, dst, cv::Size(5, 5), 0); // 5x5的高斯滤波
中值滤波
void medianBlur(InputArray src, OutputArray dst, int ksize);
- ksize: 滤波器窗口大小
示例
cv::medianBlur(src, dst, 5); // 5x5的中值滤波
示例
下面示例,将图片使用高斯噪音处理,然后再使用滤波器进行模糊,噪音就可以看起来过滤掉。
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <linux/fb.h>
#include <signal.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void add_gaussian_noise(Mat& image, double mean, double stddev)
{
Mat noise(image.size(), image.type());
randn(noise, mean, stddev);
image = image + noise;
}
void display_image(const Mat& image, int framebuffer_width = 720)
{
static std::ofstream ofs("/dev/fb0");
cv::Mat framebuffer;
cv::cvtColor(image, framebuffer, cv::COLOR_BGR2BGRA);
cv::Size2f frame_size = framebuffer.size();
for (int y = 0; y < frame_size.height; y++) {
ofs.seekp(y * framebuffer_width * 4);
ofs.write(reinterpret_cast<char*>(framebuffer.ptr(y)), frame_size.width * 4);
}
}
int main(int, char**)
{
Mat image = imread("test1.jpg", IMREAD_COLOR);
if (image.empty()) {
cerr << "Error: Could not open or find the image!" << endl;
return -1;
}
double mean = 2.0;
double stddev = 30.0;
add_gaussian_noise(image, mean, stddev);
display_image(image);
cv::imwrite("noise.jpg", image);
usleep(1000 * 1000 * 2);
Mat dst;
cv::GaussianBlur(image, dst, cv::Size(3, 3), 1);
//cv::medianBlur(image, dst, 5);
//cv::blur(src, dst, cv::Size(3, 3));
display_image(dst);
cv::imwrite("dst.jpg", dst);
}
如下图,中间是加了噪声的,最后一张是通过高斯滤波处理之后的。
图像形态学
膨胀与腐蚀
膨胀操作会增加图像中的白色区域或前景区域,使得图像中的对象或结构变得更大。具体来说,它会将图像中的每个像素点扩展到其邻域像素中(根据结构元素的大小和形状)。这意味着如果结构元素的一部分重叠在一个前景区域内,该区域就会被扩展。使白色区域扩展,物体变大。
cv2.dilate(src, dst=None, kernel, anchor=(-1, -1), iterations=1,
borderType=cv2.BORDER_CONSTANT, borderValue=0)
- iteration:膨胀迭代次数,越大效果越明显
- borderType:边界填充类型
- borderValue:边界填充值,默认0是黑色
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
//创建一个3*3的矩形结构元素
cv::dilate(src, dst, kernel);
腐蚀操作的效果与膨胀相反,它会减少图像中的白色区域或前景区域,使得图像中的对象变得更小。腐蚀操作会检查每个像素的邻域,如果邻域内的所有像素都是前景像素(白色),该像素才保持前景,否则变为背景(黑色)。使白色区域收缩,物体变小。
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::erode(src, dst, kernel);
以下是示例
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <linux/fb.h>
#include <signal.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <opencv2/opencv.hpp>
void display_image(const cv::Mat& image, int framebuffer_width = 720)
{
static std::ofstream ofs("/dev/fb0");
if (!ofs) {
std::cerr << "Error: Could not open framebuffer device!" << std::endl;
return;
}
cv::Mat framebuffer;
if (image.channels() == 1) {
cv::cvtColor(image, framebuffer, cv::COLOR_GRAY2BGRA);
} else if (image.channels() == 3) {
cv::cvtColor(image, framebuffer, cv::COLOR_BGR2BGRA);
} else if (image.channels() == 4) {
image.copyTo(framebuffer);
} else {
std::cerr << "Error: Unsupported image format!" << std::endl;
return;
}
cv::Size2f frame_size = framebuffer.size();
if (frame_size.width > framebuffer_width) {
// 计算新的宽高,保持图像的纵横比
float aspect_ratio = frame_size.height / frame_size.width;
int newWidth = framebuffer_width;
int newHeight = static_cast<int>(newWidth * aspect_ratio);
// 调整图像大小
cv::Mat resizedImage;
cv::resize(framebuffer, resizedImage, cv::Size(newWidth, newHeight));
framebuffer = resizedImage;
frame_size = resizedImage.size();
}
for (int y = 0; y < frame_size.height; y++) {
ofs.seekp(y * framebuffer_width * 4);
ofs.write(reinterpret_cast<char*>(framebuffer.ptr(y)), frame_size.width * 4);
}
}
int main(int, char**)
{
cv::Mat image = imread("test1.jpg", cv::IMREAD_COLOR);
if (image.empty()) {
std::cerr << "Error: Could not open or find the image!" << std::endl;
return -1;
}
double mean = 2.0;
double stddev = 30.0;
display_image(image);
usleep(1000 * 1000 * 1);
cv::Mat dst;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::dilate(image, dst, kernel);
display_image(dst);
usleep(1000 * 1000 * 1);
cv::erode(image, dst, kernel);
display_image(dst);
}
阈值化
阈值(Thresholding)是一种常见的图像分割方法,用于将灰度图像转换为二值图像。通过设置一个阈值,将像素值高于该阈值的区域设为白色(255),低于阈值的区域设为黑色(0),从而简化图像内容,便于后续处理。
二值化阈值:二值化是将图像中的像素值根据设定的阈值分为两类,通常用于简单的图像分割。
cv::threshold(src, dst, thresh, maxval, type);
示例:cv::threshold(src, dst, 127, 255, cv::THRESH_BINARY);
- thresh: 阈值,决定像素分类的分界线。
- maxval:根据type选择,满足条件设置为最大值。
- type: 阈值类型
自适应阈值:根据图像的局部区域动态计算阈值,适用于光照不均匀的图像。
cv::adaptiveThreshold( src, dst, maxValue, adaptiveMethod,
thresholdType, blockSize, C);
- maxVaule:二值化的最大值,一般为255
- adaptiveMethod: ADAPTIVE_THRESH_MEAN_C是使用邻域的均值作为阈值,ADAPTIVE_THRESH_GAUSSIAN_C :使用邻域的加权均值(高斯加权)作为阈值。
- thresholdType:阈值类型,通常使用 THRESH_BINARY 或 THRESH_BINARY_INV。
- blockSize:邻域的大小,必须是奇数(例如 3 , 5 , 7 , ...),该窗口在图像上滑动。
- C:调整值,用于从计算的阈值中减去。调整结果的灵敏度。
cv::adaptiveThreshold(src, dst, 255,
cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 11, 2);
开闭运算、顶帽、黑帽
- 开运算:开运算是先进行腐蚀操作再进行膨胀操作。它通常用于去除小的噪点或小的物体,同时保留较大区域的结构。
- 闭运算:闭运算是先进行膨胀操作再进行腐蚀操作。它用于去除小的黑色区域(如小孔洞或空隙),并连接物体之间的细小裂缝。
- 顶帽:顶帽是开运算的结果与原图像之间的差异,主要用于提取比背景更亮的区域或小的亮点。
- 黑帽:黑帽是闭运算的结果与原图像之间的差异,主要用于提取比背景更暗的区域或小的暗点。
void cv::morphologyEx( InputArray src, OutputArray dst, int op,
InputArray kernel, Point anchor = Point(-1, -1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar&borderValue=morphologyDefaultBorderValue() );
- kernel:结构元素,通常使用 cv::getStructuringElement() 生成。
- op:形态学操作类型。
下面是op的类型
- cv:MORPH_OPEN:开运算。
- cv::MORPH_CLOSE:闭运算。
- cv::MORPH_GRADIENT: 形态学梯度(膨胀 - 腐蚀),提取物体边缘。
- cv::MORPH_TOPHAT :顶帽运算(原图 - 开运算结果),提取比背景亮的区域。
- cv::MORPH_BLACKHAT 黑帽运算(闭运算结果 - 原图),提取比背景暗的区域。
示例
示例程序如下:
int main(int, char**)
{
cv::Mat image = imread("test1.jpg", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: Could not open or find the image!" << std::endl;
return -1;
}
cv::Mat binary_img;
cv::threshold(image, binary_img, 128, 255, cv::THRESH_BINARY);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::Mat dst;
cv::Mat opened_img, closed_img, tophat_img, blackhat_img;
cv::morphologyEx(binary_img, opened_img, cv::MORPH_OPEN, kernel);
cv::morphologyEx(binary_img, closed_img, cv::MORPH_CLOSE, kernel);
cv::morphologyEx(binary_img, tophat_img, cv::MORPH_TOPHAT, kernel);
cv::morphologyEx(binary_img, blackhat_img, cv::MORPH_BLACKHAT, kernel);
display_image(image);
usleep(1000 * 1000 * 2);
display_image(binary_img);
usleep(1000 * 1000 * 2);
display_image(closed_img);
usleep(1000 * 1000 * 2);
display_image(tophat_img);
usleep(1000 * 1000 * 2);
display_image(blackhat_img);
}
图像边缘检测
canny
Canny边缘检测是一个多阶段的图像处理算法,用于提取图像中的边缘。它的目标是识别图像中强度变化较大的区域,即边缘
int main(void)
{
cv::Mat image = cv::imread("test1.jpg", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: Could not open image!" << std::endl;
return -1;
}
// 进行 Canny 边缘检测
cv::Mat edges;
cv::Canny(image, edges, 100, 200); // 低阈值 100,高阈值 200
display_image(edges);
return 0;
}
下面是处理的前后效果。
霍夫变换
霍夫变换(Hough Transform)是一种用于检测图像中的几何形状(如直线、圆等)的方法。它主要用于图像中的特征检测,特别是在噪声较大的图像中,可以有效地进行形状的识别。霍夫变换的基本思想是将图像中的边缘点通过一种数学映射,转化到参数空间,然后在参数空间中寻找对应的曲线(或直线、圆等)。
霍夫线变换: 检测线。
cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)
- image: 输入图像,通常是经过边缘检测(如 Canny 边缘检测)后的二值图像。
- rho: 直线距离精度,单位是像素,通常设置为 1。
- theta: 角度精度,单位是弧度,通常设置为 np.pi / 180(即 1°)。
- threshold: 最小投票数,当某一候选直线在参数空间中的投票数大于此阈值时,认为检测到直线。
- minLineLength: 最小直线长度。只有长度大于该值的直线才会被返回。
- maxLineGap: 最大直线间隙。如果两段直线之间的间隙小于该值,则认为它们是同一条直线的两部分
int main(void)
{
cv::Mat img = cv::imread("test1.jpg");
if (img.empty()) {
std::cerr << "Error: Could not open image!" << std::endl;
return -1;
}
// 图像预处理:边缘检测
cv::Mat edges;
cv::Canny(img, edges, 50, 150); // 使用 Canny 边缘检测
display_image(edges);
usleep(1000 * 1000);
// 使用 HoughLinesP 进行霍夫变换(概率霍夫变换)
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 50, 10); // 参数为 (边缘图像, 输出的直线, ρ精度, θ精度, 阈值, 最小线段长度, 最大线段间隙)
// 绘制检测到的直线
cv::Mat result = img.clone();
//cv::cvtColor(result, result, cv::COLOR_GRAY2BGR); // 将灰度图转为彩色图以显示彩色直线
for (size_t i = 0; i < lines.size(); i++) {
cv::Vec4i l = lines[i];
cv::line(result, cv::Point(l[0], l[1]),
cv::Point(l[2], l[3]), cv::Scalar(255, 0, 0), 2); // 绘制直线
}
display_image(result);
return 0;
}
上面的示例中,先使用canny进行边缘处理,然后送入到霍夫变换中进行检测,最后绘制成直线。下面是检测后的效果。
霍夫圆变换: 检测圆。
cv2.HoughCircles(image, method, dp, minDist, param1, param2, minRadius, maxRadius)
- image: 输入图像,通常是灰度图像。
- method: 霍夫变换的检测方法。对于 HoughCircles,一般使用 cv2.HOUGH_GRADIENT。
- dp: 分辨率反比,表示霍夫空间的分辨率与图像分辨率的比例。通常设置为 1。
- minDist: 设定圆心之间的最小距离,防止检测到相互靠得太近的圆。
- param1: 边缘检测的高阈值(Canny 边缘检测的高阈值)。
- param2: 圆的中心检测阈值,较小的值会检测到更多的圆,较大的值会检测较少的圆。
- minRadius: 最小圆半径(单位:像素)。
- maxRadius: 最大圆半径(单位:像素)。
int main(void)
{
cv::Mat img = cv::imread("test1.jpg");
if (img.empty()) {
std::cerr << "Error: Could not open image!" << std::endl;
return -1;
}
// 进行边缘检测
cv::Mat edges;
cv::Canny(img, edges, 50, 150);
display_image(edges);
usleep(1000 * 1000);
// 存储检测到的圆
std::vector<cv::Vec3f> circles;
// 使用霍夫圆变换检测圆
cv::HoughCircles(edges, circles, cv::HOUGH_GRADIENT, 1, 23, 80, 52, 27, 100);
// 在原图上绘制检测到的圆
cv::Mat result = img.clone();
for (size_t i = 0; i < circles.size(); i++) {
cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// 绘制圆心
cv::circle(result, center, 3, cv::Scalar(0, 255, 0), -1);
// 绘制圆
cv::circle(result, center, radius, cv::Scalar(255, 0, 0), 3);
}
display_image(result);
return 0;
}
同样是先使用canny检测进行处理,然后再使用霍夫圆进行检测圆,最后试用cv::circle进行绘制。