1.图像读取与显示
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");//读取图像
if (src.empty())
{
cout << "no image..." << endl;
return -1;
}
imshow("image", src);//显示图像
waitKey(0);
return 0;
}
如果要读取灰度图像,将
Mat src = imread("lena.png");
改为
Mat src = imread("lena.png",IMREAD_GRAYSCALE);//读取灰度图像
注意:图像要在文件路径下 ,如果不在,应将lena.png改为E:/...../lena.png
上述代码读取后显示的图像窗口大小不能改变,如需改变,应在显示图像前添加:
namedWindow("image",WINDOW_FREERATIO);//任意调整窗口大小
2.Mat类图像矩阵创建
①彩色图像性质及在OpenCV中储存形式:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat m = Mat::zeros(Size(5,4), CV_8UC3);//创建大小为(5,4)的三通道0值图像
m = Scalar(200, 50, 80);//像素点赋值
cout << "Width:" << m.cols << " Height:" << m.rows << " channels:" << m.channels() << endl;
cout << m << endl;
waitKey(0);
return 0;
}
结果:

② 创建彩色图像
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat m = Mat::zeros(Size(512, 480), CV_8UC3);//创建大小为(512,480)的三通道0值图像
m = Scalar(200, 50, 80);//像素点赋值
cout << "Width:" << m.cols << " Height:" << m.rows << " channels:" << m.channels() << endl;
imshow("创建的图像",m);
waitKey(0);
return 0;
}
结果:

3.图像颜色空间转换
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");//读取图像
if (src.empty())
{
cout << "no image..." << endl;
return -1;
}
Mat hsv, gray;
cvtColor(src, hsv, COLOR_BGR2HSV);
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("image", src);//显示原图
imshow("hsv", hsv);//显示hsv图
imshow("gray", gray);//显示灰度图
waitKey(0);
return 0;
}
结果:

4.图像赋值与复制
主要有“=”、copyTo、clone三个:
①“=”
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat m1 = Mat::ones(1,3,CV_8U);
Mat m2 = m1;//"="赋值
cout << "m1" << m1 << endl;
cout << "m2" << m2 << endl;
m2 = Scalar(3);//改变其中一个
cout << "m1" << m1 << endl;
cout << "m2" << m2 << endl;
waitKey(0);
return 0;
}
结果:
开始: m1=[1 1 1] m2=[1 1 1]
处理后: m1=[3 3 3] m2=[3 3 3]
可见“=”赋值是一种浅拷贝,m1、m2共用一块数据,改变其中一个另一个也会改变。
②copyTo、clone
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat m1 = Mat::ones(1,3,CV_8U);
Mat m2;
m1.copyTo(m2);
cout << "m1" << m1 << endl;
cout << "m2" << m2 << endl;
m2 = Scalar(3);
cout << "m1" << m1 << endl;
cout << "m2" << m2 << endl;
waitKey(0);
return 0;
}
结果:
开始: m1=[1 1 1] m2=[1 1 1]
处理后: m1=[1 1 1] m2=[3 3 3]
可见copyTo赋值是一种深拷贝,改变其中一个另一个不会改变。clone与之类似,But当目标矩阵与原矩阵具有相同的type和size时,copyTo不会为目标矩阵重新分配内存,而clone总是为目标矩阵重新分配内存。解释见如下代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat m1 = Mat::ones(1, 3, CV_8U); // [1,1,1]
Mat m2 = m1; // m2与m1指向同一内存地址
Mat m3 = Mat::zeros(1, 3, CV_8U); // [0,0,0]
m3.copyTo(m1); // m1 m3大小尺寸相同,m1未被重新分配内存,通过m1可以改变m2的内容
cout << m1 << endl; // [0,0,0]
cout << m2 << endl; // [0,0,0]
waitKey(0);
return 0;
}
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat m1 = Mat::ones(1, 3, CV_8U); // [1,1,1]
Mat m2 = m1; // m2与m1指向同一内存地址
Mat m3 = Mat::zeros(1, 3, CV_8U); // [0,0,0]
m1 = m3.clone(); // m1被重新分配内存,通过m1不能改变m2的内容
cout << m1 << endl; // [0,0,0]
cout << m2 << endl; // [1,1,1]
waitKey(0);
return 0;
}
第二段代码中可见clone替m1申请了一块新内存,而m2仍指向原来的m1的地址,所以m2不变.
5.图像阈值化
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");
Mat gray, dst1, dst2;
cvtColor(src,gray,COLOR_BGR2GRAY);//获取灰度图
threshold(gray,dst1,125,255,THRESH_BINARY);//灰度图像阈值化,125
threshold(src,dst2,125,255,THRESH_BINARY);//彩色图像阈值化,125
imshow("gray", gray);//显示灰度图
imshow("dst1", dst1);//灰度阈值化图
imshow("dst2", dst2);//彩色阈值化图
waitKey(0);
return 0;
}
threshold函数:第三个参数是阈值,第四个参数是最大值,即像素中大于第三个参数数值的转为第四个参数,小于第三个参数数值的转为0,从而实现二值化。彩色图像分层处理。
结果:

6.图像大小变换
直接设置转换后图像尺寸:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png",IMREAD_GRAYSCALE);
Mat dst1, dst2, dst3;
resize(src, dst1, Size(300, 300), 0, 0, INTER_NEAREST);//最近邻插值法
resize(src, dst2, Size(300, 300), 0, 0, INTER_LINEAR);//双线性插值法
resize(src, dst3, Size(300, 300), 0, 0, INTER_CUBIC);//三次插值法
imshow("gray",src);
imshow("dst1", dst1);
imshow("dst2", dst2);
imshow("dst3", dst3);
waitKey(0);
return 0;
}
借助原图像Mat类的cols与rows属性:
Mat zoomin,zoomout;//放大缩小
int width=image.cols;//原图像宽
int height=image.rows;//原图像高
resize(image,zoomin,Size(width/2,height/2),0,0,INTER_LINEAR);
imshow("缩小",zoomin);
resize(image, zoomout, Size(1.5*width, 1.5*height),0,0,INTER_LINEAR);
imshow("放大", zoomout);
注:图像变换离不开插值方法,通常使用双线性插值法。
7.透视变换
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("ustc.jpg");
Point2f imagepoints[4];//透视变换前坐标集合
Point2f dstpoints[4]; //透视变换后坐标集合
imagepoints[0] = Point2f(2.0, 8.0);
imagepoints[1] = Point2f(64.0, 500.0);
imagepoints[2] = Point2f(507.0, 18.0);
imagepoints[3] = Point2f(412.0, 500.0);
dstpoints[0] = Point2f(0.0, 0.0);
dstpoints[1] = Point2f(0.0, 512.0);
dstpoints[2] = Point2f(512.0, 0.0);
dstpoints[3] = Point2f(512.0, 512.0);
Mat M,dst;
M=getPerspectiveTransform(imagepoints, dstpoints);
warpPerspective(src, dst, M, src.size());
imshow("原图", src);
imshow("透视变换后", dst);
waitKey(0);
return 0;
}
结果:

8.图像金字塔
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat image = imread("lena.png");
vector<Mat>v;
v.push_back(image);
int level = 3;
for (int i = 0; i < level; i++)
{
Mat guass;
pyrDown(v[i], guass);//图像金字塔
v.push_back(guass);
}
string nameSeed = "123";
for (int i = 0; i < level; i++)
{
string Name = "level";
Name += nameSeed[i];
imshow(Name, v[i]);
}
waitKey(0);
return 0;
}
结果:

9.模板匹配
目的:寻找图像中与所给模板相似的地方。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");//原图
Mat temp = imread("lena_face.png");//模板
Mat result;//匹配结果
matchTemplate(src,temp,result,TM_CCOEFF_NORMED);
double maxVal, minVal;
Point maxLoc, minLoc;
minMaxLoc(result,&minVal,&maxVal,&minLoc,&maxLoc);//找result中最大值点位置
Rect rect;
rect.x = maxLoc.x;
rect.y = maxLoc.y;
rect.width = temp.cols;
rect.height = temp.rows;
rectangle(src, rect,Scalar(0,0,255),2,8);
imshow("原图像",src);
imshow("模板",temp);
waitKey(0);
return 0;
}
结果:

10.图像卷积操作
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");
Mat kernel = (Mat_<float>(3, 3) << 1, 2, 1,
2, 0, 2,
1, 2, 1);//卷积模板
Mat kernel_norm = kernel / 12; //归一化卷积模板
Mat dst;
//第三个参数是目标图像深度,-1表示目标图像与原图像深度一致
filter2D(src, dst, -1, kernel_norm, Point(-1, -1), 0, BORDER_CONSTANT);
imshow("原图像",src);
imshow("卷积后图像", dst);
waitKey(0);
return 0;
}
结果:

11.图像滤波操作
①均值滤波
滤波模板:
n=3时
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");
Mat dst1,dst2;
blur(src, dst1, Size(3, 3), Point(-1, -1));
blur(src, dst2, Size(5, 5), Point(-1, -1));
imshow("原图像", src);
imshow("3×3均值滤波", dst1);
imshow("5×5均值滤波", dst2);
waitKey(0);
return 0;
}
结果:

②高斯滤波(适合含有高斯噪声的图像)
滤波模板:
n=3时
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");
Mat dst1,dst2;
GaussianBlur(src, dst1, Size(3, 3), 15);
GaussianBlur(src, dst2, Size(5, 5), 15);
imshow("原图像", src);
imshow("3×3高斯滤波", dst1);
imshow("5×5高斯滤波", dst2);
waitKey(0);
return 0;
}
结果:

③中值滤波(适合含有椒盐噪声的图像)
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("lena.png");
Mat dst1,dst2;
medianBlur(src, dst1, 3);
medianBlur(src, dst2, 5);
imshow("原图像", src);
imshow("3×3中值滤波", dst1);
imshow("5×5中值滤波", dst2);
waitKey(0);
return 0;
}
结果:

12.边缘检测(Sobel、Scharr)
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("bridge.jpg",IMREAD_GRAYSCALE);
Mat gray;
//Sobel算子
Mat resultX, resultY, resultXY;
Sobel(src, resultX, CV_16S, 1, 0, 3);
convertScaleAbs(resultX, resultX);//取绝对值
Sobel(src, resultY, CV_16S, 0, 1, 3);
convertScaleAbs(resultY, resultY);
resultXY = resultX + resultY;
imshow("resultX", resultX);
imshow("resultY", resultY);
imshow("resultXY", resultXY);
//Scharr算子
Mat result2X, result2Y, result2XY;
Scharr(src, result2X, CV_16S, 1, 0);
convertScaleAbs(result2X, result2X);
Scharr(src, result2Y, CV_16S, 0, 1);
convertScaleAbs(result2Y, result2Y);
result2XY = result2X + result2Y;
imshow("result2X", result2X);
imshow("result2Y", result2Y);
imshow("result2XY", result2XY);
waitKey(0);
return 0;
}
结果:
Sobel算子:


Scharr算子:


13.边缘检测(Laplacian、Canny)
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("bridge.jpg",IMREAD_GRAYSCALE);
//拉普拉斯边缘检测
Mat result_L1, temp, result_L2;
//未滤波进行拉普拉斯边缘检测
Laplacian(src, result_L1, CV_16S, 3, 1, 0);
convertScaleAbs(result_L1, result_L1);
//高斯滤波后进行拉普拉斯边缘检测
GaussianBlur(src, temp, Size(3, 3), 5);
Laplacian(temp, result_L2, CV_16S, 3, 1, 0);
convertScaleAbs(result_L2, result_L2);
imshow("result_L1", result_L1);
imshow("result_L2", result_L2);
//Canny边缘检测
Mat result_C1, result_C2;
//未滤波进行Canny边缘检测
Canny(src, result_C1, 100, 200, 3);
//高斯滤波后进行Canny边缘检测
Canny(temp, result_C2, 100, 200, 3);
imshow("result_C1", result_C1);
imshow("result_C2", result_C2);
waitKey(0);
return 0;
}
结果:
Laplacian算子:

Canny算法:
注:进行边缘检测前最好先对图像进行滤波预处理,这样经过边缘检测后效果通常更好。
14.图像距离变换
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
//计算街区距离、欧氏距离、棋盘距离
//距离:非0像素距离0像素的distance
//1.基于矩阵计算
Mat a = (Mat_<uchar>(5, 5) << 1, 1, 1, 8, 1,
1, 3, 1, 1, 1,
1, 1, 0, 1, 4,
2, 8, 1, 1, 1,
1, 1, 1, 5, 1);
Mat d1, d2, d3;
distanceTransform(a, d1, 1, 3, CV_8U);
cout << "街区距离:" << endl << d1 << endl;
distanceTransform(a, d2, 2, 5, CV_8U);
cout << "欧氏距离:" << endl << d2 << endl;
distanceTransform(a, d3, 3, 5, CV_8U);
cout << "棋盘距离:" << endl << d3 << endl;
//2.基于实际灰度图像计算
Mat src = imread("rice.png", IMREAD_GRAYSCALE);
Mat gray_BW, dst;
threshold(src, gray_BW, 50, 255, THRESH_BINARY_INV);//二值化:大于50的转为黑
imshow("gray_BW", gray_BW);
distanceTransform(gray_BW, dst, 1, 3, CV_8U);
imshow("dst", dst);
waitKey(0);
return 0;
}
结果:


15.图像形态学处理—图像腐蚀
作用:去除图像中的微小物体或者分离较近的相邻物体。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("rice.png", IMREAD_GRAYSCALE);
//1.基于自定义矩阵腐蚀
Mat a = (Mat_<uchar>(6, 6) << 0, 0, 0, 0, 255, 0,
0, 255, 255, 255, 255, 255,
0, 255, 255, 255, 255, 0,
0, 255, 255, 255, 255, 0,
0, 255, 255, 255, 255, 0,
0, 0, 0, 0, 0, 0);
cout << "a" << a << endl;
Mat struct1, struct2;//定义结构元素
struct1 = getStructuringElement(0, Size(3, 3));//获取矩形结构元素
struct2 = getStructuringElement(1, Size(3, 3));//获取十字形结构元素
Mat erode1, erode2;//定义腐蚀后图像
erode(a, erode1, struct1);
erode(a, erode2, struct2);
cout << "erode1:" << erode1 << endl;
cout << "erode2:" << erode2 << endl;
namedWindow("a", WINDOW_GUI_NORMAL);
namedWindow("erode1", WINDOW_GUI_NORMAL);
namedWindow("erode2", WINDOW_GUI_NORMAL);
imshow("a", a);
imshow("erode1", erode1);
imshow("erode2", erode2);
//2.基于实际图像腐蚀
Mat BW, dst1, dst2;
threshold(src, BW, 50, 255, THRESH_BINARY);//二值化
imshow("BW", BW);
erode(BW, dst1, struct1);
erode(BW, dst2, struct2);
imshow("dst1", dst1);
imshow("dst2", dst2);
waitKey(0);
return 0;
}
结果:

注意观察红色框内变化



16.图像形态学处理—图像膨胀
作用:填充某些缺失的区域或者连接邻近区域。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("rice.png", IMREAD_GRAYSCALE);
//1.基于自定义矩阵膨胀
Mat a = (Mat_<uchar>(6, 6) << 0, 0, 0, 0, 255, 0,
0, 255, 255, 255, 255, 255,
0, 255, 255, 255, 255, 0,
0, 255, 255, 255, 255, 0,
0, 255, 255, 255, 255, 0,
0, 0, 0, 0, 0, 0);
cout << "a" << a << endl;
Mat struct1, struct2;//定义结构元素
struct1 = getStructuringElement(0, Size(3, 3));//获取矩形结构元素
struct2 = getStructuringElement(1, Size(3, 3));//获取十字形结构元素
Mat dilate1, dilate2;//定义膨胀后图像
dilate(a, dilate1, struct1);
dilate(a, dilate2, struct2);
cout << "dilate1:" << dilate1 << endl;
cout << "dilate2:" << dilate2 << endl;
namedWindow("a", WINDOW_GUI_NORMAL);
namedWindow("dilate1", WINDOW_GUI_NORMAL);
namedWindow("dilate2", WINDOW_GUI_NORMAL);
imshow("a", a);
imshow("dilate1", dilate1);
imshow("dilate2", dilate2);
//2.基于实际图像膨胀
Mat BW, dst1, dst2;
threshold(src, BW, 50, 255, THRESH_BINARY);//二值化
imshow("BW", BW);
dilate(BW, dst1, struct1);
dilate(BW, dst2, struct2);
imshow("dst1", dst1);
imshow("dst2", dst2);
waitKey(0);
return 0;
}
结果:




17.二维码检测识别
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("QRcode.png");
Mat gray,dst;
cvtColor(src, gray, COLOR_BGR2GRAY);
QRCodeDetector QRCD;//实例化一个QRCodeDetector类对象
vector<Point>P;//定义输出点集合
bool isQRCode;//定义识别结果
string information;
isQRCode = QRCD.detect(gray, P);
if (isQRCode)
{
information = QRCD.decode(gray, P, dst);
cout << "二维码四个顶点为:" << P << endl;
}
else
{
cout << "请确认输入的是否为二维码" << endl;
}
//绘制二维码边框
for (int i = 0; i < P.size(); i++)
{
if (i == P.size() - 1)
{
line(src, P[i], P[0], Scalar(0, 0, 255), 2, 8);
break;
}
line(src, P[i], P[i + 1], Scalar(0, 0, 255), 2, 8);
}
imshow("加框后的二维码", src);
namedWindow("dst", WINDOW_NORMAL);
imshow("dst", dst);
waitKey(0);
return 0;
}
结果:

本文围绕OpenCV展开,介绍了图像读取与显示、Mat类图像矩阵创建、颜色空间转换等操作。还涉及图像赋值与复制的深浅拷贝区别、阈值化、大小变换、透视变换等内容,以及多种滤波、边缘检测方法,最后提及图像形态学处理和二维码检测识别。

3万+

被折叠的 条评论
为什么被折叠?



