【opencv】示例-image_alignment.cpp 利用ECC 算法进行图像对齐

4c10ad6b13fdc94a8674ef9ad9f5cbb9.png

444d4e512471b5f4232917db04381d54.png

affine

imshow("image", target_image);
imshow("template", template_image);
imshow("warped image", warped_image);
imshow("error (black: no error)", abs(errorImage) * 255 / max_of_error);

2778b45db8f3be9d9fb6bf48f672c073.png

homography

这段代码是一个利用ECC (Enhanced Correlation Coefficient) 算法进行图像对齐的示例。代码首先包含了OpenCV库的头文件,并且使用了OpenCV和标准库的命名空间。然后定义了几个函数和宏进行图像变换矩阵的操作,定义了一些用于解析命令行参数的关键字。

main 函数中,首先使用CommandLineParser解析命令行参数,之后加载输入图像和模板图像。如果只给出了输入图像,代码会生成一个随机的模板图像。如果给定了输入变换矩阵,则会使用该矩阵初始化。若变换类型为仿射或相似变换等,会初始化对应的变换矩阵。

代码接着执行findTransformECC函数来计算图像间的几何变换。计算完成后,保存变换矩阵和变换后的图像,并对结果进行显示(根据具体的verbose参数决定是否显示)。

整个过程包括了对图像的读取、几何变换矩阵的初始化与读取、使用ECC算法进行图像对齐、结果的保存与显示。通过这个示例,可以了解到图像处理中图像对齐的基本流程和方法

/*
* 本示例展示了使用findTransformECC函数实现图像对齐的ECC算法
* 
* Demo加载一个图像(默认为fruits.jpg),并基于给定的运动类型人工创建一个模板图像。
* 当给出两幅图像时,第一幅是输入图像,第二幅定义模板图像。在后一种情况下,
* 您还可以解析warp的初始化。
* 
* 输入和输出的warp文件由原始的warp(变换)元素组成。
* 
* 作者: G. Evangelidis, 法国国家信息与自动化研究所(INRIA), Grenoble,
*       M. Asbach, 德国Fraunhofer IAIS, St. Augustin
*/// 导入opencv库的各个模块
#include <opencv2/imgcodecs.hpp> // 图像编解码
#include <opencv2/highgui.hpp>   // 图像显示
#include <opencv2/video.hpp>     // 视频处理
#include <opencv2/imgproc.hpp>   // 图像处理
#include <opencv2/core/utility.hpp> // OpenCV实用函数#include <stdio.h>  // 包含基本输入输出函数
#include <string>   // 包含字符串相关功能
#include <time.h>   // 包含处理时间相关功能
#include <iostream> // 包含输入输出流功能
#include <fstream>  // 包含文件流操作功能using namespace cv;  // 使用OpenCV命名空间
using namespace std; // 使用标准库命名空间// 向前声明函数(函数将在下面定义)
static void help(const char** argv);
static int readWarp(string iFilename, Mat& warp, int motionType);
static int saveWarp(string fileName, const Mat& warp, int motionType);
static void draw_warped_roi(Mat& image, const int width, const int height, Mat& W);// 用于向齐次变换矩阵赋值的宏定义(x和y是坐标,H是变换矩阵)
#define HOMO_VECTOR(H, x, y)\H.at<float>(0,0) = (float)(x);\H.at<float>(1,0) = (float)(y);\H.at<float>(2,0) = 1.;//  用于从齐次坐标中读取值的宏定义(X是齐次坐标,x和y是提取的坐标)
#define GET_HOMO_VALUES(X, x, y)\(x) = static_cast<float> (X.at<float>(0,0)/X.at<float>(2,0));\(y) = static_cast<float> (X.at<float>(1,0)/X.at<float>(2,0));// 定义命令行参数,用于程序运行时接收用户输入或默认值
const std::string keys ="{@inputImage    | fruits.jpg    | input image filename }" // 输入图像文件名,默认为fruits.jpg"{@templateImage |               | template image filename (optional)}" // 模板图像文件名,可选参数"{@inputWarp     |               | input warp (matrix) filename (optional)}" // 输入变换矩阵(文件名),可选参数"{n numOfIter    | 50            | ECC's iterations }" // ECC算法迭代次数,默认值为50"{e epsilon      | 0.0001        | ECC's convergence epsilon }" // ECC算法收敛阈值,默认为0.0001"{o outputWarp   | outWarp.ecc   | output warp (matrix) filename }" // 输出变换矩阵的文件名,默认为outWarp.ecc"{m motionType   | affine        | type of motion (translation, euclidean, affine, homography) }" // 变换类型,默认为仿射变换(affine)"{v verbose      | 1             | display initial and final images }" // 是否显示处理前后的图像,默认为1(显示)"{w warpedImfile | warpedECC.png | warped input image }" // 输出变形后图像的文件名,默认为warpedECC.png"{h help | | print help message }" // 显示帮助信息
;//该文件演示了 ECC 图像对齐算法的使用。 当给定一张图像时,
//模板图像是通过随机扭曲人为形成的。当给出两个图像时,可以
//通过命令行解析来初始化扭曲。如果缺少 inputWarp,则恒等变换
//会初始化算法。
// 提供程序帮助信息和命令行示例的函数
static void help(const char** argv)
{// 输出ECC图像对齐算法使用说明cout << "\nThis file demonstrates the use of the ECC image alignment algorithm. When one image"" is given, the template image is artificially formed by a random warp. When both images"" are given, the initialization of the warp by command line parsing is possible. ""If inputWarp is missing, the identity transformation initializes the algorithm. \n" << endl;// 输出仅用一个输入图像时命令行使用示例cout << "\nUsage example (one image): \n" // 使用示例(一个图像)<< argv[0]  // 程序名<< " fruits.jpg -o=outWarp.ecc "  // 输入图像,输出变换矩阵文件"-m=euclidean -e=1e-6 -N=70 -v=1 \n" << endl; // 运动类型,收敛阈值,迭代次数,是否显示结果// 输出用两个图像以及初始变换矩阵时命令行使用示例cout << "\nUsage example (two images with initialization): \n" // 使用示例(两个图像,带初始化)<< argv[0]  // 程序名<< " yourInput.png yourTemplate.png "  // 输入图像和模板图像"yourInitialWarp.ecc -o=outWarp.ecc -m=homography -e=1e-6 -N=70 -v=1 -w=yourFinalImage.png \n" << endl; // 输入变换矩阵,输出变换矩阵文件,变换类型,收敛阈值,迭代次数,是否显示结果,输出对齐后的图像文件
}// 从文件中读取变换矩阵的函数
static int readWarp(string iFilename, Mat& warp, int motionType){// 根据不同的运动类型(motionType)确定需要从文件中读取的元素数量// 如果是透视变换(Homography),读取9个值,否则读取6个值CV_Assert(warp.type()==CV_32FC1); // 确保warp矩阵是单通道浮点型int numOfElements; // 存储元素的数量if (motionType==MOTION_HOMOGRAPHY)numOfElements=9; // 透视变换有9个参数elsenumOfElements=6; // 其它变换类型只需要6个参数int i; // 循环计数变量int ret_value; // 返回值,表示是否成功读取文件ifstream myfile(iFilename.c_str()); // 打开文件if (myfile.is_open()){ // 如果文件成功打开float* matPtr = warp.ptr<float>(0); // 获取矩阵的指针for(i=0; i<numOfElements; i++){myfile >> matPtr[i]; // 从文件读取每个值}ret_value = 1; // 读取成功,设置返回值为1}else { // 如果文件打开失败cout << "Unable to open file " << iFilename.c_str() << endl; // 输出错误信息ret_value = 0; // 设置返回值为0}return ret_value; // 返回结果
}// 将变换矩阵保存到文件中的函数
static int saveWarp(string fileName, const Mat& warp, int motionType)
{// 确保warp矩阵是单通道浮点型CV_Assert(warp.type()==CV_32FC1);const float* matPtr = warp.ptr<float>(0); // 获取矩阵的指针int ret_value; // 返回值,表示是否成功保存到文件ofstream outfile(fileName.c_str()); // 打开或创建文件来写入if( !outfile ) { // 如果文件打开失败cerr << "error in saving "<< "Couldn't open file '" << fileName.c_str() << "'!" << endl; // 输出错误信息到错误流ret_value = 0; // 设置返回值为0}else {// 如果文件成功打开, 保存warp矩阵的元素outfile << matPtr[0] << " " << matPtr[1] << " " << matPtr[2] << endl;outfile << matPtr[3] << " " << matPtr[4] << " " << matPtr[5] << endl;if (motionType==MOTION_HOMOGRAPHY){ // 如果是透视变换,需要保存额外3个参数outfile << matPtr[6] << " " << matPtr[7] << " " << matPtr[8] << endl;}ret_value = 1; // 保存成功,设置返回值为1}return ret_value; // 返回结果
}// 在图像上绘制变换后区域边界的函数
static void draw_warped_roi(Mat& image, const int width, const int height, Mat& W)
{// 定义四个角点Point2f top_left, top_right, bottom_left, bottom_right;Mat H = Mat(3, 1, CV_32F); // 定义齐次坐标向量Mat U = Mat(3, 1, CV_32F); // 存储变换后的坐标值Mat warp_mat = Mat::eye(3, 3, CV_32F); // 创建一个单位矩阵// 将变换矩阵 W 的值赋给 warp_matfor (int y = 0; y < W.rows; y++)for (int x = 0; x < W.cols; x++)warp_mat.at<float>(y,x) = W.at<float>(y,x);// 对矩形的四个角点进行变换// 左上角HOMO_VECTOR(H, 1, 1); // 将点设置为齐次坐标形式gemm(warp_mat, H, 1, 0, 0, U); // 使用矩阵乘法进行变换GET_HOMO_VALUES(U, top_left.x, top_left.y); // 获取变换后的值// 右上角HOMO_VECTOR(H, width, 1);gemm(warp_mat, H, 1, 0, 0, U);GET_HOMO_VALUES(U, top_right.x, top_right.y);// 左下角HOMO_VECTOR(H, 1, height);gemm(warp_mat, H, 1, 0, 0, U);GET_HOMO_VALUES(U, bottom_left.x, bottom_left.y);// 右下角HOMO_VECTOR(H, width, height);gemm(warp_mat, H, 1, 0, 0, U);GET_HOMO_VALUES(U, bottom_right.x, bottom_right.y);// 在图像上绘制变形后的矩形边界line(image, top_left, top_right, Scalar(255));     // 上边界line(image, top_right, bottom_right, Scalar(255)); // 右边界line(image, bottom_right, bottom_left, Scalar(255)); // 下边界line(image, bottom_left, top_left, Scalar(255));  // 左边界
}// 主函数入口
int main (const int argc, const char * argv[])
{// 解析命令行参数CommandLineParser parser(argc, argv, keys);parser.about("ECC demo"); // 关于本程序parser.printMessage(); // 打印解析的信息help(argv); // 显示帮助信息// 获取命令行参数string imgFile = parser.get<string>(0); // 输入图像文件名string tempImgFile = parser.get<string>(1); // 模板图像文件名string inWarpFile = parser.get<string>(2); // 初始变换矩阵文件名// 获取其他参数int number_of_iterations = parser.get<int>("n"); // 迭代次数double termination_eps = parser.get<double>("e"); // 精度阈值,停止条件string warpType = parser.get<string>("m"); // 变换类型int verbose = parser.get<int>("v"); // 是否显示详细信息string finalWarp = parser.get<string>("o"); // 最终变换矩阵保存文件string warpedImFile = parser.get<string>("w"); // 对齐后的图像保存文件if (!parser.check()) // 检查解析的参数是否合理{parser.printErrors();return -1; // 参数不合理则返回-1}// 确保传入的变换类型是有效的 平移、欧几里得、仿射、单应性if (!(warpType == "translation" || warpType == "euclidean"|| warpType == "affine" || warpType == "homography")){cerr << "Invalid motion transformation" << endl;return -1; // 无效变换类型,返回-1}// 根据变换类型设定变换模式int mode_temp;if (warpType == "translation")mode_temp = MOTION_TRANSLATION;else if (warpType == "euclidean")mode_temp = MOTION_EUCLIDEAN;else if (warpType == "affine")mode_temp = MOTION_AFFINE;elsemode_temp = MOTION_HOMOGRAPHY;// 读取输入图像Mat inputImage = imread(samples::findFile(imgFile), IMREAD_GRAYSCALE);if (inputImage.empty()) // 检查图像是否成功加载{cerr << "Unable to load the inputImage" <<  endl;return -1; // 加载失败,返回-1}// 初始化目标图像和模板图像Mat target_image;Mat template_image;// 如果提供了模板图像,则读取模板图像if (tempImgFile != "") {// 将输入图像复制给目标图像inputImage.copyTo(target_image);// 加载模板图像(灰度图)template_image = imread(samples::findFile(tempImgFile), IMREAD_GRAYSCALE);// 如果模板图像加载失败,则返回错误if (template_image.empty()) {cerr << "Unable to load the template image" << endl;return -1;}}else { // 如果没有指定模板图像文件名,对输入图像应用随机变换以生成模板// 对输入图像进行尺寸调整,新的尺寸为216 x 216像素resize(inputImage, target_image, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);// 声明一个Mat类型变量用于存放变换矩阵Mat warpGround;// 创建一个随机数生成器,种子为当前时间戳RNG rng(getTickCount());// 声明一个double类型变量用于存放计算得到的角度double angle;// 根据mode_temp来决定如何生成变换矩阵并对图像应用变换switch (mode_temp) {case MOTION_TRANSLATION:// 生成一个随机的平移变换矩阵warpGround = (Mat_<float>(2, 3) << 1, 0, (rng.uniform(10.f, 20.f)),0, 1, (rng.uniform(10.f, 20.f)));// 应用平移变换矩阵到目标图像,获取模板图像warpAffine(target_image, template_image, warpGround,Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);break;case MOTION_EUCLIDEAN:// 生成一个随机的欧几里得变换矩阵(含旋转和平移)angle = CV_PI / 30 + CV_PI * rng.uniform((double)-2.f, (double)2.f) / 180;warpGround = (Mat_<float>(2, 3) << cos(angle), -sin(angle), (rng.uniform(10.f, 20.f)),sin(angle), cos(angle), (rng.uniform(10.f, 20.f)));// 应用欧几里得变换矩阵到目标图像,获取模板图像warpAffine(target_image, template_image, warpGround,Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);break;case MOTION_AFFINE:// 生成一个随机的仿射变换矩阵warpGround = (Mat_<float>(2, 3) << (1 - rng.uniform(-0.05f, 0.05f)),(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),(rng.uniform(-0.03f, 0.03f)), (1 - rng.uniform(-0.05f, 0.05f)),(rng.uniform(10.f, 20.f)));// 应用仿射变换矩阵到目标图像,获取模板图像warpAffine(target_image, template_image, warpGround,Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);break;case MOTION_HOMOGRAPHY:// 生成一个随机的单应性变换矩阵warpGround = (Mat_<float>(3, 3) << (1 - rng.uniform(-0.05f, 0.05f)),(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),(rng.uniform(-0.03f, 0.03f)), (1 - rng.uniform(-0.05f, 0.05f)), (rng.uniform(10.f, 20.f)),(rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);// 应用单应性变换矩阵到目标图像,获取模板图像warpPerspective(target_image, template_image, warpGround,Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);break;}}// 根据变换类型创建适当大小的warp_matrixconst int warp_mode = mode_temp;// 初始化或加载变换矩阵Mat warp_matrix;if (warpType == "homography")// 如果是单应性变换,则warp_matrix为3x3的单位矩阵warp_matrix = Mat::eye(3, 3, CV_32F);else// 如果是其他变换,则warp_matrix为2x3的单位矩阵warp_matrix = Mat::eye(2, 3, CV_32F);// 如果提供了变换矩阵文件名,尝试读取变换矩阵if (inWarpFile != ""){int readflag = readWarp(inWarpFile, warp_matrix, warp_mode);// 如果读取失败,打印错误信息,并退出程序if ((!readflag) || warp_matrix.empty()){cerr << "-> Check warp initialization file" << endl << flush;return -1;}}else {// 如果没有提供变换矩阵文件,发出警告:假定使用单位矩阵作为初始化可能不佳,// 尤其是当图像尺寸不相同时或者形变较大时printf("\n ->Performance Warning: Identity warp ideally assumes images of ""similar size. If the deformation is strong, the identity warp may not ""be a good initialization. \n");}// 检查迭代次数是否过多if (number_of_iterations > 200)cout << "-> Warning: too many iterations " << endl;// 如果不是单应性变换,确保变换矩阵仅有两行if (warp_mode != MOTION_HOMOGRAPHY)warp_matrix.rows = 2;//开始计时const double tic_init = (double) getTickCount();// 调用findTransformECC寻找最佳变换矩阵double cc = findTransformECC(template_image, target_image, warp_matrix, warp_mode,TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, number_of_iterations, termination_eps));// 如果findTransformECC返回错误(即-1),则打印错误信息if (cc == -1){cerr << "The execution was interrupted. The correlation value is going to be minimized." << endl;cerr << "Check the warp initialization and/or the size of images." << endl << flush;}// 停止计时const double toc_final = (double)getTickCount();const double total_time = (toc_final - tic_init) / (getTickFrequency());//如果设置了verbose,打印对齐时间if (verbose) {cout << "Alignment time (" << warpType << " transformation): "<< total_time << " sec" << endl << flush;//  cout << "Final correlation: " << cc << endl << flush;}// 保存最终的变换矩阵saveWarp(finalWarp, warp_matrix, warp_mode);if (verbose) {//如果设置了verbose,打印保存变换矩阵的文件名cout << "\nThe final warp has been saved in the file: " << finalWarp << endl << flush;}// 保存最终的对齐图像Mat warped_image = Mat(template_image.rows, template_image.cols, CV_32FC1);if (warp_mode != MOTION_HOMOGRAPHY)warpAffine(target_image, warped_image, warp_matrix, warped_image.size(),INTER_LINEAR + WARP_INVERSE_MAP);elsewarpPerspective(target_image, warped_image, warp_matrix, warped_image.size(),INTER_LINEAR + WARP_INVERSE_MAP);imwrite(warpedImFile, warped_image); // 保存变形后的图像// 如果设置了verbose,显示结果图像if (verbose){cout << "The warped image has been saved in the file: " << warpedImFile << endl << flush;//创建可视化窗口namedWindow("image", WINDOW_AUTOSIZE);namedWindow("template", WINDOW_AUTOSIZE);namedWindow("warped image", WINDOW_AUTOSIZE);namedWindow("error (black: no error)", WINDOW_AUTOSIZE);//移动窗口,用于可视化布局moveWindow("image", 20, 300);moveWindow("template", 300, 300);moveWindow("warped image", 600, 300);moveWindow("error (black: no error)", 900, 300);// 绘制变换后的区域边界Mat identity_matrix = Mat::eye(3, 3, CV_32F);draw_warped_roi(target_image, template_image.cols - 2, template_image.rows - 2, warp_matrix);draw_warped_roi(template_image, template_image.cols - 2, template_image.rows - 2, identity_matrix);Mat errorImage;subtract(template_image, warped_image, errorImage);double max_of_error;minMaxLoc(errorImage, NULL, &max_of_error);// 显示图像cout << "Press any key to exit the demo (you might need to click on the images before)." << endl << flush;imshow("image", target_image);waitKey(200);imshow("template", template_image);waitKey(200);imshow("warped image", warped_image);waitKey(200);imshow("error (black: no error)", abs(errorImage) * 255 / max_of_error);waitKey(0);}// 完成程序return 0;
}
// 使用resize函数调整inputImage的大小,存入target_image中
resize(inputImage,               // 源图像target_image,             // 目标图像,输出的大小调整后的图像将被存储在这里Size(216, 216),           // 目标图像的新大小,此处指定为宽216像素,高216像素0,                        // x方向上的缩放比例,在这里缩放比例由Size参数决定,因此设置为00,                        // y方向上的缩放比例,同上设置为0INTER_LINEAR_EXACT        // 插值方式,此处使用精确线性插值算法
);

935067315c37bee2059d3c47f750f7e4.png

如果没有指定模板图像文件名,对输入图像应用随机变换以生成模板

e8b9049b400636096be2ef445f93e9f5.png

warpAffine(target_image, template_image, warpGround,Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);

66122b00a5942cd826917b4de2fcd1ea.png

// 使用增强的相关系数(ECC)算法,寻找最佳的仿射变换矩阵
double cc = findTransformECC(template_image,        // 模板图像target_image,          // 需要对齐到模板图像的对象图像warp_matrix,           // 可以指定初始估计,函数将优化这个矩阵以获得最佳变换warp_mode,             // 规定变换模型的类型,如仿射变换TermCriteria(          // 优化时的迭代终止准则,可以是最大迭代次数,变换估计的精确度,或它们的组合TermCriteria::COUNT + TermCriteria::EPS, // 使用最大迭代次数和变换估计的精确度两个条件number_of_iterations,                     // 迭代的最大次数termination_eps                            // 迭代的终止精确度)
);

ac8369ae3d026334727b5ed3b867e4d1.png

// 对target_image应用透视变换,结果输出到warped_image中
warpPerspective(target_image,     // 源图像warped_image,     // 目标图像,存储变换后的图像warp_matrix,      // 透视变换矩阵,描述了变换的参数warped_image.size(), // 最终输出图像的大小INTER_LINEAR + WARP_INVERSE_MAP // 插值方式加上变换方向
);

5c70a60275c2ca4bfb1e207a9e1cd0fa.png

仿射变换和透视变换详细对比

d1d0b2bee67381c625190ec93e47b361.png

// 使用通用矩阵乘法函数gemm计算变换矩阵和透视矩阵的乘积
gemm(warp_mat,   // 第一个矩阵A,这里指的是仿射变换矩阵或相应的变换矩阵H,          // 第二个矩阵B,透视变换矩阵1,          // alpha系数,用于缩放第一个矩阵AMat(),      // 第三个矩阵C,在这个函数调用中未使用,所以传递一个空矩阵0,          // beta系数,由于矩阵C未被使用,beta系数实际不起作用U,          // 输出矩阵D,存储A和B(M1和M2)乘积的结果0           // 标志位,在此为0,默认表示正常的矩阵乘法
);

3c406d104881a86b2922f5a9ef7b0b4c.png

The End

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【300套】基于Springboot+Vue的Java毕业设计项目(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f9e1;今天给大家分享300的Java毕业设计&#xff0c;基于Springbootvue框架&#xff0c;这些项目都经过精心挑选&#xff0c;涵盖了不同的实战主题和用例&#xff0c;可做毕业…

银行司库系统应用架构介绍

继国务院国资委印发了《关于推动中央企业加快司库体系建设进一步加强资金管理的意见》以及《关于中央企业加快建设世界一流财务管理体系的指导意见》&#xff0c;司库体系建设开始得到了更多重视。其中&#xff0c;作为改革风向标&#xff0c;央企数字化转型及司库建设对整个行…

【YUNBEE云贝-进阶课】MySQL8.0性能优化实战培训

众多已经学习过MySQL 8.0 OCP认证专家的课程的同学们对 MySQL 8.0 的安装部署、体系结构、配置监控、用户管理、主从复制、系统运维、MGR等基础操作和动手实验有了一定的学习基础.很多学员反馈希望更进一步提升技术能力、解决工作中碰到的性能问题。 针对MySQL8.0的数据库性能优…

Ubuntu 20.04 设置开启 root 远程登录连接

Ubuntu默认不设置 root 帐户和密码 Ubuntu默认不设置 root 帐户和密码 Ubuntu默认不设置 root 帐户和密码 如有需要&#xff0c;可在设置中开启允许 root 用户登录。具体操作步骤如下&#xff1a; 操作步骤 1、首先使用普通用户登录 2、设置root密码 macw:~$ sudo passwd …

基于STM32F103单片机的时间同步项目

一、前言 本项目为前一个时间同步项目的更迭版本&#xff0c;由于之前的G031开发板没有外部晶振&#xff0c;从机守时能力几乎没有&#xff0c;5秒以上不同步从机时间就开始飞了。在考虑成本选型后&#xff0c;选择了带有外部有缘晶振的STM32F103C8T6最小单片机&#xff0c;来作…

DRF的认证、权限、限流、序列化、反序列化

DRF的认证、权限、限流、序列化、反序列化 一、认证1、直接用&#xff0c;用户授权2、认证组件源码3、第三方认证djangorestframework-simplejwt 二、权限1. 直接使用&#xff0c;用户权限2.权限组件源码 三、序列化1. 序列化1.1 自定义Serailizer类序列化1.2 在视图APIView中使…

嵌入式驱动学习第七周——GPIO子系统

前言 GPIO子系统是用来统一便捷地访问实现输入输出中断等效果。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论一起学习。现在关注就是老粉啦&#xff0…

spring boot整合Redis监听数据变化

一、前言 Redis提供了数据变化的通知事件&#xff0c;可以实时监测key和value的变化&#xff0c;客户端可以通过订阅相关的channel来接收这些通知事件&#xff0c;然后做相应的自定义处理&#xff0c;详细的介绍可以参考官方文档Redis keyspace notifications | Docs 使用Red…

httpsok-快速申请谷歌SSL免费证书

&#x1f525;httpsok-快速申请谷歌SSL免费证书 使用场景&#xff1a; 部署CDN证书、OSS云存储证书证书类型&#xff1a; 单域名 多域名 通配符域名 混合域名证书厂商&#xff1a; ZeroSSL Lets Encrypt Google证书加密类型&#xff1a; ECC、 RSA 一、证书管理 进入 证书管…

【数学建模】2024Mathorcup数学建模C题完整思路与代码论文解析

2024Mathorcup数学应用挑战赛C题|图神经网络的预测模型ARIMA时间序列预测模型人员排班混合整数规划模型|完整代码和论文全解全析 我们已经完成了2024Mathorcup数学建模挑战赛C题的40页完整论文和代码&#xff0c;相关内容可见文末&#xff0c;部分图片如下&#xff1a; 问题分…

[蓝桥杯] 岛屿个数(C语言)

提示&#xff1a; 橙色字体为需要注意部分&#xff0c;红色字体为难点部分&#xff0c;会在文章“重难点解答”部分精讲。 题目链接 蓝桥杯2023年第十四届省赛真题-岛屿个数 - C语言网 题目理解 这道题让我们求岛屿个数&#xff0c;那么我们就应该先弄懂&#xff0c;对于一…

Qt5 编译oracle数据库

库文件 1、Qt源码目录&#xff1a;D:\Qt5\5.15.2\Src\qtbase\src\plugins\sqldrivers\oci 2、oracle客户端SDK: https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html 下载各版本中的如下压缩包&#xff0c;一定要版本相同的 将两个压缩包…

【简单讲解如何安装与配置Composer】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

Rust语言入门第四篇-变量与可变性以及隐藏(Shadowing)

文章目录 Rust语言入门第四篇-变量与可变性以及隐藏&#xff08;Shadowing&#xff09;概要let 关键字自动判断变量类型隐藏&#xff08;Shadowing&#xff09;let关键字支持的数据类型let 关键字声明的变量类型转换 Rust语言入门第四篇-变量与可变性以及隐藏&#xff08;Shado…

机器学习和深度学习 -- 李宏毅(笔记与个人理解)Day 13

Day13 Error surface is rugged…… Tips for training :Adaptive Learning Rate critical point is not the difficult Root mean Square --used in Adagrad 这里为啥是前面的g的和而不是直接只除以当前呢? 这种方法的目的是防止学习率在训练过程中快速衰减。如果只用当前的…

02_JavaWeb中的Tomcat(详解)

文章目录 Tomcat1, 概述1.1 安装1.2 目录结构1.3 启动/停止 2, 资源部署2.1 直接部署: 主要和重要的方式2.2 虚拟映射: 重要2.2.1 方式一:2.2.1 方式二: 2.3 原理解析 3, Tomcat组件3.1 Connector3.2 Engine3.2.1 Host3.2.1.1 Context 4, 其它: 重要4.1 设置 Tomcat 1, 概述 w…

Android网络抓包--Charles

一、Android抓包方式 对Https降级进行抓包&#xff0c;降级成Http使用抓包工具对Https进行抓包 二、常用的抓包工具 wireshark&#xff1a;侧重于TCP、UDP传输层&#xff0c;HTTP/HTTPS也能抓包&#xff0c;但不能解密HTTPS报文。比较复杂fiddler&#xff1a;支持HTTP/HTTPS…

文献速递:深度学习肝脏肿瘤诊断---基于多相增强 CT 和临床数据的恶性肝肿瘤鉴别诊断深度学习

Title 题目 Deep learning for diferential diagnosisof malignant hepatic tumors based on multi-phase contrast-enhanced CT and clinical data 基于多相增强 CT 和临床数据的恶性肝肿瘤鉴别诊断深度学习 Abstract 摘要 Liver cancer remains the leading cause of can…

云计算:Linux 部署 OVS 集群(控制端)实现OpenFlow

目录 一、实验 1.环境 2.Linux 部署 OVS 集群&#xff08;控制端&#xff09; 3.控制端对接服务端OVS网元 4.服务端OVS添加流表 5.服务端删除OVS 二、问题 1. ODL如何查找已安装插件 2.查看流表显示不全 3.如何删除OVS流表 一、实验 1.环境 (1) 主机 表1 宿主机 主…

什么是NLP?

&#x1f916;NLP是什么&#xff1f;&#x1f916; NLP&#xff08;Natural Language Processing&#xff09;&#xff0c;全称自然语言处理&#xff0c;是人工智能不可或缺的一环&#xff0c;它搭建了人与计算机之间沟通的桥梁&#x1f309;。 &#x1f6e0;️NLP强大功能一…