OpenCV图像处理基础

1 OpenCV简介

核心模块:

  • HighGUI
  • Image Process
  • 2D Featrue
  • Camera Calibration and 3D reconstruction
  • Video Analysis
  • Object Detection
  • Machine Learning
  • GPU加速

2 加载、修改、保存图片

  • 加载图像:cv::imread
  • 修改图像:cv::cvtColor
  • 保存图像:cv::imwrite

加载图像

cv::imread("FileName" ,类型 , )

加载图像成为一个Mat对象,支持JPG、PNG、TIFF等

IMREAD_UNCHANGED(<0):加载原图

IMREAD_GRAYSCALE(0):加载原图的灰度图像

IMREAD_COLOR(>0):把原图作为RGB图像加载

显示图像

namedWindow("Window Title",WINDOW_AUTOSIZE)

创建一个OpenCV窗口,由OpenCV自动释放与创建,无须手动销毁

imshow("Window Title",MatObject)

显示图像到指定窗口上

cvtColor(image,cvtedImage,COLOR_BGR2GRAY)

把图像从一个彩色空间转换到另一个彩色空间

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>
using namespace cv;
using namespace std;

int main(int argc,char ** argv) {
Mat src = imread("E:/Photograph/CV_image/lena.jpeg",IMREAD_COLOR);
if (src.empty()) {
cout << "can't find image" << endl;
return -1;
}

namedWindow("opencv setup demo",WINDOW_AUTOSIZE);
imshow("opencv setup demo",src);


Mat output_img;
cvtColor(src,output_img,COLOR_BGR2HSV);
namedWindow("output Window",WINDOW_NORMAL);
imshow("output Window",output_img);

imwrite("E:/Photograph/CV_image/cvted_lena.tif",output_img);

waitKey(0);
return 0;
}

3 图像对象的创建与赋值

Mat基本结构:头部(大小、宽高、通道数…)+数据部分】

使用m1=m2是进行引用传递

Mat m1 = src.clone();Mat m2;src.copyTo(m2);是克隆

创建空白图像

1
2
3
Mat m4 = Mat::zeros(src.size(),src.type());
Mat m5 = Mat::zeros(Size(512,512),CV_8UC3);
Mat m6 = Mat::ones(Size(512,512),CV_8UC3);

其中8UC1:8位无符号单通道;8UC3:8位无符号3通道

对Mat的加减乘除全部经过重载

m = 127; //把矩阵的第一个通道的值全变成127

m = Scalar(127,127,127); //Sclalar(b,g,r)

4 图像像素的读写操作

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
void Demo_1::VistPixel(Mat &image) {
/*//通过数组下标访问
for(int row = 0;row < image.rows;row++){
for(int col = 0;col < image.cols;col++){
//Grayscale
if(image.channels() == 1){//灰度图像
int pv = image.at<uchar>(row,col);
image.at<uchar>(row,col) = 255 - pv;
}
if(image.channels() == 3){//彩色图像
Vec3b bgr = image.at<Vec3b>(row,col);
image.at<Vec3b>(row,col)[0] = 255 - bgr[0];
image.at<Vec3b>(row,col)[1] = 255 - bgr[1];
image.at<Vec3b>(row,col)[2] = 255 - bgr[2];
}
}
}*/

//通过指针访问
for(int row = 0;row < image.rows;row++){
uchar* cur_row = image.ptr<uchar>(row);
for(int col = 0;col < image.cols;col++){
if(image.channels() == 1){
int pv = *cur_row;
*cur_row++ = 255-pv;
}
if(image.channels() == 3){
*cur_row++ = 255 - *cur_row;
*cur_row++ = 255 - *cur_row;
*cur_row++ = 255 - *cur_row;
}
}
}

imshow("negative",image);
}

5 图像像素的算术操作

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
void Demo_1::OperatePixel(Mat &image) {
Mat dst = Mat::zeros(image.size(),image.type());
Mat m = Mat::zeros(image.size(),image.type());
//dst = image+Scalar(2,2,2);
//dst = image-Scalar(2,2,2);
//dst = image/Scalar(2,2,2);
//dst = image*scalar(2,2,2); 错误,矩阵乘法
m = Scalar(20,20,20);
multiply(image,m,dst);

imshow("乘法操作",dst);

/*//MyAdd
for(int row = 0;row < image.rows;row++){
for(int col = 0;col < image.cols;col++){
Vec3b p1 = image.at<Vec3b>(row,col);
Vec3b p2 = m.at<Vec3b>(row,col);
dst.at<Vec3b>(row,col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);
dst.at<Vec3b>(row,col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);
dst.at<Vec3b>(row,col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);
}
}
imshow("自定义加法法操作",dst);
*/
}

saturate_cast<Type>():限制数据不超过Type类型

6 TrackBar滚动条操作演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int lightness = 50;
Mat m;
Mat dst;
Mat src;
static void ontrack(int,void*);
void Demo_1::TrackBar(Mat &image) {
namedWindow("亮度调整",WINDOW_AUTOSIZE);
m = Mat::zeros(image.size(),image.type());
dst = Mat::zeros(image.size(),image.type());
src = image;
int maxValue = 100;
createTrackbar("ValueBar","亮度调整",&lightness,maxValue,ontrack);
ontrack(50,0);
}
static void ontrack(int,void*){
m = Scalar(lightness,lightness,lightness);
add(src,m,dst);
//subtract(src,m,dst);
imshow("亮度调整",dst);
}

7 TrackBar参数传递(亮度、对比度调整)

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
static void onlight(int b,void* userdata);
static void oncontrast(int b,void* userdata);
void Demo_1::TrackBar(Mat &image) {
namedWindow("亮度与对比度调整",WINDOW_AUTOSIZE);
int maxValue = 100;
int lightness = 50;
int contrast_value = 100;

createTrackbar("ValueBar","亮度与对比度调整",&lightness,maxValue,onlight,(void*)(&image));
createTrackbar("ContrastBar","亮度与对比度调整",&contrast_value,200,oncontrast,(void*)(&image));
onlight(50,(void*)&image);
}
static void onlight(int b,void* userdata){
Mat image = *((Mat*)userdata);
Mat dst = Mat::zeros(image.size(),image.type());
Mat m = Mat::zeros(image.size(),image.type());
addWeighted(image,1.0,m,0,b,dst);
imshow("亮度与对比度调整",dst);
}
static void oncontrast(int b,void* userdata){
Mat image = *((Mat*)userdata);
Mat dst = Mat::zeros(image.size(),image.type());
Mat m = Mat::zeros(image.size(),image.type());
double contrast = b/100.0;
addWeighted(image,contrast,m,0.0,0,dst);
//subtract(image,m,dst);
imshow("亮度与对比度调整",dst);
}
1
PORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2,double beta, double gamma, OutputArray dst, int dtype = -1);

8 键盘响应操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Demo_1::key_demo(Mat &image) {
Mat dst = image;
bool Flag = true;
while (Flag) {
int c = waitKey(100);
switch (c) {
case 27://Key #ESC
Flag = false;
break;
case 49://Key #1
cvtColor(image, dst, COLOR_BGR2GRAY);
break;
case 50://Key #2
cvtColor(image, dst, COLOR_BGR2HSV);
break;
case 51://Key #3
dst = Scalar(50, 50, 50);
add(image, dst, dst);
break;
default:;
}
imshow("键盘响应",dst);
}
}

waitKey()无键盘输入的情况下返回的是默认值-1,键盘按下后会返回对应键值的ASCII

9 Opencv自带颜色表操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Demo_1::color_style_demo(Mat &image){
int colormap[] = {COLORMAP_AUTUMN,COLORMAP_BONE,COLORMAP_CIVIDIS,
COLORMAP_COOL,COLORMAP_DEEPGREEN};
Mat dst;
int index = 0;
int num = 0;
while(true){
int c = waitKey(2000);
if(c == 27) break;
if(c == 's'){
num++;
String filename = "save";
imwrite("/home/qingren/图片/CV_Image/" + filename + std::to_string(num) + ".jpg",dst);
}
applyColorMap(image,dst,index%5);
index++;
imshow("颜色风格",dst);
}
}

10 图像像素的逻辑操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Demo_1::bitwise_demo(Mat &image) {
Mat m1 = Mat::zeros(Size(256,256),CV_8UC3);
Mat m2 = Mat::zeros(Size(256,256),CV_8UC3);
rectangle(m1,Rect(100,100,80,80),Scalar(255,255,0),-1,LINE_8,0);
rectangle(m2,Rect(150,150,80,80),Scalar(0,255,255),-1,LINE_8,0);
imshow("m1",m1);
imshow("m2",m2);
Mat dst;
//通过“与”操作,重合部分bgr为(0,255,0),为绿色
//通过”或“操作,重合部分bgr为(255,255,255),为白色
//通过"非"操作,图片各像素变为256-,变成负片
//通过”异或“操作,重合部分bgr为(255,0,255),为紫色
bitwise_xor(m1,m2,dst);
imshow("dst",dst);
}

11 通道分离与合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Demo_1::channels_demo(Mat &image){
std::vector<Mat> mv;
split(image,mv);
// imshow("蓝色",mv[0]);
// imshow("绿色",mv[1]);
// imshow("红色",mv[2]);

Mat dst;
mv[1] = 0;
// mv[2] = 0;
merge(mv,dst);
// imshow("blue",dst);

int from_to[] = {0,2,1,1,2,0};
mixChannels(&image,1,&dst,1,from_to,3);
imshow("通道混合",dst);
}

12 图像色彩空间转换

1
2
3
4
5
6
7
8
9
10
11
12
13
void Demo_1::inrange_demo(Mat &image){
Mat hsv;
cvtColor(image,hsv,COLOR_BGR2HSV);
Mat mask;
//对图片进行阈值操作
inRange(hsv,Scalar(0,0,221),Scalar(180,43,255),mask);
Mat redback = Mat::zeros(image.size(),image.type());
redback = Scalar(40,40,200);
//背景取反后再合并,copyto只会覆盖背景为1的位置
bitwise_not(mask,mask);
image.copyTo(redback,mask);
imshow("mask",redback);
}

13 图像像素值统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Demo_1::pixel_static_demo(Mat &image){
double minv,maxv;
Point minLoc,maxLoc;
std::vector<Mat> mv;
split(image,mv);
for(int i = 0;i<mv.size();i++){
//计算每个通道的最大值和最小值
minMaxLoc(mv[i],&minv,&maxv,&minLoc,&maxLoc,Mat());
std::cout << "Channel " << i << " :min value: " << minv
<< " max value: " << maxv << std::endl;
}
Mat mean,stddev;
//计算图像均值和方差
meanStdDev(image,mean,stddev);
std::cout << "means: " << mean << std::endl <<"stddev: " << stddev << std::endl;
}