OpenCV总结之一_图像处理基本操作

目录:
*第一节——基础——加载imread、修改、显示imshow、保存imwrite
*第二节——矩阵的掩模操作filter2D+++代码的运行时间getTickCount
*第三节——Mat对象create、clone、copyTo、cvtColor、cout
*第四节——图像操作——访问图像像素点进行反差操作bitwise_not
*第五节——图像混合——两张大小相等类型相同的图像进行线性混合操作addWeighted
*第六节——提高图像亮度与对比度
*第七节——绘制形状与文字-线line、矩形rectangle、椭圆ellipse、圆circle、多边形fillPoly、随机线与文字
*第八节——图像模糊-去噪-均值blur、高斯GaussianBlur、中值medianBlur、双边bilateralFilter
*第九节——形态学操作——膨胀dilate和腐蚀erode
*第十节——形态学操作morphologyEx——开操作、闭操作、形态学梯度、顶帽、黑帽
*第十一节——形态学操作应用-提取水平与垂直线
*第十二节——图像金字塔——图像上采样pyrUp(zoom in放大)和降采样pyrDown(zoom out缩小)
*第十三节——基本阈值操作threshold
*第十四节——自定义线性滤波——Robert、Sobel、拉普拉斯算子、filter2D
*第十五节——边缘处理——copyMakeBorder用途:在卷积时边界的处理方式有4种类型
*第十六节——Sobel——边缘处理
*第十七节——Laplacian——边缘处理
*第十八节——Canny——边缘处理
*第十九节——霍夫变换——直线变换HoughLinesP
*第二十节——霍夫变换——圆变换HoughCircles
*第二十一节——像素重映射remap
*第二十二节——直方图均衡化equalizeHist
*第二十三节——直方图计算calcHist
*第二十四节——直方图比较compareHist
*第二十五节——直方图反向投影calcBackProject
*第二十六节——模板匹配matchTemplate
*第二十七节——轮廓发现findContours
*第二十八节——凸包convexHull
*第二十九节——轮廓周围绘制矩形框和圆形框
*第三十节——图像矩moments、面积contourArea, 弧长arcLength
*第三十一节——点多边形测试
*第三十二节——点多边形测试

///第一节——基础——加载imread、修改、显示imshow、保存imwrite*///

//添加头文件
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <opencv2/core/core.hpp> 
#include <opencv2/imgcodecs.hpp> 
#include <opencv2/highgui/highgui.hpp>
//命名空间
using namespace cv;
using namespace std;
//程序入口
int main(int argc, char** argv)
{
	//加载图像
	Mat src = imread("E:/images/test.jpg");
	if (src.empty()) {printf("error\n");	return -1;} 
	//if(!src1.data) {cout << "error..." << endl;  return -1;}//不同的表达方式
	///CV_Assert(testImage.depth() == CV_8U);//若括号中的表达式值为false,则返回一个错误信息_同上一句的效果
	namedWindow("opencv setup demo", CV_WINDOW_AUTOSIZE);//创建一个显示窗口
	imshow("opencv setup demo", src);//显示图像
	//修改图像
	Mat dst;
	cvtColor(src, dst, CV_BGR2HSV);//色彩空间转换
	imshow("output window", dst);//显示修改后的图像
    //保存图像
	imwrite("E:/images/repair.png", dst);
	waitKey(0);
	return 0;
}

///第二节——矩阵的掩模操作filter2D+++代码的运行时间getTickCount*/

int main(int argc, char** argv)
{
	//代码运行开始时间
	double t = getTickCount();
	//使用Filter2D函数——掩模操作
	Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	filter2D(src, dst, src.depth(), kernel);
    //filter2D(src, csrc, -1, kernel);//-1默认和src的深度一样
	//代码运行时间=结束时间-开始时间       //转换时间单位并输出代码运行时间
	double timeconsume = (getTickCount() - t) / getTickFrequency();
	printf("timeconsume %.2f\n", timeconsume);//输出消耗的时间
}

///第三节——Mat对象create、clone、copyTo、cvtColor、cout*/

int main(int argc, char** argv)
{
	//创建一个与输入图像src大小、类型一致的图像,且初始化像素为红色
	Mat dst = Mat(src.size(), src.type(), Scalar(0, 0, 255));//初始化的像素值,(0, 0, 255)表示红色
	//同上的另一种创建方法
	Mat m1;
    m1.create(src.size(), src.type());
	m1 = Scalar(0, 0, 255);    
	//克隆
	Mat dst = src.clone();
	//复制
	Mat dst;//clone  copyTo  两种
	src.copyTo(dst);  
    //将图像转为灰度图像,并显示:输入图像和输出图像的通道结果
	cvtColor(src, dst, CV_BGR2GRAY);
	printf("input image channels : %d\n", src.channels());  //3通道
	printf("output image channels : %d\n", dst.channels()); //1通道
    //显示输出图像的宽和高以及输出图像第一个像素值
	cvtColor(src, dst, CV_BGR2GRAY);
	int cols = dst.cols;//dst的全部列(宽)
	int rows = dst.rows;//dst的全部行(高)
	printf("rows : %d cols : %d\n", rows, cols);
	const uchar* firstRow = dst.ptr<uchar>(0);//访问图像的像素值
	printf("fist pixel value : %d\n", *firstRow);//第一个像素值是多少
    //定义一个3×3的图像,数据结构为CV_8UC3
	Mat M(3, 3, CV_8UC3, Scalar(0,0,255));//3通道,(0,0,255)
	imshow("out image", M);
	cout << "M =" << endl << M << endl;//一种打印方式
    //定义一个100×100的图像,数据结构为CV_8UC1
	Mat M(100, 100, CV_8UC1, Scalar(127));   //1通道   127
	cout << "M =" << endl << M << endl;
    //创建新的窗口——全部初始化为0,图像为黑漆漆的一片,类似于眼睛
	Mat m1 = Mat::zeros(2, 2, CV_8UC1);//参数(row,col,type)(行-高,列-宽,类型)
	Mat m2 = Mat::zeros(src.size(), src.type());//和原图大小一样
	Mat m3 = Mat::eye(2, 2, CV_8UC1);
	cout << "m2 =" << endl << m2 << endl;
}

///第四节——图像操作——访问图像像素点进行反差操作bitwise_not*/

int main(int argc, char** args)
{
	//单通道图像反差
	Mat grayImg;
	cvtColor(src, grayImg, CV_BGR2GRAY);//cvtColor(src, grayImg, COLOR_BGR2GRAY);
	// int height = grayImg.rows;//行
	// int width = grayImg.cols;//列
	// for (int row = 0; row < height; row++)
	// {
		// for (int col = 0; col < width; col++)
		// {
			// int gray = grayImg.at<uchar>(row, col);
			// grayImg.at<uchar>(row, col) = 255 - gray;//反差之后的像素
		// }
	// }
	bitwise_not(grayImg, grayImg); //调用函数等同于for循环的内容  反差图片
	//三通道——彩色图像的反差处理
	Mat dst;
	dst.create(src.size(), src.type());
	int height3 = src.rows;  int width3 = src.cols;  int channels = src.channels();//获取通道
	printf("height=%d width=%d channels=%d\n", height, width, channels);
	// for (int row = 0; row < height3; row++)
	// {
		// for (int col = 0; col < width3; col++)
		// {
			// if (channels == 1)
			// {
				// int gray = grayImg.at<uchar>(row, col);
				// grayImg.at<uchar>(row, col) = 255 - gray;//反差之后的像素
			// }
			// else if (channels == 3)
			// {
				// int b = src.at<Vec3b>(row, col)[0];//blue
				// int g = src.at<Vec3b>(row, col)[1];//green
				// int r = src.at<Vec3b>(row, col)[2];//red
				// dst.at<Vec3b>(row, col)[0] = 255 - b;
				// dst.at<Vec3b>(row, col)[1] = 255 - g;
				// dst.at<Vec3b>(row, col)[2] = 255 - r;
			// }
		// }
	// }
	bitwise_not(src, dst); //调用函数等同于for循环的内容  反差图片
}

///第五节——图像混合——两张大小相等类型相同的图像进行线性混合操作addWeighted*/

int main(int argc, char** argv) 
{
	double alpha = 0.5;
	if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type())
	{
		//add(src1,src2,dst,Mat());  //直接添加的效果
		//multiply(src1, src2, dst, 1.0);  //相乘的效果
		addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);//线性混合操作	
	}
	else
	{
		printf("could not blend images , the size of images is not same...\n");
		return -1;
	}
}

///第六节——提高图像亮度与对比度*/

int main(int argc, char** argv)
{
	int height = src.rows;
	int width = src.cols;
	Mat dst = Mat::zeros(src.size(), src.type());//创建一个和原图像大小和类型一致的空白图像,像素初始化为0
	float alpha = 1.5;
	float beta = 10;
	Mat m1;
	src.convertTo(m1, CV_32F);
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++) 
		{
			if (src.channels() == 3)
			{
				float b = m1.at<Vec3f>(row, col)[0];// blue
				float g = m1.at<Vec3f>(row, col)[1]; // green
				float r = m1.at<Vec3f>(row, col)[2]; // red
				// output
				dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
				dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
				dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
			}
			else if (src.channels() == 1)
			{
				float v = src.at<uchar>(row, col);
				dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);
			}
		}
	}
}

///第七节——绘制形状-线line、矩形rectangle、椭圆ellipse、圆circle、多边形fillPoly、随机线与文字*/

Mat src;//定义的全局变量
const char* drawdemo_win = "draw shapes and text demo";
//函数声明
void MyLines();/*线*/      void MyRectangle();/*矩形*/    void MyEllipse();/*椭圆*/  
void MyCircle();/*圆*/     void MyPolygon();/*多边形*/    void RandomLineDemo();/*随机线*/
int main(int argc, char** argv)
{
	//函数调用
	MyLines();
	MyRectangle();
	MyEllipse();
	MyCircle();
	MyPolygon();//多边形
	//绘制文字
	putText(src, "Hello OpenCV", Point(300, 300),CV_FONT_HERSHEY_COMPLEX, 1.0,Scalar(12, 23, 200), 3, 8);
	RandomLineDemo();
}
//函数实现
void MyLines() 
{
	Point p1 = Point(20, 30);
	Point p2;
	p2.x = 400;
	p2.y = 400;
	Scalar color = Scalar(0, 0, 255);//红色
	line(src, p1, p2, color, 1, LINE_AA);//1表示线宽
}
void MyRectangle()
{
	Rect rect = Rect(200, 100, 300, 300);
	Scalar color = Scalar(255, 0, 0);//蓝色
	rectangle(src, rect, color, 2, LINE_8);
}
void MyEllipse()
{
	Scalar color = Scalar(0, 255, 0);//绿色
    /*空心椭圆(src,点中心,尺寸轴,双角度,双起始角,双终止角,颜色,线宽,线型,int shift=0)*/
	ellipse(src,Point(src.cols / 2, src.rows / 2),Size(src.cols / 4, src.rows / 8),
		    90, 0, 360, color, 2, LINE_8);
}
void MyCircle()
{
	Scalar color = Scalar(0, 255, 255);
	Point center = Point(src.cols / 2, src.rows / 2);
	circle(src, center, 150, color, 2, 8);
}
void MyPolygon()
{
	Point pts[1][5];
	pts[0][0] = Point(100, 100);
	pts[0][1] = Point(100, 200);
	pts[0][2] = Point(200, 200);
	pts[0][3] = Point(200, 100);
	pts[0][4] = Point(100, 100);
	const Point* ppts[] = { pts[0] };//多边形的顶点
	int npt[] = { 5 };//要绘制的多边形顶点数目
	Scalar color = Scalar(255, 12, 255);//颜色
	fillPoly(src, ppts, npt, 1, color, 8);//1表示画一个多边形-实心的
}
void RandomLineDemo()
{
	RNG rng(12345);//随机数生成函数RNG   12345是随机种子
	Point pt1;
	Point pt2;
	Mat bg = Mat::zeros(src.size(), src.type());//创建一张空白图片,纯黑色
	for (int i = 0; i < 100000; i++)
	{
		pt1.x = rng.uniform(0, src.cols);
		pt2.x = rng.uniform(0, src.cols);
		pt1.y = rng.uniform(0, src.rows);
		pt2.y = rng.uniform(0, src.rows);
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		if (waitKey(50) > 0) //随时可以停止的命令
		{
			break;
		}
		line(bg, pt1, pt2, color, 1, 8);
	}
}

///第八节——图像模糊-去噪-均值blur、高斯GaussianBlur、中值medianBlur、双边bilateralFilter*/

int main(int argc, char** argv)
{
	//均值模糊
	blur(src, dst, Size(11, 11), Point(-1, -1));//Point(-1, -1)表示默认正中心
    //高斯模糊
	GaussianBlur(src, gblur, Size(11, 11), 11, 11);//Size(x,y),x,y必须是正数而且是奇数
    //中值滤波  对椒盐噪声有很好的效果
	medianBlur(src, dst, 3);
	//双边滤波  立体感更强
	bilateralFilter(src, dst, 15, 200, 5);
	//在dst的基础上进行掩模操作提升对比度
	Mat resultImg;
	Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	filter2D(dst, resultImg, -1, kernel, Point(-1, -1), 0);
}

///第九节——形态学操作——膨胀dilate和腐蚀erode*/

int element_size = 3;//为了实现动态调整大小的目的,设置结构元素的大小
int max_size = 21;
void CallBack_Demo(int, void*);
int main(int argc, char** argv)
{
	createTrackbar("Element Size :", OUTPUT_WIN, &element_size, max_size, CallBack_Demo);
	createTrackbar("Element Size :", OUTPUT_WIN2, &element_size, max_size, CallBack_Demo);
	CallBack_Demo(0, 0);//函数调用
}
void CallBack_Demo(int, void*)
{
	//结构元素
	int s = element_size * 2 + 1;
	Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
	//膨胀_变小
	dilate(src, dst1, structureElement, Point(-1, -1), 1);
	//腐蚀_变大
	erode(src, dst2, structureElement);
}

///第十节——形态学操作morphologyEx——开操作、闭操作、形态学梯度、顶帽、黑帽*/

int main(int argc, char** argv)
{
	Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));//结构元素
	morphologyEx(src, dst, CV_MOP_OPEN, kernel);//开操作——先腐蚀后膨胀(去掉小的对象)
	morphologyEx(src, dst, CV_MOP_CLOSE, kernel);//闭操作——先膨胀后腐蚀(填充小的洞)
	morphologyEx(src, dst, CV_MOP_GRADIENT, kernel);//形态学梯度——膨胀减去腐蚀
	morphologyEx(src, dst, CV_MOP_TOPHAT, kernel);//顶帽——原图像与开操作之间的差值图像
	morphologyEx(src, dst, CV_MOP_BLACKHAT, kernel);//黑帽——闭操作图像与源图像的差值图像
}

///第十一节——形态学操作应用-提取水平与垂直线*/

int main(int argc, char** argv)
{
	/*
	转换为二值图像的函数说明
	adaptiveThreshold
	(Mat src,              // 输入的灰度图像
	 Mat dst,              // 二值图像
	 double maxValue,      // 二值图像最大值
	 int adaptiveMethod    // 自适应方法,二选一,只有这两种ADAPTIVE_THRESH_MEAN_C,ADAPTIVE_THRESH_GAUSSIAN_C
	 int thresholdType,    // 阈值类型
	 int blockSize,        // 块大小
	 double C)             // 常量C 可以是正数,0,负数
	*/
	cvtColor(src, gray_src, CV_BGR2GRAY);
	Mat binImg;//二值图像
	adaptiveThreshold(~gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
	Mat temp;
	/// 水平结构元素
	Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
	erode(binImg, temp, hline);//腐蚀
	dilate(temp, dst, hline);//膨胀 /*morphologyEx(binImg, dst, CV_MOP_OPEN, hline);//先腐蚀后膨胀*/
	bitwise_not(dst, dst);//反差图片
	blur(dst, dst, Size(3, 3), Point(-1, -1));
	/// 垂直结构元素
	Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));
	erode(binImg, temp, vline);//腐蚀
	dilate(temp, dst, vline);//膨胀/*morphologyEx(binImg, dst, CV_MOP_OPEN, vline);//先膨胀后腐蚀*/
	bitwise_not(dst, dst);//反差图片
	blur(dst, dst, Size(3, 3), Point(-1, -1));
	/// 矩形结构元素
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	erode(binImg, temp, kernel);//腐蚀
	dilate(temp, dst, kernel);//膨胀/*morphologyEx(binImg, dst, CV_MOP_OPEN, kernel);*/
	bitwise_not(dst, dst);//反差图片
	blur(dst, dst, Size(3, 3), Point(-1, -1));
}

///第十二节——图像金字塔——图像上采样pyrUp(zoom in放大)和降采样pyrDown(zoom out缩小)*/

int main(int agrc, char** argv)
{
	// 上采样——生成的图像是原图在宽与高各放大两倍
	pyrUp(src, dst1, Size(src.cols * 2, src.rows * 2));
	// 降采样——生成的图像是原图在宽与高各缩小1/2
	pyrDown(src, dst2, Size(src.cols / 2, src.rows / 2));
	/* 
	DOG高斯不同,可以应用到轮廓提取
	——就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像
	*/
	Mat gray_src, g1, g2, dogImg;
	cvtColor(src, gray_src, CV_BGR2GRAY);//原图-灰度图
	GaussianBlur(gray_src, g1, Size(5, 5), 0, 0);//灰度图-g1
	GaussianBlur(g1, g2, Size(5, 5), 0, 0);//g1-g2
	subtract(g1, g2, dogImg, Mat());//g1减去g2  得到的图像dogImg
	normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);// 归一化显示
}

///第十三节——基本阈值操作threshold*/

Mat src, gray_src, dst;
int threshold_value = 127;//无厘头的值=255/2
int threshold_max = 255;//阈值最大值
int type_value = 2;
int type_max = 4;//五种方法
const char* output_title = "binary image";
void Threshold_Demo(int, void*);//定义一个变量函数
int main(int argc, char** argv)
{
	createTrackbar("Threshold Value:", output_title, &threshold_value, threshold_max, Threshold_Demo);
	createTrackbar("Type Value:", output_title, &type_value, type_max, Threshold_Demo);
	Threshold_Demo(0, 0);
}
void Threshold_Demo(int, void*)
{
	cvtColor(src, gray_src, CV_BGR2GRAY);//灰度图
	/*
	threshold(src,dst,当前阈值,阈值最大值,阈值类型)
	THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO、THRESH_TOZERO_INV
	*/
	//自动计算阈值1——THRESH_OTSU、THRESH_TRIANGLE表示自动方法——调节createTrackbar时没有反应的
	threshold(gray_src, dst, 0, 255, THRESH_OTSU | type_value);
	//自动计算阈值2————type_value表示在5种阈值类型中选择哪一种阈值类型
	threshold(gray_src, dst, 0, 255, THRESH_TRIANGLE | type_value);//表示阈值类型为THRESH_BINARY(type_value=0)
	threshold(gray_src, dst, threshold_value, threshold_max, THRESH_BINARY);
	//显示五种 阈值方法
	threshold(gray_src, dst, threshold_value, threshold_max, type_value);
}

///第十四节——自定义线性滤波——边缘——Robert、Sobel、拉普拉斯算子、filter2D*/

int main(int argc, char** argv)
{
	/* 
	filter2D(
	Mat src,      //输入图像
	Mat dst,      // 模糊图像
	int depth,    // 图像深度32/8(取-1)
    Mat kernel,   // 卷积核/模板——kernel是可以自定义的卷积核
    Point anchor, // 锚点位置
    double delta  // 计算出来的像素+delta)
	*/
	///边缘检测的算子 
	//Robert算子 X方向
	Mat Robert_dst_x;
	Mat Robert_kernel_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);
	filter2D(src, Robert_dst_x, -1, Robert_kernel_x, Point(-1, -1), 0.0);
	//Robert算子 Y方向
	Mat Robert_dst_y;
	Mat Robert_kernel_y = (Mat_<int>(2, 2) << 0, 1, -1, 0);
	filter2D(src, Robert_dst_y, -1, Robert_kernel_y, Point(-1, -1), 0.0);
	//Sobel X 方向
	Mat Sobel_dst_x;
	Mat Sobel_kernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
	filter2D(src, Sobel_dst_x, -1, Sobel_kernel_x, Point(-1, -1), 0.0);
	//Sobel Y 方向
	Mat Sobel_dst_y;
	Mat Sobel_kernel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
	filter2D(src, Sobel_dst_y, -1, Sobel_kernel_y, Point(-1, -1), 0.0);
	//拉普拉斯算子
	Mat lpls_kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
	filter2D(src, dst, -1, lpls_kernel, Point(-1, -1), 0.0);
	///自定义卷积模糊
	int c = 0;
	int index = 0;
	int ksize = 0;
	while (true)
	{
		c = waitKey(500);
		if ((char)c == 27)
		{
			break;// ESC
		}
		ksize = 5 + (index % 8) * 2;
		Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);
		filter2D(src, dst, -1, kernel, Point(-1, -1));
		index++;
		imshow("OUTPUT_WIN", dst);
	}
}

///第十五节——边缘处理——copyMakeBorder用途:在卷积时边界的处理方式有4种类型*/

int main(int argc, char** argv)
{
	//定义上下左右边
	int top = (int)(0.05*src.rows);
	int bottom = (int)(0.05*src.rows);
	int left = (int)(0.05*src.cols);
	int right = (int)(0.05*src.cols);
	RNG rng(12345);//随机数+随机种子
	int borderType = BORDER_DEFAULT;//边界类型(4种)
    /*BORDER_DEFAULT、BORDER_CONSTANT、BORDER_REPLICATE、BORDER_WRAP*/ 
	int c = 0;
	while (true)
	{
		c = waitKey(500);
		// ESC  27代表Esc退出键
		if ((char)c == 27)//退出来的条件 
		{
			break;
		}
		if ((char)c == 'r')
		{
			borderType = BORDER_REPLICATE;
		}
		else if ((char)c == 'w')
		{
			borderType = BORDER_WRAP;
		}
		else if ((char)c == 'c')
		{
			borderType = BORDER_CONSTANT;
		}
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);//创建边界,得到显示
		imshow(“OUTPUT_WIN”, dst);
	}//按键显示各种类型的效果
	 Mat dst1;//高斯模糊的效果显示
	 GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_REPLICATE);
	 GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_WRAP);//不支持
	 GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_DEFAULT);//默认处理方法
	 GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_CONSTANT);
}

///第十六节——Sobel——边缘处理*/

int main(int argc, char** argv)
{
	//高斯模糊→灰度图→求梯度X和Y→振幅图像
	GaussianBlur(src, dst, Size(3, 3), 0, 0);//高斯模糊
	cvtColor(dst, gray_src, CV_BGR2GRAY);//灰度图
	//X方向和Y方向的梯度
	Mat xgrad, ygrad;
	//输入图像-输出图像-输出图像深度大于原来的-x方向-y方向-ksize
	Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);
	Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
	/*   
	// 改进版本
	Scharr(gray_src, xgrad, CV_16S, 1, 0);
	Scharr(gray_src, ygrad, CV_16S, 0, 1);
	*/
	convertScaleAbs(xgrad, xgrad);// 计算图像A的像素绝对值,输出到图像B
	convertScaleAbs(ygrad, ygrad);
	//振幅图像    //两幅图叠加
	Mat x_ygrad;
	addWeighted(xgrad, 0.5, ygrad, 0.5, 0, x_ygrad);
	/*****公式效果  可直接使用addWeighted*****/
	// Mat xygrad = Mat(xgrad.size(), xgrad.type());
	// printf("type : %d\n", xgrad.type());
	// int width = xgrad.cols;
	// int height = ygrad.rows;
	// for (int row = 0; row < height; row++)
	// {
		// for (int col = 0; col < width; col++)
		// {
			// int xg = xgrad.at<uchar>(row, col);
			// int yg = ygrad.at<uchar>(row, col);
			// int xy = xg + yg;
			// xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
		// }
	// }
}

///第十七节——Laplacian——边缘处理*/

int main(int argc, char** argv)
{
	Mat gray_src, edge_image;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_BGR2GRAY);
	Laplacian(gray_src, edge_image, CV_16S, 3);
	convertScaleAbs(edge_image, edge_image);
	threshold(edge_image, edge_image, 0, 255, THRESH_OTSU | THRESH_BINARY);
}

///第十八节——Canny——边缘处理*/

int t1_value = 50;
int max_value = 255;
void Canny_Demo(int, void*);
int main(int argc, char** argv)
{
	cvtColor(src, gray_src, CV_BGR2GRAY);//灰度化
	createTrackbar("Threshold Value:", OUTPUT_TITLE, &t1_value, max_value, Canny_Demo);//创建阈值滑块
	Canny_Demo(0, 0);
}
void Canny_Demo(int, void*)
{
	Mat edge_dst;
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);//均值模糊
	Canny(gray_src, edge_dst, t1_value, t1_value * 2, 3, false);
	imshow("output_title1", edge_dst);//黑底白边
	imshow("OUTPUT_TITLE2", ~edge_dst);//取反,白底黑边	 
	dst.create(src.size(), src.type());
	src.copyTo(dst, edge_dst);//对图像进行初始化
	imshow("output_title13", dst);//彩色图
}

///第十九节——霍夫变换——直线变换HoughLinesP*/
/
霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,
主要用来从图像中分离出具有某种相同特征的几何形状(如:直线、圆)。
做基本的霍夫变换是从黑白图像中检测直线
/

int main(int argc, char** argv)
{
	// extract edge
	Canny(src, canny_dst, 150, 200);//Canny边缘检测
	cvtColor(canny_dst, dst, CV_GRAY2BGR);
	//HoughLinesP
	vector<Vec4f> plines;//定义一个浮点数-霍夫变换得出的结果放入plines数组中
	HoughLinesP(src_gray, plines, 1, CV_PI / 180.0, 10, 0, 30);
	Scalar color = Scalar(0, 0, 255);
	for (size_t i = 0; i < plines.size(); i++) //size_t看成int
	{
		Vec4f hline = plines[i];
		line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA);
	}
}




///***********第二十节——霍夫变换——圆变换HoughCircles************/	

```cpp
int main(int argc, char** argv)
{
	// 中值滤波
	medianBlur(src, medianBlur_output, 3);
	cvtColor(medianBlur_output, medianBlur_output, CV_BGR2GRAY);
	// 霍夫圆检测
	vector<Vec3f> pcircles;
	HoughCircles(medianBlur_output, pcircles, CV_HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50);
	src.copyTo(dst);
	for (size_t i = 0; i < pcircles.size(); i++)
	{
		Vec3f cc = pcircles[i];
		circle(dst, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA);//圆的位置
		circle(dst, Point(cc[0], cc[1]), 2, Scalar(198, 23, 155), 2, LINE_AA);//圆心位置
	}
	imshow(OUTPUT_TITLE, dst);
}

///第二十一节——像素重映射remap*/

Mat src, dst, map_x, map_y;
const char* OUTPUT_TITLE = "remap demo";
int index = 0;
void update_map(void);
int main(int argc, char** argv)
{
	//建立映射表 和输入图像的大小一致 32位单通道图像
	map_x.create(src.size(), CV_32FC1);
	map_y.create(src.size(), CV_32FC1);
	int c = 0;
	while (true)
	{
		c = waitKey(500);
		if ((char)c == 27) //Esc
		{
			break;
		}
		index = c % 4;//取模
		update_map();
		remap(src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 255, 255));
		imshow(OUTPUT_TITLE, dst);
	}
	return 0;
}
void update_map(void)
{
	for (int row = 0; row < src.rows; row++)
	{
		for (int col = 0; col < src.cols; col++)
		{
			switch (index)
			{
			case 0://缩小一半
				if (col >(src.cols * 0.25) && col <= (src.cols*0.75) && row >(src.rows*0.25) && row <= (src.rows*0.75))
				{
					map_x.at<float>(row, col) = 2 * (col - (src.cols*0.25));
					map_y.at<float>(row, col) = 2 * (row - (src.rows*0.25));
				}
				else
				{
					map_x.at<float>(row, col) = 0;
					map_y.at<float>(row, col) = 0;
				}
				break;
			case 1://X方向对调
				map_x.at<float>(row, col) = (src.cols - col - 1);
				map_y.at<float>(row, col) = row;
				break;
			case 2://Y方向对调
				map_x.at<float>(row, col) = col;
				map_y.at<float>(row, col) = (src.rows - row - 1);
				break;
			case 3://XY方向同时对调
				map_x.at<float>(row, col) = (src.cols - col - 1);
				map_y.at<float>(row, col) = (src.rows - row - 1);
				break;
			}
		}
	}
}

///第二十二节——直方图均衡化equalizeHist*/

int main(int argc, char** argv)
{
	cvtColor(src, src, CV_BGR2GRAY);
	equalizeHist(src, dst);
}

///第二十三节——直方图计算calcHist*/

int main(int argc, char** argv)
{
	// 分通道显示
	vector<Mat> bgr_planes;//vector数组
	split(src, bgr_planes);
	/*
	imshow("single channel demo1", bgr_planes[0]);
	imshow("single channel demo2", bgr_planes[1]);
	imshow("single channel demo3", bgr_planes[2]);
	*/
	// 计算直方图
	int histSize = 256;
	float range[] = { 0, 256 };
	const float *histRanges = { range };
	Mat b_hist, g_hist, r_hist;
	calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRanges, true, false);
	calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRanges, true, false);
	calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRanges, true, false);
	// 归一化 → 为了显示
	int hist_h = 400;
	int hist_w = 512;
	int bin_w = hist_w / histSize;
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
	normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	// render histogram chart 绘制直方图
	for (int i = 1; i < histSize; i++)
	{
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);

		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);

		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);
	}
	imshow(OUTPUT_T, histImage);
}

///第二十四节——直方图比较compareHist*/

string convertToString(double d);
int main(int argc, char** argv)
{
	Mat base, test1, test2;
	Mat hsvbase, hsvtest1, hsvtest2;
	base = imread("F:/images/test.png");
	test1 = imread("F:/images/rose.png");
	test2 = imread("F:/images/rosenoise.png");
	if (!base.data||!test1.data||!test2.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	//色彩空间转换
	cvtColor(base, hsvbase, CV_BGR2HSV);
	cvtColor(test1, hsvtest1, CV_BGR2HSV);
	cvtColor(test2, hsvtest2, CV_BGR2HSV);
	//定义直方图计算需要的各种参数
	//每个维度的bin数目 对Hue通道使用50个bin,对saturation通道使用60个bin
	int h_bins = 50;
	int s_bins = 60;
	int histSize[] = { h_bins, s_bins };
	// hue varies from 0 to 179, saturation(饱和度) from 0 to 255  
	//每个维度的取值范围
	float h_ranges[] = { 0, 180 };
	float s_ranges[] = { 0, 256 };
	const float* ranges[] = { h_ranges, s_ranges };
	// Use the o-th and 1-st channels    使用第0和第1通道 
	int channels[] = { 0, 1 };
	//MatND是Mat的别名,方便区分经过直方图计算处理后的输入图像
	MatND hist_base;//ND表示多维
	MatND hist_test1;
	MatND hist_test2;
	//计算直方图并进行归一化处理
	//参数:输入图像的指针,图像数目,通道数,输入mask可选不用,输出的直方图数据,维数,直方图级数,值域范围,默认,默认)
	calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false);
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());
	calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false);
	normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
	calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false);
	normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());
	//巴氏距离
	double basebase = compareHist(hist_base, hist_base, CV_COMP_BHATTACHARYYA);
	double basetest1 = compareHist(hist_base, hist_test1, CV_COMP_BHATTACHARYYA);
	double basetest2 = compareHist(hist_base, hist_test2, CV_COMP_BHATTACHARYYA);
	double tes1test2 = compareHist(hist_test1, hist_test2, CV_COMP_BHATTACHARYYA);
	printf("base compare with base correlation value :%f\n", basebase);
	printf("base compare with test1 correlation value :%f\n", basetest1);
	printf("base compare with test2 correlation value :%f\n", basetest2);
	printf("test1 compare with test2 correlation value :%f\n", tes1test2);
	//给每个图像上添加文字,内容为该图片和原始图片的比较结果
	Mat test12;
	test2.copyTo(test12);
	putText(base, convertToString(basebase), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test1, convertToString(basetest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test2, convertToString(basetest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test12, convertToString(tes1test2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	//图像的显示
	imshow("base", base);
	imshow("test1", test1);
	imshow("test2", test2);
	imshow("test12", test12);
}
string convertToString(double d)
{
	ostringstream os;
	if (os << d)
		return os.str();
	return "invalid conversion";
}

///第二十五节——直方图反向投影calcBackProject*/

Mat src; Mat hsv; Mat hue;
int bins = 12;//初始化bin数目
void Hist_And_Backprojection(int, void*);
int main(int argc, char** argv)
{
	/*
	使用mixChannels来抽取HSV图像的0通道Hue
	&hsv→一系列输入图像的数组,被拷贝的通道的来源
	1→输入数组中图像的数目
	&hue→一系列目的图像的数组,储存拷贝的通道
	1→目的数组中图像的数目
	nchannels→通道索引对的数组,指示如何将输入图像的某一通道拷贝到目的图像的某一通道,0→0
	1→通道索引对的数目
	*/
	//转换到HSV空间
	cvtColor(src, hsv, CV_BGR2HSV);
	//分离Hue通道
	/*本教程仅使用Hue通道来创建1维直方图
	(可以从其他链接下载增强版本,其使用了更常见的H-S直方图,以获得更好的新效果)*/
	hue.create(hsv.size(), hsv.depth());
	int nchannels[] = { 0, 0 };
	mixChannels(&hsv, 1, &hue, 1, nchannels, 1);
	//创建Trackbar来输入bin的数目,Trackbar的任何变动将会调用函数Hist_And_Backprojection
	createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);
	Hist_And_Backprojection(0, 0);
}
void Hist_And_Backprojection(int, void*)/* Trackbar 事件的回调函数*/
{
	float range[] = { 0, 180 };
	const float *histRanges = { range };
	Mat h_hist;
	//计算直方图并归一化
	calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
	//计算反向投影
	Mat backPrjImage;
	calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
	//显示反向投影
	imshow("BackProj", backPrjImage);
	//显示1维Hue直方图
	int hist_h = 400;
	int hist_w = 400;
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
	int bin_w = (hist_w / bins);
	for (int i = 1; i < bins; i++)
	{
		rectangle
		(histImage,
		Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
		//Point(i*bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
		Point(i*bin_w, hist_h),
		Scalar(0, 0, 255), -1);
	}
	imshow("Histogram", histImage);
	return;
}

///第二十六节——模板匹配matchTemplate*/

int match_method = TM_SQDIFF;
int max_track = 5;
void Match_Demo(int, void*);
int main(int argc, char** argv)
{
	src = imread("F:/images/test.png");// 待检测图像
	temp = imread("F:/images/matchtest.png");// 模板图像
	const char* trackbar_title = "Match Algo Type:";
	createTrackbar(trackbar_title, OUTPUT_T, &match_method, max_track, Match_Demo);
	Match_Demo(0, 0);
}
void Match_Demo(int, void*)
{
	int width = src.cols - temp.cols + 1;
	int height = src.rows - temp.rows + 1;
	Mat result(width, height, CV_32FC1);
	//模板匹配+归一化 matchTemplate在模板和输入图像之间寻找匹配获得匹配结果图像
	matchTemplate(src, temp, result, match_method, Mat());
	normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
	//通过minMaxLoc定位最匹配的位置
	Point minLoc;	Point maxLoc;
	double min, max;
	Point temLoc;
	//minMaxLoc 在给定的矩阵中寻找最大和最小值(包括他们的位置)
	/*
	minMaxLoc(匹配结果矩阵,在矩阵result中存储的最小值和最大值,在结果矩阵中最小值和最大值的坐标,可选的掩模)
	*/
	minMaxLoc(result, &min, &max, &minLoc, &maxLoc, Mat());
	//对于方法 TM_SQDIFF和TM_SQDIFF_NORMED ,越小的数值代表更高的匹配结果,而对于其他的方法,数值越大匹配越好
	if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
	{
		temLoc = minLoc;
	}
	else
	{
		temLoc = maxLoc;
	}
	// 绘制矩形
	src.copyTo(dst);
	rectangle(dst, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
	rectangle(result, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
	imshow(OUTPUT_T, result);
	imshow(match_t, dst);
}

///第二十七节——轮廓发现findContours*/

int threshold_value = 100;
int threshold_max = 255;
RNG rng;
void Demo_Contours(int, void*);
int main(int argc, char** argv)
{
	//灰度化
	cvtColor(src, gray, CV_BGR2GRAY);
	const char* trackbar_title = "Threshold Value:";
	createTrackbar(trackbar_title, output_win, &threshold_value, threshold_max, Demo_Contours);
	Demo_Contours(0, 0);
}
void Demo_Contours(int, void*)
{
	Mat canny_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	Canny(gray, canny_output, threshold_value, threshold_value * 2, 3, false);
	//发现轮廓
	findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//绘制轮廓
	dst = Mat::zeros(gray.size(), CV_8UC3);//dst初始化
	RNG rng(12345);//随机数+随机种子
	for (size_t i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));
	}
	imshow(output_win, dst);
}

///第二十八节——凸包convexHull*/

int threshold_value = 100;
int threshold_max = 255;
void Threshold_Callback(int, void*);
RNG rng(12345);
int main(int argc, char** argv)
{
	cvtColor(src, src_gray, CV_BGR2GRAY);
	blur(src_gray, src_gray, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	createTrackbar(trackbar_label, output_win, &threshold_value, threshold_max, Threshold_Callback);
	Threshold_Callback(0, 0);
}
void Threshold_Callback(int, void*)
{
	Mat bin_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	threshold(src_gray, bin_output, threshold_value, threshold_max, THRESH_BINARY);
	findContours(bin_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	vector<vector<Point>> convexs(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
		convexHull(contours[i], convexs[i], true, false);
	}
	dst = Mat::zeros(src.size(), CV_8UC3);
	vector<Vec4i> empty(0);
	for (size_t k = 0; k < contours.size(); k++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(dst, contours, k, color, 2, LINE_8, hierachy, 0, Point(0, 0));
		drawContours(dst, convexs, k, color, 2, LINE_8, empty, 0, Point(0, 0));
	}
	imshow(output_win, dst);
	return;
}

///第二十九节——轮廓周围绘制矩形框和圆形框*/

int threshold_v = 170;
int threshold_max = 255;
const char* output_win = "rectangle-demo";
RNG rng(12345);
void Contours_Callback(int, void*);
int main(int argc, char** argv)
{
	cvtColor(src, gray_src, CV_BGR2GRAY);
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1));
	createTrackbar("Threshold Value:", output_win, &threshold_v, threshold_max, Contours_Callback);
	Contours_Callback(0, 0);
}
void Contours_Callback(int, void*)
{
	Mat binary_output;//阈值二值化图像
	threshold(gray_src, binary_output, threshold_v, threshold_max, THRESH_BINARY);
	//轮廓检测参数
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));//发现轮廓
	vector<vector<Point>> contours_ploy(contours.size());//外接多边形
	vector<Rect> ploy_rects(contours.size());//外接矩形
	vector<Point2f> ccs(contours.size());//圆心
	vector<float> radius(contours.size());//圆的半径
	vector<RotatedRect> minRects(contours.size());//最小化矩形
	vector<RotatedRect> myellipse(contours.size());//椭圆
	for (size_t i = 0; i < contours.size(); i++)
	{
		//基于RDP算法实现,目的是减少多边形轮廓点数
		/* approxPolyDP(InputArray curve,OutputArray approxCurve, double epsilon任意数-表示两点之间的最小距离, bool closed闭合曲线) */
		approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true);
		ploy_rects[i] = boundingRect(contours_ploy[i]);//boundingRect——得到轮廓周围最小矩形,左上角和右下角坐标绘制一个矩形
		minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]);//得到最小区域圆形
		if (contours_ploy[i].size() > 5) //多边形最少要5个点
		{
			minRects[i] = minAreaRect(contours_ploy[i]);//得到一个旋转的矩形,返回旋转矩形
			myellipse[i] = fitEllipse(contours_ploy[i]);//得到最小椭圆
		}
	}
	// draw it
	drawImg = Mat::zeros(src.size(), src.type());
	Point2f pts[4];
	for (size_t t = 0; t < contours.size(); t++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		/*rectangle(drawImg, ploy_rects[t], color, 2, 8);
		circle(drawImg, ccs[t], radius[t], color, 2, 8);*/
		if (contours_ploy[t].size() > 5)
		{
			//绘制矩形
			minRects[t].points(pts);
			for (int r = 0; r < 4; r++)
			{
				line(drawImg, pts[r], pts[(r + 1) % 4], color, 1, 8);
			}
			//绘制椭圆
			ellipse(drawImg, myellipse[t], color, 1, 8);
		}
	}
	imshow(output_win, drawImg);
	return;
}

///第三十节——图像矩moments、面积contourArea, 弧长arcLength*/

int threshold_value = 80;
int threshold_max = 255;
RNG rng(12345);
void Demo_Moments(int, void*);
int main(int argc, char** argv)
{
	cvtColor(src, gray_src, CV_BGR2GRAY);
	GaussianBlur(gray_src, gray_src, Size(3, 3), 0, 0);//目的:降低噪声
	createTrackbar("Threshold Value : ", output_win, &threshold_value, threshold_max, Demo_Moments);
	Demo_Moments(0, 0);
}
void Demo_Moments(int, void*)
{
	Mat canny_output;
	vector<vector<Point>> contours;//轮廓点
	vector<Vec4i> hierachy;
	Canny(gray_src, canny_output, threshold_value, threshold_value * 2, 3, false);//边缘提取
	findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));//发现轮廓
	vector<Moments> contours_moments(contours.size());//矩
	vector<Point2f> ccs(contours.size());//中心点
	for (size_t i = 0; i < contours.size(); i++)
	{
		contours_moments[i] = moments(contours[i]);
		ccs[i] = Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00),
			           static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));
	}
	Mat drawImg;
	src.copyTo(drawImg);
	for (size_t i = 0; i < contours.size(); i++)
	{
		if (contours[i].size() < 100)
		{
			continue;
		}
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		printf("center point x : %.2f y : %.2f\n", ccs[i].x, ccs[i].y);
		printf("contours %d area : %.2f   arc length : %.2f\n", i, contourArea(contours[i]), arcLength(contours[i], true));
		drawContours(drawImg, contours, i, color, 1, 8, hierachy, 0, Point(0, 0));//绘制轮廓
		circle(drawImg, ccs[i], 2, color, 1, 8);
	}
	imshow(output_win, drawImg);
	return;
}

///第三十一节——点多边形测试*/

int main(int argc, char** argv)
{
	const int r = 100;
	Mat src = Mat::zeros(r * 4, r * 4, CV_8UC1);
	vector<Point2f> vert(6);//定义6个点
	vert[0] = Point(3 * r / 2, static_cast<int>(1.34*r));
	vert[1] = Point(1 * r, 2 * r);
	vert[2] = Point(3 * r / 2, static_cast<int>(2.866*r));
	vert[3] = Point(5 * r / 2, static_cast<int>(2.866*r));
	vert[4] = Point(3 * r, 2 * r);
	vert[5] = Point(5 * r / 2, static_cast<int>(1.34*r));
	//画线
	for (int i = 0; i < 6; i++)
	{
		line(src, vert[i], vert[(i + 1) % 6], Scalar(255), 3, 8, 0);
	}
	//发现轮廓
	vector<vector<Point>> contours;//轮廓点
	vector<Vec4i> hierachy;
	Mat csrc;
	src.copyTo(csrc);
	findContours(csrc, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//测试生成的图
	Mat raw_dist = Mat::zeros(csrc.size(), CV_32FC1);
	for (int row = 0; row < raw_dist.rows; row++)
	{
		for (int col = 0; col < raw_dist.cols; col++)
		{
			double dist = pointPolygonTest(contours[0],
				Point2f(static_cast<float>(col), static_cast<float>(row)), true);
			raw_dist.at<float>(row, col) = static_cast<float>(dist);
		}
	}
	double minValue, maxValue;
	minMaxLoc(raw_dist, &minValue, &maxValue, 0, 0, Mat());//最大和最小坐标位置的寻找
	Mat drawImg = Mat::zeros(src.size(), CV_8UC3);//绘制出来的图像
	for (int row = 0; row < drawImg.rows; row++)
	{
		for (int col = 0; col < drawImg.cols; col++)
		{
			float dist = raw_dist.at<float>(row, col);
			if (dist > 0) //大于0表示在内部 
			{
				drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxValue)) * 255);
				printf("距离为:%f\n", dist);
			}
			else if (dist < 0) //小于0表示在外部
			{
				drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minValue)) * 255);
			}
			else//在多边形边上白色
			{
				drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(255 - dist));
				drawImg.at<Vec3b>(row, col)[1] = (uchar)(abs(255 - dist));
				drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(255 - dist));
			}
		}
	}
}

///第三十二节——基于距离变换与分水岭的图像分割*/

int main(int argc, char** argv)
{
	// 1. change background
	for (int row = 0; row < src.rows; row++)
	{
		for (int col = 0; col < src.cols; col++)
		{
			//如果是白色的将背景变成黑色
			if (src.at<Vec3b>(row, col) == Vec3b(255, 255, 255))
			{
				src.at<Vec3b>(row, col)[0] = 0;
				src.at<Vec3b>(row, col)[1] = 0;
				src.at<Vec3b>(row, col)[2] = 0;
			}
		}
	}
	// 2. sharpen 图像锐化 拉普拉斯
	Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
	Mat imgLaplance;//拉普拉斯图像
	Mat sharpenImg = src;//等于原图
	filter2D(src, imgLaplance, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
	src.convertTo(sharpenImg, CV_32F);
	Mat resultImg = sharpenImg - imgLaplance;
	resultImg.convertTo(resultImg, CV_8UC3);
	imgLaplance.convertTo(imgLaplance, CV_8UC3);
	imshow("sharpen image", resultImg);
	// 3.convert to binary二值化
	Mat binaryImg;
	cvtColor(src, resultImg, CV_BGR2GRAY);
	threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("binary image", binaryImg);
	//4.distance transform
	Mat distImg;
	distanceTransform(binaryImg, distImg, DIST_L1, 3, 5);
	normalize(distImg, distImg, 0, 1, NORM_MINMAX);
	imshow("distance result", distImg);
	// 5.binary again 使用阈值,再次二值化,得到标记
	threshold(distImg, distImg, .4, 1, THRESH_BINARY);//此时显示比较丑
	Mat k1 = Mat::ones(13, 13, CV_8UC1);
	erode(distImg, distImg, k1, Point(-1, -1));//腐蚀得到每个Peak - erode
	imshow("distance binary image", distImg);
	// 6.markers 编号处理(发现轮廓)标记
	Mat dist_8u;
	distImg.convertTo(dist_8u, CV_8U);
	vector<vector<Point>> contours;
	findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
	// 7.create makers
	Mat markers = Mat::zeros(src.size(), CV_32SC1);
	for (size_t i = 0; i < contours.size(); i++)
	{
		drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i) + 1), -1);
	}
	circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1);
	imshow("my markers", markers * 1000);//markers灰度级别很低
	// 8.perform watershed 分水岭变换
	watershed(src, markers);
	Mat mark = Mat::zeros(markers.size(), CV_8UC1);
	markers.convertTo(mark, CV_8UC1);
	bitwise_not(mark, mark, Mat());//反变换(取反)
	imshow("watershed image", mark);
	// 9.generate random color随机颜色
	vector<Vec3b> colors;
	for (size_t i = 0; i < contours.size(); i++)
	{
		int r = theRNG().uniform(0, 255);
		int g = theRNG().uniform(0, 255);
		int b = theRNG().uniform(0, 255);
		colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
	}
	// 10.fill with color and display final result
	Mat dst = Mat::zeros(markers.size(), CV_8UC3);
	for (int row = 0; row < markers.rows; row++)
	{
		for (int col = 0; col < markers.cols; col++)
		{
			int index = markers.at<int>(row, col);
			if (index > 0 && index <= static_cast<int>(contours.size()))
			{
				dst.at<Vec3b>(row, col) = colors[index - 1];
			}
			else
			{
				dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
			}
		}
	}
	imshow("Final Result", dst);
}

完成代码下载:
链接:https://pan.baidu.com/s/1TfYM3hhI5e7BlpLxDpzmHA
提取码:x3r7

发布了8 篇原创文章 · 获赞 3 · 访问量 1911

猜你喜欢

转载自blog.csdn.net/weixin_44017727/article/details/103976087
今日推荐