一次实践:给自己的手机摄像头进行相机标定

文章目录

  • 1. 问题引入
  • 2. 准备工作
    • 2.1 标定场
    • 2.2 相机拍摄
  • 3. 基本原理
    • 3.1 成像原理
    • 3.2 畸变校正
  • 4. 标定解算
    • 4.1 代码实现
    • 4.2 详细解析
      • 4.2.1 解算实现
      • 4.2.2 提取点位
    • 4.3 解算结果
  • 5. 问题补充

1. 问题引入

不得不说,现在的计算机视觉技术已经发展到足够成熟的阶段了,还记得笔者刚工作的时候,相机标定还是个很神秘的技术,只有少数专业人员能够做,网上也找不到什么相关的资料。但是现在相机标定已经是一个非常普遍的技术了,也有不少的资料的可以参考,因此笔者突发奇想,既然那些大部头的相机可以标定,那么我们使用的手机摄像头一定也可以标定。因此,笔者就记录一下给自己手机摄像头的具体实践,算是弥补下当年没有学习到该技术的遗憾,毕竟要学习一项技术最好的办法就是亲自实践一下。

2. 准备工作

2.1 标定场

笔者见过不少正规的标定场,有的标定场很大,有很多带有标志物的竖条,还带有载动相机设备的轨道。不过目前比较流行且成本最低的办法就是使用棋盘格标定板了,也就是所谓的张正友标定法。

那么棋盘格标定板哪里来呢?打印到纸上倒是一个办法,不过可能有两个问题,一个是打印后每个格子的尺寸需要换算一下,由像素换成米制单位,这可能不是一个整数;另一个就是得找一面墙来贴上去,要贴的光滑平整还是挺难的。因此笔者没有选择这个办法,最后还是通过网上购物找的标定板。由于是给手机摄像头传感器尺寸都不是太大,标定板也不用选择太大,笔者最终选用的标定板尺寸如下所示:

图1:标定板尺寸

每个格子是5毫米,一共12X9个格子,整体尺寸还是比较小巧的,大概就一个手掌心大小。材质是玻璃基板,成本大概是50元左右。这个尺寸笔者实际体验还是有点偏小的,不过再大成本就上来了,建议有财力的同学可以适当选择大一点。

2.2 相机拍摄

接下来就是用手机摄像头对棋盘格标定板进行拍摄了。理论上进行标定解算只需要6组控制点就可以了,但是因为识别的控制点都是有误差的,需要多组点位来进行求解以提升精度。只拍摄一张照片获得的控制点也不太够,通常还需要获取多张照片上的控制点,避免局部最优的问题,提高解算过程的可靠性。如果可以的话,要使用多个视角、多个不同距离的标定板照片,同时最好保证标定板覆盖整个图像平面的不同区域,这样可以更好地估计畸变和其他参数。

在这里笔者拍摄了6张棋盘格标定板的图片,分别是前、后、左、右、上、下6个不同的位置和视角,如下所示:

图2:拍摄的棋盘格图片

可以看到拍摄的标定板区域都太靠中间了,不过也是没办法,使用的标定板尺寸确实有点偏小。拍摄的时候一旦靠的很近,手机拍照程序就会自动切换成近景拍摄。笔者不太确定切换成近景拍摄之后会不会修改相机的参数,所以都没有靠的很近。但是太远了拍照又有点糊,只能使用目前这样的效果。

现在很多手机拍照的功能会自动修正照片,比如滤镜,广角矫正等等,这些功能都尽量关了或者不使用。另外,拍照过程不要进行调焦,具体来说相机上会有0.6x、1x、2x、3x这样的参数,这代表变焦倍率,使用原始倍率(1x)进行标定即可。自动对焦功能当然也要关闭,保持镜头和焦平面的位置不变。

还有个问题是保持手机不动移动标定板来拍摄照片,还是保持标定板不动移动手机来拍摄照片?应该来说,两者原理上都可以实现,但是标定板不动,相机移动更常见一点,因为实现起来更见简单。笔者就是将棋盘格标定板通过双面胶粘在墙上实现的,也算是组成了一个成本最低的微型标定场了。

其实笔者也试过将标定板放在桌面上来拍摄,不过在室内拍摄很容易在照片上有影子,还是固定在墙上比较好一点。而且最好放采光比较好的墙面上,在白天日照充足的时候进行拍摄,以便获得最好的拍摄效果。

3. 基本原理

3.1 成像原理

相机标定虽然解算的是内参,但是其实连外参也解算了,因为相机标定解算使用的相机成像原理,这个过程中内参和外参会一起参与解算。在不考虑畸变的情况下,相机的成像原理可用下式(1)来表示:

s [ u v 1 ] = K [ R ∣ t ] [ X w Y w Z w 1 ] (1) s \begin{bmatrix} u\\ v\\ 1\\ \end{bmatrix} = K \begin{bmatrix} R|t\\ \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w\\ 1\\ \end{bmatrix} \tag{1} s uv1 =K[Rt] XwYwZw1 (1)

在这个式子中:

  • [ X w Y w Z w ] T {\begin{bmatrix}X_w & Y_w & Z_w\\\end{bmatrix}}^T [XwYwZw]T表示世界空间中的三维点,也称为物方点。
  • [ u v ] T {\begin{bmatrix}u & v\\\end{bmatrix}}^T [uv]T表示图像平面上的像素坐标,也称为像点。
  • [ R ∣ t ] \begin{bmatrix}R|t\\\end{bmatrix} [Rt]是相机的外参矩阵。具体来说,就是旋转变换和平移变换的组合, R R R就是3X3的旋转矩阵, t t t则是一个3列维向量。由于旋转变换可以用欧拉角来表示,因而也可以表示成3维向量。3个旋转量,3个平移量,这就是相机的6个外参的由来。
  • K K K是相机的内参矩阵,通常表示为下式(2):
    K = [ f x 0 c x 0 f y c y 0 0 1 ] (2) K = \begin{bmatrix} f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0 & 0 & 1\\ \end{bmatrix} \tag{2} K= fx000fy0cxcy1 (2)
    • f x f_x fx f y f_y fy分别是水平方向和垂直方向的焦距,单位为像素。
    • c x c_x cx c y c_y cy是像主点(即成像平面的光轴交点)坐标,单位为像素。
  • s s s是比例因子,这个参数是为了实现齐次坐标的转换,将其次三维坐标需要转为二维坐标。

以笔者的见识来说,上述相机成像原理其实与其他学科的一些知识有类似的地方:

  1. 计算机图形学。图形渲染中的几何变换,包含模型(model)变换、视图(view)变换和投影(projection)变换 ,合起来就是通常所说的MVP矩阵。模型变换包括旋转变换和平移变换,视图变换又是模型变换的逆变换,对应的就是式(1)的外参矩阵 [ R ∣ t ] \begin{bmatrix}R|t\\\end{bmatrix} [Rt]。不过投影矩阵有所不同,式(1)的内参矩阵 K K K是将点从相机坐标系转换为图像坐标系,图形渲染中的投影矩阵则是将点从将点从相机坐标系转换为裁剪坐标系。

  2. 摄影测量学。在摄影测量学中,这一套成像原理的公式被总结为共线方程,除了表示的形式不同,最显著的不同是内参只有三个:焦距和像主点二维坐标。这个公式个人认为并不太直观,但是比较容易进行平差计算。

如果有以上两者经验的读者,可以对照着进行理解,虽然它们看起来有点差异,但是笔者确定它们的原理都是一样的,都是基于空间的几何变换,只不过是应对于不同情况有不同的描述。

3.2 畸变校正

以上成像原理没有考虑到畸变的影响。为什么会产生畸变呢?很简单,相机镜头不是完美的平面光学系统,光线在传输时发生复杂的弯曲,这会导致图像中的直线在图像边缘发生扭曲。常见的畸变有径向畸变和切向畸变。

畸变校正看起来很玄乎,其实说穿了也非常简单,我们只需要理解一点,畸变校采用的有理函数的模型。所谓有理函数的模型,就是将校正前的位置x与校正后的位置y使用一个高阶多项式(形如 y = a x 3 + b x 2 + c x + d y=ax^3+bx^2+cx+d y=ax3+bx2+cx+d)来进行表示,没有什么物理上的原理,就是纯采用数学方式进行拟合,最后得到了每个高阶项的系数(a,b,c,d)。

鉴于畸变校正会增加对标定解算的复杂度,这里就不进行进一步论述了。对于初学者来说,理解成像原理的公式(1)更为关键一点。

4. 标定解算

4.1 代码实现

使用上述介绍的基本原理就可以进行标定解算了,不过解算方法比较复杂,我们还是结合具体的实现来解释,代码如下所示,这里主要使用了OpenCV库:

#include <filesystem>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>#ifdef _WIN32
#include <Windows.h>
#endifusing namespace cv;
using namespace std;int main() {
#ifdef _WIN32SetConsoleOutputCP(65001);
#endifvector<std::filesystem::path> imgPaths = {"C:/Work/CalibrateCamera/Data/front.jpg","C:/Work/CalibrateCamera/Data/left.jpg","C:/Work/CalibrateCamera/Data/right.jpg","C:/Work/CalibrateCamera/Data/up.jpg","C:/Work/CalibrateCamera/Data/down.jpg","C:/Work/CalibrateCamera/Data/back.jpg"};size_t imageNum = imgPaths.size();// 定义棋盘格尺寸 (内角点数)int boardWidth = 11;  // 列数int boardHeight = 8;  // 行数cv::Size boardSize(boardWidth, boardHeight);double cellSize = 0.005;Size imageSize(3072, 4096);  // 图像尺寸// 准备标定所需的物方点和像方点vector<vector<Point3f>> objectPoints(imageNum);  // 多张图像的3D物方点vector<vector<Point2f>> imagePoints(imageNum);   // 多张图像的2D像方点for (size_t ii = 0; ii < imageNum; ++ii) {// 加载棋盘格图像cv::Mat image = cv::imread(imgPaths[ii].string().c_str());if (image.empty()) {std::cerr << "Error: Could not load image!" << std::endl;return -1;}// 存储角点坐标std::vector<cv::Point2f> corners;// 转换图像为灰度cv::Mat grayImage;cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);// 寻找棋盘格角点// cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGEbool found = cv::findChessboardCorners(grayImage, boardSize, corners,cv::CALIB_CB_FAST_CHECK);// 如果找到角点,进行进一步处理if (found) {std::cout << "Chessboard corners found!" << std::endl;// 增加角点的精度cv::cornerSubPix(grayImage, corners, cv::Size(11, 11), cv::Size(-1, -1),cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER,30, 0.001));// 绘制角点std::string cornerImgPath = imgPaths[ii].parent_path().generic_string() +"/corner/" + imgPaths[ii].stem().string() +"_corner" + imgPaths[ii].extension().string();cv::drawChessboardCorners(image, boardSize, corners, found);cv::imwrite(cornerImgPath.c_str(), image);cout << corners.size() << endl;imagePoints[ii].resize(corners.size());for (size_t ci = 0; ci < corners.size(); ++ci) {imagePoints[ii][ci] = corners[ci];}objectPoints[ii].resize(corners.size());for (int hi = 0; hi < boardHeight; ++hi) {for (int wi = 0; wi < boardWidth; ++wi) {int ci = hi * boardWidth + wi;objectPoints[ii][ci].x = cellSize * wi;objectPoints[ii][ci].y = cellSize * hi;objectPoints[ii][ci].z = 0;}}} else {std::cerr << "Chessboard corners not found!" << std::endl;}}// 内参矩阵和畸变系数Mat cameraMatrix = Mat::eye(3, 3, CV_64F);  // 初始化为单位矩阵Mat distCoeffs = Mat::zeros(8, 1, CV_64F);  // 初始化为零// 外参的旋转和位移向量vector<Mat> rvecs, tvecs;// 执行标定double reprojectionError =calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix,distCoeffs, rvecs, tvecs);cout << u8"重投影误差:" << reprojectionError << endl;cout << u8"内参矩阵:" << cameraMatrix << endl;cout << u8"畸变系数:" << distCoeffs << endl;return 0;
}

4.2 详细解析

4.2.1 解算实现

代码实现的步骤很简单,就是通过函数findChessboardCorners提取棋盘格图片的角点,将其传入calibrateCamera函数中,就得到了最终的解算成果,也就是内参矩阵。这其中的关键就在于calibrateCamera这个函数,我们可以看一下它的函数原型:

CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints, Size imageSize,InputOutputArray cameraMatrix, InputOutputArray distCoeffs,OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,int flags = 0, TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );

其参数详解如下:

  • objectPoints:3D空间中的物方点坐标集合,也就是公式(1)中的 [ X w Y w Z w ] T {\begin{bmatrix}X_w & Y_w & Z_w\\\end{bmatrix}}^T [XwYwZw]T。由于是多张图片的多组点的集合,所以它的类型实际是std::vector<std::vector<cv::Point3f>>
  • imagePoints:图像中的像素坐标集合,对应公式(1)中的 [ u v ] T {\begin{bmatrix}u & v\\\end{bmatrix}}^T [uv]T,类型同样应该也是双重数组std::vector<std::vector<cv::Point2f>>
  • imageSize:输入图像的尺寸(宽度和高度),单位为像素。
  • cameraMatrix:输出的摄像机内参矩阵,也就是公式(1)中的 K K K,为3X3矩阵。
  • distCoeffs:输出的摄像机的畸变系数,通常为1X5或1X8的向量,包含径向和切向畸变系数。
  • rvecs:输出的旋转向量集合,可以转换成公式(1)中的 R R R。每个旋转向量对应一个图像,所以类型是std::vector<cv::Mat>
  • tvecs:输出的平移向量集合,对应公式(1)中的 t t t。每个平移向量对应一个图像,类型也是std::vector<cv::Mat>
  • 返回值:标定的重投影误差,用于衡量标定结果的精确度。误差越小,标定结果越准确。

通过对calibrateCamera函数的解析,相信读者就很容易明白为什么笔者要先讲公式(1)的成像原理。这个解算参数的输入输出都是根据公式(1)来的,不过另一个问题来了,输入的物方点和像方点是怎么来的呢?

4.2.2 提取点位

答案很简单,就是棋盘格上的角点。棋盘格由黑白相间的格子组成,所以它的角点是很容易提取的;另外一方面,棋盘格也是规整的,只要每个格子的尺寸都是一样,就很容易知道物方坐标。理论上,只要对图像提取角点,然后剔除掉非棋盘的角点就可以作为相机标定的像点了。不过,OpenCV提供了更进一步的接口findChessboardCorners,直接输入棋盘格的内角点个数,就可以自动检测出像点。如下图所示在一张图片上笔者提取的像点:

图3: 提取棋盘格的角点作为像点

正如上图所示,findChessboardCorners提取的是内角点,例如12X9的棋盘格,提取的内角点是11X8个,并且结果是按照从左到右,从上往下进行排序的。为什么要这么排序呢?因为很容易帮我们算出物方点。在相机标定这个应用中,相机的外参是不重要的,因此我们可以就以棋盘格标定板的左上角作为世界坐标系的原点,第1个点的坐标是(0,0,0),第2个点的坐标是(0.005,0,0),第3个点坐标是(0.010,0,0)…第12个点坐标是(0,0.005,0),第13个点坐标是(0.005,0.005,0)…就这么依次类推得到所有角点对应的世界空间坐标系坐标。

另外一点要提醒读者的是,findChessboardCorners这里我配置的是参数是cv::CALIB_CB_FAST_CHECK,是一种快速算法,cv::CALIB_CB_ADAPTIVE_THRESH和cv::CALIB_CB_NORMALIZE_IMAGE会对图像作预处理,能够增加提取棋盘格角点的稳健性。但是我实际使用发现程序卡住了,不知道是效率很低还是OpenCV的问题,就没有使用这两个选项。

4.3 解算结果

最终,笔者的结算结果如下所示:

重投影误差:0.166339
内参矩阵:[2885.695162446343, 0, 1535.720945173723;0, 2885.371543143629, 2053.122840953737;0, 0, 1]
畸变系数:[0.181362004467736;-3.970106972775221;0.0005157812878172198;0.0004644406171824815;23.559069196518]

解算的结果重投影误差是0.166339,表示每个物体点在重新投影到图像上时与实际检测到的角点位置的误差为0.166339像素。通常来说,这样的误差已经算是非常小,表明标定结果较为精确。

不过笔者还考虑一个问题,误差为0.166339像素,那么具体是多少米呢?以前做测绘软件的时候,平差的结果也是以像素为单位,总会有客户对我发起灵魂拷问:那具体是多少米呢?这次笔者也关注了一下这个问题,个人认为在相机标定这样的应用场景,确实无法直接使用物理单位表示精度,因为这个算法的结果就在于重投影到图像上的像素差为量度,这一点与相机外参的定向的误差量度有所不同。

对照内参矩阵,可得解算的焦距是 f x = 2885.695 f_x=2885.695 fx=2885.695 f y = 2885.372 f_y=2885.372 fy=2885.372,单位也是像素。那么这个焦距换算成物理单位是多少米呢?根据笔者查找的资料显示,焦距在像素和毫米之间的转换公式如下所示:
焦距(毫米) = 焦距(像素) × 传感器尺寸(毫米) 图像分辨率(像素) 焦距(毫米)= \frac{焦距(像素)×传感器尺寸(毫米)}{图像分辨率(像素)} 焦距(毫米)=图像分辨率(像素)焦距(像素)×传感器尺寸(毫米)

也就是说与相机传感器尺寸有关,不过关于传感器尺寸的描述有点蛋疼,比如网上显示我手机摄像头的传感器是1/1.49英寸,这通常表示传感器的对角线长度。可以根据对角线长度加上宽高比(例如4:3还是16:9)算出相机传感器的物理尺寸,进而知道具体物理单位的焦距值大小。不过传感器的对角线长度标称值和真实物理尺寸之间,会因为行业惯例和历史标准有所差异,所以算出来的也不一定正确,最好还是联系官方来确定。不过,标定出像素单位的焦距已经足够后续满足后续的使用场景了,笔者这里也就是寻根究底一下。

5. 问题补充

最后,补充一些没搞定或者暂时没理解的问题吧:

  1. 关于成像原理列出的公式(1)的内参矩阵部分,其实笔者也没弄清楚为什么将焦距分成X方向上的 f x f_x fx和y方向上的 f y f_y fy,有些资料上的内参矩阵并不是这么列的,《摄影测量学》教材上列出的共线方程更是只有一个焦距值 f f f
  2. 笔者记得似乎有个操作“相机重标定”,可以将使用固定焦距 f f f,调整像主点到图像中心,以及消除畸变的重投影,可以简化后续的空间计算,使得计算更为便捷。时间关系就留待后续研究了。
  3. 本文笔者并没有具体解释解算的算法原理,因为这不是一两句话就能说清楚的,在测绘学中有个过程有个专门的名词叫做平差;或者叫做状态估计、最大似然估计、非线性优化等等,至少我们需要知道最小二乘法原理才能继续论述这个,就留待后续的文章中进行讨论吧。

列出一些文章以供参考:

  1. 相机标定:从入门到实战
  2. 相机系列——相机标定简述
  3. 相机标定之张正友标定法数学原理详解
  4. 计算机视觉----相机标定

本文源代码和数据地址

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

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

相关文章

c++day08

思维导图 栈 #include <iostream>using namespace std;template <typename T> class Stack { private:static const size_t MAX 100; // 定义固定容量T data[MAX]; // 存储栈元素的数组size_t len; // 当前栈的大小public:…

浅谈电气火灾监控系统在变电所的应用

摘要&#xff1a;阐述电气火灾监控系统在变电所的应用&#xff0c;电气火灾监控系统的管理措施&#xff0c;包括运行标准、运行模式、运行原则、警报阈值、监控显示。安科瑞叶西平1870*6160015 关键词:监控系统&#xff1b;警报阀值&#xff1b;运行模式&#xff1b;医院&…

高效免费!PDF秒变Word,在线免费转换工具推荐!!!

#创作灵感# 工作中&#xff0c;总是需要将pdf文件转换成word文件&#xff0c;便于后期编辑、处理、使用&#xff0c;但是又没有wps会员&#xff0c;虽然去淘宝买&#xff0c;一天也就8毛钱左右&#xff0c;但是转换文件的工作几乎每天都需要做&#xff0c;长此以往&#xff0c;…

7.字符串 Strings

作业系统链接 字符串文字可以使用单引号、双引号或三引号来定义&#xff0c;其中三引号特别适用于多行字符串。转义序列如\n&#xff08;换行&#xff09;和\t&#xff08;制表符&#xff09;在字符串中起到特殊作用。字符串方法如replace()、strip()、lower()和upper()提供了丰…

外国名人面孔识别系统源码分享

外国名人面孔识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

艺术作品风格识别系统源码分享

艺术作品风格识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

Java数据结构--List介绍

前言&#xff1a; 数据结构可以说是一门思想&#xff0c;当我们在对数据处理、储存的时候需要用到。 前面我用C语言写过数据结构的相关内容&#xff0c;在Java阶段的数据结构思想是一样的&#xff0c;就是有些地方实现的方式是有区别的。 因此在Java阶段前期的数据结构&#xf…

Python的包管理工具pip安装

Python的包管理工具pip安装 一、安装步骤1.检查 pip是否已安装2.安装 pip方法一&#xff1a;通过 ​ensurepip​ 模块安装(推荐)方法二&#xff1a;通过 ​get-pip.py​ 脚本安装&#xff08;经常应为网络域名问题连接不上&#xff09; 3.验证pip安装4.创建别名5.更新pip 二、常…

找不到msvcr100.dll怎么解决?总结6个有效的解决方法

在使用计算机的过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcr100.dll丢失”。这个问题可能会让我们感到困惑和无助&#xff0c;但是不用担心&#xff0c;本文将为大家介绍六种实用的解决方法&#xff0c;帮助你轻松解决这个问题。 一&#xff…

Lenovo SR850服务器亮黄灯维修和升级CPU扩展模块

佛山市三水区某高校1台Lenovo Thinksystem SR850服务器黄灯故障到现场检修 和 升级3号和4号CPU。加强服务器的计算性能&#xff1b; 故障情况是该学校it管理员这一天看到这台SR850服务器前面板亮了一个黄灯&#xff0c;但是目前系统运行正常&#xff0c;出于安全考虑&#xff0…

【如何学习操作系统】——学会学习的艺术

&#x1f41f;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢编程&#x1fab4; &#x1f421;&#x1f419;个人主页&#x1f947;&#xff1a;Aic山鱼 &#x1f420;WeChat&#xff1a;z7010cyy &#x1f988;系列专栏&#xff1a;&#x1f3de;️ 前端-JS基础专栏✨前…

[数据结构] 二叉树题目(一)

目录 一.翻转二叉树 1.1 题目 1.2 示例 1.3 分析 1.4 解决 ​编辑 二. 相同的树 2.1 题目 2.2 示例 2.3 分析 2.4 解决 三. 对称二叉树 3.1 题目 3.2 示例 3.3 分析 3.4 解决 一.翻转二叉树. - 力扣&#xff08;LeetCode&#xff09; 1.1 题目 1.2 示例 1.3 分…

python的 __name__和__doc__属性

__name__属性 __name__属性 用于判断当前模块是不是程序入口&#xff0c;如果当前程序正在使用&#xff0c;__name__的值为__main__。 在编写程序时&#xff0c;通常需要给每个模块添加条件语句&#xff0c;用于单独测试该模块的功能。 每个模块都有一个名称&#xff0c;当一…

大屏走马灯与echarts图表柱状图饼图开发小结

一、使用ant-design-vue的走马灯(a-carousel)注意事项 <!-- 左边的轮播图片 --><a-carousel :after-change"handleCarouselChange" autoplay class"carousel" :transition"transitionName"><div v-for"(item, index) in it…

每日论文4——09TCAS1分数锁相环的动态电流匹配电荷泵和门偏置线性化技术

《Dynamic Current-Matching Charge Pump and Gated-Offset Linearization Technique for Delta-Sigma Fractional- PLLs》09TCAS1 本文CP的结构比较简洁&#xff0c;没有使用OPA&#xff0c;所以相比起来减小了功耗和面积&#xff0c;但是目测起来匹配效果是不如用OPA的CP的。…

MySQL—索引机制详解

索引介绍 基本介绍 MySQL 官方定义索引为一种帮助数据库高效获取数据的结构&#xff0c;其本质是排好序的快速查找数据结构。数据库除了存储表中的数据&#xff0c;还维护一些额外的满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式指向实际的数据&#xff0c;…

2024年在线音频剪辑工具推荐。这4个你都知道哪些?

音频剪辑在很多的场景中都会使用到&#xff0c;比如个人制作音乐混剪&#xff0c;制作视频配乐&#xff0c;剪辑会议、讲座等记录&#xff0c;播客制作等等。对于一些媒体创作者是一种必备的技能。但其实音频剪辑并不难&#xff0c;如果掌握了一些剪辑工具的话。所以&#xff0…

计算机毕业设计 基于Python的热门微博数据可视化分析系统的设计与实现 Python+Django+Vue 可视化大屏 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

powerbi计算销售额累计同比增长率——dax

目录 效果展示&#xff1a; 一、建立日期表 二、建立度量值 1.销售收入 2.本年累计销售额 3.去年累计销售额 4.累计同比增长率 三、矩阵表制作 效果展示&#xff1a; 数据包含2017-2019年的销售收入数据 一、建立日期表 日期表建立原因及步骤见上一篇文章https://blog…

Idea中的导出分包层次结构图(项目结构树)

导出分包层次结构图 在Terminal中, 输入命令&#xff1a;Get-ChildItem “项目地址” | Tree | Out-File -FilePath “输出地址” Get-ChildItem D:\work\workspace\test | Tree | Out-File -FilePath D:\test.txt