简单介绍
opencv是用来做原始图片,视频帧级别处理加工,若缩放,裁剪,混合,叠加,灰度以及更高级如AI视觉算法支持等相关功能的代码库,提供各种操作接口。
编译和安装
编译和安装:
- 编译ffmpeg:https://linuxize.com/post/how-to-install-ffmpeg-on-ubuntu-20-04/
- 编译安装opencv:https://blog.csdn.net/ai_faker/article/details/117020252
编译工程:
cmake: 用sample/cpp/example_cmake/下的工程: cmake . & make
g++ :g++ xxx.cpp -o xxxpkg-config --cflags --libs opencv
或者:
遵循官网的操作:
https://docs.opencv.org/4.5.2/df/d65/tutorial_table_of_content_introduction.html
YUV:
1 YUV格式:
- 是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
- YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance或Luma),也就是灰阶值,“U”和“V”表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
分为两种排列方式:
1 | 1) planar |
opencv使用
基于opencv3-4.15
基本例子:
1 |
|
The Core Functionality (core module) - basic building blocks of the library
Mat - The Basic Image Container 创建矩阵:
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
using namespace cv;
using namespace std;
int main()
{
cout << "Built with OpenCV " << CV_VERSION << endl;
Mat M(2,2,CV_8UC3,Scalar(0,0,255));//2*2 matrx, 每个元素都是0,0,255
cout<< "M= :" << endl << "" << M << endl;
int sz[3]={2,2,2};
Mat L(3,sz,CV_8UC(1),Scalar::all(4));//第一个参数是维度,这里是三维,第二个参数是每个维度是多少;
// cout << "L:" << endl << "" << L << endl; opencv 三维打印不了
int sz1[2]={2,2};//是矩阵,所以这两个相等
Mat L2(2,sz,CV_8UC(1),Scalar::all(4));//这里是二维,每个维度是二
cout << "L2:" << endl << "" << L2 << endl;
//看看默认值: 输出为1
Mat default2();
cout << "default:" << default2 << endl;
//通过创建矩阵 :这种情况下默认值是0
Mat L3;
L3.create(4,4,CV_8UC(2));
cout << "M " << endl << L3 << endl;
//左上角到右下角对角线为1,其他都是0
Mat E = Mat::eye(4,4,CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);//全1的
cout << "O = " << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3,3, CV_8UC1);//全0的
cout << "Z = " << endl << " " << Z << endl << endl;
//对小矩阵,还可以这样处理:
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
C = (Mat_<double>({0, -1, 0, -1, 5, -1, 0, -1, 0})).reshape(3);//和上面一样,只是通过reshape指定
cout << "C = " << endl << " " << C << endl << endl;
//通过clone :下面是取出第一行:
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl; //[-1, 5, -1]
//初始化为随机值,使用randu函数,需要提供范围
Mat R = Mat(3, 2, CV_8UC3);
randu(R, Scalar::all(0), Scalar::all(255));
cout << "R:" << R << endl;
return 0;
}How to scan images, lookup tables and time measurement with OpenCV
使用时,三种可以遍历mat的方式,用第三种LUT内建优化的,速度最快: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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/highgui.hpp>
#include <iostream>
#include <sstream>
using namespace std;
using namespace cv;
static void help()
{
cout
<< "\n--------------------------------------------------------------------------" << endl
<< "This program shows how to scan image objects in OpenCV (cv::Mat). As use case"
<< " we take an input image and divide the native color palette (255) with the " << endl
<< "input. Shows C operator[] method, iterators and at function for on-the-fly item address calculation."<< endl
<< "Usage:" << endl
<< "./how_to_scan_images <imageNameToUse> <divideWith> [G]" << endl
<< "if you add a G parameter the image is processed in gray scale" << endl
<< "--------------------------------------------------------------------------" << endl
<< endl;
}
Mat& ScanImageAndReduceC(Mat& I, const uchar* table);
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* table);
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar * table);
int main( int argc, char* argv[])
{
help();
if (argc < 3)
{
cout << "Not enough parameters" << endl;
return -1;
}
Mat I, J;
if( argc == 4 && !strcmp(argv[3],"G") )
I = imread(argv[1], IMREAD_GRAYSCALE);
else
I = imread(argv[1], IMREAD_COLOR);
if (I.empty())
{
cout << "The image" << argv[1] << " could not be loaded." << endl;
return -1;
}
//! [dividewith]
int divideWith = 0; // convert our input string to number - C++ style
stringstream s;
s << argv[2];
s >> divideWith;
if (!s || !divideWith)
{
cout << "Invalid number entered for dividing. " << endl;
return -1;
}
uchar table[256];
for (int i = 0; i < 256; ++i)
table[i] = (uchar)(divideWith * (i/divideWith));
//! [dividewith]
const int times = 100;
double t;
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
J = ScanImageAndReduceC(clone_i, table);
}
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;
cout << "Time of reducing with the C operator [] (averaged for "
<< times << " runs): " << t << " milliseconds."<< endl;
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
J = ScanImageAndReduceIterator(clone_i, table);
}
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;
cout << "Time of reducing with the iterator (averaged for "
<< times << " runs): " << t << " milliseconds."<< endl;
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
ScanImageAndReduceRandomAccess(clone_i, table);
}
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;
cout << "Time of reducing with the on-the-fly address generation - at function (averaged for "
<< times << " runs): " << t << " milliseconds."<< endl;
//! [table-init]
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
p[i] = table[i];
//! [table-init]
t = (double)getTickCount();
for (int i = 0; i < times; ++i)
//! [table-use]
LUT(I, lookUpTable, J);
//! [table-use]
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;
cout << "Time of reducing with the LUT function (averaged for "
<< times << " runs): " << t << " milliseconds."<< endl;
return 0;
}
//! [scan-c]
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
//! [scan-c]
//! [scan-iterator]
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = table[*it];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
}
}
return I;
}
//! [scan-iterator]
//! [scan-random]
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch(channels)
{
case 1:
{
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];
break;
}
case 3:
{
Mat_<Vec3b> _I = I;
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
{
_I(i,j)[0] = table[_I(i,j)[0]];
_I(i,j)[1] = table[_I(i,j)[1]];
_I(i,j)[2] = table[_I(i,j)[2]];
}
I = _I;
break;
}
}
return I;
}
//! [scan-random]
use: ./how_to_scan_images lena.jpeg 4 [G]
Time of reducing with the C operator [] (averaged for 100 runs): 0.897661 milliseconds.
Time of reducing with the iterator (averaged for 100 runs): 2.54602 milliseconds.
Time of reducing with the on-the-fly address generation - at function (averaged for 100 runs): 2.3317 milliseconds.
Time of reducing with the LUT function (averaged for 100 runs): 0.171338 milliseconds.Mask operations on matrices
如何做掩码操作:使用内建的掩码操作会更快:传入一个矩阵做运算,这里涉及到复杂的滤波,暂时不深究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#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
static void help(char* progName)
{
cout << endl
<< "This program shows how to filter images with mask: the write it yourself and the"
<< "filter2d way. " << endl
<< "Usage:" << endl
<< progName << " [image_path -- default lena.jpg] [G -- grayscale] " << endl << endl;
}
void Sharpen(const Mat& myImage,Mat& Result);
int main( int argc, char* argv[])
{
help(argv[0]);
const char* filename = argc >=2 ? argv[1] : "lena.jpg";
Mat src, dst0, dst1;
if (argc >= 3 && !strcmp("G", argv[2]))
src = imread( samples::findFile( filename ), IMREAD_GRAYSCALE);
else
src = imread( samples::findFile( filename ), IMREAD_COLOR);
if (src.empty())
{
cerr << "Can't open image [" << filename << "]" << endl;
return EXIT_FAILURE;
}
namedWindow("Input", WINDOW_AUTOSIZE);
namedWindow("Output", WINDOW_AUTOSIZE);
imshow( "Input", src );
double t = (double)getTickCount();
Sharpen( src, dst0 );
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Hand written function time passed in seconds: " << t << endl;
imshow( "Output", dst0 );
waitKey();
//![kern]
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
//![kern]
t = (double)getTickCount();
//![filter2D]
filter2D( src, dst1, src.depth(), kernel );
//![filter2D]
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Built-in filter2D time passed in seconds: " << t << endl;
imshow( "Output", dst1 );
waitKey();
return EXIT_SUCCESS;
}
//! [basic_method]
void Sharpen(const Mat& myImage,Mat& Result)
{
//! [8_bit]
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
//! [8_bit]
//! [create_channels]
const int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());
//! [create_channels]
//! [basic_method_loop]
for(int j = 1 ; j < myImage.rows-1; ++j)//行数遍历
{
const uchar* previous = myImage.ptr<uchar>(j - 1);//获取myImage的某一行,c风格
const uchar* current = myImage.ptr<uchar>(j );
const uchar* next = myImage.ptr<uchar>(j + 1);
uchar* output = Result.ptr<uchar>(j);
for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
{
*output++ = saturate_cast<uchar>(5*current[i]
-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
}
}
//! [basic_method_loop]
//! [borders]
Result.row(0).setTo(Scalar(0));
Result.row(Result.rows-1).setTo(Scalar(0));
Result.col(0).setTo(Scalar(0));
Result.col(Result.cols-1).setTo(Scalar(0));
//! [borders]
}
//! [basic_method]
Operations with images
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
751) Mat img = imread(filename); 加载一个图片,通过后缀名识别;
Mat img = imread(filename, IMREAD_GRAYSCALE);//加载图片,只是加载灰度图,即一个channel
IMREAD_UNCHANGED 其他:https://docs.opencv.org/master/d8/d6a/group__imgcodecs__flags.html#ga61d9b0126a3e57d9277ac48327799c80
If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation.
2)imwrite(filename, img); 写同图片到文件filename中;
3) Scalar intensity = img.at<uchar>(y, x); 整型矩阵中,处于(y,x)的元素;
//对于一个单channel的灰度图,类型是8UC1的,根据x,y获取像素点值:(8UC1:0-255)
//注意:因为opencv图是表示和矩阵相同的结构,所以,使用相同的转换:0-based row(行) index(or y-coordinate)为第一个参数,0开始的列(or x)为第二个参数;
//所以上面的img.at<uchar>(y,x)为从0数起来的第y行,第x列,或者使用下面的形式
Scalar intensity = img.at<uchar>(Point(x, y));
Vec3b intensity = img.at<Vec3b>(y, x);
uchar blue = intensity.val[0];
uchar green = intensity.val[1];
uchar red = intensity.val[2];
改变数据:
Vec3b inte5(3,4,5);
img.at<Vec3b>(y,x) = inte5;
Vec3b inte6 = img.at<Vec3b>(y,x);
cout << "inet6:" << inte6 << endl;
4)内存管理和引用计数
Mat是一种保持矩阵/图像特征(行和列数,数据类型等)和指向数据的指针的结构。所以没有什么可以阻止我们有几个Mat对应于相同数据的实例。当Mat的特定实例被破坏时,Mat保留一个引用计数,指示数据是否必须被释放。以下是创建两个矩阵而不复制数据的示例:
std::vector<Point3f> points;
// .. fill the array
Mat pointsMat = Mat(points).reshape(1); //数据内容还是一样,但是打印channels()会变为1
真正的拷贝克隆 数据,要用:
可以使用例如cv :: Mat :: copyTo或cv :: Mat :: clone:
Mat img = imread(“image.jpg”);
Mat img1 = img.clone();
5)create:
Mat::create函数:
An empty output Mat can be supplied to each function. Each implementation calls Mat::create for a destination matrix. This method allocates data for a matrix if it is empty. If it is not empty and has the correct size and type, the method does nothing. If however, size or type are different from the input arguments, the data is deallocated (and lost) and a new data is allocated. For example:
一个空的输出Mat可以被用于每个函数,每个实现会调用create来输出矩阵。这个方法会分配数据,如果是空的,或者如果不空且有正确的size和类型,则不做事。然而
若size 或type不同于输入的参数,就会先析构,在创建新的;例如:
Mat img = imread("image.jpg");
Mat sobelx;
Sobel(img, sobelx, CV_32F, 1, 0);
6)原子操作:
有很多方式可以用来定义一个矩阵,如,这里我们可以make a black image from an existing 灰度图img:
img = Scalar(0);
选择一个感兴趣的区域:
Rect r(10, 10, 100, 100);
Mat smallImg = img(r);
把图片转换为灰度图:
Mat img = imread("image.jpg"); // loading a 8UC3 image
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
Change image type from 8UC1 to 32FC1:
src.convertTo(dst, CV_32F);
7)展示图片:
展示图片是一个很好展示结果的方式,opencv提供了一种方式,如展示一个8U图片
Mat img = imread("image.jpg");
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", img);
waitKey();//调用waitKey()开始一个消息传递循环,等待“image”窗口中的按键。32F图像需要转换为8U类型。
其他例子:
Mat img = imread("image.jpg");
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
Mat sobelx;
Sobel(grey, sobelx, CV_32F, 1, 0);
double minVal, maxVal;
minMaxLoc(sobelx, &minVal, &maxVal); //find minimum and maximum intensities
Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", draw);
waitKey();
这里cv::namedWindow是没有必要的,因为它是紧接着cv::imshow。然而,它可以用来改变窗口属性或当使用cv::createTrackbar将两个图片叠加在一起—线性混合:
https://www.cnblogs.com/laizhenghong2012/p/11253100.html
https://docs.opencv.org/master/d5/dc4/tutorial_adding_images.html
需要理解的是,单纯乘以某个系数,改变像素值的大小,就能往透明走吗? 如果是透明通道呢?如何改变一副图片的对比度和亮度:
这部分的解释来自书籍《计算机视觉:算法和应用》
- 关于图像处理:
一个广义上的图像处理,是一个函数,能输入一个或多个图像,然后产生一个输出图像;
图像转换也可以被看做是:
点的操作(像素转换)
邻居(area-based)操作; - 关于像素转换:
在这种图像处理转换中,每个输出的像素点的值,仅仅依赖于相关的输入像素值(以及,潜在的,一些全局的收集的信息或参数)
这种例子包括 rightness and contrast adjustments as well as color correction and transformations. - Brightness and contrast adjustments
Two commonly used point processes are multiplication and addition with a constant:
两种常用的点处理方法是常数的乘法和加法: g(x)=αf(x)+β
The parameters α>0 and β are often called the gain and bias parameters; sometimes these parameters are said to control contrast and brightness respectively.
参数α>0和β常被称为增益和偏置参数;有时这些参数被称为分别控制对比度和亮度。
可以把f(x)看作源图像像素,把g(x)看作输出图像像素。然后,我们可以更方便地将表达式写成: g(i,j)=α⋅f(i,j)+β 其中I和j表示该像素位于第I行第j列。
一个例子: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
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cin;
using std::cout;
using std::endl;
using namespace cv;
int main( int argc, char** argv )
{
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( image.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
Mat new_image = Mat::zeros( image.size(), image.type() );
double alpha = 1.0; /*< Simple contrast control */
int beta = 0; /*< Simple brightness control */
cout << " Basic Linear Transforms " << endl;
cout << "-------------------------" << endl;
cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
cout << "* Enter the beta value [0-100]: "; cin >> beta;
for( int y = 0; y < image.rows; y++ ) {
for( int x = 0; x < image.cols; x++ ) {
for( int c = 0; c < image.channels(); c++ ) {
new_image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
}
}
}
imshow("Original Image", image);
imshow("New Image", new_image);
waitKey();
return 0;
}
解释:略 https://docs.opencv.org/master/d3/dc1/tutorial_basic_linear_transform.html
我们可以简单地使用以下命令,而不是使用for循环来访问每个像素:
image.convertTo(new_image, -1, alpha, beta);
where cv::Mat::convertTo
将有效地执行*new_image = a*image + beta*。但是,我们想向您展示如何访问每个像素。在任何情况下,这两种方法都给出相同的结果,但convertTo更优化,工作速度更快。、
执行:
Running our code and using α=2.2 and β=50
$ ./BasicLinearTransforms lena.jpg
Basic Linear Transforms
-------------------------
* Enter the alpha value [1.0-3.0]: 2.2
* Enter the beta value [0-100]: 50
在这一段中,我们将把我们所学到的通过调整图像的亮度和对比度来纠正曝光不足的图像付诸实践。我们还将看到另一种校正图像亮度的技术,称为伽马校正
疑问:为什么提高RBG的值就能提高亮度?图像的亮度怎么定义的?
https://bj.96weixin.com/tools/rgb
这个网站可以看到测试:全0为黑色,全255为白色,随着数值增大,颜色越来越浅,越来越明亮;
亮度和对比度调整:
如上图,增加(/减少)β值将为每个像素增加(/减去)一个常量值。[0;255]范围将被饱和(即高于(/低于)255(/ 0)的像素值将被固定为255(/ 0))。
如上图,α参数将改变能级的传播方式。如果α<1,颜色等级将被压缩,结果将是一个低对比度的图像。(如 (1,2,4) (2,4,8) -->a=2 (2,4,8) (3,8,16)两个点的对比度就更明显了;
注意,这些直方图是使用Gimp软件中的亮度-对比度工具获得的。亮度工具应该是相同的β偏差参数,但对比工具似乎不同的α增益的输出范围似乎是中心与Gimp(正如你可以注意到在前面的直方图)。
它可以发生,玩β偏差将提高亮度,但在同一时间图像将出现轻微面纱,因为对比度将降低。α增益可以用来减弱这种效果,但由于饱和度的原因,我们将失去一些原始明亮区域的细节。
上面的调整是线性的,Gamma校正可以通过使用输入值和映射的输出值之间的非线性变换来校正图像的亮度:
更多:https://docs.opencv.org/master/d3/dc1/tutorial_basic_linear_transform.html
- 离散傅里叶变换 :略
- 使用XML和YAML文件的文件输入和输出
- 如何使用opencv并行多线程化你的代码;
ref:https://docs.opencv.org/4.5.2/de/d7a/tutorial_table_of_content_core.html更多例子:
源码目录下:
/opencv-3.4.15/samples/cpp/tutorial_code/core/…
函数简记
基础
add函数:
https://cppsecrets.com/users/203110310511410511510410011599115495764103109971051084699111109/C00-OpenCV-cvadd.php
用于将两个矩阵的值相加,并输出:会出现图像叠加的效果1
2
3
4
5
6
7
8
9
10
11
12
13This is one of the builtin-function provided by an Opencv() which helps in making addition operation between the two elements (matrix/images).
Syntax :
Void cv::add( cv::InputArray src1 , cv::InputArray src2 ,cv::OutputArray dst, cv::InputArray mask=cv::noArray() , int dtype=-1 );
parameters:
src1 - first input array or scalar.
src2 - second input array or a scalar.
dst - output array that has the same size and number of channels as the input array(s);
The depth is defined by dtype or src1/src2 .
mask - optional operation mask- 8 -bit single channel array ,that specifies elements of the output array to be changed.
dtype - optional depth of the output array.
Generalization :-
dst[i] = saturate ( src1[i] + src2[i] )
cv::add() is a simple addition function : It adds all of the element in src1 to the corresponding element in src2 & puts the result in dest.mul :Opencv中mul会计算两个Mat矩阵对应位的乘积,所以要求参与运算的矩阵A的行列和B的行列数一致。计算结果是跟A或B行列数一致的一个Mat矩阵。
如何将透明通道结合到yuv/rgb中;https://cloud.tencent.com/developer/article/1650070
resize: 将mat的 size等做变化:
1
resize(srcLocMask, small_mask, cv::Size(w / 2, h / 2), 0.0, 0.0, RESIZE_ALGORITHM); 如果第4,5个参数为0,则用第三个,否则按第4,5个缩放;
Rect矩形:
1
2
3
4
5
6
7
8Rect_(
_Tp _x,
_Tp _y,
_Tp _width,
_Tp _height
);
x,y:左上角的坐标。这是OpenCV中Rect_::x和Rect_::y的默认解释。不过,在你的算法中,你可以从左下角开始计算x和y。
width,heigth:矩形的宽度和高度。下列用法是拷贝构造?
1
2srcY = cv::Mat(srcH, srcAVFrame->linesize[0], CV_8UC1, (void*)srcAVFrame->data[0]);
srcY = srcY(cv::Rect(0, 0, srcAVFrame->width, srcAVFrame->height));copyto
convertTo: 转换
9): set
10) 通道处理:
1 | cv::split(rgbaImg, channels); 通道分离 |
11 一个例子:
把png图片透明度调高:即更透明:
1 | 1) mat rgba 分离出a通道; |
基本形状函数:
- ellipse 画椭圆形,可以指定角度,中心,填充,粗细等; Draws a simple or thick elliptic arc or fills an ellipse sector.
- circle:画一个圆圈,可以指定中心等;
- fillPoly 指定多个点,会连接成多边形,可以在参数指定填充等;填充由一个或多个多边形包围的区域。
- rectangle: 绘制一个简单的、粗的或向上填充的矩形。
- line() 画一条连接两点的线段
一个随机生成的动画:
1 | /** |
混画操作:
1 | s1 : 创建画布: |