相机内参标定及去畸变矫正代码示例

2023-11-09 02:59

本文主要是介绍相机内参标定及去畸变矫正代码示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、相机标定

由于廉价的相机会给图像带来极大的失真,主要是径向变形和切向变形,导致直线变的弯曲,离图像中心越远的像素,失真越严重。

为了得到正确的图像,首先需要纠正这些失真。为了找到所有这些参数,我们要做的是提供一些定义良好的图案的示例图像(棋盘格)。我们在其中找到一些特定点(棋盘上的格子的角点)。我们知道它在现实空间中的坐标,也知道它在图像中的坐标。利用这些数据,可以在后台通过解决一些数学问题,获得失真系数。

标定板

标定本质上是借助一个已知确定的空间关系(标定板),通过分析拍照的图片像素,逆向推出相机固有且真实的参数(内参)。

原则上,任何有合适表面特征的物体都可以作为标定物体,包括三维的物体,二维的图案。但是由于平面的更容易处理,并且相对三维物体来说,制作精准的二维标定位要简单的多。因此,我们常使用平面的标定板作为标定物,并通过多个不同角度和距离的图像提高精准度。

标定板一般使用平面上的规则图案,主要由以下几种类型:

1.棋盘格chessboard
最常用的标定图案:棋盘格。常用的规格有

角点 6 x 9 , 6 x 7 , 8 x 11

方格 20mm , 30mm

1571122320047

2.圆网格
圆网格分为对称圆网格和非对称圆网格,一般来说,无论是最终结果的质量,还是多次运行的结果之间的稳定性,非对称圆网格常常会优于棋盘格,进而其逐渐成为相机标定标准工具包的而一部分。

  • 圆网格Circles
    src

  • 非对称圆网格Assymetric Circles
    由指定半径,指定间隔的多个圆组成的图案,在一些情况下,圆形网格会得到比棋盘格更好的标定效果。
    1571122339580
    注意,此时的宽高读取有所不同,如下为11列,4行,即宽11,高4。

acircle
非对称圆网格也使用规则的圆阵列(左上),圆的中心类似于棋盘的角进行校准。 从透视图(右下角)看时,圆的变形是有规律且可预测的。

3.随机图案
由高度纹路化的随机图案组成的标定图案

在这里插入图片描述

4.ArUco
由Augmented Reality 增强现实二维码组成的标定图案,由于每个方格都可以单独识别其ArUco图案,则即使大部分被遮挡,仍会有足够的标记点用来正确的标定。

1571122430610

5.ChArUco
ChArUco = 棋盘 + ArUco

内部嵌有ArUco的棋盘格,原本每块白色区域由ArUco图案填充,同样允许大部分棋盘被遮挡,可以使角点的检测达到更高的精度,如下:

1571122472570

利用ArUco实现的增强现实

内参标定(棋盘格)

在各个方向、位置面对摄像机,使相机拍照至少10张图片,在这些图片中查找角点,得到每张图所有的角点的坐标,结合其实际的宽高和个字大小,进行相机标定计算。
在这里插入图片描述
仅考虑棋盘的一个图像的情况下。相机校准所需的重要输入数据是一组3D现实世界点及其对应的2D图像点。可以从图像中轻松找到2D图像点。 (这些图像点是棋盘上两个黑色正方形相互接触的位置)

问题是,如何获取现实世界中的3D点呢? 由于这些图像是从静态相机拍摄的,而棋盘放置在不同的位置和方向。我们需要知道(X,Y,Z)的值。但是换个角度思考,假如我们定义棋盘在X、Y平面上保持静止(Z始终为0),让照相机发生移动了。这种思考仅有助于我们找到X,Y值。现在对于X,Y值,我们可以简单地将点记为(0,0),(1,0),(2,0),…,这表示实际点的位置。在这种情况下,我们得到的结果只是棋盘正方形的大小比例。但是,如果我们知道正方形尺寸(例如20 mm),则可以将值记为(0,0),(20,0),(40,0),…,且得到的结果以mm为单位。(在这种情况下,我们不知道正方形尺寸,因为我们没有拍摄图像,因此我们以正方形尺寸表示)。
计算中,我们将3D对象点取名object points,2D图像点取名image points

1.加载图片

#include <iostream>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;// 保存多张图片对象点列表
vector<vector<Point3f>> objectPoints;
// 保存多张图片的角点列表
vector<vector<Point2f>> cornerPoints;int main(){// 图片像素尺寸Size imgSize;// 图片路径cv::String src_path = "./assets/camerargb_*.jpg";std::vector<String> filenames;cv::glob(src_path, filenames);//获取路径下所有文件名cout << "filenames.size:" << filenames.size() << endl;for (auto& imgName : filenames) {// 读取图片Mat img = imread(imgName, IMREAD_COLOR);// 获取图片像素尺寸imgSize = img.size();std::cout << "name: " << imgName<< " imgSize: " << imgSize << std::endl;//...}return 0;
}

2.查找角点
由于OpenCV提供的函数参数为灰度图,所以要提前将彩图转为灰度图

  • 首先定义交点查找函数
// 棋盘格的尺寸(宽6,高9)
const Size patternSize(6, 9);
// 黑方格的大小 20mm
const int squareSize = 20;
/*** 在指定图片中查找角点,并将结果输出到corners中* @param img     待检测图片* @param corners 检测到的焦点列表* @return 是否检测到角点(两个黑方格的交点)*/
bool findCorners(Mat &img, vector<Point2f> &corners) {Mat gray;// 将图片转成灰度图cvtColor(img, gray, COLOR_RGB2GRAY);// 查找当前图片所有的角点bool patternWasFound = findChessboardCorners(gray, patternSize, corners);if (patternWasFound) { // 找到角点// 提高角点的精确度// 原理:https://docs.opencv.org/4.1.0/dd/d1a/group__imgproc__feature.html#ga354e0d7c86d0d9da75de9b9701a9a87ecornerSubPix(gray, corners, Size(11, 11), Size(-1, -1),TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1));}// 将所有的焦点在原图中绘制出来drawChessboardCorners(img, patternSize, corners, patternWasFound);// 绘制完角点之后,显示原图imshow("src", img);if (!patternWasFound){cout << "角点检测失败!" << endl;}return patternWasFound;
}
  • 查找角点,创建其对象点
// 保存多张图片对象点列表
vector<vector<Point3f>> objectPoints;
// 保存多张图片的角点列表
vector<vector<Point2f>> cornerPoints;void calcObjectPoints(vector<Point3f> &objPoint) {// 计算uv空间中角点对应的相机坐标系坐标值,设Z为0for (int i = 0; i < patternSize.height; ++i)for (int j = 0; j < patternSize.width; ++j)objPoint.emplace_back(j * squareSize, i * squareSize, 0);
}// 图片像素尺寸
Size imgSize;int main(){// 图片路径cv::String src_path = "./assets/camerargb_*.jpg";std::vector<String> filenames;cv::glob(src_path, filenames);//获取路径下所有文件名cout << "filenames.size:" << filenames.size() << endl;for (auto& imgName : filenames) {// 读取图片Mat img = imread(imgName, IMREAD_COLOR);// 获取图片像素尺寸imgSize = img.size();std::cout << "name: " << imgName<< " imgSize: " << imgSize << std::endl;// 声明每张图片的角点vector<Point2f> corners;bool found = findCorners(img, corners);if (found) {vector<Point3f> objPoints;calcObjectPoints(objPoints);// 找到角点,证明这张图是有效的objectPoints.push_back(objPoints);cornerPoints.push_back(corners);}}return 0;
}

3.执行相机标定

Mat cameraMatrix; // 相机参数矩阵
Mat disCoffes; // 失真系数 distortion coefficients
Mat rvecs; // 图片旋转向量
Mat tvecs; // 图片平移向量calibrateCamera(objectPoints, cornerPoints, imgSize, cameraMatrix, disCoffes, rvecs, tvecs);cout << "标定矩阵:" << cameraMatrix << endl;
cout << "畸变矩阵:" << disCoffes << endl;
// save2xml(cameraMatrix, distCoffes);waitKey();

4.保存标定结果

void save2xml(const Mat &cameraMatrix, const Mat &disCoffes) {// 获取当前时间time_t tm;time(&tm);struct tm *t2 = localtime(&tm);char buf[1024];strftime(buf, sizeof(buf), "%c", t2);// 写出数据String inCailFilePath = "./inCailFilePath.xml";FileStorage inCailFs(inCailFilePath, FileStorage::WRITE);inCailFs << "calibration_time" << buf;inCailFs << "cameraMatrix" << cameraMatrix;inCailFs << "distCoffes" << disCoffes;inCailFs.release();
}

标定结果示例

  • xml版本
<?xml version="1.0"?>
<opencv_storage>
<calibration_time>"2019年10月15日 星期二 17时25分25秒"</calibration_time>
<cameraMatrix type_id="opencv-matrix"><rows>3</rows><cols>3</cols><dt>d</dt><data>1.0743566574494685e+03 0. 9.5548426688037193e+02 0.1.0758663741535415e+03 5.4946692912295123e+02 0. 0. 1.</data></cameraMatrix>
<disCoffes type_id="opencv-matrix"><rows>1</rows><cols>5</cols><dt>d</dt><data>7.2163806117565163e-02 -1.1302001810933321e-011.4988711762593146e-03 -3.0609919169149657e-034.2575786627597728e-02</data></disCoffes>
</opencv_storage>
  • yml版本
%YAML:1.0
---
calibration_time: "2019年10月20日 星期日 23时25分26秒"
frame_count: 15
image_width: 640
image_height: 480
board_width: 6
board_height: 9
square_size: 1.9999999552965164e-02
rms: 1.2085572726037488e-01
cameraMatrix: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 6.0447700370270286e+02, 0., 3.3181544620019122e+02, 0.,6.0479737087406238e+02, 2.9128742694561294e+02, 0., 0., 1. ]
distCoeffs: !!opencv-matrixrows: 5cols: 1dt: ddata: [ 1.9175212705577635e-01, -7.0334052488796239e-01,6.9245817187035201e-04, -1.5403756810363311e-03,4.2337569654123880e-01 ]

内参标定(圆网格)

将“棋盘格”标定中,查找角点的步骤更改为findCirclesGrid即可

bool found = findCirclesGrid(image, board_sz, corners);

摄像头实时标定

#include <iostream>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;const int ACTION_ESC = 27;
const int ACTION_SPACE = 32;static void print_help() {printf("通过棋盘格标定相机\n");printf("参数:CalibrationChessboard <board_width> n<board_height> <square_size> [number_of_boards] ""[--delay=<delay>] [-s=<scale_factor>]\n");printf("例子:CalibrationACircle2 6 9 30 500 1.0\n");
}/*** 执行标定并保存标定结果* @param square_size   格子尺寸* @param board_sz      格子尺寸Size* @param image_size    图片尺寸Size* @param image_points  图片角点集合* @param cameraMatrix  相机参数* @param distCoeffs    畸变参数*/
void
runAndSave(float square_size, const Size board_sz, const Size image_size,const vector<vector<Point2f>> &image_points,Mat &cameraMatrix, Mat &distCoeffs) {vector<vector<Point3f>> object_points;vector<Point3f> objPoints;for (int i = 0; i < board_sz.height; ++i) {for (int j = 0; j < board_sz.width; ++j) {// 注意非对称圆网格的对象坐标计算方式objPoints.push_back(Point3f(float((2*j + i%2) * square_size),float(i * square_size), 0));}}object_points.resize(image_points.size(), objPoints);vector<Mat> rvecs, tvecs;// 执行标定double rms = calibrateCamera(object_points, image_points, image_size, cameraMatrix, distCoeffs, rvecs, tvecs);// 均方根值(RMS)printf("RMS error reported by calibrateCamera: %g\n", rms);// 检查标定结果误差bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);if (ok) {cout << "标定参数:" << endl;cout << cameraMatrix << endl;cout << "畸变参数:" << endl;cout << distCoeffs << endl;// 时间、图片个数、图片尺寸、标定板宽高、标定板块尺寸、RMS// 标定参数、畸变参数// 获取当前时间time_t tm;time(&tm);struct tm *t2 = localtime(&tm);char buf[1024];strftime(buf, sizeof(buf), "%c", t2);// 写出数据
//        String inCailFilePath = "./calibration_in_params.xml";String inCailFilePath = "./calibration_in_params2.yml";FileStorage inCailFs(inCailFilePath, FileStorage::WRITE);inCailFs << "calibration_time" << buf;inCailFs << "frame_count" << (int)image_points.size();inCailFs << "image_width" << image_size.width;inCailFs << "image_height" << image_size.height;inCailFs << "board_width" << board_sz.width;inCailFs << "board_height" << board_sz.height;inCailFs << "square_size" << square_size;inCailFs << "rms" << rms;inCailFs << "cameraMatrix" << cameraMatrix;inCailFs << "distCoeffs" << distCoeffs;inCailFs.release();std::cout << "标定结果已保存:"<< inCailFilePath << std::endl;}else {std::cout << "标定结果有误,请重新标定!" << std::endl;}
}int main(int argc, char **argv) {std::cout << cv::getVersionString() << std::endl;bool flipHorizontal = false;// 解析参数cv::CommandLineParser parser(argc, argv,"{@arg1||}{@arg2||}{@arg3|20|}{@arg4|15|}""{help h||}{delay d|500|}{scale s|1.0|}{f||}");if (parser.has("help")) {print_help();return 0;}int board_width = parser.get<int>(0);int board_height = parser.get<int>(1);float square_size = parser.get<float>(2);int num_boards = parser.get<int>(3);int delay = parser.get<int>("delay");auto image_sf = parser.get<float>("scale");if (board_width < 1 || board_height < 1) {printf("Command-line parameter error: both image of width and height must be specified\n");print_help();return -1;}if (parser.has("f")) {flipHorizontal = true;}std::cout << "board_width: " << board_width << std::endl;std::cout << "board_height: " << board_height << std::endl;std::cout << "square_size: " << square_size << std::endl;std::cout << "num_boards: " << num_boards << std::endl;std::cout << "delay: " << delay << std::endl;std::cout << "image_sf: " << image_sf << std::endl;cv::Size board_sz = cv::Size(board_width, board_height);cv::VideoCapture capture(0);if (!capture.isOpened()) {std::cout << "无法开启摄像头!" << std::endl;return -1;}vector<vector<Point2f>> image_points;vector<vector<Point3f>> object_points;cv::Size image_size;while (image_points.size() < num_boards){Mat image0, image;capture >> image0;image_size = image0.size();// 将图像复制到imageimage0.copyTo(image);// 缩放if (image_sf != 1.0) {resize(image0, image, Size(), image_sf, image_sf, cv::INTER_LINEAR);}// 水平翻转if (flipHorizontal) {flip(image, image, 1);}// 查找标定板(不对称圆网格板)vector<Point2f> corners;bool found = findCirclesGrid(image, board_sz, corners, CALIB_CB_ASYMMETRIC_GRID);// 画上去drawChessboardCorners(image, board_sz, corners, found);int action = waitKey(30) & 255;// 判断动作if (action == ACTION_SPACE) { // 用户按下了空格if (found) {// 闪屏bitwise_not(image, image);// 保存角点printf("%s: %d/%d \n", "save角点", (int)image_points.size() + 1, num_boards);image_points.push_back(corners);// 保存图片}else {printf("%s\n", "未检测到角点");}} else if (action == ACTION_ESC) { // 用户按下了ESCbreak;}cv::imshow("Calibration", image);}if (image_points.size() < num_boards) {printf("角点未达到目标个数,标定已终止!");return -1;}printf("角点收集完毕, 执行标定中... 图片尺寸 %d:%d\n", image_size.width, image_size.height);Mat cameraMatrix = Mat::eye(3, 3, CV_64F);Mat distCoeffs = Mat::zeros(8, 1, CV_64F);runAndSave(square_size, board_sz, image_size, image_points, cameraMatrix, distCoeffs);cv::destroyWindow("Calibration");return 0;
}

标定参数及优化

(1)findChessboardCorners
通常在执行cv::findChessboardCorners棋盘格角点查找时,会在最后一个参数flags设置一些参数进行角点查找优化,默认参数是CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE,以下是这些参数的意义,这些参数可以单数使用也可以组合使用:

  • CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转换为黑色和白色,而不是一个固定的阈值水平(从图像的平均亮度计算出来的阈值)。

  • CALIB_CB_NORMALIZE_IMAGE:在自适应二值化之前,对图片的gamma值进行直方图均衡化

  • CALIB_CB_FAST_CHECK:运行一个快速棋盘格角点检查,如果没有找到则尽快返回。这可以大大加快在界面中没有棋盘格时候的查找速度。

  • CALIB_CB_FILTER_QUADS:使用其他条件(例如轮廓区域,周长,正方形形状)过滤掉在轮廓检索阶段提取的假四边形。

推荐使用组合:CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK

该功能需要在木板周围留有空白(如正方形的边框,越宽越好),以使检测在各种环境中都更加可靠。否则,如果没有边框且背景较暗,则无法正确分割外部黑色正方形。

(2)cornerSubPix

// 提高角点精度
TermCriteria criteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1);
cv::cornerSubPix(gray, corners, cv::Size(5, 5), cv::Size(-1, -1), criteria);

函数参数说明如下:

  • image:输入图像

  • corners:输入角点的初始坐标以及精准化后的坐标用于输出。

  • winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为11的搜索窗口将被使用。

  • zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。

  • criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。

(3)calibrateCamera
使用示例:

cv::Mat cameraMatrix = cv::Mat::eye(3, 3, CV_64F);
cv::Mat distCoeffs = cv::Mat::zeros(8, 1, CV_64F);
vector<cv::Mat> rvecs, tvecs;
double rms = cv::calibrateCamera(objectPoints, imagePoints, imgSize, cameraMatrix, distCoeffs, rvecs, tvecs);
  • objectPoints: 对象坐标点列表

  • imagePoints:图像像素点列表

  • imageSize:图像的大小,在计算相机的内参数和畸变矩阵需要用到

  • cameraMatrix:内参矩阵。输入cv::Mat cameraMatrix即可

  • distCoeffs:畸变矩阵。输入cv::Mat distCoeffs即可

  • rvecs:旋转向量vectorcv::Mat rvecs

  • tvecs:位移向量vectorcv::Mat tvecs

  • flags为标定是所采用的算法。可如下一个或者多个参数,通过+号连接即可:

  • CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,cx,cy的估计值。否则将初始化(cx,cy)图像的中心点,使用最小二乘估算出fx,fy。如果内参矩阵和畸变矩阵已知,应使用标定模块中的solvePnP()函数计算外参数矩阵。

  • CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。

  • CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。

  • CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。

  • CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。

  • CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个参数。
    如果对calibrateCamera的详细算法感兴趣,可以阅读张正友的标定算法《A Flexible New Technique for Camera Calibration》

二、去畸变矫正

img

remap

  • 读取相机矩阵、畸变系数
// 读取相机矩阵、畸变系数
cv::FileStorage fs("./calibration_in_params.yml", FileStorage::READ);
int image_width{0}, image_height{0};
fs["image_width"] >> image_width;
fs["image_height"] >> image_height;
Size image_size = Size(image_width, image_height);Mat intrinsic_matrix, distortion_coeffs;
fs["cameraMatrix"] >> intrinsic_matrix;
fs["distCoeffs"] >> distortion_coeffs;
fs.release();
std::cout << intrinsic_matrix << std::endl;
std::cout << distortion_coeffs << std::endl;
std::cout << image_size << std::endl;
  • 初始化去畸变纠正变换Map
// 初始化去畸变纠正变换Map
Mat map1, map2;
initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, Mat(),intrinsic_matrix, image_size, CV_16SC2, map1, map2);
  • 显示原图和去畸变后的图
 cv::VideoCapture capture(0);
if (!capture.isOpened()) {cout << "\nCouldn't open the camera\n";return -1;
}
// 显示原图和去畸变后的图
while (true) {Mat image, image0;capture >> image0;if (image0.empty()) {break;}// 执行映射转换remap(image0, image, map1, map2,cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar());imshow("original", image0);imshow("undistorted", image);if ((waitKey(30) & 255) == 27) {break;}
}

undistort

有时,我们只需要对一张图像进行去畸变,则可以使用更简洁的cv::undistort()函数,可以更方便的计算映射关系病将之应用于单个图像。

void undistort( InputArray src,         // 输入图像OutputArray dst,        // 校正后的图像InputArray cameraMatrix,// 标定矩阵InputArray distCoeffs,  // 畸变系数InputArray newCameraMatrix = noArray() // 额外的偏移缩放矩阵
);

范例:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>using namespace cv;
using namespace std;int main(int argc, char **argv) {// 读取相机矩阵、畸变系数cv::FileStorage fs("./calibration_in_params.yml", FileStorage::READ);int image_width{0}, image_height{0};fs["image_width"] >> image_width;fs["image_height"] >> image_height;Size image_size = Size(image_width, image_height);Mat intrinsic_matrix, distortion_coeffs;fs["cameraMatrix"] >> intrinsic_matrix;fs["distCoeffs"] >> distortion_coeffs;fs.release();std::cout << intrinsic_matrix << std::endl;std::cout << distortion_coeffs << std::endl;std::cout << image_size << std::endl;const Mat &image0 = imread("./calib_chess_img/image_0.jpg", IMREAD_COLOR);Mat image;undistort(image0, image, intrinsic_matrix, distortion_coeffs);imshow("original", image0);imshow("undistorted", image);waitKey();return 0;
}

注:以上文字和图片均来源于链接,若有侵权请联系转载方删除。

这篇关于相机内参标定及去畸变矫正代码示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/373714

相关文章

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi

在Pandas中进行数据重命名的方法示例

《在Pandas中进行数据重命名的方法示例》Pandas作为Python中最流行的数据处理库,提供了强大的数据操作功能,其中数据重命名是常见且基础的操作之一,本文将通过简洁明了的讲解和丰富的代码示例,... 目录一、引言二、Pandas rename方法简介三、列名重命名3.1 使用字典进行列名重命名3.编

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.

Go Gorm 示例详解

《GoGorm示例详解》Gorm是一款高性能的GolangORM库,便于开发人员提高效率,本文介绍了Gorm的基本概念、数据库连接、基本操作(创建表、新增记录、查询记录、修改记录、删除记录)等,本... 目录1. 概念2. 数据库连接2.1 安装依赖2.2 连接数据库3. 数据库基本操作3.1 创建表(表关

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是

SpringBoot生成和操作PDF的代码详解

《SpringBoot生成和操作PDF的代码详解》本文主要介绍了在SpringBoot项目下,通过代码和操作步骤,详细的介绍了如何操作PDF,希望可以帮助到准备通过JAVA操作PDF的你,项目框架用的... 目录本文简介PDF文件简介代码实现PDF操作基于PDF模板生成,并下载完全基于代码生成,并保存合并P