前言:
😊😊😊欢迎来到本博客😊😊😊
🌟🌟🌟 本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。
😊😊😊 具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客👉通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。
🎁🎁🎁支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!😙😙😙
文章目录
- 学习目标
- 一、均值卷积核的构建及分离性
- 1.1 相关概念
- 1.2 均值卷积核构建与分离
- 二、快速均值平滑
- 三、 C++实现
- 3.1 均值平滑
- 3.2 快速均值平滑
- 四、 总结
学习目标
- 了解均值平滑含义
- 熟悉均值卷积核的构建及分离
- 熟悉快速均值平滑原理及实现
- C++实现均值平滑、快速均值平滑案例
每一张图像都可能包含某种程度的噪声,噪声可以理解为由一种或者多种原因造成的灰度值的随机变化。
在大多数情况下,通过平滑技术(也常称为滤波技术)进行抑制或者去除,其中具备保持边缘(Edge Preserving)作用的平滑技术得到了更多的关注。
常用的平滑处理算法包括基于二维离散卷积的高斯平滑、均值平滑,基于统计学方法的中值平滑,具备保持边缘作用的平滑算法的双边滤波、导向滤波等。
下面将详细介绍均值平滑技术原理、常见应用及实现。
一、均值卷积核的构建及分离性
1.1 相关概念
均值平滑,顾名思义,图像中每一个位置的邻域的平均值作为该位置的输出值。
高为H、宽为W 的均值卷积算子的构建方法很简单,令所有元素均为1/(W*H),即:
1.2 均值卷积核构建与分离
与高斯滤波核一样,均值平滑算子也是可分离卷积核,即:
例如:5行3列的均值平滑算子可以进行以下分离:
代码实现与分离的高斯卷积是类似的,只需将高斯算子替换成均值算子即可。利用卷积核的分离性和卷积的结合律。
虽然减少了运算量,但是随着卷积核窗口大小的增加,计算量仍会继续增大,可以利用图像的积分,实现时间复杂度为O(1)的快速均值平滑。
二、快速均值平滑
学习快速均值平滑之前,先了解一下图像的积分。r行c列的图像矩阵I
的积分Integral
可以这样定义:
即任意一个位置的积分等于该位置左上角所有值的和。例如:
同时,也可以利用矩阵的积分,计算出矩阵中任意矩形区域的和:
例如:计算I
的以(2,2)为中心,从左上角(rTop,cLeft)=(1,1)至右下角(rBottom,cRight)=(3,3)的矩形区域的和:
可以从积分后的图像矩阵中找到对应的值计算:
即:5+1+7+1+5+9+2+6+2=54+1-8-9
均值平滑的原理本质上是计算任意一个点的邻域的平均值,而平均值是由该邻域的和除以邻域的面积得到的。这样无论怎样改变平滑窗口的大小,都可以利用图像的积分图快速计算每个点的邻域的和。
对于图像积分,OpenCV提供了函数:
void cv::integral(InputArray src,
OutputArray sum,
int sdepth = -1
)
参数 | 解释 |
---|---|
src | 输入H×W 矩阵,数据类型为CV_8U、CV_32F或者为CV_64F |
sum | 输出矩阵,大小为(H+1)×(W+1) |
depth | 输出图位深,若为-1则与src一致 |
接下来介绍OpenCV均值平滑函数及C++实现快速均值平滑。
三、 C++实现
3.1 均值平滑
OpenCV提供了函数:
void cv::blur(InputArray src,
OutputArray dst,
Size ksize,
Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT
)
参数 | 解释 |
---|---|
src | 输入矩阵,数据类型为CV_8U、CV_32F或者为CV_64F |
dst | 输出矩阵,大小和数据类型与src一致 |
ksize | 均值平滑卷积核尺寸大小,Size(宽,高) |
anchor | 锚点,Point(-1,-1)表示中心点 |
borderType | 边界扩充类型 |
注:边界扩充说明
例如:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include <cmath>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
int main(){
cv::Mat src=cv::imread("image/path");
cv:Mat dst;
cv::blur(src,dst,Size(3,3),cv::Point(-1,-1));
cv::imshow("src",src);
cv::imshow("dst",dst);
cv::waitKey(0);
return 0;
还有一个函数,也能实现均值平滑功能:
void cv::boxFilter(InputArray src,
OutputArray dst,
int ddepth,
Size ksize,
Pointanchor = Point(-1,-1),
bool normalize = true,
int borderType = BORDER_DEFAULT
)
参数 | 解释 |
---|---|
src | 输入矩阵,数据类型为CV_8U、CV_32F或者为CV_64F |
dst | 输出矩阵,大小和数据类型与src一致 |
depth | 输出图位深,若为-1则与src一致 |
ksize | 均值平滑卷积核尺寸大小,Size(宽,高) |
anchor | 锚点,Point(-1,-1)表示中心点 |
normalize | 是否归一化 |
borderType | 边界扩充类型(具体详见上面blur() ) |
3.2 快速均值平滑
通过定义函数fastMeanBlur()
来实现快速均值平滑,其中该函数的参数image
代表输入图像,winSize
代表平滑窗口的尺寸,borderType
代表边界扩充类型。具体代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include <cmath>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
//快速均值平滑滤波
cv::Mat fastMeanBlur(cv::Mat image,cv::Size winSize,int boderType,Scalar value=Scalar()) {
//判断滑动窗口大小是否为奇数
int hei = winSize.height;
int wei = winSize.width;
CV_Assert(hei % 2 == 1 && wei % 2 == 1);
//滑动窗口的中心点
int h_center = (hei - 1) / 2;
int w_center = (wei - 1) / 2;
//滑动窗口面积
float area = float(hei * wei);
cv::Mat padImg;
//边界扩充
cv::copyMakeBorder(image,padImg,h_center, h_center,w_center,w_center,boderType,value);
//图像积分
cv::Mat integralImage;
cv::integral(padImg, integralImage,CV_32FC1);
//输入图像矩阵宽高
int cols = image.cols;
int rows = image.rows;
int c = 0, r = 0;
Mat meanImage = Mat::zeros(image.size(), CV_32FC1);
for (int h = h_center;h < h_center+rows; h++)
{
for (int w = 0; w < w_center+cols; w++)
{
float BottomRight = integralImage.at<float>(h + h_center + 1, w + w_center + 1);
float TopLeft = integralImage.at<float>(h - h_center, w - w_center);
float TopRight = integralImage.at<float>(h + h_center + 1, w -w_center);
float BottomLeft = integralImage.at<float>(h - h_center,w + w_center+1);
meanImage.at<float>(r, c) =(BottomRight + TopLeft - TopRight - BottomLeft) / area;
c++;
}
r++;
c = 0;
}
return meanImage;
}
}
四、 总结
最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。