c++版opencv基础学习Day2_图像的阈值处理及形态学变换

图像的阈值处理

二值化处理

  • 将设置的阈值应用于每个通道(阵列)的每个像素,进而过滤太小或太大的像素值。常用于去除噪声。
  • 缺点:具有主观性,难以达到理想的分割效果。
    #include <opencv2/imgproc.hpp>
    函数说明:double cv::threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type );
    输入参数:
    				src				输入阵列(多通道、8位或32位浮点)。
    				dst				与src具有相同大小和类型以及相同通道数的输出数组。
    				thresh			阈值。
    				maxval			与THRESH_BINARY和THRESH_ BINARY_INV阈值类型一起使用的最大值。
    				type			阈值类型。
    						cv::THRESH_BINARY = 0 				若大于thresh,则设置为maxval,否则设置为0。(常用)
    						cv::THRESH_BINARY_INV = 1 			若大于thresh,则设置为0,否则设置为maxval(反操作)。
    						cv::THRESH_TRUNC = 2 				若大于thresh,则设置为thresh,否则保持不变。
    						cv::THRESH_TOZERO = 3 				若大于thresh,则保持不变,否则设置为0。
    						cv::THRESH_TOZERO_INV = 4 			若大于thresh,则设置为0,否则保持不变(反操作)。
    						cv::THRESH_MASK = 5 
    						cv::THRESH_OTSU = 6 				全局自适应阈值化(仅适用于8位单通道图像)。适合于直方图具有双峰的情况,其在双峰之间找到阈值;对于非双峰图像不是很好用。
    						cv::THRESH_TRIANGLE = 7 			全局自适应阈值化(仅适用于8位单通道图像)。在直方图中,在最亮到最暗处连接一条直线,得到的最大直线距离所对应的直方图位置就是阈值thresh。
    								
    输出参数:仅当阈值类型为Otsu或Triangle方法时使用,输出自适应的阈值。
    

    自适应二值化处理

  • 通过窗口内像素的分布特征自适应计算阈值,并进行阈值化处理。详细步骤如下:

        (1)将图像拆分为M x N个区域;
        (2)采用自适应阈值算法(窗口均值阈值法、高斯分布阈值法),计算每个区域的(均值、高斯均值),该值即当前区域的二值化阈值;
        (3)根据每个窗口计算得到的不同阈值(动态),进行阈值化处理。
     

    #include <opencv2/imgproc.hpp>
    函数说明:void cv::adaptiveThreshold( InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C );
    输入参数:
    				src					8位单通道图像。
    				dst					与src大小和类型相同的目标图像。
    				maxValue			指定给满足条件的像素的非零值
    				adaptiveMethod		自适应阈值算法。BORDER_REPLICATE|BORDER_ISOLATED用于处理边界。
    							cv::ADAPTIVE_THRESH_MEAN_C = 0 			窗口均值阈值法。计算出领域的平均值再减去参数double C的值
    							cv::ADAPTIVE_THRESH_GAUSSIAN_C = 1		高斯分布阈值法。计算出领域的高斯均值再减去参数double C的值
    				thresholdType		阈值化类型(只有两个取值)。
    							cv::THRESH_BINARY = 0 				若大于thresh,则设置为maxval,否则设置为0。(常用)
    							cv::THRESH_BINARY_INV = 1 			若大于thresh,则设置为0,否则设置为maxval(反操作)。
    				blockSize			像素邻域大小(单位):3、5、7,依此类推。自适应阈值算法的阈值计算时使用。
    				C					偏移值。自适应阈值算法的阈值计算时使用。
    

    案例

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{

	string img_path1 = "D:/系统默认/桌面/RM/sha.jpeg";
	string img_path2 = "D:/系统默认/桌面/RM/tou.jpeg";

	Mat src = imread(img_path1, 1);
	Mat src2 = imread(img_path2, 1);
	
	if (src.empty() || src2.empty()) {
		cout << "can't read image!!" << endl;
		return -1;
	}
	//转换为灰度图
	cv::Mat srcGray;
	cv::cvtColor(src, srcGray, cv::COLOR_RGB2GRAY);

	//(4)阈值化处理
	
	cv::Mat THRESH_BINARY, THRESH_BINARY_INV, THRESH_TRUNC, THRESH_TOZERO, THRESH_TOZERO_INV, THRESH_MASK, THRESH_OTSU, THRESH_TRIANGLE;
	double thresh = 125;
	//设定的阈值
	double maxval = 255;
	//当像素超过我们设定的阈值时赋的值,设为255也就是赋白
	cv::threshold(srcGray, THRESH_BINARY, thresh, maxval, 0);
	cv::threshold(srcGray, THRESH_BINARY_INV, thresh, maxval, 1);
	//以上两个刚好相反,第一个小于阈值置0,第二个相反
	cv::threshold(srcGray, THRESH_TRUNC, thresh, maxval, 2);
	//小于阈值保持原色,大于置灰色
	cv::threshold(srcGray, THRESH_TOZERO, thresh, maxval, 3);
	cv::threshold(srcGray, THRESH_TOZERO_INV, thresh, maxval, 4);
	//以上两个也相反,第一个小于阈值置0,大于保持原色,第二个相反

	//(5)自适应阈值化处理
	/*自适应阈值是更趋于局部性的阈值,将像素点的像素值与该点所在区域的像素值的平均值
	(也有可能是最大值,中位数)来决定该点是属于0还是1*/
	cv::Mat ADAPTIVE_THRESH_MEAN_C0, ADAPTIVE_THRESH_MEAN_C1, ADAPTIVE_THRESH_GAUSSIAN_C0, ADAPTIVE_THRESH_GAUSSIAN_C1;
	int blockSize = 5;
	//方阵大小
	int constValue = 10;
	//常数,每个区域计算出阈值基础上减去这个常熟作为这个区域的最终阈值
	const int maxVal = 255;
	//像素值上限
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_MEAN_C0, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, constValue);
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_MEAN_C1, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, constValue);
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_GAUSSIAN_C0, maxVal, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, blockSize, constValue);
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_GAUSSIAN_C1, maxVal, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, blockSize, constValue);
	//不同的赋值方式,加INV的与上面的刚好相反

	//(6)显示图像
	cv::imshow("srcGray", srcGray);

	cv::imshow("img1", THRESH_BINARY);
	cv::imshow("img2", THRESH_BINARY_INV);
	cv::imshow("img3", THRESH_TRUNC);
	cv::imshow("img4", THRESH_TOZERO);
	cv::imshow("img5", THRESH_TOZERO_INV);
	//cv::imshow("img6", THRESH_MASK);
	//cv::imshow("img7", THRESH_OTSU);
	//cv::imshow("img8", THRESH_TRIANGLE);

	cv::imshow("img11", ADAPTIVE_THRESH_MEAN_C0);
	cv::imshow("img22", ADAPTIVE_THRESH_MEAN_C1);
	cv::imshow("img33", ADAPTIVE_THRESH_GAUSSIAN_C0);
	cv::imshow("img44", ADAPTIVE_THRESH_GAUSSIAN_C1);

	cv::waitKey(0);
	return 0;
}

很多很乱哈哈哈

图像的形态学转换

腐蚀

关于腐蚀就是将图像的边界腐蚀掉,或者说使得图像整体上看起来变瘦了。它的操作原理就是卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是1,那么中心元素保持原来的值,否则就变为0。这对于去除白噪声很有用,也可以用于断开两个连载一起的物体。

腐蚀可以应用多次(迭代)。在多通道图像的情况下,每个通道都是独立处理的。若需要将不同的卷积核应用于不同的通道,可使用cv::split。

#include <opencv2/imgproc.hpp>
函数说明:void cv::erode( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int 	borderType = BORDER_CONSTANT, const Scalar &borderValue = morphologyDefaultBorderValue() );	
输入参数:
				src									输入图像;通道的数量可以是任意的,这些通道是独立处理的,但深度应该是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F中的一个。
				dst									输出与src大小和类型相同的图像。
				kernel								卷积核大小(单通道浮点矩阵)。如果element=Mat(),则使用一个3x3矩形结构化元素。内核可以使用getStructureElement创建。
				anchor = Point(-1,-1)				锚点。位于卷积核内;默认值(-1,-1):表示锚点位于内核中心。
				iterations = 1						腐蚀迭代次数(默认1)。迭代N次与连续调用N次的效果是不等同的。
				borderType = BORDER_CONSTANT		边界类型(即边界填充方式)。默认BORDER_CONSTANT。不支持BORDER_WRAP。
									cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
									cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
									cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
									cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
									cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
									cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
									cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
									cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
									cv::BORDER_ISOLATED = 8 			do not look outside of ROI
				borderValue = morphologyDefaultBorderValue() 		边界值(在边界不变的情况下)

膨胀

膨胀原理与腐蚀相同,只不过膨胀的时候与卷积核对应的原图像的像素值只要有一个为1,那么中心元素就是1。这么做带来的变化就是图像膨胀了,或者说图像变胖了。

膨胀也可以多次迭代,也可以用split

#include <opencv2/imgproc.hpp>
函数说明:void cv::dilate( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar &borderValue = morphologyDefaultBorderValue() );	
输入参数:
				src									输入图像;通道的数量可以是任意的,这些通道是独立处理的,但深度应该是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F中的一个。
				dst									输出与src大小和类型相同的图像。
				kernel								卷积核大小(单通道浮点矩阵)。如果element=Mat(),则使用一个3x3矩形结构化元素。内核可以使用getStructureElement创建。
				anchor = Point(-1,-1)				锚点。位于卷积核内;默认值(-1,-1):表示锚点位于内核中心。
				iterations = 1						膨胀迭代次数(默认1)。迭代N次与连续调用N次的效果是不等同的。
				borderType = BORDER_CONSTANT		边界类型(即边界填充方式)。默认BORDER_CONSTANT。不支持BORDER_WRAP。
									cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
									cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
									cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
									cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
									cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
									cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
									cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
									cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
									cv::BORDER_ISOLATED = 8 			do not look outside of ROI
				borderValue = morphologyDefaultBorderValue() 		边界值(在边界不变的情况下)

形态学变换

#include <opencv2/imgproc.hpp>
函数说明:void cv::morphologyEx( InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int 	iterations = 1, int borderType = BORDER_CONSTANT, const Scalar &borderValue = morphologyDefaultBorderValue() );
输入参数:
				src									输入图像;通道的数量可以是任意的,这些通道是独立处理的,但深度应该是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F中的一个。
				dst									输出与src大小和类型相同的图像。
				op									形态学运算的类型。
									cv::MORPH_ERODE = 0			腐蚀操作
									cv::MORPH_DILATE = 1		膨胀操作
									cv::MORPH_OPEN = 2			开运算:先腐蚀再膨胀。用于去除微小干扰点/块。
									cv::MORPH_CLOSE = 3			闭运算:先膨胀再腐蚀。用于填充闭合区域。
									cv::MORPH_GRADIENT = 4		基本梯度:膨胀图(减去)腐蚀图
									cv::MORPH_TOPHAT = 5		顶帽:原图(减去)开运算图。注:顶帽和黑帽用于获取图像中的微小细节。
									cv::MORPH_BLACKHAT = 6		黑帽:闭运算图(减去)原图
									cv::MORPH_HITMISS = 7		“命中或未命中”。仅支持CV_8UC1二进制图像。
				kernel								卷积核大小(单通道浮点矩阵)。如果element=Mat(),则使用一个3x3矩形结构化元素。内核可以使用getStructureElement创建。
				anchor = Point(-1,-1)				锚点。位于卷积核内;默认值(-1,-1):表示锚点位于内核中心。
				iterations = 1						腐蚀和膨胀的迭代次数(默认1)。两次迭代的顺序:侵蚀+侵蚀+扩张+扩张(而不是侵蚀+扩张+侵蚀+扩张)。
				borderType = BORDER_CONSTANT		边界类型(即边界填充方式)。默认BORDER_CONSTANT。不支持BORDER_WRAP。
									cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
									cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
									cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
									cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
									cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
									cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
									cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
									cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
									cv::BORDER_ISOLATED = 8 			do not look outside of ROI
				borderValue = morphologyDefaultBorderValue() 		边界值(在边界不变的情况下)

 获取指定大小和形状的结构化元素

#include <opencv2/imgproc.hpp>
函数说明:Mat cv::getStructuringElement( int shape, Size ksize, Point anchor = Point(-1,-1) );	
输入参数:
				shape				结构化元素的形状。
						cv::MORPH_RECT = 0 			矩形结构化元素:E(i,j)=1
						cv::MORPH_CROSS	= 1			十字形结构元素:E(i,j)=10 if i=anchor.y or j=anchor.x else 0
						cv::MORPH_ELLIPSE = 2		椭圆形结构元素:即内切到矩形Rect(0, 0, esize.width, 0.esize.height)中的填充椭圆。
				ksize				结构化元素的大小。
				anchor				锚点。默认值(−1,−1)表示锚点位于中心。注意,只有十字形元件的形状取决于锚定位置。在其他情况下,锚点只是调节形态学运算的结果偏移了多少。

 案例1

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{

	string img_path1 = "D:/系统默认/桌面/RM/sha.jpeg";
	string img_path2 = "D:/系统默认/桌面/RM/tou.jpeg";

	Mat src = imread(img_path1, 1);
	Mat src2 = imread(img_path2, 1);
	
	if (src.empty() || src2.empty()) {
		cout << "can't read image!!" << endl;
		return -1;
	}

	Mat gray, binary;
	cvtColor(src, gray, cv::COLOR_BGR2GRAY);
	//灰度化
	threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
	//二值化



	Mat img_h, img_v;
	int x_size = binary.cols / 30;			//像素的水平长度=width/30
	int y_size = binary.rows / 30;			//像素的垂直长度=height/30
	Mat h_line = getStructuringElement(cv::MORPH_RECT, cv::Size(x_size, 1), cv::Point(-1, -1));
	Mat v_line = getStructuringElement(cv::MORPH_RECT, cv::Size(1, y_size), cv::Point(-1, -1));
	morphologyEx(binary, img_h, cv::MORPH_OPEN, h_line, cv::Point(-1, -1), 1, 0);			//开运算(提取水平线)
	//先腐蚀在膨胀
	morphologyEx(binary, img_v, cv::MORPH_CLOSE, v_line, cv::Point(-1, -1), 1, 0);			//闭运算(提取垂直线)
	//先膨胀,再腐蚀

	imshow("binary", binary);
	imshow("img_h", img_h);
	imshow("img_v", img_v);

	cv::waitKey(0);
	return 0;

	
}

案例

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{

	string img_path1 = "D:/系统默认/桌面/RM/sha.jpeg";
	string img_path2 = "D:/系统默认/桌面/RM/tou.jpeg";

	Mat src = imread(img_path1, 1);
	Mat src2 = imread(img_path2, 1);
	
	if (src.empty() || src2.empty()) {
		cout << "can't read image!!" << endl;
		return -1;
	}

	//(3)灰度化+二值化
	cv::Mat gray, binary;
	cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
	cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);


	//(4)形态学变化
	int kernel_size = 5;
	//getStructuringElement:返回指定大小和形状的结构化元素以进行形态学运算。
	cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(kernel_size, kernel_size), cv::Point(-1, -1));

	cv::Mat img1, img2, img3, img4;
	cv::erode(binary, img1, kernel);							//腐蚀
	cv::erode(binary, img2, kernel, cv::Point(-1, -1), 3);		//腐蚀(迭代三次)
	cv::dilate(binary, img3, kernel);							//膨胀
	cv::dilate(binary, img4, kernel, cv::Point(-1, -1), 3);		//膨胀(迭代三次)

	cv::Mat img11, img22, img33, img44, img55, img66, img77, img88;
	cv::morphologyEx(binary, img11, cv::MORPH_ERODE, kernel, cv::Point(-1, -1), 1, 0);			//腐蚀
	cv::morphologyEx(binary, img22, cv::MORPH_DILATE, kernel, cv::Point(-1, -1), 1, 0);			//膨胀
	cv::morphologyEx(binary, img33, cv::MORPH_OPEN, kernel, cv::Point(-1, -1), 1, 0);			//开运算
	cv::morphologyEx(binary, img44, cv::MORPH_CLOSE, kernel, cv::Point(-1, -1), 1, 0);			//闭运算

	cv::morphologyEx(binary, img55, cv::MORPH_GRADIENT, kernel, cv::Point(-1, -1), 1, 0);		//基本梯度
	cv::morphologyEx(binary, img66, cv::MORPH_TOPHAT, kernel, cv::Point(-1, -1), 1, 0);			//顶帽
	cv::morphologyEx(binary, img77, cv::MORPH_BLACKHAT, kernel, cv::Point(-1, -1), 1, 0);		//黑帽
	cv::morphologyEx(binary, img88, cv::MORPH_HITMISS, kernel, cv::Point(-1, -1), 1, 0);		//击中击不中

	//(4)显示图像
	cv::imshow("src", src);

	cv::imshow("腐蚀1", img1);
	cv::imshow("腐蚀3", img2);
	cv::imshow("膨胀1", img3);
	cv::imshow("膨胀3", img4);

	cv::imshow("腐蚀", img11);
	cv::imshow("膨胀", img22);
	cv::imshow("开运算", img33);
	cv::imshow("闭运算", img44);
	cv::imshow("基本梯度", img55);
	cv::imshow("顶帽", img66);
	cv::imshow("黑帽", img77);
	cv::imshow("击中击不中", img88);



	waitKey();
	return 0;

	
}

可以明显看到迭代一遍和三遍的区别

学习链接

 主要学习来源:Opencv C++图像处理(全)_c++ opencv 图像处理-CSDN博客

关于阈值处理详细理解:[2] 图像处理之----二值化处理-CSDN博客

关于腐蚀和膨胀原理:图像腐蚀和膨胀的原理_腐蚀膨胀原理_WitransFer的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值