(一)相机标定——四大坐标系的介绍、对应转换、畸变原理以及OpenCV完整代码实战(C++版)

一、四大坐标系介绍

1,世界坐标系

从这个世界(world)的视角来看物体
世界坐标系是3D空间坐标,每个点的位置用 ( X w , Y w , Z w ) (X_w,Y_w,Z_w) (Xw,Yw,Zw)表示

2,相机坐标系

相机本身具有一个坐标系,其也是3D空间坐标
从相机(camera)的视角来看物体,每个点的位置用 ( X c , Y c , Z c ) (X_c,Y_c,Z_c) (Xc,Yc,Zc)表示

3,图像坐标系

相机坐标系是3D空间的,而我们通过相机拍照得到的照片却是2D平面,这之间涉及到了透视投影(perspective projection),大白话就是相似三角形,将3D空间上的点映射到2D平面上
图像坐标系是实际的物理坐标系 ( x , y ) (x,y) (x,y),其原点位置一般在相机光轴成像与成像平面的交点位置,通常为成像平面的中心点,物理单位为mm

4,像素坐标系

我们在处理图像数据的时候,使用的是像素坐标系 ( u , v ) (u,v) (u,v),比如这个图像的大小为1080*720,即长和宽为1080和780个像素;分辨率等相关概念也是这个大概意思,单位是像素pixel
每个像素都有对应的实际物理尺寸,比如1像素=0.5mm
像素有些情况是矩形,分为x和y方向的长度

5,总结

我们需要通过一些手段,将原本在世界坐标系下的点转换到像素坐标系下
世界坐标系是因为物体本身真实存在的位置,而转到像素坐标系下是因为我们在进行图像处理的时候针对的是像素
故,相机标定的最终目的是实现世界坐标系和像素坐标系之间的转换

二、四大坐标系转换

1,世界坐标系——相机坐标系

相机坐标系和世界坐标系都是3D空间坐标系,任何一个空间中的点,都可以通过旋转平移进行相互转换

假设世界坐标系下有个点 P w ( X w , Y w , Z w ) P_w(X_w,Y_w,Z_w) Pw(Xw,Yw,Zw),通过乘以一个变换矩阵(旋转R+平移T)就可以得到相机坐标系下的对应位置 P c ( X c , Y c , Z c ) P_c(X_c,Y_c,Z_c) Pc(Xc,Yc,Zc)

[ X c Y c Z c ] = [ R 11 R 12 R 13 R 21 R 22 R 23 R 31 R 32 R 33 ] [ X w Y w Z w ] + [ T 1 T 2 T 3 ] \begin{bmatrix} X_c\\ Y_c\\ Z_c \end{bmatrix}= \begin{bmatrix} R_{11}&R_{12}&R_{13}\\ R_{21}&R_{22}&R_{23}\\ R_{31}&R_{32}&R_{33} \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w \end{bmatrix}+ \begin{bmatrix} T_1\\ T_2\\ T_3 \end{bmatrix} XcYcZc = R11R21R31R12R22R32R13R23R33 XwYwZw + T1T2T3

为了后续的计算方便,转换为齐次坐标系进行表示
[ X c Y c Z c 1 ] = [ R 11 R 12 R 13 T 1 R 21 R 22 R 23 T 2 R 31 R 32 R 33 T 3 0 0 0 1 ] [ X w Y w Z w 1 ] \begin{bmatrix} X_c\\ Y_c\\ Z_c\\ 1 \end{bmatrix}= \begin{bmatrix} R_{11}&R_{12}&R_{13}&T_1\\ R_{21}&R_{22}&R_{23}&T_2\\ R_{31}&R_{32}&R_{33}&T_3\\ 0&0&0&1 \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w\\ 1 \end{bmatrix} XcYcZc1 = R11R21R310R12R22R320R13R23R330T1T2T31 XwYwZw1

其中这个变换矩阵(旋转R和平移T)称为相机外参
[ R 11 R 12 R 13 T 1 R 21 R 22 R 23 T 2 R 31 R 32 R 33 T 3 0 0 0 1 ] \begin{bmatrix} R_{11}&R_{12}&R_{13}&T_1\\ R_{21}&R_{22}&R_{23}&T_2\\ R_{31}&R_{32}&R_{33}&T_3\\ 0&0&0&1 \end{bmatrix} R11R21R310R12R22R320R13R23R330T1T2T31

相机外参实现了某点所在的世界坐标系相机坐标系之间的转换

2,相机坐标系——图像坐标系

就此我们实现了世界坐标系下点 P w ( X w , Y w , Z w ) P_w(X_w,Y_w,Z_w) Pw(Xw,Yw,Zw)到其所对应的相机的坐标系下位置 P c ( X c , Y c , Z c ) P_c(X_c,Y_c,Z_c) Pc(Xc,Yc,Zc)之间的转换

相机坐标系是3D空间坐标系,而相机拍出来的图片是2D平面,这之间涉及透视投影(perspective projection),大白话为相似三角形
在这里插入图片描述
我们可以看到 X c , Y c , Z c , O c X_c,Y_c,Z_c,O_c Xc,Yc,Zc,Oc这个坐标系为相机坐标系,淡蓝色的平面 x , y , o x,y,o x,y,o为图像坐标系(成像平面)
相机成像的原理是小孔成像,故相机坐标系和图像坐标系的y轴是相反的
相机坐标系的原点 O c O_c Oc与图像坐标系的原点 o o o之间的距离为焦距 f f f这里假设相机坐标系和图像坐标系的轴是相互平行且放置位置为正中心,但也有不平行情况,就需要考虑角度了

相机坐标系下的点 P c ( X c , Y c , Z c ) P_c(X_c,Y_c,Z_c) Pc(Xc,Yc,Zc)与相机坐标系原点 O c O_c Oc的连线过图像坐标系平面上的点 p ( x , y ) p(x,y) p(x,y)
也就是相机坐标系下的点 P c ( X c , Y c , Z c ) P_c(X_c,Y_c,Z_c) Pc(Xc,Yc,Zc)对应的图像坐标系下的位置为 p ( x , y ) p(x,y) p(x,y)
根据相似三角形可知:
{ x f = X c Z c y f = Y c Z c ⇒ { Z c ⋅ x = f ⋅ X c Z c ⋅ y = f ⋅ Y c \begin{cases} \frac{x}{f} = \frac{X_c}{Z_c}\\ \frac{y}{f} = \frac{Y_c}{Z_c} \end{cases} \Rightarrow \begin{cases} Z_c·x = f·X_c\\ Z_c·y = f·Y_c \end{cases} {fx=ZcXcfy=ZcYc{Zcx=fXcZcy=fYc

转化为矩阵形式
Z c [ x y 1 ] = [ f 0 0 0 0 f 0 0 0 0 1 0 ] [ X c Y c Z c 1 ] Z_c \begin{bmatrix} x\\y\\1\end{bmatrix} =\begin{bmatrix} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0 \end{bmatrix} \begin{bmatrix} X_c\\ Y_c\\ Z_c\\ 1 \end{bmatrix} Zc xy1 = f000f0001000 XcYcZc1
其中 Z c Z_c Zc为点所在相机坐标系下的Z轴方向位置,是个常量,又称为比例因子
f f f为相机的焦距

通过该矩阵就可以实现相机坐标系图像坐标系之间的转换

3,图像坐标系——像素坐标系

图像坐标系是物理坐标系,因为它涉及到具体的尺寸大小,每个像素都有其对应的物理尺寸
一般情况下像素是矩形,通常情况下假设 1 p i x e l = d x m m , 1 p i x e l = d y m m 1 pixel = dx mm,1 pixel = dy mm 1pixel=dxmm1pixel=dymm,其中 d x dx dx d y dy dy表示一个像素的长宽分别为多少mm

在这里插入图片描述

已知有个小蓝点在图像坐标系下的位置为 ( x , y ) (x,y) (x,y),图像坐标系原点所在像素坐标系下的位置为 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)
求解:小蓝点所对应的像素坐标系为多少?

假设:1个像素的长和宽分别为 d x dx dx d y dy dy mm,图像坐标系下1mm对应像素坐标系下为 1 d x \frac{1}{dx} dx1个像素
小蓝点在图像坐标系下 ( x , y ) (x,y) (x,y)应像素坐标为 ( x ∗ 1 d x , y ∗ 1 d y ) (x * \frac{1}{dx} , y * \frac{1}{dy}) (xdx1,ydy1),即 ( x d x , y d y ) (\frac{x}{dx},\frac{y}{dy}) (dxx,dyy)

u = x d x + u 0 v = y d y + v 0 u = \frac{x}{dx} + u_0\\ v = \frac{y}{dy} + v_0 u=dxx+u0v=dyy+v0

整理成矩阵形式:
[ u v 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ x y 1 ] \begin{bmatrix} u\\ v\\ 1 \end{bmatrix} = \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} uv1 = dx1000dy10u0v01 xy1

也可以写成另一种形式

[ x y 1 ] = [ d x 0 − u 0 d x 0 d y − v 0 d y 0 0 1 ] [ u v 1 ] \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} = \begin{bmatrix} dx&0&-u_0dx\\ 0&dy&-v_0dy\\ 0&0&1 \end{bmatrix} \begin{bmatrix} u\\ v\\ 1 \end{bmatrix} xy1 = dx000dy0u0dxv0dy1 uv1

由此可以得到一个矩阵,实现该点在图像坐标系 ( x , y ) (x,y) (x,y)和像素坐标系 ( u , v ) (u,v) (u,v)下的直接转换

4,各个坐标系转换相互推导结合

我们的最终目的是:世界坐标系转换到像素坐标系

若已知世界坐标系下点的坐标为 P w ( X w , Y w , Z w ) P_w(X_w,Y_w,Z_w) Pw(Xw,Yw,Zw)

将世界坐标系 P w ( X w , Y w , Z w ) P_w(X_w,Y_w,Z_w) Pw(Xw,Yw,Zw)转换为相机坐标系 P c ( X c , Y c , Z c ) P_c(X_c,Y_c,Z_c) Pc(Xc,Yc,Zc)
[ X c Y c Z c 1 ] = [ R 11 R 12 R 13 T 1 R 21 R 22 R 23 T 2 R 31 R 32 R 33 T 3 0 0 0 1 ] [ X w Y w Z w 1 ] \begin{bmatrix} X_c\\ Y_c\\ Z_c\\ 1 \end{bmatrix}= \begin{bmatrix} R_{11}&R_{12}&R_{13}&T_1\\ R_{21}&R_{22}&R_{23}&T_2\\ R_{31}&R_{32}&R_{33}&T_3\\ 0&0&0&1 \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w\\ 1 \end{bmatrix} XcYcZc1 = R11R21R310R12R22R320R13R23R330T1T2T31 XwYwZw1

将相机坐标系 P c ( X c , Y c , Z c ) P_c(X_c,Y_c,Z_c) Pc(Xc,Yc,Zc)转化为图像坐标系 p ( x , y ) p(x,y) p(x,y)

Z c [ x y 1 ] = [ f 0 0 0 0 f 0 0 0 0 1 0 ] [ X c Y c Z c 1 ] Z_c \begin{bmatrix} x\\y\\1\end{bmatrix} =\begin{bmatrix} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0 \end{bmatrix} \begin{bmatrix} X_c\\ Y_c\\ Z_c\\ 1 \end{bmatrix} Zc xy1 = f000f0001000 XcYcZc1

将图像坐标系 ( x , y ) (x,y) (x,y)转化为像素坐标系 ( u , v ) (u,v) (u,v),这里的 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)图像坐标系的原点所对应的像素坐标系下的位置
[ u v 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ x y 1 ] \begin{bmatrix} u\\ v\\ 1 \end{bmatrix} = \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} uv1 = dx1000dy10u0v01 xy1

最终进行前后整理可得:
Z c [ u v 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ f 0 0 0 0 f 0 0 0 0 1 0 ] [ R 11 R 12 R 13 T 1 R 21 R 22 R 23 T 2 R 31 R 32 R 33 T 3 0 0 0 1 ] [ X w Y w Z w 1 ] Z_c \begin{bmatrix} u\\ v\\ 1 \end{bmatrix} = \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0 \end{bmatrix} \begin{bmatrix} R_{11}&R_{12}&R_{13}&T_1\\ R_{21}&R_{22}&R_{23}&T_2\\ R_{31}&R_{32}&R_{33}&T_3\\ 0&0&0&1 \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w\\ 1 \end{bmatrix} Zc uv1 = dx1000dy10u0v01 f000f0001000 R11R21R310R12R22R320R13R23R330T1T2T31 XwYwZw1

其中 Z c Z_c Zc表示该点在相机坐标系下的Z轴方向的位置,又称为比例因子,本质是常量

相机内参为:
[ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ f 0 0 0 0 f 0 0 0 0 1 0 ] \begin{bmatrix} \frac{1}{dx}&0&u_0\\ 0&\frac{1}{dy}&v_0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0 \end{bmatrix} dx1000dy10u0v01 f000f0001000

相机外参为:
[ R 11 R 12 R 13 T 1 R 21 R 22 R 23 T 2 R 31 R 32 R 33 T 3 0 0 0 1 ] \begin{bmatrix} R_{11}&R_{12}&R_{13}&T_1\\ R_{21}&R_{22}&R_{23}&T_2\\ R_{31}&R_{32}&R_{33}&T_3\\ 0&0&0&1 \end{bmatrix} R11R21R310R12R22R320R13R23R330T1T2T31

就此我们实现了世界坐标系和像素坐标系的转换,这就是相机标定的意义所在

5,总结

理想状态下的相机标定,其本质是求解相机的内外参数矩阵

相机外参矩阵需要求解旋转和平移共6个参数
相机内参菊展需要求解焦距 f f f、图像坐标系原点所在的像素坐标系的坐标 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)也称为像主点坐标、单个像素点的长和宽 d x dx dx d y dy dy mm,共5个参数

相机内外参数矩阵需要求解11个参数,这是不考虑畸变的理想状况,但实际相机都不可避免存在畸变,故还需要求解畸变系数

三、畸变

相机标定的最终目的是拿到相机的内外参数矩阵,这些内外参数针对同一个相机是固定的,只需要标定一次即可
相机硬件本身多多少少不可避免存在一定的误差,故需要求解畸变系数用于相机的校准,然后再进行求解内外参矩阵

畸变主要包括(影响最大):切向畸变径向畸变,是相机本身无法避免的误差

1,切向畸变

切向畸变产生于相机组装过程中,透镜本身与相机传感器成像平面不平行
在这里插入图片描述

2,径向畸变

径向畸变产生于透镜本身的形状,光线在远离透镜中心的地方比靠近中心的地方更加弯曲

径向畸变主要包括:桶形畸变枕形畸变
在这里插入图片描述

切向畸变和径向畸变有对应的模型公式,网上一大堆教程,需要的小伙伴自行学习推导哈

畸变涉及到五个参数:径向畸变参数 k 1 、 k 2 、 k 3 k_1、k_2、k_3 k1k2k3;切向畸变参数 p 1 、 p 2 p_1、p_2 p1p2

四、OpenCV代码实战

求解方法很多,这里以张正友标定法(也称为棋盘格标定法)为例进行演示

1,准备棋盘格数据

①OpenCV自带几张棋盘格图片,大致路径为:opencv\sources\samples\data,当然也可以自己去拍几张棋盘格
把这几张图片放到VS项目中
在这里插入图片描述
在这里插入图片描述
②新建一个项目,需要用到OpenCV
③跑一下代码即可

2,完整代码

修改地方:
①棋盘格角点行列数:int CHECKERBOARD[2]{ 6,9 };
在这里插入图片描述

②图片所在文件夹路径:std::string path = "./image/*.jpg";

#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <iostream>

using namespace std;
using namespace cv;

// Defining the dimensions of checkerboard
// 定义棋盘格的尺寸
int CHECKERBOARD[2]{ 6,9 }; // 一行有6个,一共有9行     数点的行列---6行9列

int main()
{
	// Creating vector to store vectors of 3D points for each checkerboard image
	// 创建矢量以存储每个棋盘图像的三维点矢量
	std::vector<std::vector<cv::Point3f> > objpoints;

	// Creating vector to store vectors of 2D points for each checkerboard image
	// 创建矢量以存储每个棋盘图像的二维点矢量
	std::vector<std::vector<cv::Point2f> > imgpoints;

	// Defining the world coordinates for 3D points
	// 为三维点定义世界坐标系
	std::vector<cv::Point3f> objp;
	for (int i{ 0 }; i < CHECKERBOARD[1]; i++)
	{
		for (int j{ 0 }; j < CHECKERBOARD[0]; j++)
		{
			objp.push_back(cv::Point3f(j, i, 0));
		}
	}

	// Extracting path of individual image stored in a given directory
	// 提取存储在给定目录中的单个图像的路径
	std::vector<cv::String> images;

	// Path of the folder containing checkerboard images
	// 包含棋盘图像的文件夹的路径
	std::string path = "./image/*.jpg";

	// 使用glob函数读取所有图像的路径
	cv::glob(path, images);

	cv::Mat frame, gray;

	// vector to store the pixel coordinates of detected checker board corners
	// 存储检测到的棋盘转角像素坐标的矢量
	std::vector<cv::Point2f> corner_pts;
	bool success;

	// Looping over all the images in the directory
	// 循环读取图像
	for (int i{ 0 }; i < images.size(); i++)
	{
		frame = cv::imread(images[i]);
		if (frame.empty())
		{
			continue;
		}
		if (i == 40)
		{
			int b = 1;
		}
		cout << "the current image is " << i << "th" << endl;
		cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);

		// Finding checker board corners
		// 寻找角点
		// If desired number of corners are found in the image then success = true
		// 如果在图像中找到所需数量的角,则success = true
		// opencv4以下版本,flag参数为CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE
		success = cv::findChessboardCorners(gray, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);

		/*
		 * If desired number of corner are detected,
		 * we refine the pixel coordinates and display
		 * them on the images of checker board
		*/
		// 如果检测到所需数量的角点,我们将细化像素坐标并将其显示在棋盘图像上
		if (success)
		{
			// 如果是OpenCV4以下版本,第一个参数为CV_TERMCRIT_EPS | CV_TERMCRIT_ITER
			cv::TermCriteria criteria(TermCriteria::EPS | TermCriteria::Type::MAX_ITER, 30, 0.001);

			// refining pixel coordinates for given 2d points.
			// 为给定的二维点细化像素坐标
			cv::cornerSubPix(gray, corner_pts, cv::Size(11, 11), cv::Size(-1, -1), criteria);

			// Displaying the detected corner points on the checker board
			// 在棋盘上显示检测到的角点
			cv::drawChessboardCorners(frame, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, success);

			objpoints.push_back(objp);
			imgpoints.push_back(corner_pts);
		}

		cv::imshow("Image", frame);
		cv::waitKey(0);
	}

	cv::destroyAllWindows();

	cv::Mat cameraMatrix, distCoeffs, R, T;

	/*
	 * Performing camera calibration by
	 * passing the value of known 3D points (objpoints)
	 * and corresponding pixel coordinates of the
	 * detected corners (imgpoints)
	*/
	// 通过传递已知3D点(objpoints)的值和检测到的角点(imgpoints)的相应像素坐标来执行相机校准
	cv::calibrateCamera(objpoints, imgpoints, cv::Size(gray.rows, gray.cols), cameraMatrix, distCoeffs, R, T);

	// 内参矩阵
	std::cout << "cameraMatrix : " << std::endl;
	std::cout << cameraMatrix << std::endl;
	// 透镜畸变系数
	std::cout << "distCoeffs : " << std::endl;
	std::cout << distCoeffs << std::endl;
	// rvecs
	std::cout << "Rotation vector : " << std::endl;
	std::cout << R << std::endl;
	// tvecs
	std::cout << "Translation vector : " << std::endl;
	std::cout << T << std::endl;

	return 0;
}

3,运行效果

在这里插入图片描述
在这里插入图片描述
求解得到内参矩阵透镜畸变系数旋转和平移向量

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hugging Face功能介绍,及在线体验文生图模型Flux

Hugging Face简介 对于非机器学习或深度学习领域的人士来说&#xff0c;Hugging Face这个名字可能并不耳熟。然而&#xff0c;随着近年来大模型的迅速崛起&#xff0c;相信大家或多或少都有所接触。如果你对这一领域感兴趣&#xff0c;并在GitHub上查阅过一些开源资料&#xf…

华为ENSP:STP和链路聚合的管理与配置

这里将不再过度阐述STP和链路聚合的理论知识&#xff0c;不清楚的同学可以去观看Cisco文章中的理论知识 理论知识https://blog.csdn.net/2301_76341691/article/details/145166547?fromshareblogdetail&sharetypeblogdetail&sharerId145166547&sharereferPC&…

Asp.Net Core 8.0 使用 Serilog 按日志级别写入日志文件的两种方式

1、所需的Nuget包 本文项目的版本是.NET 8.0&#xff0c;如果使用其它版本安装适配版本即可。 Serilog.AspNetCore(8.0.2) Serilog.Sinks.File(5.0.0) Serilog.Expressions(5.0.0) 2、两种配置方式 2.1 代码形式&#xff08;Program.cs&#xff09; 在Program.cs文件中&am…

基于ESP32+VUE+JAVA+Ngnix的一个小型固件编译系统

一、前提 开发ESP32固件时&#xff0c;使用本地环境输出固件时&#xff0c;存在多个开发多种开发平台的问题。会导致最终输出的固件不统一。更可能因为本地的开发环境差异导致固件无法追溯。 基于上述原因&#xff0c;开发了一个小型的固件编译系统。将该系统部署在一台ubutn…

自然语言处理——自注意力机制

一、文字表示方法 在自然语言处理中&#xff0c;如何用数据表示文字是基础问题。独热编码&#xff08;One-hot Encoding &#xff09;是一种简单的方法&#xff0c;例如对于 “我”“你”“他”“猫”“狗” 等字&#xff0c;会将其编码为如 “我 [1 0 0 0 0 ……]”“你 [0 …

[JavaScript] 运算符详解

文章目录 算术运算符&#xff08;Arithmetic Operators&#xff09;注意事项&#xff1a; 比较运算符&#xff08;Comparison Operators&#xff09;注意事项&#xff1a; 逻辑运算符&#xff08;Logical Operators&#xff09;短路运算&#xff1a;逻辑运算符的返回值&#xf…

《汽车维护与修理》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答&#xff1a; 问&#xff1a;《汽车维护与修理》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《汽车维护与修理》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;中国汽车维修行业协会 …

VUE学习笔记(入门)17__小黑记事本综合案例

目录结构 记得引入css样式 css代码 /* 清除浏览器的默认内外间距 */ html, body {margin: 0;padding: 0; } body {background: #fff; } button {margin: 0;padding: 0;border: 0;background: none;font-size: 100%;vertical-align: baseline;font-family: inherit;font-weig…

React的应用级框架推荐——Next、Modern、Blitz等,快速搭建React项目

在 React 企业级应用开发中&#xff0c;Next.js、Modern.js 和 Blitz 是三个常见的框架&#xff0c;它们提供了不同的特性和功能&#xff0c;旨在简化开发流程并提高应用的性能和扩展性。以下是它们的详解与比较&#xff1a; Next、Modern、Blitz 1. Next.js Next.js 是由 Ve…

【React】插槽渲染机制

目录 通过 children 属性结合条件渲染通过 children 和 slot 属性实现具名插槽通过 props 实现具名插槽 在 React 中&#xff0c;并没有直接类似于 Vue 中的“插槽”机制&#xff08;slot&#xff09;。但是&#xff0c;React 可以通过 props和 children 来实现类似插槽的功能…

常见IQ测试题:把下面的六个图形分为两类,使每一类图形都有各自的共同特征或规律,分类正确的一项是( )。​​​​​​​

[单选题] 把下面的六个图形分为两类&#xff0c;使每一类图形都有各自的共同特征或规律&#xff0c;分类正确的一项是&#xff08; &#xff09;。 A A ①②③&#xff0c;④⑤⑥ B B ①⑤⑥&#xff0c;②③④ C C ①②④&#xff0c;③⑤⑥ &#xff08;正确答案&#xff0…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(七)

文章目录 一、题库管理模块实现1、新增题目功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、题目列表功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询题目列表接口实现2.3.2 后端编辑试题接口实现2.4 效果展示二、代码下载一、题库管…

opentelemetry-collector docker安装

一、编写配置 nano /root/otelcol-config.yamlreceivers:otlp:protocols:grpc:endpoint: 0.0.0.0:4317http:endpoint: 0.0.0.0:4318 exporters:debug:verbosity: detailed service:pipelines:traces:receivers: [otlp]exporters: [debug]metrics:receivers: [otlp]exporters: …

xiao esp32 S3播放SD卡wav音频

本文旨在使用xiao esp32 S3 播放SD卡上的音频文件 1 硬件准备 SD卡 2 代码实现 2.1 依赖库 ESP32-audioI2S-master 2.2 代码 #include "Arduino.h" #include "Audio.h" #include "SD.h"// Digital I/O used #define I2S_DOUT 6 #defi…

要获取本地的公网 IP 地址(curl ifconfig.me)

文章目录 通过命令行查询&#xff08;适用于 Linux/Mac/Windows&#xff09;Linux/MacWindows 注意事项 要获取本地的公网 IP 地址&#xff0c;可以通过以下简单的方法&#xff1a; 通过命令行查询&#xff08;适用于 Linux/Mac/Windows&#xff09; Linux/Mac 打开终端。输入…

金融项目实战 07|Python实现接口自动化——连接数据库和数据清洗、测试报告、持续集成

目录 一、投资模块&#xff08;投资接口投资业务&#xff09; 二、连接数据库封装 和 清洗数据 1、连接数据库 2、数据清洗 4、调用 三、批量执行测试用例 并 生成测试报告 四、持续集成 1、代码上传gitee 2、Jenkin持续集成 一、投资模块&#xff08;投资接口投资业务…

Zemax 中的二向色分束器和荧光波偏移

二向色分光镜是一种专用光学元件&#xff0c;用于根据光的波长将一束光分成两束独立的光。“二向色”是指该元件根据光的波长选择性地透射或反射光的性质。 在大多数情况下&#xff0c;二向色分束器由一种对不同波长的光具有不同折射率的材料制成。分束器的表面涂有一层薄膜&a…

一文大白话讲清楚webpack基本使用——2——css相关loader的配置和使用

一文大白话讲清楚webpack基本使用——2——css相关loader的配置和使用 1. 建议按文章顺序从头看是看 第一篇&#xff1a;一文大白话讲清楚啥是个webpack第二篇&#xff1a;一文大白话讲清楚webpack基本使用——1——完成webpack的初步构建然后看本篇&#xff0c;Loader的配置…

微信消息群发(定时群发)-UI自动化产品(基于.Net平台+C#)

整理 | 小耕家的喵大仙 出品 | CSDN&#xff08;ID&#xff1a;lichao19897314&#xff09; 关联源码及工具下载https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/…

游戏引擎学习第81天

仓库:https://gitee.com/mrxiao_com/2d_game_2 或许我们应该尝试在地面上添加一些绘图 在这段时间的工作中&#xff0c;讨论了如何改进地面渲染的问题。虽然之前并没有专注于渲染部分&#xff0c;因为当时主要的工作重心不在这里&#xff0c;但在实现过程中&#xff0c;发现地…