OpenCV3 imgproc模块的基本方法

本文用于实战OpenCV3 imgproc模块的基本方法!!!

具体函数定义可参考OpenCV3 Library API参考网站:

https://docs.opencv.org/3.0-beta/modules/refman.html

/* main.c */
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <vector>
#include <iostream>

using namespace std;
using namespace cv;

#define WINDOW_NAME "window"
int g_nThresholdValue = 100;
int g_nThresholdType = 3;
Mat g_thresholdImg, g_grayImg;
static void on_Threshold(int, void*);

int main(int *argc, int **argv)
{
	Mat srcImg = imread("lena.jpg");

	//1.线性滤波操作
	Mat boxFilterImg, blurImg, GaussianBlurImg;

	//方框滤波
	boxFilter(srcImg, boxFilterImg, -1, Size(5, 5));
	imshow("boxFilter", boxFilterImg);

	//均值滤波
	blur(srcImg, blurImg, Size(5, 5));
	imshow("blur", blurImg);

	//高斯滤波
	GaussianBlur(srcImg, GaussianBlurImg, Size(5, 5), 0, 0);
	imshow("GassianBlur", GaussianBlurImg);

	//2.非线性滤波
	Mat medianBlurImg, bilateralFilterImg;
	//中值滤波
	medianBlur(srcImg, medianBlurImg, 5);
	imshow("medianBlur", medianBlurImg);

	//双边滤波
	bilateralFilter(srcImg, bilateralFilterImg, 5, 5 * 2, 5 / 2);
	imshow("bilateraFilter", bilateralFilterImg);

	//3.形态学滤波
	Mat dilateImg, erodeImg, morphologyExImg;
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
	//膨胀操作
	dilate(srcImg, dilateImg, element);
	imshow("dilate", dilateImg);

	//腐蚀操作
	erode(srcImg, erodeImg, element);
	imshow("erode", erodeImg);

	//高级形态学操作,开运算、闭运算、形态学梯度、顶帽、黑帽(MORPH_BLACKHAT)
	//通过设置不同的标识符选择不同的方法
	morphologyEx(srcImg, morphologyExImg, MORPH_BLACKHAT, element);
	imshow("MORPH_BLACKHAT", morphologyExImg);

	//4.漫水填充
	Rect rect;
	Mat floodFillImg = srcImg.clone();
	floodFill(floodFillImg, Point(100, 100), Scalar(10, 10, 10), &rect, Scalar(10, 10, 10), Scalar(10, 10, 10));
	imshow("floodFill", floodFillImg);

	//5.图像尺寸调整
	Mat resizeImg1, resizeImg2;
	resize(srcImg, resizeImg1, Size(srcImg.cols / 2, srcImg.rows / 2), 0, 0, INTER_LINEAR);
	imshow("resize1", resizeImg1);

	resize(srcImg, resizeImg2, Size(), 0.5, 0.5, INTER_AREA);
	imshow("resize2", resizeImg2);

	//6.图像金字塔
	Mat pyrUpImg, pyrDownImg;
	//向上采样
	pyrUp(srcImg, pyrUpImg, Size(srcImg.cols * 2, srcImg.rows * 2));
	imshow("pyrUp", pyrUpImg);
	//向下采样
	pyrDown(srcImg, pyrDownImg, Size(srcImg.cols / 2, srcImg.rows / 2));
	imshow("pyrDown", pyrDownImg);

	//7.固定阈值操作
	cvtColor(srcImg, g_grayImg,COLOR_RGB2GRAY);
	namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);//先创建窗口再创建滑动条
	createTrackbar("model", WINDOW_NAME, &g_nThresholdType, 4, on_Threshold);
	createTrackbar("threshold", WINDOW_NAME, &g_nThresholdValue, 255, on_Threshold);
	on_Threshold(0, 0);

	//8.Canny边缘检测
	Mat grayImg, edgeImg, colorImg;
	colorImg.create(srcImg.size(), srcImg.type());
	colorImg = Scalar::all(0);
	cvtColor(srcImg, grayImg, COLOR_RGB2GRAY);
	blur(grayImg, edgeImg, Size(3, 3));
	Canny(edgeImg, edgeImg, 3, 9, 3);
	imshow("gray edge", edgeImg);

	srcImg.copyTo(colorImg, edgeImg);
	imshow("color edge", colorImg);

	//9.重映射,不改变像素值,仅移动像素位置
	Mat map_x, map_y, remapImg;
	map_x.create(srcImg.size(), CV_32FC1);
	map_y.create(srcImg.size(), CV_32FC1);
	for (int j = 0; j < srcImg.rows; ++j)
	{
		for (int i = 0; i < srcImg.cols; ++i)
		{
			map_x.at<float>(j, i) = static_cast<float>(map_x.cols - i);
			map_y.at<float>(j, i) = static_cast<float>(map_y.rows - j);
		}
	}
	remap(srcImg, remapImg, map_x, map_y, INTER_LINEAR);
	imshow("remap", remapImg);

	//10.仿射变换 仿射变换旋转
	Mat warpImg, warpRotImg;
	Point2f srcWarp[3],dstWarp[3];
	srcWarp[0] = Point2f(0, 0);
	srcWarp[1] = Point2f(static_cast<float>(srcImg.cols - 1), 0);
	srcWarp[2] = Point2f(0, static_cast<float>(srcImg.rows - 1));

	dstWarp[0] = Point2f(0.0, static_cast<float>(srcImg.rows *0.4));
	dstWarp[1] = Point2f(static_cast<float>(srcImg.cols *0.8), static_cast<float>(srcImg.rows *0.3));
	dstWarp[2] = Point2f(static_cast<float>(srcImg.cols *0.5), static_cast<float>(srcImg.rows *0.6));
	Mat warpMat(2,3,CV_32FC1);
	//获得仿射矩阵
	warpMat = getAffineTransform(srcWarp, dstWarp);
	warpAffine(srcImg, warpImg, warpMat, srcImg.size());
	imshow("warpImg", warpImg);

	Mat warpRotMat(2, 3, CV_32FC1);
	Point2f center=Point(warpImg.cols/2, warpImg.rows/2);
	//获得旋转矩阵
	warpRotMat = getRotationMatrix2D(center, 30, 0.5);
	warpAffine(warpImg, warpRotImg, warpRotMat, srcImg.size());
	imshow("warpRotImg", warpRotImg);

	//11.直方图均衡化
	Mat tempImg = srcImg.clone();
	Mat equalizeImg;
	vector<Mat> channels;
	split(tempImg, channels);
	Mat blueImg = channels.at(0);
	equalizeHist(blueImg, equalizeImg);//处理蓝色通道
	imshow("equalizeHist", equalizeImg);

	//12.查找并绘制轮廓
	Mat grayImg, contoursImg;
	contoursImg = Mat::zeros(srcImg.size(), CV_8UC3);
	cvtColor(srcImg, grayImg, COLOR_RGB2GRAY);
	grayImg = grayImg > 50;//灰度图二值化
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(grayImg, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);//寻找轮廓
	int index = 0;
	for (; index >= 0; index = hierarchy[index][0])//将下一个轮廓的索引编号赋值给index
	{
		Scalar color(255, 255, 255);
		drawContours(contoursImg, contours, index, color, CV_FILLED, 8, hierarchy);
	}
	imshow("grayIage", grayImg);
	imshow("contours", contoursImg);

	//13.寻找凸包,绘制凸包
	Mat whiteImg(512,512,CV_8UC3);
	whiteImg = Scalar::all(0);
	RNG &rng = theRNG();
	int countRNG = (unsigned)rng % 100 + 3;
	vector<Point> points;
	for (int i = 0; i < countRNG; ++i)//生成随机数
	{
		Point point;
		point.x = rng.uniform(whiteImg.cols/4, whiteImg.cols/4 * 3);
		point.y = rng.uniform(whiteImg.rows / 4, whiteImg.rows / 4 * 3);
		points.push_back(point);
	}
	Scalar color = Scalar::all(255);
	for (int j = 0; j < countRNG; ++j)//绘制随机数
		circle(whiteImg, points[j], 3, color, CV_FILLED, CV_AA);
	vector<int> hull;
	convexHull(Mat(points), hull, true);//返回凸包点在点集中的索引
	imshow("points", whiteImg);

	int hullCount = (int)hull.size();
	Point point0 = points[hull[hullCount - 1]];
	for (int k = 0; k < hullCount; ++k)//连接各个凸包点
	{
		Point point = points[hull[k]];
		line(whiteImg, point0, point, Scalar::all(255), 2, 8);
		point0 = point;
	}
	imshow("hull", whiteImg);

	//14.RGB转化为HSV色彩空间
	Mat hsvImg;
	cvtColor(srcImg, hsvImg, COLOR_RGB2HSV);
	imshow("hsvImg", hsvImg);
	
	while (1)
	{
		char c = waitKey(5);//获取按键
		if ((char)c == 49)//按键为1,退出窗口
			break;
	}

	return 0;
}

static void on_Threshold(int, void*)
{
	threshold(g_grayImg, g_thresholdImg, g_nThresholdValue, 255, g_nThresholdType);
	imshow(WINDOW_NAME, g_thresholdImg);
}

所用知识:

1.若以彩色模式载入图像,即通道顺序为RGB,解码后的图像以BGR的通道顺序进行存储;

2.waitKey()函数的功能是不断刷新图像,频率时间为delay,单位为ms,返回值为当前键盘按键值。delay>0时,延迟"delay"ms,在显示视频时这个函数是有用的,用于设置在显示完一帧图像后程序等待"delay"ms再显示下一帧视频;如果使用waitKey(0)则只会显示第一帧视频;

3. copyTo最一般的用法是src.copyTo(dst),将src复制到dst矩阵中;src.copyTo( dst,detected_edges); 是将src中detected_edges矩阵对应的非零部分(即边缘检测结果)复制到dst中。所以最终显示的边缘和原图颜色一样,也可以直接显示detected_edges矩阵(黑白)。

4.现实世界的三维空间映射到图像显示的二维空间中会丢失很多信息,也会添进来一部分类似光照、场景等的干扰。如何获得二维图像中的边缘?图像处理中认为,灰度值变化剧烈的地方就是边缘。那么如何判断灰度值变化?如何度量“剧烈”?sobel算子就对这些问题做了自己的规范,而且命名为sobel算子。

sobel算子的思想,Sobel算子认为,邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不同的权值,对算子结果产生的影响也不同。一般来说,距离越远,产生的影响越小。

sobel算子的原理,对传进来的图像像素做卷积,卷积的实质是在求梯度值,或者说给了一个加权平均,其中权值就是所谓的卷积核;然后对生成的新像素灰度值做阈值运算,以此来确定边缘信息。

Sobel算子边缘检测过程:(1)滤波预处理,减少噪声对sobel算子的影响;(2) 对原图像素进行卷积获得新的像素(X方向上和Y方向上的梯度);(3) 给定阈值,大于阈值的为边缘,不大于的不为边缘;

5.膨胀、腐蚀、开、闭运算是数学形态学最基本的变换。
膨胀:求局部最大值(卷积),把二值图像各1像素连接成分的边界扩大一层(填充边缘或0像素内部的孔);

腐蚀:求局部最小值(卷积),把二值图像各1像素连接成分的边界点去掉从而缩小一层(可提取骨干信息,去掉毛刺,去掉孤立的0像素);开:先腐蚀再膨胀,可以去掉目标外的孤立点;闭:先膨胀再腐蚀,可以去掉目标内的孔。

6.漫水填充,简单来说,就是自动选中了和种子点相连的区域,接着将该区域替换成指定的颜色,这是个非常有用的功能,经常用来标记或者分离图像的一部分进行处理或分析.漫水填充也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或者只处理掩码指定的像素点. 漫水填充不会填充掩膜mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。另外需要注意的是,掩膜mask会比需填充的图像大,所以 mask 中与输入图像(x,y)像素点相对应的点的坐标为(x+1,y+1)。

在OpenCV 2.X中,使用C++重写过的FloodFill函数有两个版本。一个不带掩膜mask的版本,和一个带mask的版本。这个掩膜mask,就是用于进一步控制哪些区域将被填充颜色(比如说当对同一图像进行多次填充时)。

这两个版本的FloodFill,都必须在图像中选择一个种子点,然后把临近区域所有相似点填充上同样的颜色,不同的是,不一定将所有的邻近像素点都染上同一颜色,漫水填充操作的结果总是某个连续的区域。当邻近像素点位于给定的范围(从loDiff到upDiff)内或在原始seedPoint像素值范围内时,FloodFill函数就会为这个点涂上颜色。

参考资料:

1.毛星云等编著《OpenCV3编程入门》;

2.CSDN相关博客和其他网页资料,因为是随用随查,仅将关键内容存于笔记中,然后在此贴出,所以并未保存链接;


猜你喜欢

转载自blog.csdn.net/attitude_yu/article/details/79956953
今日推荐