当前位置: 首页 > news >正文

OpenCV图片校正

OpenCV图片校正

  • 背景
  • 几种校正方法
  • 1.傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转
  • 3.四点透视 + 角度 + 旋转
  • 4.检测矩形轮廓 + 角度 + 旋转
  • 参考

背景

遇到偏的图片想要校正成水平或者垂直的。

几种校正方法

对于倾斜的图片通过矫正可以得到水平的图片。一般有如下几种基于opencv的组合方式进行图片矫正。

  • 1、傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转
  • 2、边缘检测 + 霍夫变换 + 直线+角度 + 旋转
  • 3、四点透视 + 角度 + 旋转
  • 4、检测矩形轮廓 + 角度 + 旋转

1.傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>using namespace cv;
using namespace std;// 二值化阈值
#define GRAY_THRESH 150// 直线上点的个数
#define HOUGH_VOTE 50int main(int argc, char **argv)
{//Read a single-channel imageconst char* filename = "31.png";Mat srcImg = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);if (srcImg.empty())return -1;imshow("source", srcImg);Point center(srcImg.cols / 2, srcImg.rows / 2);//Expand image to an optimal size, for faster processing speed//Set widths of borders in four directions//If borderType==BORDER_CONSTANT, fill the borders with (0,0,0)Mat padded;int opWidth = getOptimalDFTSize(srcImg.rows);int opHeight = getOptimalDFTSize(srcImg.cols);copyMakeBorder(srcImg, padded, 0, opWidth - srcImg.rows, 0, opHeight - srcImg.cols, BORDER_CONSTANT, Scalar::all(0));Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };Mat comImg;//Merge into a double-channel imagemerge(planes, 2, comImg);//Use the same image as input and output,//so that the results can fit in Mat welldft(comImg, comImg);//Compute the magnitude//planes[0]=Re(DFT(I)), planes[1]=Im(DFT(I))//magnitude=sqrt(Re^2+Im^2)split(comImg, planes);magnitude(planes[0], planes[1], planes[0]);//Switch to logarithmic scale, for better visual results//M2=log(1+M1)Mat magMat = planes[0];magMat += Scalar::all(1);log(magMat, magMat);//Crop the spectrum//Width and height of magMat should be even, so that they can be divided by 2//-2 is 11111110 in binary system, operator & make sure width and height are always evenmagMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));//Rearrange the quadrants of Fourier image,//so that the origin is at the center of image,//and move the high frequency to the cornersint cx = magMat.cols / 2;int cy = magMat.rows / 2;Mat q0(magMat, Rect(0, 0, cx, cy));Mat q1(magMat, Rect(0, cy, cx, cy));Mat q2(magMat, Rect(cx, cy, cx, cy));Mat q3(magMat, Rect(cx, 0, cx, cy));Mat tmp;q0.copyTo(tmp);q2.copyTo(q0);tmp.copyTo(q2);q1.copyTo(tmp);q3.copyTo(q1);tmp.copyTo(q3);//Normalize the magnitude to [0,1], then to[0,255]normalize(magMat, magMat, 0, 1, CV_MINMAX);Mat magImg(magMat.size(), CV_8UC1);magMat.convertTo(magImg, CV_8UC1, 255, 0);imshow("magnitude", magImg);//imwrite("imageText_mag.jpg",magImg);//Turn into binary imagethreshold(magImg, magImg, GRAY_THRESH, 255, CV_THRESH_BINARY);imshow("mag_binary", magImg);//imwrite("imageText_bin.jpg",magImg);//Find lines with Hough Transformationvector<Vec2f> lines;float pi180 = (float)CV_PI / 180;Mat linImg(magImg.size(), CV_8UC3);HoughLines(magImg, lines, 1, pi180, HOUGH_VOTE, 0, 0);int numLines = lines.size();for (int l = 0; l<numLines; l++){float rho = lines[l][0], theta = lines[l][1];Point pt1, pt2;double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;pt1.x = cvRound(x0 + 1000 * (-b));pt1.y = cvRound(y0 + 1000 * (a));pt2.x = cvRound(x0 - 1000 * (-b));pt2.y = cvRound(y0 - 1000 * (a));line(linImg, pt1, pt2, Scalar(255, 0, 0), 3, 8, 0);}imshow("lines", linImg);//imwrite("imageText_line.jpg",linImg);if (lines.size() == 3){cout << "found three angels:" << endl;cout << lines[0][1] * 180 / CV_PI << endl << lines[1][1] * 180 / CV_PI << endl << lines[2][1] * 180 / CV_PI << endl << endl;}//Find the proper angel from the three found angelsfloat angel = 0;float piThresh = (float)CV_PI / 90;float pi2 = CV_PI / 2;for (int l = 0; l<numLines; l++){float theta = lines[l][1];if (abs(theta) < piThresh || abs(theta - pi2) < piThresh)continue;else{angel = theta;break;}}//Calculate the rotation angel//The image has to be square,//so that the rotation angel can be calculate rightangel = angel<pi2 ? angel : angel - CV_PI;if (angel != pi2){float angelT = srcImg.rows*tan(angel) / srcImg.cols;angel = atan(angelT);}float angelD = angel * 180 / (float)CV_PI;cout << "the rotation angel to be applied:" << endl << angelD << endl << endl;//Rotate the image to recoverMat rotMat = getRotationMatrix2D(center, angelD, 1.0);Mat dstImg = Mat::ones(srcImg.size(), CV_8UC3);warpAffine(srcImg, dstImg, rotMat, srcImg.size(), 1, 0, Scalar(255, 255, 255));imshow("result", dstImg);//imwrite("imageText_D.jpg",dstImg);waitKey(0);return 0;
}

opencv4x

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h> 
#include <iostream>using namespace cv;
using namespace std;// 二值化阈值
#define GRAY_THRESH 150// 直线上点的个数
#define HOUGH_VOTE 50int main(int argc, char **argv)
{//Read a single-channel imageconst char* filename = argv[1];Mat srcImg = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);if (srcImg.empty())return -1;imshow("source", srcImg);Point center(srcImg.cols / 2, srcImg.rows / 2);//Expand image to an optimal size, for faster processing speed//Set widths of borders in four directions//If borderType==BORDER_CONSTANT, fill the borders with (0,0,0)Mat padded;int opWidth = getOptimalDFTSize(srcImg.rows);int opHeight = getOptimalDFTSize(srcImg.cols);copyMakeBorder(srcImg, padded, 0, opWidth - srcImg.rows, 0, opHeight - srcImg.cols, BORDER_CONSTANT, Scalar::all(0));Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };Mat comImg;//Merge into a double-channel imagemerge(planes, 2, comImg);//Use the same image as input and output,//so that the results can fit in Mat welldft(comImg, comImg);//Compute the magnitude//planes[0]=Re(DFT(I)), planes[1]=Im(DFT(I))//magnitude=sqrt(Re^2+Im^2)split(comImg, planes);magnitude(planes[0], planes[1], planes[0]);//Switch to logarithmic scale, for better visual results//M2=log(1+M1)Mat magMat = planes[0];magMat += Scalar::all(1);log(magMat, magMat);//Crop the spectrum//Width and height of magMat should be even, so that they can be divided by 2//-2 is 11111110 in binary system, operator & make sure width and height are always evenmagMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));//Rearrange the quadrants of Fourier image,//so that the origin is at the center of image,//and move the high frequency to the cornersint cx = magMat.cols / 2;int cy = magMat.rows / 2;Mat q0(magMat, Rect(0, 0, cx, cy));Mat q1(magMat, Rect(0, cy, cx, cy));Mat q2(magMat, Rect(cx, cy, cx, cy));Mat q3(magMat, Rect(cx, 0, cx, cy));Mat tmp;q0.copyTo(tmp);q2.copyTo(q0);tmp.copyTo(q2);q1.copyTo(tmp);q3.copyTo(q1);tmp.copyTo(q3);//Normalize the magnitude to [0,1], then to[0,255]//normalize(magMat, magMat, 0, 1, CV_MINMAX);normalize(magMat, magMat, 0, 1, NORM_MINMAX);Mat magImg(magMat.size(), CV_8UC1);magMat.convertTo(magImg, CV_8UC1, 255, 0);imshow("magnitude", magImg);//imwrite("imageText_mag.jpg",magImg);//Turn into binary imagethreshold(magImg, magImg, GRAY_THRESH, 255, cv::THRESH_BINARY);imshow("mag_binary", magImg);//imwrite("imageText_bin.jpg",magImg);//Find lines with Hough Transformationvector<Vec2f> lines;float pi180 = (float)CV_PI / 180;Mat linImg(magImg.size(), CV_8UC3);HoughLines(magImg, lines, 1, pi180, HOUGH_VOTE, 0, 0);int numLines = lines.size();for (int l = 0; l<numLines; l++){float rho = lines[l][0], theta = lines[l][1];Point pt1, pt2;double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;pt1.x = cvRound(x0 + 1000 * (-b));pt1.y = cvRound(y0 + 1000 * (a));pt2.x = cvRound(x0 - 1000 * (-b));pt2.y = cvRound(y0 - 1000 * (a));line(linImg, pt1, pt2, Scalar(255, 0, 0), 3, 8, 0);}imshow("lines", linImg);//imwrite("imageText_line.jpg",linImg);if (lines.size() == 3){cout << "found three angels:" << endl;cout << lines[0][1] * 180 / CV_PI << endl << lines[1][1] * 180 / CV_PI << endl << lines[2][1] * 180 / CV_PI << endl << endl;}//Find the proper angel from the three found angelsfloat angel = 0;float piThresh = (float)CV_PI / 90;float pi2 = CV_PI / 2;for (int l = 0; l<numLines; l++){float theta = lines[l][1];if (abs(theta) < piThresh || abs(theta - pi2) < piThresh)continue;else{angel = theta;break;}}//Calculate the rotation angel//The image has to be square,//so that the rotation angel can be calculate rightangel = angel<pi2 ? angel : angel - CV_PI;if (angel != pi2){float angelT = srcImg.rows*tan(angel) / srcImg.cols;angel = atan(angelT);}//float angelD = angel * 180 / (float)CV_PI;float angelD = angel * 180 / (float)CV_PI;cout << "the rotation angel to be applied: "<< angelD << endl << endl;//Rotate the image to recoverMat rotMat = getRotationMatrix2D(center, angelD, 1.0);Mat dstImg = Mat::ones(srcImg.size(), CV_8UC3);warpAffine(srcImg, dstImg, rotMat, srcImg.size(), 1, 0, Scalar(255, 255, 255));imshow("result", dstImg);imwrite("imageText_D.jpg",dstImg);waitKey(0);return 0;
}

CMakeLists.txt

project( main )
cmake_minimum_required(VERSION 3.10) 
#添加头文件路径
include_directories(/usr/local/include /usr/local/include/opencv4 /usr/local/include/opencv4/opencv2)
#添加库文件路径
link_directories(/usr/local/lib)add_executable(main test.cpp)
target_link_libraries( main -lopencv_core  -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs)

在这里插入图片描述
在这里插入图片描述

3.四点透视 + 角度 + 旋转

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;// 直线上点的个数
#define HOUGH_VOTE 50//度数转换
double DegreeTrans(double theta)
{double res = theta / CV_PI * 180;return res;
}//逆时针旋转图像degree角度(原尺寸)    
void rotateImage(Mat src, Mat& img_rotate, double degree)
{//旋转中心为图像中心    Point2f center;center.x = float(src.cols / 2.0);center.y = float(src.rows / 2.0);int length = 0;length = sqrt(src.cols*src.cols + src.rows*src.rows);//计算二维旋转的仿射变换矩阵  Mat M = getRotationMatrix2D(center, degree, 1);warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色  
}//通过霍夫变换计算角度
double CalcDegree(const Mat &srcImage, Mat &dst)
{Mat midImage, dstImage;Canny(srcImage, midImage, 50, 200, 3);cvtColor(midImage, dstImage, CV_GRAY2BGR);//通过霍夫变换检测直线vector<Vec2f> lines;HoughLines(midImage, lines, 1, CV_PI / 180, HOUGH_VOTE);//第5个参数就是阈值,阈值越大,检测精度越高//cout << lines.size() << endl;//由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢//所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。float sum = 0;//依次画出每条线段for (size_t i = 0; i < lines.size(); i++){float rho = lines[i][0];float theta = lines[i][1];Point pt1, pt2;//cout << theta << endl;double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;pt1.x = cvRound(x0 + 1000 * (-b));pt1.y = cvRound(y0 + 1000 * (a));pt2.x = cvRound(x0 - 1000 * (-b));pt2.y = cvRound(y0 - 1000 * (a));//只选角度最小的作为旋转角度sum += theta;line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA); //Scalar函数用于调节线段颜色imshow("直线探测效果图", dstImage);}float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好cout << "average theta:" << average << endl;double angle = DegreeTrans(average) - 90;rotateImage(dstImage, dst, angle);//imshow("直线探测效果图2", dstImage);return angle;
}void ImageRecify(const char* pInFileName, const char* pOutFileName)
{double degree;Mat src = imread(pInFileName);imshow("原始图", src);Mat dst;//倾斜角度矫正degree = CalcDegree(src, dst);rotateImage(src, dst, degree);cout << "angle:" << degree << endl;imshow("旋转调整后", dst);Mat resulyImage = dst(Rect(0, 0, dst.cols, 500)); //根据先验知识,估计好文本的长宽,再裁剪下来imshow("裁剪之后", resulyImage);imwrite("recified.jpg", resulyImage);
}int main()
{ImageRecify("31.png", "FinalImage.jpg");waitKey();return 0;
}

opencv4.x

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;// 直线上点的个数
#define HOUGH_VOTE 50//度数转换
double DegreeTrans(double theta)
{double res = theta / CV_PI * 180;return res;
}//逆时针旋转图像degree角度(原尺寸)    
void rotateImage(Mat src, Mat& img_rotate, double degree)
{//旋转中心为图像中心    Point2f center;center.x = float(src.cols / 2.0);center.y = float(src.rows / 2.0);int length = 0;length = sqrt(src.cols*src.cols + src.rows*src.rows);//计算二维旋转的仿射变换矩阵  Mat M = getRotationMatrix2D(center, degree, 1);warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色  
}//通过霍夫变换计算角度
double CalcDegree(const Mat &srcImage, Mat &dst)
{Mat midImage, dstImage;Canny(srcImage, midImage, 50, 200, 3);cvtColor(midImage, dstImage, COLOR_GRAY2BGR);//通过霍夫变换检测直线vector<Vec2f> lines;HoughLines(midImage, lines, 1, CV_PI / 180, HOUGH_VOTE);//第5个参数就是阈值,阈值越大,检测精度越高//cout << lines.size() << endl;//由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢//所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。float sum = 0;//依次画出每条线段for (size_t i = 0; i < lines.size(); i++){float rho = lines[i][0];float theta = lines[i][1];Point pt1, pt2;//cout << theta << endl;double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;pt1.x = cvRound(x0 + 1000 * (-b));pt1.y = cvRound(y0 + 1000 * (a));pt2.x = cvRound(x0 - 1000 * (-b));pt2.y = cvRound(y0 - 1000 * (a));//只选角度最小的作为旋转角度sum += theta;line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA); //Scalar函数用于调节线段颜色imshow("直线探测效果图", dstImage);}float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好cout << "average theta:" << average << endl;double angle = DegreeTrans(average) - 90;rotateImage(dstImage, dst, angle);//imshow("直线探测效果图2", dstImage);return angle;
}void ImageRecify(const char* pInFileName, const char* pOutFileName)
{double degree;Mat src = imread(pInFileName);imshow("原始图", src);Mat dst;//倾斜角度矫正degree = CalcDegree(src, dst);rotateImage(src, dst, degree);cout << "angle:" << degree << endl;imshow("旋转调整后", dst);Mat resulyImage = dst(Rect(0, 0, dst.cols, 1000)); //根据先验知识,估计好文本的长宽,再裁剪下来imshow("裁剪之后", resulyImage);imwrite("recified.jpg", resulyImage);
}int main()
{ImageRecify("test.jpg", "FinalImage.jpg");waitKey();return 0;
}

4.检测矩形轮廓 + 角度 + 旋转

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#include <algorithm>bool x_sort(const Point2f & m1, const Point2f & m2)
{return m1.x < m2.x;
}//第一个参数:输入图片名称;第二个参数:输出图片名称
void GetContoursPic(const char* pSrcFileName, const char* pDstFileName)
{Mat srcImg = imread(pSrcFileName);imshow("原始图", srcImg);Mat gray, binImg;//灰度化cvtColor(srcImg, gray, COLOR_RGB2GRAY);imshow("灰度图", gray);//二值化threshold(gray, binImg, 150, 200, CV_THRESH_BINARY);imshow("二值化", binImg);vector<Point>  contours;vector<vector<Point> > f_contours;//注意第5个参数为CV_RETR_EXTERNAL,只检索外框  findContours(binImg, f_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓int max_area = 0;int index;for (int i = 0; i < f_contours.size(); i++){double tmparea = fabs(contourArea(f_contours[i]));if (tmparea > max_area){index = i;max_area = tmparea;}}contours = f_contours[index];CvBox2D rect = minAreaRect(Mat(contours));float angle = rect.angle;cout << "before angle : " << angle << endl;if (angle < -45)angle = (90 + angle);elseangle = -angle;cout << "after angle : " << angle << endl;//新建一个感兴趣的区域图,大小跟原图一样大  Mat RoiSrcImg(srcImg.rows, srcImg.cols, CV_8UC3); //注意这里必须选CV_8UC3RoiSrcImg.setTo(0); //颜色都设置为黑色  //imshow("新建的ROI", RoiSrcImg);//对得到的轮廓填充一下  drawContours(binImg, f_contours, 0, Scalar(255), CV_FILLED);//抠图到RoiSrcImgsrcImg.copyTo(RoiSrcImg, gray);//再显示一下看看,除了感兴趣的区域,其他部分都是黑色的了  namedWindow("RoiSrcImg", 1);imshow("RoiSrcImg", RoiSrcImg);//创建一个旋转后的图像  Mat RatationedImg(RoiSrcImg.rows, RoiSrcImg.cols, CV_8UC1);RatationedImg.setTo(0);//对RoiSrcImg进行旋转  Point2f center = rect.center;  //中心点  Mat M2 = getRotationMatrix2D(center, angle, 1);//计算旋转加缩放的变换矩阵 warpAffine(RoiSrcImg, RatationedImg, M2, RoiSrcImg.size(), 1, 0, Scalar(0));//仿射变换 imshow("旋转之后", RatationedImg);
}void main()
{GetContoursPic("34.png", "FinalImage.jpg");waitKey();
}

opencv4.x

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <opencv2/imgproc/types_c.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>using namespace cv;
using namespace std;
#include <algorithm>bool x_sort(const Point2f & m1, const Point2f & m2)
{return m1.x < m2.x;
}//第一个参数:输入图片名称;第二个参数:输出图片名称
void GetContoursPic(const char* pSrcFileName, const char* pDstFileName)
{Mat srcImg = imread(pSrcFileName);imshow("原始图", srcImg);Mat gray, binImg;//灰度化cvtColor(srcImg, gray, COLOR_RGB2GRAY);imshow("灰度图", gray);//二值化threshold(gray, binImg, 150, 200, cv::THRESH_BINARY);imshow("二值化", binImg);vector<Point>  contours;vector<vector<Point> > f_contours;//注意第5个参数为CV_RETR_EXTERNAL,只检索外框  findContours(binImg, f_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓int max_area = 0;int index;for (int i = 0; i < f_contours.size(); i++){double tmparea = fabs(contourArea(f_contours[i]));if (tmparea > max_area){index = i;max_area = tmparea;}}contours = f_contours[index];RotatedRect rect = minAreaRect(Mat(contours));float angle = rect.angle;cout << "before angle : " << angle << endl;if (angle < -45)angle = (90 + angle);elseangle = -angle;cout << "after angle : " << angle << endl;//新建一个感兴趣的区域图,大小跟原图一样大  Mat RoiSrcImg(srcImg.rows, srcImg.cols, CV_8UC3); //注意这里必须选CV_8UC3RoiSrcImg.setTo(0); //颜色都设置为黑色  //imshow("新建的ROI", RoiSrcImg);//对得到的轮廓填充一下  drawContours(binImg, f_contours, 0, Scalar(255), cv::FILLED);//抠图到RoiSrcImgsrcImg.copyTo(RoiSrcImg, gray);//再显示一下看看,除了感兴趣的区域,其他部分都是黑色的了  namedWindow("RoiSrcImg", 1);imshow("RoiSrcImg", RoiSrcImg);//创建一个旋转后的图像  Mat RatationedImg(RoiSrcImg.rows, RoiSrcImg.cols, CV_8UC1);RatationedImg.setTo(0);//对RoiSrcImg进行旋转  Point2f center = rect.center;  //中心点  Mat M2 = getRotationMatrix2D(center, angle, 1);//计算旋转加缩放的变换矩阵 warpAffine(RoiSrcImg, RatationedImg, M2, RoiSrcImg.size(), 1, 0, Scalar(0));//仿射变换 imshow("旋转之后", RatationedImg);imwrite("recified.jpg", RatationedImg);
}int main()
{GetContoursPic("test.jpg", "FinalImage.jpg");waitKey();return 0;
}

参考

  • 榴莲小怪兽 opencv-图片矫正
  • OpenCV利用透视变换矫正图像
http://www.lryc.cn/news/134613.html

相关文章:

  • 数字孪生流域共建共享相关政策解读
  • FSC147数据集格式解析
  • el-element中el-tabs案例的使用
  • tomcat结构目录有哪些?
  • 生成式AI系列 —— DCGAN生成手写数字
  • vscode-vue项目格式化+语法检验-草稿
  • 【Java从0到1学习】10 Java常用类汇总
  • 第三届人工智能与智能制造国际研讨会(AIIM 2023)
  • 层次分析法
  • Error Handling
  • leetcode:字符串相乘(两种方法)
  • 【爬虫练习之glidedsky】爬虫-基础2
  • 03.有监督算法——决策树
  • 网络协议详解之STP
  • Eltima USB Network Gate 10.0 Crack
  • SpringCloudGateway网关实战(一)
  • django中使用ajax发送请求
  • C++之std::list<string>::iterator迭代器应用实例(一百七十九)
  • VSCode好用的插件
  • js实现滚轮滑动到底部自动加载(完整版)
  • 如何限制PDF打印?限制清晰度?
  • python计算模板图像与原图像各区域的相似度
  • 阿里云云解析DNS核心概念与应用
  • 计算机竞赛 垃圾邮件(短信)分类算法实现 机器学习 深度学习
  • compositionAPI
  • vscode配置调试环境-windows系统
  • 智慧城市能实现嘛?数字孪生又在其中扮演什么角色?
  • 【置顶帖】关于博主/关于博客/博客大事记
  • 华为数通方向HCIP-DataCom H12-821题库(单选题:01-20)
  • Java【手撕双指针】LeetCode 11. “盛水最多的容器“, 图文详解思路分析 + 代码