一:内容介绍
本节主要介绍OpenCV的imgproc模块的图像处理部分:
1. 线性滤波:均值滤波与高斯滤波
2. 非线性滤波:中值滤波、双边滤波
3. 图像形态学:腐蚀与膨胀,开运算、闭运算,形态学梯度,顶帽、黑帽
4. 漫水填充
5. 图像金字塔及图片尺寸缩放
6. 阈值化
二:学习笔记
- 方框滤波(box filter)是不一定归一化的,而这里说的几种线性和非线性滤波方法都是必须归一化的。
- 这里需要说一下卷积和滤波还是有点区别的,不过当核对称时它们俩就一样了。
2维滤波公式:
h[m,n]=∑k,lg[k,l]f[m+k,n+l]
2维卷积公式:
h[m,n]=∑k,lg[k,l]f[m−k,n−l]
- 高斯分布一定要牢记,随手写出。
期望为0的一维高斯分布:
G(x)=12π√σe−x22σ2
二维:
G(x)=12πσ2e−x2+y22σ2
- 这里说一下双边滤波,是结合图像的空间邻近度和像素值相似度的一种折中处理,既能平滑过渡区域、又可以保持边缘等细节信息,达到保边去噪的目的。但是其速度很慢。
- 图像形态学,是基于形状的一系列图像处理操作,最基本的就是膨胀(dilate)和腐蚀(erode)。腐蚀膨胀操作也可以理解为 max, min操作。如图:
话说感觉好多形态学并不是太常用啊。 - 图像金字塔是一种以多分辨率来解释图像的有效但概念简单的结构,主要用到了卷积。
- 关于阈值化,现在有很多自动阈值化的方法。如 大津算法(OTSU)
- 本节函数清单:
三:相关源码及解析
本章示例较多,示例列表:
1.图像滤波
2.腐蚀与膨胀
3.漫水填充
4.图像金字塔
5.基本阈值操作
1. 图像滤波
源码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5;
int g_nBoxFilterValue = 6;
int g_nMeanBlurValue = 10;
int g_nGaussianBlurValue = 6;
int g_nMedianBlurValue = 10;
int g_nBilateralFilterValue = 10;
static void on_BoxFilter(int, void *);
static void on_MeanBlur(int, void *);
static void on_GaussianBlur(int, void *);
static void on_MedianBlur(int, void *);
static void on_BilateralFilter(int, void *);
int main()
{
g_srcImage = imread("poster_girl_2.jpg");
g_dstImage1 = g_srcImage.clone();
g_dstImage2 = g_srcImage.clone();
g_dstImage3 = g_srcImage.clone();
g_dstImage4 = g_srcImage.clone();
g_dstImage5 = g_srcImage.clone();
namedWindow("【原图窗口】");
imshow("【原图窗口】", g_srcImage);
namedWindow("【方框滤波】");
createTrackbar("内核值:", "【方框滤波】", &g_nBoxFilterValue, 50, on_BoxFilter);
on_BoxFilter(g_nBoxFilterValue, 0);
namedWindow("【均值滤波】");
createTrackbar("内核值:", "【均值滤波】", &g_nMeanBlurValue, 50, on_MeanBlur);
on_MeanBlur(g_nMeanBlurValue, 0);
namedWindow("【高斯滤波】");
createTrackbar("内核值:", "【高斯滤波】", &g_nGaussianBlurValue, 50, on_GaussianBlur);
on_GaussianBlur(g_nGaussianBlurValue, 0);
namedWindow("【中值滤波】");
createTrackbar("内核值:", "【中值滤波】", &g_nMedianBlurValue, 50, on_MedianBlur);
on_MedianBlur(g_nMedianBlurValue, 0);
namedWindow("【双边滤波】");
createTrackbar("内核值:", "【双边滤波】", &g_nBilateralFilterValue, 50, on_BilateralFilter);
on_BilateralFilter(g_nBilateralFilterValue, 0);
cout << "运行成功,请调整滚动条观察图像效果" << endl << "按下'q'键时,程序退出";
while (char(waitKey(1)) != 'q');
return 0;
}
static void on_BoxFilter(int, void *)
{
boxFilter(g_srcImage, g_dstImage1, -1, Size(g_nBoxFilterValue+1, g_nBoxFilterValue+1));
imshow("【方框滤波】", g_dstImage1);
}
static void on_MeanBlur(int, void *)
{
blur(g_srcImage, g_dstImage2, Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1));
imshow("【均值滤波】", g_dstImage2);
}
static void on_GaussianBlur(int, void *)
{
GaussianBlur(g_srcImage, g_dstImage3, Size(g_nGaussianBlurValue*2 + 1, g_nGaussianBlurValue*2 + 1), 0);
imshow("【高斯滤波】", g_dstImage3);
}
static void on_MedianBlur(int, void *)
{
medianBlur(g_srcImage, g_dstImage4, g_nMedianBlurValue*2 + 1);
imshow("【中值滤波】", g_dstImage4);
}
static void on_BilateralFilter(int, void *)
{
bilateralFilter(g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue*2, g_nBilateralFilterValue / 2);
imshow("【双边滤波】", g_dstImage5);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
素材:
效果图:
提示:
无
2. 腐蚀与膨胀
源码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
Mat g_srcImage, g_dstImage;
int g_erodeOrDilate = 0;
int g_nStructElementSize = 3;
void Process();
void on_TrackbarNumChange(int, void*);
void on_ElementSizeChange(int, void*);
int main()
{
g_srcImage = imread("poster_cat_1.jpg");
namedWindow("【原始图】");
imshow("【原始图】", g_srcImage);
namedWindow("【效果图】");
Mat element = getStructuringElement(MORPH_RECT, Size(2*g_nStructElementSize+1, 2 * g_nStructElementSize + 1));
erode(g_srcImage, g_dstImage, element);
imshow("【效果图】", g_dstImage);
createTrackbar("腐蚀/膨胀", "【效果图】", &g_erodeOrDilate, 1, on_TrackbarNumChange);
createTrackbar("内核尺寸", "【效果图】", &g_nStructElementSize, 21, on_ElementSizeChange);
cout << "运行成功,请调整滚动条观察图像效果" << endl << "按下'q'键时,程序退出";
while (char(waitKey(1)) != 'q');
return 0;
}
void Process()
{
Mat element = getStructuringElement(MORPH_RECT, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));
if (g_erodeOrDilate==0) {
erode(g_srcImage, g_dstImage, element);
}
else {
dilate(g_srcImage, g_dstImage, element);
}
imshow("【效果图】", g_dstImage);
}
void on_TrackbarNumChange(int, void *)
{
Process();
}
void on_ElementSizeChange(int, void *)
{
Process();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
素材:
效果图:
提示:
无
3 . 漫水填充
源码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage;
int g_nFillMode = 1;
int g_nLowDifference = 20, g_nUpDifference = 20;
int g_nConnectivity = 4;
int g_bIsColor = true;
bool g_bUseMask = false;
int g_nNewMaskVal = 255;
static void onMouse(int event, int x, int y, int, void*);
int main()
{
g_srcImage = imread("poster_home.jpg");
g_dstImage=g_srcImage.clone();
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
g_maskImage.create(g_srcImage.rows+2, g_srcImage.cols+2, CV_8UC1);
namedWindow("【效果图】");
createTrackbar("负差最大值", "【效果图】", &g_nLowDifference, 255, 0);
createTrackbar("正差最大值", "【效果图】", &g_nUpDifference, 255, 0);
setMouseCallback("【效果图】", onMouse, 0);
while (1)
{
imshow("【效果图】", g_bIsColor ? g_dstImage:g_grayImage);
int c = waitKey(0);
if ((char)c == 27)
{
cout << "程序退出........." << endl;
break;
}
switch ((char)c)
{
case'1':
if (g_bIsColor) {
cout << "键盘'1'被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】" << endl;
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
g_maskImage = Scalar::all(0);
g_bIsColor = false;
}
else {
cout << "键盘'1'被按下,切换彩色/灰度模式,当前操作为将【灰度模式】切换为【彩色模式】" << endl;
g_srcImage.copyTo(g_dstImage);
g_maskImage = Scalar::all(0);
g_bIsColor = true;
}
break;
case'2':
if (g_bUseMask) {
destroyWindow("【mask】");
g_bUseMask = false;
}
else {
namedWindow("【mask】", WINDOW_NORMAL);
g_maskImage = Scalar::all(0);
imshow("【mask】", g_maskImage);
g_bUseMask = true;
}
break;
case'3':
cout << "按键'3'被按下,恢复原始图像" << endl;
g_srcImage.copyTo(g_dstImage);
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
g_maskImage = Scalar::all(0);
break;
case'4':
cout << "按键'4'被按下,使用空范围的漫水填充" << endl;
g_nFillMode = 0;
break;
case'5':
cout << "按键'5'被按下,使用渐变、固定范围的漫水填充" << endl;
g_nFillMode = 1;
break;
case'6':
cout << "按键'6'被按下,使用渐变、浮动范围的漫水填充" << endl;
g_nFillMode = 2;
break;
case'7':
cout << "按键'7'被按下,操作标志符的低八位使用4位的连接模式" << endl;
g_nConnectivity = 4;
break;
case'8':
cout << "按键'8'被按下,操作标志符的低八位使用8位的连接模式" << endl;
g_nConnectivity = 8;
break;
default:
break;
}
}
return 0;
}
static void onMouse(int event, int x, int y, int, void*)
{
if (event != EVENT_LBUTTONDOWN)
return;
Point seed = Point(x, y);
int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;
int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;
int flags = g_nConnectivity + (g_nNewMaskVal<<8) + (g_nFillMode==1?FLOODFILL_FIXED_RANGE:0);
int b = (unsigned)theRNG() & 255;
int g = (unsigned)theRNG() & 255;
int r = (unsigned)theRNG() & 255;
Rect ccomp;
Scalar newVal = g_bIsColor ? Scalar(b, g, r) : Scalar(r*0.299+g*0.587+b*0.114);
Mat dst = g_bIsColor ? g_dstImage : g_grayImage;
int area;
if (g_bUseMask) {
threshold(g_maskImage, g_maskImage, 1, 128, THRESH_BINARY);
area = floodFill(dst, g_maskImage, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference),
Scalar(UpDifference, UpDifference, UpDifference), flags);
imshow("【mask】", g_maskImage);
}
else {
area = floodFill(dst, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference),
Scalar(UpDifference, UpDifference, UpDifference), flags);
}
imshow("【效果图】", dst);
cout << area << " 个像素被重绘" << endl;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
素材:
效果图:
提示:
无
4 . 图像金字塔
源码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME "【程序窗口】"
Mat g_srcImage, g_dstImage, g_tmpImage;
int main()
{
g_srcImage = imread("poster_car_1.jpg");
namedWindow(WINDOW_NAME);
imshow(WINDOW_NAME, g_srcImage);
g_tmpImage = g_srcImage;
g_dstImage = g_tmpImage;
int key = 0;
while (1)
{
key = waitKey(9);
switch (key) {
case 27:
case 'q':
return 0;
break;
case '1':
pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols*2, g_tmpImage.rows*2));
cout << "检测到按键【1】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸*2" << endl;
break;
case '2':
pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
cout << "检测到按键【2】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2" << endl;
break;
case '3':
resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
cout << "检测到按键【3】被按下,开始进行基于【resize】函数的图片放大:图片尺寸*2" << endl;
break;
case '4':
resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
cout << "检测到按键【4】被按下,开始进行基于【pyrUp】函数的图片缩小:图片尺寸/2" << endl;
break;
}
imshow(WINDOW_NAME, g_dstImage);
g_tmpImage = g_dstImage;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
素材:
效果图:
提示:
无
5 . 基本阈值操作
源码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void on_Threshold(int, void*);
#define WINDOW_NAME "【程序窗口】"
int g_nThresholdValue = 100;
int g_nThresholdType = 0;
Mat g_srcImage, g_grayImage, g_dstImage;
int main()
{
g_srcImage = imread("poster_landscape_1.jpg");
cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
namedWindow(WINDOW_NAME);
createTrackbar("模式", WINDOW_NAME, &g_nThresholdType, 4, on_Threshold);
createTrackbar("参数值", WINDOW_NAME, &g_nThresholdValue, 255, on_Threshold);
on_Threshold(0, 0);
while (waitKey(10) != 27);
return 0;
}
void on_Threshold(int, void*)
{
threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, g_nThresholdType);
imshow(WINDOW_NAME, g_dstImage);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
素材:
效果图:
提示:
无