OpenCV计算机图像视觉基础学习笔记4——还是对图像的操作

这部分主要是讲述调整图像亮度和对比度以及模糊图像。


调整图像亮度和对比度

对图像的变换可以看作两种方式。一种是像素变换,对点的操作;另一种是邻域变换,对区域的操作。调整图像的亮度和对比度是基于像素变换的。至于邻域变换,它可以用来做图像的卷积,图像特征的提取,图像梯度的计算,模糊操作,平滑操作等等。。。

这里给出调整亮度和对比度的一般公式。

其中β是增益变量,且满足α>0(对于常规图像)。

思考一下,对亮度的提高是怎样体现在公式上呢?不难想到,当图像亮度提高,图像的整体颜色是会发白的,整张图的颜色会越来越向白色靠拢。思考至每个像素点,当图像亮度提高,其RGB值的三个数值也会提高。为什么?因为(255,255,255)是白色,根据之前的讨论,图像向白色靠拢也就意味着每个像素点都像(255,255,255)靠拢。

图像的对比度简单来理解是像素值之间的绝对差距。它的严格定义是这样的:对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量。那么,让这个最亮的白和这个最暗的黑之间的差距变大,反映在图中也就是对比度增大了。反映在公式上,我们可以调整α的值改变图像的像素,当像素值成倍增大时,对比度也就增大了。举个不太严谨的例子,假设说原来的最小值和最大值为[2,102],差值为100,通过调整α把它扩大一倍,就变成了[4,204],差值变成了200,则对比度也变成了原来的一倍。

复习一下可能会用到的API:

1
2
3
4
5
6
Mat new_image = Mat::zeros(image.size(),image.type());
// 创建一张和原图大小一致的空白图像,由于使用了Mat::zeros,确保了所有像素都是(0,0,0)
saturate_cast<uchar>(value);
// 确保像素值不会低于0或高于255
Mat.at<Vec3b>(y,x)[index] = value;
// 给每个像素点通道赋值

做出来的效果是这样的(alpha = 1.2, beta = 30):

代码思路和之前类似,核心语句dst.at<uchar>(row, col) = saturate_cast<uchar>(v * alpha + beta);,即对每个像素点跑一遍之前的公式。

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
#include <opencv2/opencv.hpp>
#include <iostream>
#define debug cout << "ok" << endl;

using namespace cv;
using namespace std;

int main() {
Mat src, dst;
src = imread("F:/blog配图/avatar1.jpg");
if (!src.data) {
cout << "could not load image..." << endl;
return -1;
}
char input_title[] = "input image";
namedWindow(input_title, CV_WINDOW_AUTOSIZE);
imshow(input_title , src);

int height = src.rows;
int width = src.cols;
dst = Mat::zeros(src.size(), src.type());
float alpha = 1.2;
float beta = 30;

for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (src.channels() == 1) {
float v = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(v * alpha + beta);
}
else if (src.channels() == 3) {
float b = src.at<Vec3b>(row, col)[0];
float g = src.at<Vec3b>(row, col)[1];
float r = src.at<Vec3b>(row, col)[2];

dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b * alpha + beta);
dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g * alpha + beta);
dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r * alpha + beta);

}
}
}

char output_title[] = "contrast and brightness change demo";
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
imshow(output_title, dst);

waitKey(0);
return 0;
}

模糊图像

Smooth/Blur是图像处理中最简单和常用的操作之一,使用该操作的原因之一就是为了给图像预处理的时候减低噪声。该操作背后是一个卷积计算。通常这些卷积算子计算都是线性操作,所以又叫线性滤波。

计算过程可以这样直观的看一下:

这是一个6×6网格,计算卷积的算子为一个3×3的区域,该区域从左向右,从上向下移动,计算方式为所有黄色块的像素点值求和取平均赋给中间的红色块。每次移动一个像素格。

这种操作叫做归一化盒子滤波,也叫均值滤波。公式如下。

此外还有一种比较有用的滤波器叫高斯滤波。 高斯滤波是将输入数组的每一个像素点与高斯内核卷积,将卷积和当作输出像素值。一维的高斯函数像下图这样:

给出二维高斯函数的表达式。

其中 μ为均值 (峰值对应位置),σ代表标准差 (变量x和变量y各有一个均值,也各有一个标准差),A是归一化系数。

可能会用到的API:

1
2
3
4
blur(Mat src, Mat dst, Size(xraius, yradius), Point(-1, -1)); 
// 均值模糊,Point(-1, -1)为默认中心位置,一般不用改
GaussianBlur(Mat src, Mat dst, Size(11, 11), sigmax, sigmay);
// 高斯模糊,其中Size(x, y)中的x和y必须是正奇数

使用均值模糊之后的效果:

其实很简单。源代码:

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
#include <opencv2/opencv.hpp>
#include <iostream>
#define debug cout << "ok" << endl;

using namespace cv;
using namespace std;

int main() {
Mat src, dst;
src = imread("F:/blog配图/avatar1.jpg");
if (!src.data) {
cout << "could not load image..." << endl;
return -1;
}
char input_title[] = "input image";
char output_title[] = "blur image";
namedWindow(input_title, CV_WINDOW_AUTOSIZE);
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
imshow(input_title, src);

blur(src, dst, Size(5, 5), Point(-1, -1));
imshow(output_title, dst);

waitKey(0);
return 0;
}

稍微修改一下代码得到高斯模糊:

1
2
GaussianBlur(src, dst, Size(111, 111), 1919, 810);
imshow(output_title, dst);

不过这两种模糊都有一些缺陷。均值模糊无法克服边缘像素信息丢失缺陷,原因是均值滤波是基于平均权重的。高斯模糊部分克服了这种缺陷,但是无法完全避免,因为没有考虑到像素值的不同。


此外还有中值滤波和双边滤波。中值滤波是一种统计排序滤波器,中值对椒盐噪声有很好的抑制作用。双边滤波是边缘保留的滤波方法,避免了边缘信息丢失,保留了图像轮廓不变。

相关API:

1
2
3
4
5
6
7
8
medianBlur(Mat src, Mat dest, size); // 中值模糊
// 中值模糊的size大小必须是正奇数
bilateralFilter(src, dest, d = 15, 150, 3); // 双边模糊
/*
15为计算的半径,半径之内的像素都会被纳入计算,如果提供-1则会根据sigma space参数取值
150为sigma color,它决定多少差值之内的像素会被计算
3为sigma space,如果d>0则声明无效,否则根据它来计算d值
*/

(这张插图其实不太能很好的体现中值滤波的作用,我这里找不到椒盐噪点比较多的图片)

一行代码即可:medianBlur(src, dst, 3);

其实用它来提升肤质细节也是不错的。还有双边滤波也是。比如使用bilateralFilter(src, dst, 15, 45, 3);可以达到一个类似于美颜的效果(大雾)。

那么,再加一个掩膜让图片锐化一下会怎样呢(滑稽

也许就能达到一个假装ps过的效果了(


-------------本文结束,感谢您的阅读转载请注明原作者及出处-------------


本文标题:OpenCV计算机图像视觉基础学习笔记4——还是对图像的操作

文章作者:Shawn Zhou

发布时间:2019年08月02日 - 15:08

最后更新:2019年08月03日 - 15:08

原始链接:http://shawnzhou.xyz/2019/08/02/19-08-02-02/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

知识无价,码字不易。对您有用,敬请打赏。金额随意,感谢关心。
0%