Notas de OpenCV_2


Directorio de artículos


nota 2

Conversión de tamaño de imagen

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

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

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

inserte la descripción de la imagen aquí

cambiar el tamaño de la escala de la imagen (restablecer el tamaño de la imagen)

inserte la descripción de la imagen aquí

indicador de interpolación
inserte la descripción de la imagen aquí

voltear imagen voltear

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí

unión horizontal hconcat

conexión vertical vconcat

inserte la descripción de la imagen aquí

	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);

conversión de modo de imagen

inserte la descripción de la imagen aquí

función de transformación afín warpAffine: matriz M (2*3)

inserte la descripción de la imagen aquí

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

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí

getRotationMatrix2D Obtener la matriz de rotación de la imagen M: matriz M (2*3)

inserte la descripción de la imagen aquí

getAffineTransform Obtenga la matriz de transformación afín M: matriz M (2*3)

inserte la descripción de la imagen aquí

demostración de código

	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);

Transformación de perspectiva de imagen

inserte la descripción de la imagen aquí

getPerspectiveTransform : Obtiene la matriz de transformación de perspectiva M (3*3)

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí

warpPerspective: matriz de transformación de perspectiva M (3*3)

inserte la descripción de la imagen aquí

demostración de código

	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));

Dibujar gráficos básicos en imágenes

línea dibuja una línea recta

inserte la descripción de la imagen aquí

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 dibuja una línea con una flecha

arrowedLinefunción es una función en la biblioteca OpenCV para dibujar una línea recta con una flecha en una imagen. Puede dibujar segmentos de línea recta con flechas en la imagen de entrada.

La siguiente es arrowedLinela firma de la función C++ de la función:

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)

Descripción de parámetros:

  • img: Imagen de entrada, que puede ser una imagen de un solo canal o de varios canales.
  • pt1: Las coordenadas del punto inicial de la línea.
  • pt2: Las coordenadas del punto final de la línea.
  • color: El color para dibujar la línea, Scalarrepresentado como un objeto, que puede ser un valor escalar o RGB.
  • thickness: El grosor de la línea, el valor predeterminado es 1.
  • line_type: El tipo de línea recta, el valor predeterminado es LINE_8, lo que significa 8 líneas de conexión. También puede elegir LINE_4(4 líneas conectadas) o LINE_AA(líneas rectas suavizadas).
  • shift: El número de lugares decimales del punto de coordenadas, el valor predeterminado es 0.
  • tipLength: La relación entre la longitud de la flecha y la longitud del segmento de línea, el valor predeterminado es 0,1, lo que significa que la longitud de la flecha es el 10 % de la longitud del segmento de línea.

círculo dibuja un círculo

inserte la descripción de la imagen aquí

ellipse dibuja una elipse, los demás parámetros son los anteriores

inserte la descripción de la imagen aquí

rectángulo dibuja un rectángulo, otros parámetros son como arriba

inserte la descripción de la imagen aquí

fillPoly dibuja polígonos, otros parámetros son los anteriores

inserte la descripción de la imagen aquí

putText dibuja texto, otros parámetros son los anteriores

inserte la descripción de la imagen aquí

fontFace 标志如下图:

inserte la descripción de la imagen aquí

demostración de código:

	//创建三通道都为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);

inserte la descripción de la imagen aquí

Intercepción de área de retorno de la inversión

Rango

Rect_

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

copiar a datos de copia profunda

inserte la descripción de la imagen aquí


    浅拷贝:
    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()函数。
    无论使用哪种方法,都可以确保生成的新对象是独立的,不会影响原始对象。
    */

    

demostración de código:

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();

Pirámide de imágenes gaussianas, pirámide de imágenes laplacianas

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

muestreo pyrDown

inserte la descripción de la imagen aquí

sobremuestreo pyrUp

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí

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

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

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

图像金字塔

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

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

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

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

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

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

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí

	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]);
	}

crear control deslizante

crearTrackbar

inserte la descripción de la imagen aquí

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

demostración de código

//声明
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);
}

inserte la descripción de la imagen aquí

respuesta de evento del ratón

función de evento de respuesta del mouse setMouseCallback

inserte la descripción de la imagen aquí

Función de devolución de llamada del mouse MouseCallback

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

demostración de código:

//回调函数
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);
	}
}

inserte la descripción de la imagen aquí

Dibujo de histograma de imagen

calcHist

inserte la descripción de la imagen aquí

Información guardada por hist:
inserte la descripción de la imagen aquí

demostración de código:

    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);
	}

inserte la descripción de la imagen aquí

Ecualización de histograma

inserte la descripción de la imagen aquí

igualarHist

inserte la descripción de la imagen aquí

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

normalizar la normalización de datos

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

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

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

inserte la descripción de la imagen aquí

norm_type如下:
inserte la descripción de la imagen aquí

注意:   对于多通道数据,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方法可以将图像的像素值归一化到指定的范围内,便于显示和后续处理。

inserte la descripción de la imagen aquí

demostración de código:

//归一化并绘制直方图函数
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;
}

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_43763292/article/details/131252269
Recomendado
Clasificación