OpenCV图像处理基础

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

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.图像滤波操作  

①均值滤波

滤波模板:

\begin{bmatrix} 1/9&1/9 &1/9 \\ 1/9&1/9 &1/9 \\ 1/9&1/9 & 1/9 \end{bmatrix}  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;
}

结果: 

②高斯滤波(适合含有高斯噪声的图像)

滤波模板:

\begin{bmatrix} 0.075&0.124 &0.075 \\ 0.124&0.204 &0.124 \\ 0.075&0.124 & 0.075 \end{bmatrix}  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;
}

结果:

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值