OpenCV实战5 车牌号识别

news2024/11/27 10:34:42

原文在这里,参考这个进行了改进

感觉学到了很多东西,便在这里作下笔记。

效果:

目录

一、知识点学习:

1. fstream

2. 形态学开操作与形态闭操作

2.1 第一个角度:消除较小的联通区域 vs 弥合较小的联通区域

2.2 第二个角度:消除背景噪音 vs 消除前景噪音

3、approPolyDp函数

4、冒泡排序

5、匹配目标

6、putText函数打印中文

 7、文字文件、标签文件

7.1 文字文件

7.2 标签文件

二、车牌识别代码

三、项目总结


一、知识点学习:

1. fstream

作用:输入输出文件;

例子:

    fstream fin;
	fin.open(filename, ios::in);
	if (!fin.is_open())
	{
		cout << "can not open the file!" << endl;
		return false;
	}

	string s;
	while (std::getline(fin, s))
	{
		string str = s;
		data_name.push_back(str);
	}
	fin.close();

上面用到的open函数详细介绍:

void open ( const char * filename,  
            ios_base::openmode mode = ios_base::in | ios_base::out );  

filename 操作文件名

mode 打开文件的方式,常用的有下面这两种

ios::in:     //文件以输入方式打开(文件数据输入到内存)  
ios::out:    //文件以输出方式打开(内存数据输出到文件)

2. 形态学开操作与形态闭操作

这两个我一直不太懂,今天正好称这机会学习下。

2.1 第一个角度:消除较小的联通区域 vs 弥合较小的联通区域

形态学开运算的作用有以下这些:

  • 消除值高于邻近点的孤立点,达到去除图像中噪声的作用;
  • 消除较小的连通域,保留较大的连通域;
  • 断开较窄的狭颈,可以在两个物体纤细的连接处将它们分离
  • 不明显改变较大连通域的面积的情况下平滑连通域的连界、轮廓;

形态学闭运算的作用有以下这些:

  • 消除值低于邻近点的孤立点,达到去除图像中噪声的作用;
  • 连接两个邻近的连通域;
  • 弥合较窄的间断和细长的沟壑
  • 去除连通域内的小型空洞
  • 和开运算一样也能够平滑物体的轮廓;

2.2 第二个角度:消除背景噪音 vs 消除前景噪音

开操作:消除背景噪音

 闭操作:填充前景物体中的小洞,或者前景物体上的小黑点

3、approPolyDp函数

函数的作用:对图像轮廓点进行多边形拟合

函数的的调用形式:

void approxPolyDP( InputArray curve,
                   OutputArray approxCurve,
                   double epsilon, 
                   bool closed );

 参数详解:

InputArray curve:一般是由图像的轮廓点组成的点集

OutputArray approxCurve:表示输出的多边形点集

 double epsilon:主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,,

bool closed:表示输出的多边形是否封闭

4、冒泡排序

这里有直观的动图展示:动图

这里用到冒泡排序对车牌字符的Rect进行排序:

    for (size_t i =0; i< Character_ROI.size(); i++)
    {
        for (size_t j=0; j< Character_ROI.size() -1 -i; j++)
        {
            if (Character_ROI[j].rect.x > Character_ROI[j+1].rect.x)
            {
                License temp = Character_ROI[j];
                Character_ROI[j] = Character_ROI[j+1];
                Character_ROI[j+1] = temp;
            }
        }
    }

 假设有5个字符,它们的Rect的X坐标是  4 1 3 0 2, 现在用冒泡排序进行排序:

5、匹配目标

这里使用OpenCV absdiff函数计算两张图像的像素差,以此来判断图像的相似程度。

其他方法除了模板匹配,基于Hu矩轮廓匹配,基于篇幅原因就在另外博客再学习。

6、putText函数打印中文

我用的是OpenCV4.5.5,Ubuntun20.04,直接引入头文件就好了。

字体文件路径(windows系统):

/Windows/Fonts/

然后复制到Ubuntu系统下某个目录就行了

示例:

#include <iostream>
#include <opencv2/freetype.hpp>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;


int main()
{
    Mat src = imread("/home/jason/work/01-img/dog.png");

    string text = "中华田园犬";

    Ptr<cv::freetype::FreeType2> ft2;
    ft2 = cv::freetype::createFreeType2();
    ft2->loadFontData("/usr/share/fonts/winFonts/SIMYOU.TTF",0);

    ft2->putText(src, text, Point(300, 200), 30 , Scalar(0, 0,255), 2, 8, true);

    imshow("src", src);
    waitKey();

    return 0;
}

 7、文字文件、标签文件

向博主私信了,但是没有回复,那就自己做一个。

7.1 文字文件

wps word导出的图片:

扣出字符来:

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

using namespace std;
using namespace cv;

void Get_character(Mat & src, Mat & result)
{
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);

    // 黑色的点
    vector<Point> locations;
    for (int x=0; x< src.cols; x++)
        for (int y=0; y< src.rows; y++)
        {

            if(gray.at<uchar>(y, x) < 255)
            {
                locations.push_back(Point(x,y));
            }
        }

    // 字符左上角 右下角
    double xmin, ymin, xmax, ymax;
    vector<int> xs, ys;
    for(size_t i=0; i<locations.size(); i++)
    {
        xs.push_back(locations[i].x);
        ys.push_back(locations[i].y);

    }
    Mat tempX(xs);
    Mat tempY(ys);
    Point p1;
    minMaxLoc(tempX, &xmin, &xmax,0,0);
    minMaxLoc(tempY, &ymin, &ymax, 0,0);


    // 画框
    Rect roi;
    Mat temp = src.clone();
    roi.x = xmin - 30;
    roi.y = ymin - 30;
    roi.width = xmax - xmin + 60;
    roi.height = ymax - ymin + 60;
    rectangle(src,roi, Scalar(255, 0,0), 1, 8);

    // 扣出来
    Mat ROI = temp(roi);
    imshow("ROI", ROI);
    result = ROI.clone();


    imshow("src", src);
    waitKey(10);
}


int main()
{

    string tail = ".png";
    string head;
    string path, outpath;
    string outpath_head = "/home/jason/work/01-img/car/car_roi/";
    for (int i=0; i<= 69; i++)
    {
        if (i<10)
        {
            head = "/home/jason/work/01-img/car/car/0";
        }

        else
        {
            head = "/home/jason/work/01-img/car/car/";
        }
        path = head + to_string(i) + tail;
        outpath = outpath_head + to_string(i) + tail;

        Mat src = imread(path);
        Mat result;
        Get_character(src, result);
        imwrite(outpath,result);

    }

    return 0;
}

7.2 标签文件

二、车牌识别代码

我在识别俄过程中,发现自己字母字体与车牌字体对应不上,就可能出现偏差。

怎么办?我干脆就把车牌字符扣下来保存为模板!

locate.hpp

#include <iostream>
#include <opencv2/opencv.hpp>
#include<opencv2/freetype.hpp>
#include <fstream>
using namespace cv;
using namespace std;



using namespace cv;
using namespace std;

// 自定义车牌结构体
struct License
{
    Mat mat; // ROI图片
    Rect rect; // ROI所在矩形
};


class Locate
{
private:
    // 车牌字符模板图片
    vector<Mat>  Dataset;

    // 车牌字符名
    vector<string> Data_name;

    // 字体文件路径
    string Font_Path;

    // 车牌字符扣出来另存路径
    string Character_Out_Path;

    bool Read_Data(string filename, vector<Mat>& dataset);
    bool Read_Data(string filename, vector<string>&data_name);

    void Image_Preprocessing(Mat& gray, Mat& result);

    void Morphological_Process(Mat& preprocess, Mat& result);

    void Character_ROI_Preprocessing(vector<License>& License_ROI);

    void Get_License_ROI(Mat &morpho, Mat &src,
                              vector<License>& License_ROI);
    void Remove_vertial_Border(Mat& car_bord, Mat& result);
    void Remove_Horizon_Border(Mat& car_bord, Mat & result);


public:
    bool Set_Input(string label_Path, string template_Path,
                   string font_Path, string character_out_path);

    void Get_License_ROI(Mat& src, vector<License>& License_ROI);

    void Get_Character_ROI(vector<License>& License_ROI,
                           vector<vector<License>>&Character_ROI,
                           Mat &src, bool character_save);

    int pixCount(Mat image);

    void License_Recognition(vector<vector<License>>&Character_ROI,
                             vector<vector<int>>&result_index);

    void Draw_Result(Mat &src,
                     vector<License>& License_ROI,
                     vector<vector<License>>&Character_ROI,
                     vector<vector<int>>&result_index);

};

locate.cpp

#include "Locate_License.h"


// 读取文件 图片
bool Locate::Read_Data(string filename, vector<Mat>& dataset)
{
    vector<String> imagePathList;
    glob(filename, imagePathList); // 遍历文件夹下所有文件
    if (imagePathList.empty()) return  false;

    for (size_t i=0; i<imagePathList.size(); i++)
    {
        cout << imagePathList[i] << endl;
        Mat image = imread(imagePathList[i]);
        resize(image, image, Size(50, 100), 1, 1, INTER_LINEAR);
        cvtColor(image, image, COLOR_BGR2GRAY);
        threshold(image, image, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); // 字符需要是白色

        Mat kernel = getStructuringElement(MORPH_RECT, Size(3,3));
        dilate(image, image,kernel,Point(-1,-1),1);


//        imshow(to_string(i), image);
        dataset.push_back(image);


    }
    this->Dataset = dataset;
    return true;
}

//读取文件 标签
bool Locate::Read_Data(string filename, vector<string>&data_name)
{
    fstream fin;
    fin.open(filename, ios::in);
    if(!fin.is_open())
    {
        cout << "can not open the file!" << endl;
        return false;
    }

    string s;
    while (getline(fin, s))
    {
        string str = s;
        data_name.push_back(str);

    }
    fin.close();

    this->Data_name = data_name;
    return  true;
}

bool Locate::Set_Input(string label_Path,
                       string template_Path,
                       string font_Path="/usr/share/fonts/winFonts/SIMYOU.TTF",
                       string character_out_path = "/home/jason/work/01-img/car/out")
{
    this->Font_Path = font_Path;
    printf("字体路径设置为: %s, 请检查该目录是否正确\n",font_Path.c_str());

    this->Character_Out_Path = character_out_path;
    printf("车牌字符输出路径设置为: %s, 请检查该目录是否正确\n",character_out_path.c_str());

    if (Read_Data(label_Path, this->Data_name) &&
            Read_Data(template_Path, this->Dataset))
    {
        printf("***** 成功读取模板图片、标签数据\n");
        return true;
    }
    else
    {
        printf("***** err:读取模板图片、标签数据\n");
        return false;
    }
}

// 突出字符
void Locate::Image_Preprocessing(Mat& gray, Mat& result)
{

    // 开操作,平滑作用,断开较窄的狭颈和消除细的突出物
    Mat kernel = getStructuringElement(MORPH_RECT, Size(25,25));
    Mat gray_blur;
    morphologyEx(gray, gray_blur, MORPH_OPEN, kernel);
    imshow("open1", gray_blur);

    // 灰度图-开操作图,突显字符等部分
    Mat rst;
    subtract(gray, gray_blur, rst, Mat());
    imshow("rst", rst);

    // Canny算子进行边缘检测
    Mat canny_Image;
    Canny(rst, canny_Image, 400, 200, 3);

    imshow("canny_Image", canny_Image);

    result=canny_Image.clone();
}


// 通过膨胀连接相近的图像区域,
// 利用腐蚀去除孤立细小的色块,从而将所有的车牌上所有的字符都连通起来
void Locate::Morphological_Process(Mat& preprocess, Mat& result)
{
    // 图片膨胀处理
    Mat dilate_image, erode_image;

    //自定义核:进行 x 方向的膨胀腐蚀
    Mat elementX = getStructuringElement(MORPH_RECT, Size(19, 1));
    Mat elementY = getStructuringElement(MORPH_RECT, Size(1, 19));
    Point point(-1, -1);

    dilate(preprocess, dilate_image, elementX, point, 2);
    imshow("dilate1", dilate_image);


//    // 闭操作,避免车牌与 其他区域联通在一起
//    Mat kernel = getStructuringElement(MORPH_RECT, Size(10, 10));
//    morphologyEx(dilate_image, dilate_image, MORPH_OPEN,
//                 kernel, Point(-1,-1),2);
//    imshow("MORPH_OPEN", dilate_image);

    erode(dilate_image, erode_image, elementX, point, 3);
    imshow("erode1", erode_image);

    dilate(erode_image, dilate_image, elementX, point, 2);
    imshow("dialte2", dilate_image);

    //自定义核:进行 Y 方向的膨胀腐蚀
    erode(dilate_image, erode_image, elementY, point, 1);
    imshow("yerode", erode_image);

    dilate(erode_image, dilate_image, elementY, point, 2);
    imshow("Ydilate", erode_image);

    // 平滑处理
    Mat median_Image;
    medianBlur(dilate_image, median_Image, 15);
    imshow("median1",median_Image);

    medianBlur(median_Image, median_Image, 15);
    imshow("median2", median_Image);
    result = median_Image.clone();
}


// 扣出车牌
void Locate::Get_License_ROI(Mat &morpho, Mat &src, vector<License>& License_ROI)
{
    vector<vector<Point>> contours;
    findContours(morpho, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    //
    Mat temp =src.clone();
    drawContours(temp, contours, -1, Scalar(255,0,0), 4);

    //
    double area;
    for (size_t i=0; i< contours.size(); i++)
    {
        // 轮廓 --》 rect
        Rect rect = boundingRect(contours[i]);

        // 车牌的宽高比大约为3.3
        double width_height = (double)rect.width/ (double)rect.height;
        printf("height_width:%.2f\n", width_height);
        if (width_height>2.5 && width_height < 4.0)
        {
            rectangle(temp, rect, Scalar(0,0, 255), 4, 8);
            License temp_license = {src(rect), rect};
            License_ROI.push_back(temp_license);
        }

    }
    imshow("标出车牌",temp);

    if (License_ROI.size() > 0)
    {
        printf("****** 共提取到 %d 块车牌\n",(int)License_ROI.size());
        for (size_t i = 0; i< License_ROI.size(); i++)
        {
            string tempName = "第" + to_string(i) + "块车牌";
            imshow(tempName, License_ROI[i].mat);
        }
    }
    else
    {
        printf("****** 没有发现车牌\n");
    }



}

// 从图片中扣出车牌
void Locate::Get_License_ROI(Mat& src, vector<License>& License_ROI)
{

    // 灰度图
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    imshow("gray", gray);

    // 均衡化
    equalizeHist(gray, gray);


    // 突出字符,并获得canny边缘
    Mat preprocess_result;
    Image_Preprocessing(gray, preprocess_result);

    // 将车牌字符形成一个整体
    Mat morpho_image;
    Morphological_Process(preprocess_result, morpho_image);


    // 扣出整块车牌
    Get_License_ROI(morpho_image, src, License_ROI);

}


void Locate::Remove_vertial_Border(Mat& car_bord, Mat & result)
{
    Mat vline = getStructuringElement(MORPH_RECT, Size(1,car_bord.rows));
    Mat dst1, temp1;

    erode(car_bord, temp1, vline);
//    imshow("V-erode",temp1);

    dilate(temp1, dst1, vline);
//    imshow("V-dilate",dst1);

    subtract(car_bord, dst1, result, Mat());
//    imshow("V-result",result);
}




void Locate::Remove_Horizon_Border(Mat& car_bord, Mat & result)
{
    Mat hline = getStructuringElement(MORPH_RECT, Size(car_bord.rows,1));
    Mat dst1, temp1;

    erode(car_bord, temp1, hline);
//    imshow("H-erode",temp1);

    dilate(temp1, dst1, hline);
//    imshow("H-dilate",dst1);

    subtract(car_bord, dst1, result, Mat());
//    imshow("H-result",result);
}


// 对整块车牌进行预处理,
void Locate::Character_ROI_Preprocessing(vector<License>& License_ROI)
{
    for (size_t i=0; i<License_ROI.size(); i++)
    {
        // 灰度化
        Mat gray;
        cvtColor(License_ROI[i].mat, gray, COLOR_BGR2GRAY);
        imshow("gray--", gray);



//        // 均衡化 这里不需要用,用了方而效果不好,因为车牌中车牌字符本身就很显眼,不需要用均衡
//        equalizeHist(gray, gray);


        // 大津阈值化
        Mat thresh;
        threshold(gray, thresh, 0, 255, THRESH_BINARY|THRESH_OTSU ); // 字是白色的的
        imshow("thres", thresh);

        Mat hori;
        Remove_Horizon_Border(thresh, hori);

        Mat vert;
        Remove_vertial_Border(hori,vert);
        imshow("H V", vert);

        Mat open;
        Mat kernel = getStructuringElement(MORPH_RECT, Size(2,2));
        morphologyEx(vert, open,MORPH_CLOSE, kernel, Point(-1,-1),1);
        imshow("连接汉字两边", open);

        License_ROI[i].mat = open.clone();

    }

}

//
void Locate::Get_Character_ROI(vector<License>& License_ROI,
                               vector<vector<License>>&Character_ROI,
                               Mat &src,bool character_save=true)
{
    Character_ROI_Preprocessing(License_ROI);
    Mat temp = src.clone();

    for (size_t j=0; j<License_ROI.size(); j++)
    {
        Mat temp_carbod = License_ROI[j].mat.clone();
        Character_ROI.push_back({}); // 必须先添加一个空项进去


        vector<vector<Point>> contours;
        findContours(License_ROI[j].mat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
        drawContours(temp, contours, -1, Scalar(255,0,0), 2, 8);
        imshow("Get_Character_ROI", temp);

        for (size_t i = 0; i<contours.size(); i++)
        {
            double area = contourArea(contours[i]);
            //由于我们筛选出来的轮廓是无序的,故后续我们需要将字符重新排序
            if (area > 100)
            {
                Rect rect = boundingRect(contours[i]);
                // 计算外接矩形框高比
                double ratio = double(rect.height)/ double(rect.width);
                if (ratio > 1)
                {
                    // 字符扣出来
                    Mat roi = License_ROI[j].mat(rect);
                    resize(roi, roi, Size(50, 100), 1, 1, INTER_LINEAR);
                    Character_ROI[j].push_back({roi, rect});  // 前面不添加一个空项进去,这就就报错

                    // 字符在原图画框
                    rectangle(temp_carbod ,rect, Scalar(255, 0, 0), 2, 8);
                    imshow("字符框",temp_carbod);


                    // 字符另外为
                    if (character_save)
                    {
                        threshold(roi,roi,0, 255, THRESH_BINARY_INV|THRESH_OTSU);
                        string outpath = this->Character_Out_Path + "/" + to_string(i) + ".png";
                        imwrite(outpath,roi);

                    }
                }
            }
        }

        //将筛选出来的字符轮廓 按照其左上角点坐标从左到右依次顺序排列
        // 冒泡排序 ; 你查一下,用41302自己排下序就懂了
        for (size_t k =0; k<Character_ROI.size(); k++)
        {
            for (size_t ii =0; ii< Character_ROI[k].size(); ii++)
            {
                for (size_t jj=0; jj< Character_ROI[k].size() -1 -ii; jj++)
                {
                    if (Character_ROI[k][jj].rect.x > Character_ROI[k][jj+1].rect.x)
                    {
                        License temp = Character_ROI[k][jj];
                        Character_ROI[k][jj] = Character_ROI[k][jj+1];
                        Character_ROI[k][jj+1] = temp;
                    }
                }
            }
        }

    }

    if (Character_ROI.size() > 0)
    {
        for (size_t k =0; k<Character_ROI.size(); k++)
        {
            printf("******* 第 %d 块车牌共扣出: %d 个字符\n", (int)k,(int)Character_ROI[k].size());
        }
    }
    else
    {
        printf("***** err :第车牌没有扣出字符!\n");

    }

}



int Locate::pixCount(Mat image)
{
    int count =0;
    if (image.channels() == 1)
    {
        for (int i=0; i<image.rows; i++)
        {
            for (int j=0; j<image.cols; j++)
            {
                if (image.at<uchar>(i, j) == 255) // 数的是白色像素
                {
                    count++;
                }
            }
        }
        return count;
    }
    else
    {
        return -1;
    }
}

// 识别车牌字符
// 使用OpenCV absdiff函数计算两张图像的像素差,以此来判断图像的相似程度
// 进行字符匹配的方法还有:模板匹配,基于Hu矩轮廓匹配
void Locate::License_Recognition(vector<vector<License>>&Character_ROI,
                                 vector<vector<int>>& result_inedx)
{

    for (size_t k =0; k<Character_ROI.size(); k++)
    {
        result_inedx.push_back({});

        for (int i=0; i<Character_ROI[k].size(); i++)
        {
            // 车牌单个字符预处理
            Mat roi_thresh;
            threshold(Character_ROI[k][i].mat, roi_thresh, 0, 255, THRESH_BINARY_INV); // 车牌字符需是白色
            string car = "car" + to_string(i);
            imshow(car,roi_thresh);


            int minCount = 1000000000;
            int index = 0;

            for (int j=0; j < this->Dataset.size(); j++)
            {

                // 计算车牌字符与模板的像素差,以此判断两张图片是否相同
                Mat templa = this->Dataset[j];
                Mat dst;
                absdiff(roi_thresh, templa, dst);


                // 白字黑底,两图像素相减,白色像素越少,两图越接近
                int  count = pixCount(dst);
                if (count< minCount)
                {
                    minCount = count;
                    index = j;
                }
    //            imshow(to_string(j),dst);
            }
            string p = "templ" + to_string(i);
            imshow(p, this->Dataset[index]);
            result_inedx[k].push_back(index);
        }
    }

    printf("*****共对 %d 块车牌的字符完成字符匹配\n",(int)Character_ROI.size());

}


// 显示最终效果
void Locate::Draw_Result(Mat &src, vector<License> &License_ROI,
                         vector<vector<License>>&Character_ROI,
                 vector<vector<int>>&result_index)
{
    Ptr<cv::freetype::FreeType2> ft2;
    ft2 = cv::freetype::createFreeType2();

    ft2->loadFontData(this->Font_Path,0);

    for (size_t k=0; k<License_ROI.size(); k++)
    {

        // 原图上框出车牌
        rectangle(src, License_ROI[k].rect, Scalar(0, 255, 0), 2);

        // 在原图车牌框上方上打印车牌字符
        for (size_t i=0; i< Character_ROI[k].size(); i++)
        {
    //        cout << data_name[result_index[i]] << " ";
            string str = this->Data_name[result_index[k][i]];
            ft2->putText(src, str,
                         Point(License_ROI[k].rect.x + Character_ROI[k][i].rect.x,
                               License_ROI[k].rect.y - Character_ROI[k][i].rect.y),
                         30,Scalar(255, 0, 0), 1, 8, true);
        }
    //    cout  << endl;
    }

}

main.cpp

#include "Locate_License.h"



int main()
{

    Mat src = imread("/home/jason/work/01-img/car.png");
    if (src.empty())
    {
        cout << "No image!" << endl;
        system("pause");
        return -1;
    }

    Locate locate;
    locate.Set_Input("/home/jason/work/01-img/car/car.txt",
                     "/home/jason/work/01-img/car/template",
                     "/usr/share/fonts/winFonts/SIMYOU.TTF",
                     "/home/jason/work/01-img/car/out");

    vector<License> License_ROI;
    locate.Get_License_ROI(src, License_ROI);

    vector<vector<License>> Character_ROI;
    locate.Get_Character_ROI(License_ROI, Character_ROI,
                             src, true);

    vector<vector<int>> result_index;
    locate.License_Recognition(Character_ROI,result_index);

    locate.Draw_Result(src, License_ROI,
                       Character_ROI, result_index);

    imshow("车牌识别结果", src);
    waitKey();






    return 0;
}

想要模板图片文件和标签文件可以在评论区留言或者私信我,上传在CSDN还得是VIP你们才能下载。

三、项目总结

代码思路:

  1. 获取整个车牌  (这里部分涉及预处理很有意思)
  2. 对车牌进行切割,获得7个字符
  3. 将获得的车牌字符与模板匹配

项目不足:

  1. 本项目仅仅对车牌字符为白色的车牌有用
  2. 未对车牌作旋转矫正【p1, p2】,透视矫正,这两个因素影响很大,后面有空再补上

ps: 这个项目做了好几天,cpp文件干到了500行,原文才300行,增加近一半代码,短时间不想改了

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

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

相关文章

【LeetCode】222.完全二叉树的节点数

1.问题 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位…

【MySQL】存放页面的大池子——InnoDB的表空间

1 前置知识回顾 1.1 页面类型 InnoDB是以页为单位管理存储空间的。我们的聚簇索引&#xff08;也就是完整的表数据&#xff09;和其他的二级索引都是以B树的形式保存到表空间中&#xff0c;而B树中的节点就是数据页&#xff0c;这个数据页的类型名其实是 FIL_PAGE_INDEX。 除…

JS手撕代码系列【手写实现Promise.all】

Promise.all() 方法接收一个 Promise 对象数组作为参数&#xff0c;返回一个新的 Promise 对象。该 Promise 对象在所有的 Promise 对象都成功时才会成功&#xff0c;其中一个 Promise 对象失败时&#xff0c;则该 Promise 对象立即失败。 本篇博客将手写实现 Promise.all() 方…

初始 Ajax

文章目录 一、服务器的基本概念1、客户端与服务器2、url地址3、客户端与服务器的通信过程4、服务器对外提供资源5、资源的请求方式 二、初始 Ajax1、了解Ajax2、JQuery中的Ajax&#xff08;1&#xff09;$.get()函数的用法&#xff08;2&#xff09;$.post()函数的语法&#xf…

国民技术N32G430开发笔记(7)- Gpio EXTI中断的使用

GPIO EXTI中断的使用 1、N32G430C8L7_STB板卡带有三个用户按键&#xff0c;我们初始化key1 key2 按键&#xff0c;当按键按下时&#xff0c;在中断处理函数里输出我们的打印信息。 2、根据芯片手册上N32G430C8L7有24 条中断线&#xff0c;16条分配给GPIO使用&#xff0c;其余八…

中国移动发布COCA软硬一体片上计算架构,引领云计算市场下一个黄金十年

当前&#xff0c;数字经济发展已经成为改变全球竞争格局的关键力量&#xff0c;随着算力成为数字经济新引擎&#xff0c;算力规模持续增长&#xff0c;算力结构发生改变。主动拥抱智算浪潮&#xff0c;持续输出优质算力支撑数字中国建设&#xff0c;适配泛在化、异构化算力推动…

JavaWeb之过滤器Filter(通俗易懂版)

今天开发遇到了&#xff0c;简单记录一下&#xff01; 简介&#xff1a;Filter是JavaWeb三大组件之一&#xff08;Servlet程序、Listener监听器、Filter过滤器&#xff09; 作用&#xff1a;既可以对请求进行拦截&#xff0c;也可以对响应进行处理。 1、Filter中的三个方法 …

【无标题】c++异常机制的一些总结以及思考

在谈及c处理异常机制的方法之前我们不妨来回顾一下c语言是如何应对这块的。 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以接受。如发生内存错误&#xff0c;除0错误时就会终止程序。 返回错误码&#xff0c;缺陷&#xff1a;需要程序员自己去查找对应的…

深兰科技再获欧洲订单,中国造智能机器人出海“服务”西班牙

近日&#xff0c;经过包含关键技术、场景适配性、任务完成效率、产品操作便捷度等考核标准在内多轮综合对比&#xff0c;深兰科技突出重围&#xff0c;斩获西班牙一笔价值百万的智能室内清洁机器人采购订单。 多重问题显示&#xff0c;欧洲清洁市场亟待科技赋能 在当前的经济形…

VUE初级知识点总结

前言 近几年随着HTML5的普及&#xff0c;原来的jsp逐渐在被淘汰&#xff0c;而vue成了很多前端开发者的心仪的js框架&#xff0c;因为它相对于其他两大框架&#xff08;Angula、React&#xff09;更简单易学&#xff0c;当然了这里的简单易学指的是上手快&#xff0c;在不知道…

7.3 股票分析(project)

目录 第1关&#xff1a;涨幅和成交量 第2关 涨幅与最高价 第3关 跌幅与最低价 本关任务&#xff1a;完成涨幅和成交量股票分析。 相关知识 1.sorted()函数 2.集合运算 sorted()函数 sorted() 函数对所有可迭代的对象进行排序操作。 sorted(iterable, keyNone, reverseFal…

Linxu下性能指标采集工具之nmon工具的使用

前言 近期在测试JefLogTail&#xff0c;由于JefLogTail使用的是轮询的方式来监听文件夹&#xff0c;所以对cpu的消耗可能会高一些&#xff0c;所以在测试的时候着重关注CPU,Linux下查看CPU信息一般采用top命令来实时观察&#xff0c;但是这种对于只是通过观察数据的变化来评估…

跟着我学习 AI丨初识 AI

人工智能&#xff08;AI&#xff09;是一种模拟人类思维和行为的计算机技术&#xff0c;通过学习、推理和自我修正等方式&#xff0c;使机器能够模拟人类智能&#xff0c;并具有一定的自主决策能力。AI 可以被用于解决各种难题&#xff0c;如自动化、机器人、自动驾驶、语音识别…

【LeetCode】53. 最大子数组和

53. 最大子数组和&#xff08;中等&#xff09; 思路 这道题的状态设计和状态转移和 300. 最长递增子序列 类似。但是这里要求的是连续子数组&#xff0c;和子序列不同。 状态定义 首先定义 dp[i]&#xff1a;以 nums[i] 结尾的具有最大和的连续子数组。 状态转移方程 根据…

idea项目打成war包,出现路径问题(已解决)

参考文档&#xff1a; https://www.cnblogs.com/huaixiaonian/p/10521460.html 解释说明&#xff1a; 什么路径问题呢&#xff0c;就比如你们合作开发了一个项目&#xff0c;然后打成了一个war包&#xff0c;然后路径以前的是这种的 http://localhost:8080 就可以直接运行&…

《计算机网络——自顶向下方法》精炼

“An investment in knowledge pays the best interest.” - Benjamin 文章目录 分布式散列表&#xff08;键-值&#xff09;对散列函数逻辑上的实现环形DHT对等方扰动对等方离开对等方加入 UDP套接字编程客户端代码服务器端代码 分布式散列表 分布式散列表是一种数据库。集中…

锁相环的工作原理

锁相环是一个能够比较输入与输出信号相位差的反馈系统,利用外部输入的参考信号控制环路内部的振荡信号的频率和相位,使振荡信号同步至参考信号。 问题:既然是利用外部的参考时钟控制内部的振荡信号频率和相位使内部信号同步至外部参考,那为什么不直接用导线将外部参考信号…

微服务监控系统选型:Zabbix、Open-Falcon、Prometheus

监控系统的 7 大作用 实时采集监控数据&#xff1a;包括硬件、操作系统、中间件、应用程序等各个维度的数据。实时反馈监控状态&#xff1a;通过对采集的数据进行多维度统计和可视化展示&#xff0c;能实时体现监控对象的状态是正常还是异常。预知故障和告警&#xff1a;能够提…

08 【Sass语法介绍-混合指令】

1.前言 混合指令在 Sass 中也是一个比较常用的指令&#xff0c;在前面我们讲解的内容中有编写过混合指令 mixin &#xff0c;本节我们将详细讲解混合指令 mixin 的语法包括定义混合指令和引用混合指令等等&#xff0c;混合指令同样非常好用&#xff0c;我们一起来学习它吧。 …

python 离线安装pyinstaller

Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirrorhttps://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/下载各类anaconda Archived: Python Extension Packages for Windows - Christoph Gohlke (uci.edu)https://www.lfd.uci.edu/~g…