OpenCV 笔记_2

news2024/12/25 8:56:30

文章目录

  • 笔记_2
    • 图像尺寸变换
      • resize 图像缩放 (重置图像大小)
      • flip 图像翻转
      • hconcat 横向连接
      • vconcat 纵向连接
    • 图像方式变换
      • warpAffine 仿射变换函数:矩阵M(2*3)
      • getRotationMatrix2D 获取图像旋转矩阵M:矩阵M(2*3)
      • getAffineTransform 获取仿射变换矩阵M:矩阵M(2*3)
    • 图像透视变换
      • getPerspectiveTransform :获取透视变换矩阵M(3*3)
      • warpPerspective :透视变换矩阵M(3*3)
    • 图像中绘制基本图形
      • line 绘制直线
      • arrowedLine 绘制带有箭头的直线
      • circle 绘制圆
      • ellipse 绘制椭圆形 ,其他参数如上
      • rectangle 绘制矩形 ,其他参数如上
      • fillPoly 绘制多边形 ,其他参数如上
      • putText 绘制文字 ,其他参数如上
    • ROI区域截取
      • Range
      • Rect_
      • copyTo 深拷贝数据
    • 高斯图像金字塔,拉普拉斯图像金字塔
      • pyrDown 下采样
      • pyrUp 上采样
    • 创建滑动条
      • createTrackbar
    • 鼠标事件响应
      • setMouseCallback 鼠标响应事件函数
      • MouseCallback 鼠标回调函数
    • 图像直方图的绘制
      • calcHist
    • 直方图均衡化
      • equalizeHist
      • normalize 数据归一化


笔记_2

图像尺寸变换

最邻近法
选择临近的像素。简单,效果较差

线性插值法
选择临近两个像素,建立一次函数,进行投影,得到的数据

双线性插值法
选择临近的四个像素,四个像素两两建立一次函数,再把像素投影到建立好的一次函数上再次建立一次函数得到数据。
应用图像的拉伸变换、旋转、仿射变换、透视变换等涉及到对图像像素位置改变的情况。

在这里插入图片描述

resize 图像缩放 (重置图像大小)

在这里插入图片描述

interpolation标志
在这里插入图片描述

flip 图像翻转

在这里插入图片描述在这里插入图片描述

hconcat 横向连接

vconcat 纵向连接

在这里插入图片描述

	Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");
	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
			return -1;
	}
	
	//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenaleftTop.jpg", leftTop);
	//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenarightTop.jpg", rightTop);
	//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenaleftDown.jpg", leftDown);
	//imwrite("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lenarightDown.jpg", rightDown);
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	Mat smallImg, bigImg0, bigImg1, bigImg2;
	//先将图像缩小
	resize(gray, smallImg, Size(50, 50), 0, 0, INTER_AREA);
	//最近邻插值
	resize(smallImg, bigImg0, Size(100, 100), 0, 0, INTER_NEAREST);
	//双线性插值
	resize(smallImg, bigImg1, Size(100, 100), 0, 0, INTER_LINEAR);
	//双三次插值
	resize(smallImg, bigImg2, Size(100, 100), 0, 0, INTER_CUBIC);



	//翻转图像
	Mat img_x, img_y, img_xy;
	//等于0,沿x轴翻转
	flip(gray, img_x, 0);
	//大于0,沿y轴翻转
	flip(gray, img_x, 1);
	//小于0,沿xy轴翻转(原点翻转)
	flip(gray, img_x, -1);

	
	//平均分割图像四个部分
	//Range(*,*) 按照矩阵中的元素抠图

	Mat leftTop = Mat(img, Range(0, img.rows / 2), Range(0, img.cols / 2));
	Mat rightTop = Mat(img, Range(0, img.rows / 2), Range(img.cols / 2, img.cols));
	Mat leftDown = Mat(img, Range(img.rows / 2, img.rows), Range(0, img.cols / 2));
	Mat rightDown = Mat(img, Range(img.rows / 2, img.rows), Range(img.cols / 2, img.cols));


	//图像拼接
	Mat img0, img1, img_img;
	//图像横向连接,图像通道,高度y一致
	hconcat(leftTop, rightTop, img0);
	hconcat(leftDown, rightDown, img1);
	//图像纵向连接,图像通道,宽度x一致
	vconcat(img0, img1, img_img);

图像方式变换

在这里插入图片描述

warpAffine 仿射变换函数:矩阵M(2*3)

在这里插入图片描述

最常用的用特定值填充,BORDER_CONSTANT

在这里插入图片描述在这里插入图片描述

getRotationMatrix2D 获取图像旋转矩阵M:矩阵M(2*3)

在这里插入图片描述

getAffineTransform 获取仿射变换矩阵M:矩阵M(2*3)

在这里插入图片描述

代码演示

	Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");
	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
			return -1;
	}

	Mat rotation0, img_warp0;
	//设置图像旋转的角度
	double angle = 30;
	//Size(宽,高) 设置输出图像的尺寸
	Size dst_size(img.rows+100, img.cols+100);
	//设置图像的旋转中心
	Point2f center(img.rows / 2.0, img.cols / 2.0);
	//计算仿射变换矩阵(旋转矩阵)
	rotation0 = getRotationMatrix2D(center, angle, 1);
	//进行仿射变换
	warpAffine(img, img_warp0, rotation0, dst_size);




	//根据定义的三个点进行仿射变换
	Point2f src_points[3];
	Point2f dst_points[3];

	src_points[0] = Point2f(0, 0);
	src_points[1] = Point2f(5, 50);
	src_points[2] = Point2f(30, 90);

	dst_points[0] = Point2f(2, 5);
	dst_points[1] = Point2f(33, 55);
	dst_points[2] = Point2f(100, 150);


	Mat rotation1, img_warp1;
	//计算仿射变换矩阵
	rotation1 = getAffineTransform(src_points, dst_points);
	//进行仿射变换
	warpAffine(img, img_warp1, rotation1, dst_size);

图像透视变换

在这里插入图片描述

getPerspectiveTransform :获取透视变换矩阵M(3*3)

在这里插入图片描述在这里插入图片描述

warpPerspective :透视变换矩阵M(3*3)

在这里插入图片描述

代码演示

	Mat img = imread("D:/OpenCV4.5.1/opencv/sources/modules/core/misc/objc/test/resources/chessboard.jpg");
	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
		return -1;
	}
	Point2f src_points[4];
	Point2f dst_points[4];
	//获取原图像四个坐标
	src_points[0] = Point2f(224, 61);
	src_points[1] = Point2f(535, 46);
	src_points[2] = Point2f(231, 281);
	src_points[3] = Point2f(528, 302);

	//期望透视变换后四个坐标的位置
	dst_points[0] = Point2f(0, 0);
	dst_points[1] = Point2f(0, 500);
	dst_points[2] = Point2f(500, 0);
	dst_points[3] = Point2f(500, 500);

	Mat perspectiveMat, img_warp;
	//计算获取透视变换矩阵
	perspectiveMat = getPerspectiveTransform(src_points, dst_points);
	//进行透视变换
	warpPerspective(img, img_warp, perspectiveMat, Size(500,500));

图像中绘制基本图形

line 绘制直线

在这里插入图片描述

color:线条的颜色,可以是一个标量值,也可以是一个三元素元组,分别表示BGR三个通道的颜色值;
thickness:线条的粗细,默认为1;

lineType  怎么理解?
cv2.LINE_4:表示绘制4连接线,即线条的端点只与相邻的4个像素相连;
cv2.LINE_8:表示绘制8连接线,即线条的端点可以与相邻的8个像素相连;
cv2.LINE_AA:表示绘制抗锯齿线条。
其中,4连接线表示线条的端点只与相邻的4个像素相连,8连接线表示线条的端点可以与相邻的8个像素相连,
而抗锯齿线条则可以使线条的显示更加平滑。可以看出,不同类型的线条在显示效果和绘制速度上都有所不同。


shift参数理解?
shift参数用于指定坐标点小数部分的位数,通常情况下可以将它设置为0。
如果shift的值为0,则坐标点将被解释为整数坐标值。如果shift的值为1,则坐标点将被解释为包含一位小数的坐标值,以此类推。
在实际应用中,shift参数通常用于提高坐标精度。例如,在一些需要精细控制图像变换的场景中,
可以将shift设置为较大的值,使得坐标点的小数部分可以更精细地表示。
但是需要注意的是,如果shift的值过大,可能会导致计算量增大,从而影响程序的执行效率。通常情况下,可以根据实际需求选择适当的shift值。

arrowedLine 绘制带有箭头的直线

arrowedLine函数是OpenCV库中用于在图像上绘制带有箭头的直线的函数。它可以在输入图像上绘制带有箭头的直线段。

以下是arrowedLine函数的C++函数签名:

void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
                 int thickness = 1, int line_type = LINE_8, int shift = 0,
                 double tipLength = 0.1)

参数说明:

  • img:输入图像,可以是单通道或多通道图像。
  • pt1:直线的起点坐标。
  • pt2:直线的终点坐标。
  • color:绘制直线的颜色,以Scalar对象表示,可以是标量或RGB值。
  • thickness:直线的粗细,默认为1。
  • line_type:直线的类型,默认为LINE_8,表示8连接线。也可以选择LINE_4(4连接线)或LINE_AA(抗锯齿直线)。
  • shift:坐标点的小数位数,默认为0。
  • tipLength:箭头的长度与线段长度之比,默认为0.1,表示箭头长度为线段长度的10%。

circle 绘制圆

在这里插入图片描述

ellipse 绘制椭圆形 ,其他参数如上

在这里插入图片描述

rectangle 绘制矩形 ,其他参数如上

在这里插入图片描述

fillPoly 绘制多边形 ,其他参数如上

在这里插入图片描述

putText 绘制文字 ,其他参数如上

在这里插入图片描述

fontFace 标志如下图:

在这里插入图片描述

代码演示:

	//创建三通道都为0
	Mat img = Mat::zeros(Size(512, 512), CV_8UC3);
	
	//绘制直线从开始点(100,100),结束点(200, 100),颜色(255, 255, 255)
	line(img, Point(100, 100), Point(200, 100), Scalar(255, 255, 255), 2, LINE_4, 0);
	//绘制一个实心圆,圆心(50, 50),半径25,颜色(255, 255, 255),-1表示填充
	circle(img, Point(50, 50), 25, Scalar(255, 255, 255), -1);
	//绘制一个空心圆,圆心(100, 50),半径20,颜色(255, 255, 255),轮廓宽度为4
	circle(img, Point(100, 50), 20, Scalar(255, 255, 255), 4);

	//绘制椭圆 ,圆心(300, 255),x半径100,y半径70,椭圆角度0,开始角度0,结束角度210(角度顺时针为正)
	ellipse(img, Point(300, 255), Size(100, 70), 0, 0, 210, Scalar(255, 255, 255), -1);

	//绘制矩形  ,左上角坐标(50, 400),右下角坐标(100, 450),颜色(255, 255, 255),-1表示填充
	rectangle(img, Point(50, 400), Point(100, 450), Scalar(255, 255, 255), -1);
	
	//绘制多边形
	Point pp[2][5];
	pp[0][0] = Point(72, 200);
	pp[0][1] = Point(142, 204);
	pp[0][2] = Point(226, 263);
	pp[0][3] = Point(172, 310);
	pp[0][4] = Point(177, 319);


	pp[1][0] = Point(447, 351);
	pp[1][1] = Point(504, 349);
	pp[1][2] = Point(484, 433);

	//pts变量的生成
	const Point *pts[2] = { pp[0],pp[1] };
	//顶点个数数组的生成
	const int npts[] = { 5,3 };
	//绘制2个多边形
	fillPoly(img, pts, npts, 2, Scalar(255, 255, 255), 8);
	

	//生成文字 
	putText(img, "learn OpenCv 4", Point(100, 130), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255));
	putText(img, "learn OpenCv 4", Point(100, 150), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255), 1, LINE_4, true);

在这里插入图片描述

ROI区域截取

Range

Rect_

在这里插入图片描述
在这里插入图片描述

copyTo 深拷贝数据

在这里插入图片描述


    浅拷贝:
    Mat a;
    Mat b;
    //把b的头信息复制到a,数据都指向同一个数据
    a = b;


    /*
    浅拷贝是指拷贝了Mat对象的指针和数据头信息,但并没有拷贝数据本身。
    这种拷贝方式通常是通过赋值操作来实现的。浅拷贝后的对象和原始对象共享数据,
    因此当其中一个对象发生变化时,另一个对象也会受到影响。

    深拷贝是指拷贝了Mat对象的数据和数据头信息,生成了一个新的独立对象。
    这种拷贝方式通常是通过clone()函数来实现的。深拷贝后的对象和原始对象是独立的,互相不受影响。

    一般来说,当我们需要对Mat对象进行修改时,应该使用深拷贝,以免原始对象被意外修改。
    而在仅需要读取Mat对象时,使用浅拷贝可以节省内存开销和时间。
    
    clone()和copyTo()是OpenCV中两种不同的复制Mat对象的方法,它们的实现方式不同,
    但都可以用来创建一个独立的Mat对象,使其与原始对象互相独立,互不影响。

    clone()函数用于创建一个独立的Mat对象,它会复制原始对象的所有数据和元数据。
    所以,clone()函数会产生一个完全独立的对象,其内存空间与原始对象的内存空间是不同的。
    这意味着,在复制后,任何对新对象的更改都不会影响原始对象。
    */


    cv::Mat img1 = cv::imread("image.jpg");  // 读取原始图像
    cv::Mat img2 = img1.clone();             // 创建一个独立的Mat对象

    /*copyTo()函数也用于创建一个独立的Mat对象,但它可以将数据复制到一个现有的Mat对象中。
    如果目标Mat对象与原始Mat对象的大小和类型不同,则会自动进行缩放或类型转换。
    copyTo()函数还可以选择复制部分数据,而不是全部数据。
    */


    cv::Mat img1 = cv::imread("image.jpg");       // 读取原始图像
    cv::Mat img2(img1.size(), img1.type());       // 创建一个新的Mat对象
    img1.copyTo(img2);                            // 将img1的数据复制到img2中

    /*
    因此,如果需要创建一个完全独立的Mat对象,可以使用clone()函数,
    而如果需要将数据复制到现有的Mat对象中,则可以使用copyTo()函数。
    无论使用哪种方法,都可以确保生成的新对象是独立的,不会影响原始对象。
    */

    

代码演示:

Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");
	
	Mat orange = imread("D:/OpenCV4.5.1/opencv/sources/samples/data/orange.jpg");
	if (img.empty() || orange.empty())
	{
		cout << "图像打开失败。。。" << endl;
		return -1;
	}

	Mat ROI1, ROI2, ROI2_copy, mask, img2, img_copy;

	//重置图像orange像素到mask大小(200, 200)
	resize(orange, mask, Size(200, 200));

	//浅拷贝
	img2 = img;
	 //深拷贝的两种方式
	img.copyTo(img_copy);

	//两种在图中截取ROI区域的方式:左上角坐标(150, 150),宽200,高200
	Rect rect(150, 150, 200, 200);
	//抠图
	ROI1 = img(rect);
	ROI2 = img(Range(300, 400), Range(300, 400));

	img(Range(300, 400), Range(300, 400)).copyTo(ROI2_copy);

	//ROI1 如果是已存在的矩阵大小和mask矩阵大小一致,直接把数据复制到ROI1原来的内存,
	//不一致的话,ROI1重新分配内存,不会把mask数据复制到ROI1原来的内存。
	mask.copyTo(ROI1);
	//img 和img2 和 ROI1 和 ROI2 数据都是在同一个内存中
	


	Mat clone1;
	//克隆数据  img和clone1所有数据都一样,但是两个内存地址不一样
	clone1 = img.clone();

高斯图像金字塔,拉普拉斯图像金字塔

在这里插入图片描述
在这里插入图片描述

pyrDown 下采样

在这里插入图片描述

pyrUp 上采样

在这里插入图片描述在这里插入图片描述

resize、pyrUp 和 pyrDown 图像金字塔(高斯金字塔、拉普拉斯金字塔)与尺寸缩放(向上采样、向下采样)

    resize 函数,最直接的方法。
    pyrUp 和 pyrDown 函数,即图像金字塔相关的两个函数,对图像进行向上采样和向下采样的操作。

pyrUp 和 pyrDown 其实和专门用于放大缩小图像尺寸的 resize 在功能上差不多,批着图像金字塔的皮,说白了还是对图像进行放大和缩小操作。

图像金字塔

    一幅图像的金字塔是一系列以金字塔形状排列,分辨率逐渐降低且源于同一张原始图的图像集合。
    金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。层级越高,图像越小,分辨率越低。
    图像金字塔是图像中多尺度表达的一种,最初用于机器视觉和图像压缩,
    最主要功能用于图像分割,是一种以多分辨率来解释图像的有效但概念简单的结构。

向上、向下采样
图像金字塔中的向上和向下采样分别通过 pyrUp 和 pyrDown 实现。
这里的向上向下采样,是针对图像的尺寸而言的(和金字塔的方向相反),
向上就是图像尺寸加倍,向下就是图像尺寸减半。但需要注意的是,pyrUp 和 pyrDown 不是互逆的。

    对于 pyrUp,图像首先在每个维度上扩大为原来的两倍,新增的行和列(偶数行和列)以 0 填充。
    然后用指定的滤波器进行卷积(实际上是一个在每个维度上都扩大为原来两倍的过滤器)去估计”丢失“像素的近似值。
    对于 pyrDown,我们先要用高斯核对图像进行卷积,然后删除所有偶数行和偶数列,新的到的图像面积就会变成源图像的四分之一。

高斯金字塔
高斯金字塔是通过高斯平滑和亚采样获得的一系列采样图像。

    向下采样方法:① 对图像进行高斯内核卷积;② 将所有偶数行和列去除。
    向上采样方法:① 将图像在每个方向上扩大为原来的两倍,新增的行和列以 0 填充;
    ② 使用原先同样的内核(乘以 4)与放大后的图像卷积,获得”新增像素“的近似值。
    在缩放过程中已经丢失了一些信息,如果想在缩放过程中减少信息的丢失,就需要用到拉普拉斯金字塔。

拉普拉斯金字塔
拉普拉斯金字塔是通过源图像减去先缩小再放大的图像的一系列图像构成。

在这里插入图片描述在这里插入图片描述

	Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");

	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
		return -1;
	}
	vector<Mat> Guass;
	int level = 3;
	//图从大到小保存到vector
	Guass.push_back(img);
	for (int i = 0; i < level; i++)
	{
		Mat guass;
		pyrDown(Guass[i], guass);
		Guass.push_back(guass);
	}

	vector<Mat> Lap;
	//图从小到到大保存到vector
	for (int i = Guass.size() - 1; i > 0; i--)
	{
		Mat lap, upGuass;
		if (i == Guass.size() - 1)
		{
			Mat down;
			pyrDown(Guass[i], down);
			pyrUp(down, upGuass);
			lap = Guass[i] - upGuass;
			Lap.push_back(lap);

		}
		pyrUp(Guass[i], upGuass);
		lap = Guass[i - 1] - upGuass;
		Lap.push_back(lap);
	}

	for (int i = 0; i < Guass.size(); i++)
	{
		string name = to_string(i);
		imshow("G" + name, Guass[i]);
		imshow("L" + name, Lap[Guass.size()-1-i]);
	}

创建滑动条

createTrackbar

在这里插入图片描述

回调函数原型:
typedef void (*TrackbarCallback)(int pos, void* userdata);

代码演示

//声明
void callBack(int value, void *);

int main()
{
		Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");

		if (img.empty())
		{
			cout << "图像打开失败。。。" << endl;
			return -1;
		}

		namedWindow("img");
		imshow("img", img);
		int value = 100;

		createTrackbar("百分比", "img", &value, 600, callBack, &img);

	
	waitKey(0);
	return 0;
}

//回调函数
void callBack(int value, void *data)
{
	Mat img = *(Mat*)data;
	float a = value / 100.0;

	imshow("img", img * a);
}

在这里插入图片描述

鼠标事件响应

setMouseCallback 鼠标响应事件函数

在这里插入图片描述

MouseCallback 鼠标回调函数

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

代码演示:

//回调函数
void mouseCB(int evnet,int x,int y,int flags,void *);
//全局变量
Point perPoint;
int main()
{

	Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");
	Mat imgPoint;
	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
		return -1;
	}

	img.copyTo(imgPoint);
	string str1 = "img";
	imshow(str1, img);
	setMouseCallback(str1, mouseCB, &img);
		
		
	
	waitKey(0);
	return 0;
}

//回调函数
void mouseCB(int event, int x, int y, int flags, void *data)
{
	Mat img = *(Mat*)data;

	//单击右键
	if (event == EVENT_RBUTTONDOWN)
	{
		cout << "点击鼠标左键才可以绘制轨迹" << endl;
	}
	//单击左键,输出坐标
	if (event == EVENT_LBUTTONDOWN)
	{
		//记录开始
		perPoint = Point(x, y);
		cout << "轨迹其实坐标: " <<perPoint<< endl;
	}
	//鼠标必须移动 并且 鼠标长按左键   flags & EVENT_FLAG_LBUTTON   在这里类似 flags == EVENT_FLAG_LBUTTON
	if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
	{
		Point pt(x, y);
		//划线
		line(img, perPoint, pt, Scalar(255, 255, 255), 3);
		perPoint = pt;
		imshow("img", img);
	}
}

在这里插入图片描述

图像直方图的绘制

calcHist

在这里插入图片描述

hist保存的信息:
在这里插入图片描述

代码演示:

    Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/lena.jpg");
	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
		return -1;
	}

	Mat gray;
	//bgr 2 gray
	cvtColor(img, gray, COLOR_BGR2GRAY);

	//设置提取直方图的相关变量
	//用于存放直方图计算结果
	Mat hist;
	//通道索引
	const int channels[1] = { 0 };
	//直方图的维度,其实就是像素灰度值的最大值
	const int bins[1] = { 256 };
	float inRanges[2] = { 0,255 };
	//像素灰度值范围
	const float*ranges[1] = { inRanges };
	//计算图像直方图
	calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges );

	//绘制直方图
	int hist_w = 512;
	int hist_h = 5000;
	int width = 2;
	Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC1);
	for (int i = 0; i < hist.rows; i++)
	{

		cout << "左上角: " << Point(width*(i), hist_h - 1) << endl;
		cout << "有下角: " << Point(width*(i + 1), hist_h - 1 - hist.at<float>(i)) << endl;
		rectangle(histImage, Point(width*(i), hist_h-1), Point(width*(i+1), hist_h - 1 - hist.at<float>(i)), Scalar(255, 255, 255), -1);//绘制直方图
	}
	namedWindow("img", WINDOW_NORMAL);
	imshow("img", histImage);

	{
		//设置提取直方图的相关变量
		//用于存放直方图计算结果
		Mat histB, histG, histR;
		//通道索引
		const int channels[1] = { 0 };
		//直方图的维度,其实就是像素灰度值的最大值
		const int bins[1] = { 256 };
		float inRanges[2] = { 0,255 };
		//像素灰度值范围
		const float*ranges[1] = { inRanges };

		Mat imgs[3];
		//分割三通道图像 按照BGR形式分割
		split(img, imgs);

		//计算图像直方图
		calcHist(&imgs[0], 1, channels, Mat(), histB, 1, bins, ranges);
		calcHist(&imgs[1], 1, channels, Mat(), histG, 1, bins, ranges);
		calcHist(&imgs[2], 1, channels, Mat(), histR, 1, bins, ranges);
		//绘制直方图
		int hist_w = 512;
		int hist_h = 5000;
		int width = 2;
		//创建三通道背景
		Mat histImageBGR = Mat::zeros(hist_h, hist_w, CV_8UC3);
		for (int i = 1; i < 256; i++)
		{
			line(histImageBGR, Point(width*(i - 1), hist_h - cvRound(histB.at<float>(i - 1))), Point(width*(i), hist_h - cvRound(histB.at<float>(i))), Scalar(255, 0, 0), width);//绘制直方图
			line(histImageBGR, Point(width*(i - 1), hist_h - cvRound(histG.at<float>(i - 1))), Point(width*(i), hist_h - cvRound(histG.at<float>(i))), Scalar(0, 255, 0), width);//绘制直方图
			line(histImageBGR, Point(width*(i - 1), hist_h - cvRound(histR.at<float>(i - 1))), Point(width*(i), hist_h - cvRound(histR.at<float>(i))), Scalar(0, 0, 255), width);//绘制直方图

		}
		namedWindow("histImageBGR", WINDOW_NORMAL);
		imshow("histImageBGR", histImageBGR);
	}

在这里插入图片描述

直方图均衡化

在这里插入图片描述

equalizeHist

在这里插入图片描述

直方图均衡化的定义:将随机分布的直方图修改为均匀分布的直方图,其实质是对图像进行非线性拉伸,重新分配图像像元值,使一定灰度范围内的像元的数量大致相等。

normalize 数据归一化

归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。

首先归一化是为了后面数据处理的方便,其次是保证程序运行时收敛加快。
归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在某个区间上是统计的坐标分布。
归一化有同一、统一和合一的意思。

归一化的目的,是使得没有可比性的数据变得具有可比性,同时又保持相比较的两个数据之间的相对关系,
如大小关系;或是为了作图,原来很难在一张图上作出来,归一化后就可以很方便的给出图上的相对位置等。

在这里插入图片描述

norm_type如下:
在这里插入图片描述

注意:   对于多通道数据,normalize()函数直接将其按内存中的顺序展开为数组,及当作一个向量进行处理。

实例:
 vector<double> positiveData = { 2.0, 8.0, 10.0 };
 vector<double> normalizedData_l1, normalizedData_l2, normalizedData_inf, normalizedData_minmax;
范数归一化:
1.    1范数:

 // sum(numbers) = 20.0 //2.0+8.0+10.0 

 // 2.0 0.1 (2.0/20.0)

 // 8.0 0.4 (8.0/20.0)

 // 10.0 0.5 (10.0/20.0)

 normalize(positiveData, normalizedData_l1, 1.0, 0.0同上最终归一化的值为单位向量的每个值乘以参数要归一化的范数值alpha。, NORM_L1);

直接求和后算出每一个算数比上总和的比值,加起来总为1。这里要归一化的范数值为1.0,
所求出的比值即为最后归一化后的值,若归一化范数值alpha为2.0,
则每个比值分别乘以2.0即得到最后归一化后的结果为0.2, 0.8, 1.0,以此类推。

2.   2范数:


 // Norm to unit vector: ||positiveData|| = 1.0
//模长 = ((2.0)²+(8.0)²+(10.0)²)的结果开根号 = 12.961481396815720461931934872176
 // 2.0 0.15   = 2 / 12.961481396815720461931934872176

 // 8.0 0.62    = 8 / 12.961481396815720461931934872176

 // 10.0 0.77    = 10 / 12.961481396815720461931934872176

 normalize(positiveData, normalizedData_l2, 1.0, 0.0, 
 NORM_L2);

即将该向量归一化为单位向量,每个元素值除以该向量的模长。同上最终归一化的值为单位向量的每个值乘以参数要归一化的范数值alpha。

3.   无穷范数


 // Norm to max element

 // 2.0 0.2 (2.0/10.0)

 // 8.0 0.8 (8.0/10.0)

 // 10.0 1.0 (10.0/10.0)

 normalize(positiveData, normalizedData_inf, 1.0, 0.0, 
 NORM_INF);

每个值除以最大值来进行无穷范数归一化。同上最终归一化的值为单位向量的每个值乘以参数要归一化的范数值alpha。


4:范围
NORM_MINMAX

normalize函数中的norm_type参数表示归一化的方法,NORM_MINMAX表示使用最小值和最大值归一化。具体来说,对于输入的数组src,
该方法会通过下面的公式对其进行归一化:

dst(x,y) = (src(x,y) - minVal) * (beta - alpha) / (maxVal - minVal) + alpha

其中,minVal和maxVal分别为src数组中的最小值和最大值,alpha和beta为指定的下限和上限。在归一化过程中,
src数组中的所有元素都会被映射到指定的范围内。因此,使用NORM_MINMAX方法可以将图像的像素值归一化到指定的范围内,便于显示和后续处理。

在这里插入图片描述

代码演示:

//归一化并绘制直方图函数
void drawHist(Mat &hist, int type, string name)
{
	//绘制直方图
	int hist_w = 512;
	int hist_h = 400;
	int width = 2;
	Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC1);

	//直方图hist数据归一化  (这里归hist_h = 400化)
	normalize(hist, hist, hist_h, 0, type);

	for (int i = 0; i < hist.rows; i++)
	{
		rectangle(histImage, Point(width*(i), hist_h - 1), Point(width*(i + 1), cvRound(hist_h - 1 - hist.at<float>(i))), Scalar(255, 255, 255), -1);//绘制直方图
	}
	imshow(name, histImage);
}
int main()
{

	Mat img = imread("D:/OpenCV4.5.1/opencv/sources/doc/js_tutorials/js_assets/Snipaste_2023-04-05_17-29-56shangu.png");
	if (img.empty())
	{
		cout << "图像打开失败。。。" << endl;
		return -1;
	}

	Mat gray, hist, hist2;
	//bgr 2 gray
	cvtColor(img, gray, COLOR_BGR2GRAY);

	Mat equalImg;
	//将图像直方图均衡化  必须是单通道
	equalizeHist(gray,equalImg);

	//通道索引
	const int channels[1] = { 0 };
	//直方图的维度,其实就是像素灰度值的最大值
	const int bins[1] = { 256 };
	float inRanges[2] = { 0,255 };
	//像素灰度值范围
	const float*ranges[1] = { inRanges };
	//计算图像直方图
	calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
	calcHist(&equalImg, 1, channels, Mat(), hist2, 1, bins, ranges);

	drawHist(hist, NORM_INF, "hist");
	drawHist(hist2, NORM_INF, "hist2");
	
	namedWindow("原图", WINDOW_NORMAL);
	namedWindow("原图均衡化后", WINDOW_NORMAL);
	imshow("原图", gray);
	imshow("原图均衡化后", equalImg);
	
	waitKey(0);
	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/654053.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑出现MSVCP100.dll丢失错误怎么办?

MSVCP100.dll是一个Windows系统文件&#xff0c;它可以确保Windows程序正确平稳运行&#xff0c;如果该文件安装不正确、丢失或损坏&#xff0c;则使用该文件的应用程序将无法正常启动&#xff0c;会出现“无法启动此程序&#xff0c;因为计算机中丢失MSVCP100.dll”的错误提示…

OpenCV 笔记_4

文章目录 笔记_4图像细化thinning 图像细化函数 轮廓检测findContours 轮廓检测函数drawContours 轮廓绘制函数contourArea 计算轮廓面积&#xff1a;返回值 double类型arcLength 计算轮廓长度&#xff1a;返回值 double类型 轮廓外接多边形boundingRect 给定轮廓的外接矩形min…

Hook攻防之InlineHook

什么是InlineHook Inline Hook&#xff0c;又称为超级Hook&#xff0c;是一种强大而又灵活的Hook技术。 Inline Hook的主要思想就是直接修改目标函数的代码&#xff0c;通常是在目标函数的开头插入一个跳转指令&#xff08;jmp&#xff09;。这个跳转指令会将程序的执行流跳转…

PPT写作要义:信息效率

PPT写作的要义&#xff1a;信息效率 抖音里一个有货的讲师的总结 花里胡哨的PPT一定偏离了初衷 这点上要像互联网公司学习 其实有些PPT制作的初衷&#xff0c;就是搞神秘感 故意让人听不懂 趣讲大白话&#xff1a;大道至简 【趣讲信息科技198期】 ****************************…

基于Java绿色农产品推广应用网站设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

CRC原理

文章目录 简介 CRC思想错误检测基本思想多项式运算没有进位的二进制计算 CRC计算方式发送方计算接收方校验多项式选择 CRC实现原理CRC8整体处理数据单个处理数据与整体数据处理比较使用查找表加速计算 扩展到CRC16整体处理数据单个处理数据与整体数据处理比较使用查找表加速计算…

项目管理中,如何实现有效的项目预算管理?

在《PMBOK指南》第七版中&#xff0c;变化较大的一点是从以成果为导向演变为以价值为导向&#xff0c;其十二项指导原则之一——“聚焦于价值”也阐述了价值是项目的最终成功指标和驱动因素。在这一指导原则下&#xff0c;项目经理就不能只关注在范围、进度、成本三重要素约束下…

UmiJs - 拆包优化

UmiJs - 拆包优化 前言一. 如何拆包&#xff0c;怎么拆1.1 分析自己项目的编译产物结构1.2 开始拆包 二. 有哪些注意点2.1 样式丢失2.2 存在需单独打包的页面 前言 我们在写前端代码的时候&#xff0c;难以避免的是&#xff0c;我们可能引入的依赖越来越多。那么随之而来的&am…

Redis入门 - Lua脚本

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis入门 - Lua脚本 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-scription.html Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。 …

【Golang系列】Golang环境配置和第一个Go程序

⭐️前面的话⭐️ 本篇文章将介绍Golang语言的环境配置&#xff0c;以及如何在VS code中运行第一个golang程序。 &#x1f4d2;博客主页&#xff1a;未见花闻的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4cc;本文由未…

Pytest教程__定制allure报告(12)

定制报告需要先导入allure模块&#xff0c;再使用以下装饰器方法&#xff1a; feature: 标注主要功能模块。story: 标注feature功能模块下的分支功能。description&#xff1a;在报告中显示用例描述。step: 标注测试用例步骤。issue && testcase&#xff1a;标注用例关…

单链表刷题(1-3)

目录 反转链表 移除元素 合并有序链表 反转链表 力扣 我们用取头节点依次进行头插的方式解决这道题。需要注意的是头插前要保存下一个节点。 struct ListNode* reverseList(struct ListNode* head){typedef struct ListNode SL;SL* cur head;SL* rhead NULL;//初始指向空…

TienChin 代码格式化-项目结构大改造

代码格式化 博主下载项目之后发现&#xff0c;整体的代码格式化风格&#xff0c;与 C 那种语言很相似&#xff0c;说明这个作者之前就是从事这块的导致风格有点类似&#xff0c;我们来格式化一下&#xff0c;当然这不是必要的&#xff0c;我是没习惯这种写法所以这里我写一下我…

2023年测试岗,你真的懂测试吗?凭什么他能月薪25k+

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 测试人员应该居安…

Redis入门 - 事务

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis入门 - 事务 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-transaction.html Redis 事务可以一次执行多个命令&#xff0c; 并且带有以下三个重要的保证&#xff1a; 批量操作在发送 EXEC 命…

STM32串口通信详解(嵌入式学习)

STM32串口通信 1.通信基础知识1.1 时钟信号区分同步通信异步通信波特率总线协议(电气协议) 1.2 通信方式划分串行通信并行通信 1.3 通信方向划分单工通信半双工通信全双工通信常见通信总结 2. USARTUSART 介绍 3. 串口通信协议4. 相关寄存器串口控制寄存器波特率寄存器中断和状…

segment anything环境配置与使用测试

硬件&#xff1a;RTX3070 i9-11900H 内存16G 目录 一、环境配置 二、使用测试--predictor_example.ipynb 1.jupyter notebook准备操作 2.Object masks from prompts with SAM与Environment Set-up 3.Set-up 4.Example image 5.Selecting objects with SAM 6.Specifyin…

GeoServer安装部署

GeoServer是一款开源的GIS服务器,用于管理、共享和编辑空间数据。 它的主要功能包括: 管理空间数据&#xff1a;GeoServer可以连接各种空间数据源,包括文件(SHP、CSV等)、数据库(PostGIS,Oracle,SQL Server等)和云存储(S3,Swift,Azure等)。并提供数据的浏览、上传、下载和删除…

webgpu之旅04

继续继续 319854902 319854902 319854902 319854902 webgpu交Q流群首先准备好绘制到屏幕所需的这个descriptor if rendertarget this._textures.initRenderTarget( renderTarget ); 来看一下这个函数里面会做什么 renderTargetProperties是这个target的properties 创建一个co…

历时一个月,腾讯认证python全套项目实战笔记,终于整理出来了

前言 之前拿到一份关于腾讯认证的python的全套项目实战脑图&#xff0c;于是历时花费一个月&#xff0c;终于是熬夜加点的给肝出来了&#xff0c;先用typora全部写出来&#xff0c;然后再导出成PDF文件&#xff0c;目前已经完全搞定。 总共划分内容为&#xff08;七大模块&am…