OpenCV入门笔记-更新篇

分成三个部分,环境配置,代码过程中遇到的问题以及代码学习 ,包括贾志刚老师opencv基础课程中演示的代码,持续记录中
VS:
注释: 先CTRL+K,然后CTRL+C
取消注释: 先CTRL+K,然后CTRL+U

环境配置

  借鉴了这位老哥的,vs2017,vs2017+opencv3.4.1

遇到的问题

1.Mat未定义

  参考Mat未定义解决

#include   //opencv申明
#include 
#include 

#include 
using namespace cv;     //不然会报错显示没有定义的符号
using namespace std; 

  只需要第三行的申明就OK了

2.无法解析的外部命令
 a.通常情况
 b.debug库的问题,在附加依赖项里加入release库opencv_world341.lib
(我的问题不是这个=。=)
 c.对象错了,第一次配成了Debug|win32,无语
第一次配成了Debug|win32

代码学习

  在B站看的opencv基础课程,老师讲课磕磕巴巴的,但好歹内容循序渐进
1.图像加载、显示、保存

int load_image()
{
	//加载
	Mat src = imread("D:\\opencv_test_img\\sz.jpg", -1);    
	//0是灰度图像,>0 BGR格式,<0原格式读取
	Mat cvt_src;
	if (src.empty())
	{
		cout<<"fail";
		return 0;
	}
	namedWindow("load img", WINDOW_AUTOSIZE);   
	 //打开一个新的窗口,根据str寻找对应的窗口内容,
	//WINDOW_NORMAL图案自动填充窗口,图片可根据窗口拉伸,WINDOW_AUTOSIZE窗口自动适应图片大小,不可调整窗口
	imshow("load img", src);  //如果未在此功能之前创建窗口,则假定使用创建窗口CV_WINDOW_AUTOSIZE。第一个参数是窗口名称
	cvtColor(src,cvt_src,CV_BGR2HLS);   //转换图像的色彩模式CV_X2X
	imshow("change img", cvt_src);
	imwrite("D:\\opencv_test_img\\cvt_src.png",cvt_src);  //根据路径中的后缀名保存图像
	return 1;
}

  imwrite可以参考:imwrite参数以及示例代码

2.mask操作(filter_2D)(更像是卷积,不过没有翻转操作)
  手写的卷积操作

//手动卷积= =
int mask()
{
	Mat src = imread("D:\\opencv_test_img\\sz.jpg", -1);    //0是灰度图像,>0 BGR格式,<0原格式读取
	Mat dst;
	if (src.empty())
	{
		cout << "fail";
		return 0;
	}
	//获取图片长宽、通道数,列数=图像列数*通道数,通道数=处理的步长
	int cols = src.cols*src.channels();
	int rows = src.rows;
	int offset = src.channels();
	dst = Mat::zeros(src.size(),src.type());
	//卷积操作
	for (int row = 1; row < rows - 1; row++)
	{
		const uchar* previous = src.ptr<uchar>(row - 1);
		const uchar* current = src.ptr<uchar>(row);
		const uchar* next = src.ptr<uchar>(row + 1);
		uchar* output = dst.ptr<uchar>(row);
		for (int col= offset;col<cols-1;col++)   //每行3个数据,构成一个像素点
		{
			output[col] = saturate_cast<uchar>(5*current[col]-previous[col]-current[col-offset]-current[col+ offset]-next[col]);   //saturate_cast 
		}
	}
	namedWindow("raw img", WINDOW_AUTOSIZE);
	imshow("raw img", src);
	namedWindow("mask img", WINDOW_AUTOSIZE);    
	imshow("mask img", dst);
	waitKey(0);
	system("pause");
	return 0;
}

  新内容主要有:

src.ptr<uchar>(row)  //获取某一行的指针,返回uchar类型的指针
dst = Mat::zeros(src.size(),src.type());   //创建0矩阵,等会儿存放卷积结果
saturate_cast<uchar>()   //剪切数据在0~255
//两个for循环,外层控制行,内层控制像素点
//前面cols处理了一下(dst = Mat::zeros(src.size(),src.type());),所以内层是逐数据进行的

  filter_2D

//直接调用函数,所以之前写了个寂寞
int filter()
{
	Mat src = imread("D:\\opencv_test_img\\sz.jpg", -1);    //0是灰度图像,>0 BGR格式,<0原格式读取
	Mat dst;
	if (src.empty())
	{
		cout << "fail";
		return 0;
	}
	//获取图片长宽、通道数,列数=图像列数*通道数,通道数=处理的步长
	Mat kernel = (Mat_<char>(3, 3) << 0,-1,0,-1,5,-1,0,-1,0);
	dst = Mat::zeros(src.size(), src.type());
	filter2D(src,dst,src.depth(),kernel);
	namedWindow("row img", WINDOW_AUTOSIZE);
	imshow("row img", src);
	namedWindow("conv img", WINDOW_AUTOSIZE);
	imshow("conv img", dst);

	waitKey(0);
	return 0;
}

  调用之后的结果是一样的,主要的新东西有

	Mat kernel = (Mat_<char>(3, 3) << 0,-1,0,-1,5,-1,0,-1,0);   //创建一个卷积核
	filter2D(src,dst,src.depth(),kernel);  //调用2D卷积
	//(操作对象,存放结果,位图深度(-1是万能参数),卷积核)

3.Mat类型
另一个总结

4.像素读写
OpenCV入门笔记-更新篇_第1张图片
  首先确定定位像素点的坐标,(0,1)即第0行第1列,Point(0,1)表示第0列第1行
像素读写的代码

	float g = gray.at<uchar>(0, 0);
	Vec3b v = src.at<Vec3b>(0, 0);   //取一个像素点[B,G,R]
	//Vec3b,3表示3通道,b表示BGR,Vec3f,则表示三通道的float类型
	//如果要使用Vec3f,需要先使用convertTo转换到CV_32F
	float B = v[0];

  像素值取反的示例代码

void read_pixel()
{
	Mat src, gray;
	src = imread("D:\\opencv_test_img\\sz.jpg", 1);
	
	
	cvtColor(src, gray,CV_BGR2GRAY);
	int col = src.cols;
	int row = src.rows;
	int channel = src.channels();
	
	for (int i=0;i<row;i++)
	{
		for (int j=0;j<col;j++)
		{
			if (channel == 1)
			{
				gray.at<uchar>(i, j) = 255 - gray.at<uchar>(i, j);  //读取灰度图(1通道)的像素点
			}
			else if(channel==3)
			{
				int R = src.at<Vec3b>(i, j)[0];   //读取BGR的像素点(3通道)
				int B = src.at<Vec3b>(i, j)[1];
				int G = src.at<Vec3b>(i, j)[2];
				src.at<Vec3b>(i, j)[0]=  255 - R;
				src.at<Vec3b>(i, j)[1] = 255 - B;
				src.at<Vec3b>(i, j)[2] = 255 - G;
			}
		}
	}
	bitwise_not(gray,gray);    //像素取反函数

	namedWindow("gray",WINDOW_AUTOSIZE);
	imshow("gray",gray);
	namedWindow("src", WINDOW_AUTOSIZE);
	imshow("src", src);
}

  生成空白图像

	dst=Scalar(0);

5.图像混合
  两张图像线性加权,权重和为1,相关API
OpenCV入门笔记-更新篇_第2张图片

addWeighted(src1,0.5,src2,0.5,0,src);   //权重和
add(src1,src2,src);    //和
multiply(src1,src2,src,0.002);      //带参乘积

6.调整图像亮度和对比度

void change_pb_pc()
{
	Mat src;
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);

	cvtColor(src,src,CV_BGR2GRAY);
	int col = src.cols;
	int row = src.rows;
	float alpha = 0.8;
	float gamma = 100;
	int channel = src.channels();

	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (channel == 3)
			{
				float R = src.at<Vec3b>(i, j)[0];   //读取BGR的像素点(3通道)
				float B = src.at<Vec3b>(i, j)[1];
				float G = src.at<Vec3b>(i, j)[2];
				src.at<Vec3b>(i, j)[0] = saturate_cast<uchar>( R * alpha + gamma);
				src.at<Vec3b>(i, j)[1] = saturate_cast<uchar>(B * alpha + gamma);
				src.at<Vec3b>(i, j)[2] = saturate_cast<uchar>(G * alpha + gamma);
			}
			else if (channel==1)
			{
				int gray = src.at<uchar>(i, j);
				src.at<uchar>(i, j) = saturate_cast<uchar>(gray * alpha + gamma);
			}
			
		}
	}
	imshow("src", src);
}

  alpha改变对比度(倍乘),gamma改变亮度(相加)
tips:
  alpha和gamma定义为浮点型,使用saturate_cast()裁剪值

7.绘制图形和文字

	Mat src;
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);

	Point p1 = Point(src.cols / 2, src.rows / 2);
	Point p2 = Point(src.cols/4,src.rows/8);
	Rect r = Rect(100, 100, 200, 200);
	Scalar color = Scalar(0, 255, 255);
	//线条
	line(src,p1,p2,color,1,LINE_AA);   //LINE_AA非锯齿形
	//矩形
	rectangle(src,r,color);
	//圆形
	circle(src,p1,100,color);
	//椭圆
	ellipse(src,RotatedRect(p1,Size(src.cols / 4, src.rows / 8),90),color,1,8);
	//文字
	putText(src,"hello beauty",p1,CV_FONT_BLACK,5,color,5);

8.模糊

//均值滤波&高斯滤波
void blur()
{
	Mat src ,dst;
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}
	blur(src,dst,Size(5,5),Point(-1,-1));
	imshow("blur",dst);
	GaussianBlur(src,dst,Size(5,5),5,5);
	imshow("GaussianBlur",dst);

	waitKey(0);
}
//双边滤波&中值滤波
void blur2()
{
	Mat src, dst;
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}
	medianBlur(src,dst,3);
	imshow("medianblur", dst);
	bilateralFilter(src,dst,15,100,3);    //(输入,输出,半径,允许的像素差值,半径非0时自动确定)
	imshow("bilateralFilter", dst);
	Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	filter2D(dst,dst,-1,kernel,Point(-1,-1));
	imshow("shaper", dst);
	waitKey(0);

}

双边滤波可以在模糊的时候保留图形边缘信息,双边+锐化可以实现磨皮美白的效果

9.膨胀和腐蚀

int size_e_d = 3;
void CallBack_Demo(int,void*);
void main()
{
	namedWindow("window",WINDOW_AUTOSIZE);
	createTrackbar("element size","window",&size_e_d,21, CallBack_Demo);   //21是max_size
	CallBack_Demo(0,0);
	waitKey(0);
}

void CallBack_Demo(int,void*)
{
	Mat src, dst; 
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	int s = size_e_d*2+1;
	Mat s_element = getStructuringElement(MORPH_ELLIPSE,Size(s,s),Point(-1,-1));   //MORPH_X
	//dilate(src,dst, s_element,Point(-1,-1));
	erode(src, dst, s_element, Point(-1, -1));
	imshow("window",dst);
}

10.形态学操作
OpenCV入门笔记-更新篇_第3张图片
  形态学操作主要针对二值图像进行处理:
开操作:先腐蚀后膨胀,可以去掉背景中小对象
闭操作:先膨胀后腐蚀,填充小洞
形态学梯度:膨胀减去腐蚀,可以突出边缘信息
顶帽:原图-开操作,留下比较亮的区域
黑帽:闭操作-原图,突出填补的小洞部分

//基本操作
void mor_EX()
{
	Mat src, dst;
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	Mat kernel = getStructuringElement(MORPH_ELLIPSE,Size(9,9),Point(-1,-1));
	morphologyEx(src,dst,CV_MOP_BLACKHAT,kernel);

	namedWindow("mor_EX",WINDOW_AUTOSIZE);
	imshow("mor_EX",dst);
	namedWindow("ori", WINDOW_AUTOSIZE);
	imshow("ori", src);
	
	waitKey(0);
}

11.提取水平、垂直线
提取步骤:
OpenCV入门笔记-更新篇_第4张图片
OpenCV入门笔记-更新篇_第5张图片
  adaptiveThreshold的计算单位是像素的邻域块,邻域块取多大,由blocksize作决定。
  OpenCV自适应阈值化函数adaptiveThreshold详解,并附实例源码!

void abs_l()
{
	Mat src, gray_img,bit_img,h_ele,v_ele,dst,s1;
	src = imread("D:\\opencv_test_img\\ab_hv.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}
	
	//先转换为灰度再转换为二值
	cvtColor(src,gray_img,CV_BGR2GRAY); 
	adaptiveThreshold(~gray_img,bit_img,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,15,-2);   //得到背景为黑色
	
	 //定义提取元素
	v_ele = getStructuringElement(MORPH_RECT,Size(1,src.cols/16),Point(-1,-1));   //生成列值为1
	h_ele = getStructuringElement(MORPH_RECT, Size(src.rows/16,1), Point(-1, -1));   //生成行值为1

	//提取操作
	//morphologyEx(~bit_img,dst,CV_MOP_CLOSE,h_ele);
	//morphologyEx(bit_img,dst,CV_MOP_OPEN,h_ele);
	erode(bit_img,s1,h_ele,Point(-1,-1));
	dilate(s1, dst, h_ele, Point(-1, -1));
	//开操作先腐蚀再膨胀,bit_img背景为黑色
	//闭操作先膨胀再腐蚀,~bit_img背景为白色
	//如果背景是黑色,应该先腐蚀再膨胀,如果是白色,先膨胀再腐蚀
	
	//显示
	imshow("bit_img", bit_img);
	imshow("s1", s1);
	imshow("dst",dst);
	waitKey(0);
}

12.图像金字塔-上采样和下采样
  高斯金字塔
(1)进行高斯模糊
(2)删除偶数行和偶数列
每一层迭代进行。
  高斯不同:对一张图像在不同参数的情况下,得到的高斯模糊结果相减,得到的图像。
高斯不同是图像的内在特征,在灰度图像增强和角点检测中经常使用。
OpenCV入门笔记-更新篇_第6张图片

void Gaussian_pyr()
{
	Mat src, src_up,src_down;
	src = imread("D:\\opencv_test_img\\bea2.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}
	pyrUp(src,src_up,Size(src.cols*2,src.rows*2));
	pyrDown(src, src_down, Size(src.cols / 2, src.rows / 2));

	//高斯不同
	Mat g1, g2, gray,sub;
	cvtColor(src,gray,CV_BGR2GRAY);
	GaussianBlur(gray,g1,Size(9,9),0,0);
	GaussianBlur(g1, g2, Size(9, 9), 0, 0);
	subtract(g1,g2,sub);
	normalize(sub,sub,255,0,NORM_MINMAX);

	//展示
	imshow("up",src_up);
	imshow("down",src_down);
	imshow("sub",sub);
	waitKey(0);
}

13.阈值操作
threshold只能处理灰度图像:(二值化、反二值化、截断、取0、反取0)
THRESH_BINARY:阈值二值化,大于阈值部分取设定的最大值,小于阈值取0
THRESH_BINARY_INV:阈值反二值化,大于取0,小于取设定值
THRESH_TRUNC:超过阈值则等于阈值,否则不变
THRESH_TOZERO:小于阈值则阈值取0
THRESH_TOZERO_INV:大于阈值取0

THRESH_OTSU、THRESH_TRIANGLE:帮助计算阈值,不需设定阈值参数

int max_size=255;
int type=0;
Mat th_src, th_dst,th_gray;

void threshold_demo(int, void*);
void main()
{
	namedWindow("treshold", WINDOW_AUTOSIZE);
	createTrackbar("threshold size", "treshold", &max_size, 255, threshold_demo);
	createTrackbar("threshold type", "treshold", &type, 4, threshold_demo);
	threshold_demo(0,0)

	waitKey(0);
	system("pause");
}

void threshold_demo(int, void*)
{
	th_src = imread("D:\\opencv_test_img\\zf.jpg", 1);
	if (th_src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}
	cvtColor(th_src, th_gray,CV_BGR2GRAY);

	threshold(th_gray,th_dst,0, max_size, type);
	imshow("treshold",th_dst);
}

14.卷积,三种提取边缘信息的算子
Robert算子:
Y方向[1,0,
   0,-1]
X方向[0,1,
   -1,0]
Sobel算子:
水平方向:[-1,0,1;
      -2,0,2;
      -1,0,1]
垂直方向:[-1,-2,-1;
      0,0,0;
      1,2,1]
拉普拉斯算子:[0,-1,0
         -1,4,-1
         0,-1,0]

void fil_blur()
{
	Mat src = imread("D:\\opencv_test_img\\d.png", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}
	Mat ro_dst, so_dst, lap_dst,dst;
	Mat r_k, s_k, l_k,k;
	r_k = (Mat_<int>(2, 2) << 1, 0, 0, -1);
	s_k= (Mat_<int>(3, 3) << -1, -2, -1, 0,0,0,1,2,1);
	l_k= (Mat_<int>(3, 3) << 0, -1, 0, -1,4,-1,0,-1,0);
//模糊动态展示
	int k_size = 0;
	int index=0;
	namedWindow("src", WINDOW_AUTOSIZE);
	while (true)
	{
		char c = waitKey(500);
		if ((int)c == 27)
			break;
		k_size = (index % 5) * 2+1;
		k = (Mat::ones(Size(k_size, k_size),CV_32F))/ (k_size* k_size);
		filter2D(src,dst,-1,k,Point(-1,-1));
		index++;
		imshow("blur", dst);
	}
//边缘提取
	//filter2D(src,ro_dst,-1,r_k,Point(-1,-1));
	//filter2D(src, so_dst, -1, s_k, Point(-1, -1));
	//filter2D(src, lap_dst, -1, l_k, Point(-1, -1));
	//
	//namedWindow("blur_robet",WINDOW_AUTOSIZE);
	//namedWindow("blur_sobel", WINDOW_AUTOSIZE);
	//namedWindow("blur_lap", WINDOW_AUTOSIZE);
	//namedWindow("src", WINDOW_AUTOSIZE);
	//imshow("src", src);
	//imshow("blur_robet", ro_dst);
	//imshow("blur_sobel", so_dst);
	//imshow("blur_lap", lap_dst);

	waitKey(0);
}

15.边缘处理
边缘填充

void border_fi()
{
	Mat src = imread("D:\\opencv_test_img\\d3.png", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	Mat dst;
	Scalar color;
	int border_type = BORDER_DEFAULT;
	int top = (0.05*src.rows);
	int bottom = (0.05*src.rows);
	int left = (0.05*src.cols);
	int right = (0.05*src.cols);
	RNG r = RNG(12345);
	int c;
	namedWindow("border_fill",WINDOW_AUTOSIZE);

	while (true)
	{
		c = waitKey(500);  //ESC退出
		if (c == 27)
		{
			break;
		}else if (c=='r')   //按r
		{
			border_type = BORDER_REPLICATE;  // 重复:对边界像素进行复制
		}else if (c=='c')
		{
			border_type = BORDER_CONSTANT;   //赋值常数,此时color起作用,和BORDER_ISOLATED效果一样
		}else if (c == 'w')
		{
			border_type = BORDER_WRAP;      //用对边、对角像素进行填充
		}else if (c == 'f')
		{
			border_type = BORDER_REFLECT;  //反射
		}
		else if (c == 'd')
		{
			border_type = BORDER_DEFAULT;   //效果与反射类似
		}
		color = Scalar(r.uniform(0,255), r.uniform(0, 255), r.uniform(0, 255));
		copyMakeBorder(src,dst,top,bottom,left,right,border_type,color);
		imshow("border_fill",dst);
	}

	waitKey(0);
}

GaussianBlur高斯模糊中的边界参数

while (true)
	{
		c = waitKey(500);
		if (c == 27)
		{
			break;
		}
		else if (c == 'r')
		{
			border_type = BORDER_REPLICATE;  // 重复:对边界像素进行复制
		}
		else if (c == 'c')
		{
			border_type = BORDER_CONSTANT;   //赋值常数,此时color起作用,和BORDER_ISOLATED效果一样
		}
		else if (c == 'w')
		{
			border_type = BORDER_WRAP;      //用对边、对角像素进行填充
		}
		else if (c == 'f')
		{
			border_type = BORDER_REFLECT;  //反射
		}
		else if (c == 'd')
		{
			border_type = BORDER_DEFAULT;   //效果与反射类似
		}
		
		GaussianBlur(src,dst,Size(5,5),-1,-1, border_type);
		imshow("border_fill", dst);
	}

16.Sobel算子
  Sobel计算图像灰度的近似梯度,结合了高斯平滑和微分求导,计算X方向和Y方向上的梯度图像,
水平方向:[-1,0,1;
      -2,0,2;
      -1,0,1]
垂直方向:[-1,-2,-1;
      0,0,0;
      1,2,1]
在方向上扩大差异,最终近似的图像梯度=|Gx|+|Gy|。
  Sobel算子对噪声比较敏感,容易受到影响,使用高斯模糊降噪精度对算法的影响很重要,输出精度一定要比输入精度大,最后再使用convertScaleAbs,将数据转换为8位。
python opencv 4.1.0 cv2.convertScaleAbs()函数
  有两个sobel算子的函数Soble,Scharr

void f_sobel()
{

	Mat src = imread("D:\\opencv_test_img\\d3.png", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	Mat gray, xgrad, ygrad, xygrad;
	GaussianBlur(src,gray,Size(3,3),-1);
	cvtColor(gray,gray,CV_BGR2GRAY);
	imshow("src",src);
	imshow("gray",gray);

	Sobel(gray, xgrad, CV_16S,1,0);
	Sobel(gray, ygrad, CV_16S, 0, 1);
	/*Scharr(gray, xgrad, CV_16S, 1, 0);
	Scharr(gray, ygrad, CV_16S, 0, 1);*/

	addWeighted(xgrad,0.5,ygrad,0.5,-1,xygrad);    //近似梯度
	convertScaleAbs(xygrad,xygrad);   //转换为8位
	convertScaleAbs(xgrad, xgrad);
	convertScaleAbs(ygrad, ygrad);
	
	imshow("xgrad",xgrad);
	imshow("ygrad", ygrad);
	imshow("xygrad", xygrad);
	waitKey(0);
}

两个算子函数有区别,第一幅为Scharr,第二幅为Soble
OpenCV入门笔记-更新篇_第7张图片 OpenCV入门笔记-更新篇_第8张图片
17.拉普拉斯算子
  计算二阶导数,获取图像边缘信息。
-高斯模糊去噪点
-转换为灰度图像
-拉普拉斯算子
-转换为8位图像
-获取更高边缘图像质量的其它操作(阈值操作等)

void lap_edg()
{
	Mat src = imread("D:\\opencv_test_img\\d3.png", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	Mat gray, lap;
	namedWindow("src",WINDOW_AUTOSIZE);
	namedWindow("gray",WINDOW_AUTOSIZE);
	namedWindow("lap",WINDOW_AUTOSIZE);

	GaussianBlur(src,gray,Size(3,3),-1,-1);
	cvtColor(gray,gray,CV_BGR2GRAY);
	Laplacian(gray,lap,CV_16U,3);
	convertScaleAbs(lap,lap);

	imshow("src",src);
	imshow("gray",gray);
	imshow("lap",lap);
	imwrite("lap.jpg",lap);
	waitKey(0);
}

结果:
OpenCV入门笔记-更新篇_第9张图片
18.边缘提取-canny
  在sobel算子的前提下,主要有一步非最大信号抑制以及高低阈值输出二值图像,依然是对灰度图像进行处理,之后可以利用copyTo转换到三通道图像。

Mat c_src, c_gray;
int c_th, c_max = 255;
void f_canny(int,void*);
void main()
{
	c_src = imread("D:\\opencv_test_img\\d3.png", 1);
	if (c_src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	cvtColor(c_src,c_gray,CV_RGB2GRAY);
	blur(c_gray, c_gray, Size(3, 3), Point(-1, -1),BORDER_DEFAULT);
	namedWindow("canny", WINDOW_AUTOSIZE);
	createTrackbar("canny threshold", "canny", &c_th, 255, f_canny);
	f_canny(0,0);
	
	system("pause");
}

void f_canny(int,void*)
{
	Mat c_edge,dst;
	
	Canny(c_gray,c_edge,c_th,c_th*2);

	dst.create(c_src.size(),c_src.type());
	c_src.copyTo(dst,c_edge);
	imshow("canny", dst);
}

处理结果(最低阈值为34,最高为68时的结果):OpenCV入门笔记-更新篇_第10张图片
19.霍夫变换-几何检测
  用于几何检测,先将图像使用canny处理,再使用霍夫变换。直线的霍夫变换即将每一个像素点转换到极坐标空间(r,θ),n个像素点就有n条曲线(三角函数曲线),转换公式:
         rθ=xicosθ+yisinθ
每个(r,θ)点有曲线经过就加一,最后最大的点,就是n条曲线相交次数最多的点,取这个点的r、θ值,将这两个值看作已知的参数,代回上面的方程就能得到直线方程。
  最后调用直线检测霍夫变换API得到的是几组坐标点。
  霍夫变换具有两个API:Houghlines以及HoughlinesP,前一个返回(r,θ),后一个返回直线的坐标点。

void hough_getl()
{
	Mat src = imread("D:\\opencv_test_img\\d3.png", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	Mat dst_c, dst_BGR;
	Canny(src,dst_c,46,46*2);
	cvtColor(dst_c,dst_BGR,CV_GRAY2BGR);
	imshow("after canny",dst_BGR);

	vector<Vec4f> f_p;

	HoughLinesP(dst_c,f_p,1,CV_PI/180,10,0,0);   //检测直线的像素间隔,若直线不连续可以调整最后一个参数
	Scalar color = Scalar(200,0,0);
	for (size_t i = 0; i < f_p.size(); i++)
	{
		Vec4f p = f_p[i];
		line(dst_BGR,Point(p[0],p[1]),Point(p[2],p[3]),color,2,LINE_AA);
	}

	imshow("hough",dst_BGR);

	waitKey(0);
}

  霍夫空间圆检测,利用圆的方程组进行变换:
         x=a+Rcosθ
         y=b+Rsinθ
其中a、b表示圆形,R表示半径,在给定R时,变换θ,得到不同的a、b值,以此得到一条关于a、b的圆曲线,n个像素点有n条圆曲线,相交次数最多,即在霍夫空间中的高亮点,就是检测到的圆心。
  在opencv中,霍夫变换是基于图像梯度实现的,先检测边缘,发现可能的圆心,再计算最佳半径大小。

void hough_getc()
{
	Mat src = imread("D:\\opencv_test_img\\sz.jpg", 1);
	if (src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	Mat blur_m, gray, dst_f;
	vector<Vec3f> p_c;

	//中值模糊
	medianBlur(src,blur_m,3);
	imshow("blur_m", blur_m);
	cvtColor(blur_m,gray,CV_BGR2GRAY);
	imshow("gray",gray);

	霍夫检测
	HoughCircles(gray,p_c,CV_HOUGH_GRADIENT,1,10,100,30,0,100);  
	CV_HOUGH_GRADIENT表示霍夫梯度法,累加器与原图具有相同分辨率
	两个圆心之间最短距离为10,canny高阈值为100,圆心累加器阈值为30(超过则判定为圆心)
	圆半径的最小值为0.最大值为100

	for (size_t i=0;i<p_c.size();i++)
	{
		Vec3f p = p_c[i];
		circle(gray,Point(p[0],p[1]),p[2],Scalar(0,0,255),2,LINE_AA);
		circle(gray,Point(p[0],p[1]),1, Scalar(0, 0, 255), 2, LINE_AA);
	}

	imshow("hough",gray);
	waitKey(0);

}

cv2.HoughCircles函数的参数
  圆心累加器阈值过低会导致误判大大增加,过高会导致无法检测出该检测的圆。

20.像素重映射
  操作步骤:创建映射表(Mat)、计算新的像素数位置(映射表里存放的是像素坐标),调用映射API。

Mat ys_src, ys_dst, x_map, y_map;
int index = 0;

void ys()
{
	ys_src = imread("D:\\opencv_test_img\\d3.png", 1);
	if (ys_src.empty())
	{
		cout << "can not read img" << endl;
		return;
	}

	x_map.create(ys_src.size(),CV_32FC1);
	y_map.create(ys_src.size(), CV_32FC1);

	namedWindow("remap",WINDOW_AUTOSIZE);
	while (true)
	{
		index = waitKey(500);
		if (index == 27)
		{
			break;
		}
		index = index % 4;
		update_map();
		remap(ys_src, ys_dst,x_map,y_map,INTER_LINEAR,BORDER_CONSTANT,Scalar(0,0,255));
		imshow("remap",ys_dst);
	}
}
//获取新的坐标点,存放新图中,对应的旧图的像素位置
void update_map()
{
	int c = ys_src.cols;
	int r = ys_src.rows;
	for (int col = 0; col < c; col++)
	{
		for (int row = 0; row < r; row++)
		{
			switch (index)
			{
			case 0:  //缩效二分之一
				if (col>c*0.25&&col<c*0.75&&row>r*0.25&&row<r*0.75)
				{
					x_map.at<float>(row, col) = 2* (col-c*0.25);   //相当于插值的操作,取偶数行的像素
					y_map.at<float>(row, col) = 2* (row - r * 0.25);
				}
				else
				{
					x_map.at<float>(row, col) = 0;
					y_map.at<float>(row, col) = 0;
				}
				break;
			case 1: //水平翻转
				x_map.at<float>(row, col) = c - col-1;
				y_map.at<float>(row, col) = row;
				break;
			case 2:  //垂直翻转
				x_map.at<float>(row, col) = col;
				y_map.at<float>(row, col) = r - row - 1;
				break;
			case 3:  //水平+垂直翻转
				x_map.at<float>(row, col) = c - col - 1;
				y_map.at<float>(row, col) = r - row - 1;
				break;
			}
		}
	}
}

21.直方图均衡化

//获取运行时间
double t=getTickCount();   
double time=(getTickCount()-t)/getTickFrequency();

你可能感兴趣的:(图像处理基础知识,opencv,opencv)