• 《图像处理实例》 之 答题卡检测


    提前说明一下:这是“禾路”老师博客上的一个例子,老师在51cto上有课程,大家如果需要可以去看一下http://edu.51cto.com/lecturer/8887491.html

    本博文是参考老师的教程,自己消化理解之后进行了部分代码的改进,发表未经原作者允许,如果有侵犯版权请告知立马删除!


     目标:检测以下相机没有拍摄好的答题卡:

    第一步:定位点检测

            从上图可以看到四个黑圆圈,这个就是定位用的四个角,我们检测这四个角就可以进行答题卡的定位:

              方法一:利用霍夫圆变换,进行圆心的查找。

              方法二:轮廓区域检测

              方法三:模板匹配

              方法四:特征检测匹配

            本文利用方法三的模板匹配,其它方法完全可行的,如果不知道其它方法可以看看我的其它博文,都有例子。

    --------->>>>模板匹配的黑点截取出来(照一张好的图片去测量截取)

    上代码:此代码都是原作者发表过的,版权的代码不会发表

     1 //--------------------------------注释代码部分为未用掩码操作--------------------------//
     2 void FindAnchorPoint(const Mat& src,const Mat& matchMask,vector<Point2f>& anchorPoint)
     3 {
     4     Mat matchResult;
     5     matchResult.create(Size(src.cols - matchMask.cols, src.rows - matchMask.rows), CV_16SC1);
     6     //----模板匹配找四个定位点,同时得归一化(初始数据范围太大,自己通过image watch 查看)
     7     matchTemplate(src, matchMask, matchResult, TM_CCOEFF, Mat());
     8     normalize(matchResult, matchResult, 0, 1, NORM_MINMAX);
     9     //----查找匹配的四个点,分成四个区域查找,因为一个区域没办法查找四个值
    10     /*Mat topleft  = matchResult(Rect(Point(0, 0), Point(matchResult.cols / 2, matchResult.rows / 2)));
    11     Mat topright = matchResult(Rect(Point(matchResult.cols / 2, 0), Point(matchResult.cols, matchResult.rows / 2)));
    12     Mat botleft  = matchResult(Rect(Point(0, matchResult.rows / 2), Point(matchResult.cols / 2, matchResult.rows)));
    13     Mat botright = matchResult(Rect(Point(matchResult.cols / 2, matchResult.rows / 2), Point(matchResult.cols , matchResult.rows)));*/
    14     double maxValue[4] = { 0 }, minValue[4] = {0};
    15     vector<Point2i> maxPoint(4), minPoint(4);
    16     Mat topleftMask  = Mat::zeros(matchResult.size(), CV_8UC1);
    17     Mat toprightMask = Mat::zeros(matchResult.size(), CV_8UC1);
    18     Mat botleftMask  = Mat::zeros(matchResult.size(), CV_8UC1);
    19     Mat botrightMask = Mat::zeros(matchResult.size(), CV_8UC1);
    20     topleftMask(Rect(Point(0, 0), Point(matchResult.cols / 2, matchResult.rows / 2))).setTo(255);
    21     toprightMask(Rect(Point(matchResult.cols / 2, 0), Point(matchResult.cols, matchResult.rows / 2))).setTo(255);
    22     botleftMask(Rect(Point(0, matchResult.rows / 2), Point(matchResult.cols / 2, matchResult.rows))).setTo(255);
    23     botrightMask(Rect(Point(matchResult.cols / 2, matchResult.rows / 2), Point(matchResult.cols, matchResult.rows))).setTo(255);
    24     vector<Mat> vectorMask;//注意此处如果用vector<Mat> vectorMask(4);对应的下面写法是vectorMask[0]=topleftMask;
    25     vectorMask.push_back(topleftMask);
    26     vectorMask.push_back(toprightMask);
    27     vectorMask.push_back(botleftMask);
    28     vectorMask.push_back(botrightMask);
    29     for (size_t i = 0; i < vectorMask.size(); i++)
    30     {
    31         minMaxLoc(matchResult, &minValue[i], &maxValue[i], &minPoint[i], &maxPoint[i], vectorMask[i]);
    32     }
    33     //minMaxLoc(topleft, &minValue[0], &maxValue[0], &minPoint[0], &maxPoint[0]);
    34     //minMaxLoc(topright, &minValue[1], &maxValue[1], &minPoint[1], &maxPoint[1]);
    35     //maxPoint[1].x = maxPoint[1].x + matchResult.cols / 2;
    36     //minMaxLoc(botleft,  &minValue[2], &maxValue[2], &minPoint[2], &maxPoint[2]);
    37     //maxPoint[2].y = maxPoint[2].y + matchResult.rows / 2;
    38     //minMaxLoc(botright, &minValue[3], &maxValue[3], &minPoint[3], &maxPoint[3]);
    39     //maxPoint[3].x = maxPoint[3].x + matchResult.cols / 2;
    40     //maxPoint[3].x = maxPoint[3].y + matchResult.rows / 2;
    41 }

    结果图片:

     第二步:定位线检测

            定位线:每一个涂卡区域都是由X、Y两个轴共同定位。

    由以上的分析可知,我们这一步的操作是找到这些定位线,再由这些定位线去找每个涂卡区的坐标。

    ------>>>>>裁剪定位上下左右四个区域,利用投影算法找出定位线。

                                                  

     

    上代码:

     1 //------------------------------------图像投影算法-----------------------------------------------//
     2 //*************@src------------------输入矩阵为单通道********************************************//
     3 //*************@leftUpJumpWave-------上升跳变沿存储**********************************************//
     4 //*************@rightDownJumpWave----下降跳变沿存储**********************************************//
     5 //*************@maxInterval----------允许高电平(像素)最大间隔,也可以说是允许的最大误差********//
     6 //-----------------------------------------------------------------------------------------------//
     7 void projectionAlgorithm(Mat src,vector<int>& UpJumpWave,vector<int>& DownJumpWave,bool Axis,int maxInterval)
     8 {
     9     vector<int> pixNum(src.rows > src.cols ? src.rows : src.cols);
    10     //------对X、Y做直方图类似的投影,统计一行或者一列的非零个数--------//
    11     if (Axis)
    12     {
    13         for (size_t i = 0; i < src.cols; i++)
    14         {
    15             Mat col = src.col(i);//一列数据
    16             pixNum[i] = countNonZero(col) > 1 ? countNonZero(col) : 0;
    17         }
    18     }
    19     else
    20     {
    21         
    22         for (size_t i = 0; i < src.rows; i++)
    23         {
    24             Mat row = src.row(i);//一行数据
    25             pixNum[i] = countNonZero(row) > 1 ? countNonZero(row) : 0;
    26         }
    27     }
    28     if (pixNum.size() < maxInterval) return;//防止有空洞(实际没见过,如果有的话那程序架构会奔溃了)
    29     //-----对上面的数据进行二值化0-1,同时对于不满足maxInterval的数据进行剔除--------//
    30     for (int k = 1; k < pixNum.size()-maxInterval; k++)//去除了第一个和最后一个像素
    31     {
    32         if (pixNum[k] > 0 && pixNum[k + maxInterval] > 0)
    33         {
    34             for (size_t j = k; j < k + maxInterval; j++)
    35             {
    36                 pixNum[j] = 1;
    37             }
    38             k = k + maxInterval-1;
    39         }
    40         else
    41         {
    42             pixNum[k] = 0;
    43         }
    44     }
    45     //----对跳变的电平进行存储,高->低,低->高,-----//
    46     for (size_t i = 1 ; i < pixNum.size()-2; i++)//去除了第一个和最后一个像素
    47     {
    48         if (pixNum[i] == 0 && pixNum[i + 1] == 1) UpJumpWave.push_back(i);
    49         if (pixNum[i] == 1 && pixNum[i + 1] == 0) DownJumpWave.push_back(i);
    50     }
    51     //----对得到的结果进行处理,定位点被误判----//
    52     vector<int>::iterator begin = UpJumpWave.begin();    
    53     if (UpJumpWave[0] < 15)                    UpJumpWave.erase(begin);
    54     vector<int>::iterator end = UpJumpWave.end()-1;
    55     if (UpJumpWave[UpJumpWave.size()-1] > 330) UpJumpWave.erase(end);
    56 }

     

    第三步:检测涂卡区域的状态

            这一步是我自己写的,没有参考别人程序,如果有错误的地方请不吝指教!

            思路:找到检测的点,然后利用非零区域进行判断,想法很简单但是实现完全实现很多小技巧,具体看代码。

     上代码:

     1 //-----------------------------------------------------------------------------------//
     2 //************************************检测涂卡区域函数***********************************//
     3 void checkKeypoint(Mat& _src,vector<Point2f>& allPoint,vector<Point2f>& testkeyPoint)
     4 {
     5     Mat src = _src.clone();
     6     Mat show = Mat::zeros(src.size(), CV_8UC3);
     7     morphologyEx(src, src, MORPH_DILATE, Mat::ones(3, 3, CV_8UC1));
     8     for (size_t i = 0; i < allPoint.size(); i++)
     9     {
    10         //------判断检测点的正方形涂卡区的非零个数---------//
    11         if (allPoint[i].x == 0 || allPoint[i].y == 0)
    12         {
    13             allPoint[i].x += 1;
    14             allPoint[i].y += 1;
    15         }
    16         Mat rec = src(Rect(static_cast<int>(allPoint[i].x - 1 ), static_cast<int>(allPoint[i].y - 1 ), 12, 5));
    17         int count = countNonZero(rec);
    18         if (count > 15)
    19         {
    20             testkeyPoint.push_back(allPoint[i]);
    21             rectangle(show, Rect(allPoint[i], Point(allPoint[i].x + 13, allPoint[i].y + 5)), Scalar(0, 0, 255));
    22         }
    23     }
    24 }

     整体代码:(再次申明:核心是参考禾路老师的,细节处理和部分代码是自己加的,如有侵权请告知,立马删除)

      1 #include <opencv2/opencv.hpp>
      2 #include <iostream>
      3 #include "math.h"
      4 using namespace cv;
      5 using namespace std;
      6 
      7 #if 1
      8 const bool X_Axis = true;
      9 const bool Y_Axis = false;
     10 
     11 void FindAnchorPoint(const Mat& src, const Mat& matchMask, vector<Point2f>& anchorPoint);
     12 void projectionAlgorithm(Mat src, vector<int>& UpJumpWave, vector<int>& DownJumpWave, bool Axis, int maxInterval);
     13 void checkKeypoint( Mat& _src, vector<Point2f>& allPoint, vector<Point2f>& testkeyPoint);
     14 int main(int argc,char** argv)
     15 {
     16     //变量
     17     //读取图片
     18     Mat standImage = imread("SheetStand.jpg");
     19     Mat perImage   = imread("perspective3.bmp");
     20     //Mat perImage = imread("perspective.jpg");
     21     //Mat matchMask  = imread("Circle.jpg");
     22     //-----------生成模板图片R = 11
     23     Mat matchMask;
     24     matchMask.create(Size(24, 24), CV_8UC3);
     25     matchMask.setTo(255);
     26     circle(matchMask, Point(11, 11), 11, Scalar(0), -1);
     27 
     28     resize(perImage, perImage, Size(600, 600));
     29     vector<Point2f> stdAncherPoint(4);
     30     vector<Point2f> perAncherPoint(4);
     31     FindAnchorPoint(standImage, matchMask, stdAncherPoint);
     32     FindAnchorPoint(perImage,   matchMask, perAncherPoint);
     33     Mat change = getPerspectiveTransform(perAncherPoint, stdAncherPoint);
     34     Mat resultPerImage;
     35     warpPerspective(perImage, resultPerImage, change, resultPerImage.size());
     36     FindAnchorPoint(resultPerImage, matchMask, perAncherPoint);
     37 
     38     Mat grayImage = resultPerImage;//.clone();
     39     Mat show = resultPerImage.clone();
     40     cvtColor(grayImage, grayImage, CV_BGR2GRAY);
     41     threshold(grayImage, grayImage,90,255, THRESH_BINARY_INV);
     42     vector<Mat> vectorGrayImage(4);
     43     vectorGrayImage[0] = grayImage(Rect(perAncherPoint[0].x+4, 0, 15, standImage.rows));//LEFT
     44     vectorGrayImage[1] = grayImage(Rect(perAncherPoint[1].x+4 , 0, 15, standImage.rows));//RIGHT
     45     vectorGrayImage[2] = grayImage(Rect(0,perAncherPoint[0].y+4, standImage.cols, 15));//TOP
     46     vectorGrayImage[3] = grayImage(Rect(0,perAncherPoint[2].y+4, standImage.cols, 15));//BOTTOM
     47     vector<vector<int>> upJumpWave(4);
     48     vector<vector<int>> downJumpWave(4);    
     49     for (size_t i = 0; i < 4; i++)
     50     {
     51         if (i<2) projectionAlgorithm(vectorGrayImage[i], upJumpWave[i], downJumpWave[i], Y_Axis, 2);
     52         else     projectionAlgorithm(vectorGrayImage[i], upJumpWave[i], downJumpWave[i], X_Axis, 2);
     53     }
     54     //-----------------------绘制检测的跳变线-------------------------//
     55     for (size_t i = 0; i < upJumpWave[0].size(); i++)
     56     {
     57         line(grayImage, Point(perAncherPoint[0].x + 11, upJumpWave[0][i]), Point(perAncherPoint[0].x + 22, upJumpWave[0][i]), Scalar(255, 255, 255));
     58     }
     59     for (size_t i = 0; i < upJumpWave[3].size(); i++)
     60     {
     61         line(grayImage, Point(upJumpWave[3][i], perAncherPoint[3].y), Point(upJumpWave[3][i], perAncherPoint[3].y + 11), Scalar(255, 255, 255));
     62     }
     63     for (size_t i = 0; i < upJumpWave[1].size(); i++)
     64     {
     65         line(grayImage, Point(perAncherPoint[1].x, upJumpWave[1][i]), Point(perAncherPoint[1].x + 11, upJumpWave[1][i]), Scalar(255, 255, 255));
     66     }
     67     for (size_t i = 0; i < upJumpWave[2].size(); i++)
     68     {
     69         line(grayImage, Point(upJumpWave[2][i], perAncherPoint[0].y + 11), Point(upJumpWave[2][i], perAncherPoint[0].y + 22), Scalar(255, 255, 255));
     70     }
     71     //-------------把所有的点存储在容器里,以供下面的函数调用--------------//
     72     vector<Point2f> allPoint;
     73     for (size_t i = 1; i < upJumpWave[2].size(); i++)//存储上半部分图卡点(准考证号区+旁边那个看不清的区域)
     74     {
     75         for (size_t j = 0; j < 10; j++)
     76         {
     77             allPoint.push_back(Point(upJumpWave[2][i], upJumpWave[1][j]));
     78         }
     79     }
     80     for (size_t i = 0; i < upJumpWave[3].size(); i++)//存储下半部分图卡点(答题区)
     81     {
     82         for (size_t j = 10; j < upJumpWave[1].size(); j++)
     83         {
     84             allPoint.push_back(Point(upJumpWave[3][i], upJumpWave[1][j]));
     85         }
     86     }
     87     //--------------检测涂上铅笔的区域--------------//
     88     vector<Point2f> testKeyPoint;
     89     checkKeypoint(grayImage, allPoint, testKeyPoint);
     90     for (size_t i = 0; i < testKeyPoint.size(); i++)
     91     {
     92         rectangle(show, Rect(testKeyPoint.at(i), Point(testKeyPoint.at(i).x + 12, testKeyPoint.at(i).y + 5)), Scalar(0, 0, 255));
     93     }
     94     waitKey();
     95     return 0;
     96 }
     97 //--------------------------------注释代码部分为未用掩码操作--------------------------//
     98 void FindAnchorPoint(const Mat& src,const Mat& matchMask,vector<Point2f>& anchorPoint)
     99 {
    100     Mat matchResult;
    101     matchResult.create(Size(src.cols - matchMask.cols, src.rows - matchMask.rows), CV_16SC1);
    102     //----模板匹配找四个定位点,同时得归一化(初始数据范围太大,自己通过image watch 查看)
    103     matchTemplate(src, matchMask, matchResult, TM_CCOEFF_NORMED, Mat());
    104     normalize(matchResult, matchResult, 0, 1, NORM_MINMAX);
    105     //----查找匹配的四个点,分成四个区域查找,因为一个区域没办法查找四个值
    106     /*Mat topleft  = matchResult(Rect(Point(0, 0), Point(matchResult.cols / 2, matchResult.rows / 2)));
    107     Mat topright = matchResult(Rect(Point(matchResult.cols / 2, 0), Point(matchResult.cols, matchResult.rows / 2)));
    108     Mat botleft  = matchResult(Rect(Point(0, matchResult.rows / 2), Point(matchResult.cols / 2, matchResult.rows)));
    109     Mat botright = matchResult(Rect(Point(matchResult.cols / 2, matchResult.rows / 2), Point(matchResult.cols , matchResult.rows)));*/
    110     double maxValue[4] = { 0 }, minValue[4] = {0};
    111     vector<Point2i> maxPoint(4), minPoint(4);
    112     Mat topleftMask  = Mat::zeros(matchResult.size(), CV_8UC1);
    113     Mat toprightMask = Mat::zeros(matchResult.size(), CV_8UC1);
    114     Mat botleftMask  = Mat::zeros(matchResult.size(), CV_8UC1);
    115     Mat botrightMask = Mat::zeros(matchResult.size(), CV_8UC1);
    116     topleftMask(Rect(Point(0, 0), Point(matchResult.cols / 2, matchResult.rows / 2))).setTo(255);
    117     toprightMask(Rect(Point(matchResult.cols / 2, 0), Point(matchResult.cols, matchResult.rows / 2))).setTo(255);
    118     botleftMask(Rect(Point(0, matchResult.rows / 2), Point(matchResult.cols / 2, matchResult.rows))).setTo(255);
    119     botrightMask(Rect(Point(matchResult.cols / 2, matchResult.rows / 2), Point(matchResult.cols, matchResult.rows))).setTo(255);
    120     vector<Mat> vectorMask;//注意此处如果用vector<Mat> vectorMask(4);对应的下面写法是vectorMask[0]=topleftMask;
    121     vectorMask.push_back(topleftMask);
    122     vectorMask.push_back(toprightMask);
    123     vectorMask.push_back(botleftMask);
    124     vectorMask.push_back(botrightMask);
    125     for (size_t i = 0; i < vectorMask.size(); i++)
    126     {
    127         minMaxLoc(matchResult, &minValue[i], &maxValue[i], &minPoint[i], &maxPoint[i], vectorMask[i]);
    128     }
    129     anchorPoint.assign(maxPoint.begin(), maxPoint.end());
    130     //minMaxLoc(topleft, &minValue[0], &maxValue[0], &minPoint[0], &maxPoint[0]);
    131     //minMaxLoc(topright, &minValue[1], &maxValue[1], &minPoint[1], &maxPoint[1]);
    132     //maxPoint[1].x = maxPoint[1].x + matchResult.cols / 2;
    133     //minMaxLoc(botleft,  &minValue[2], &maxValue[2], &minPoint[2], &maxPoint[2]);
    134     //maxPoint[2].y = maxPoint[2].y + matchResult.rows / 2;
    135     //minMaxLoc(botright, &minValue[3], &maxValue[3], &minPoint[3], &maxPoint[3]);
    136     //maxPoint[3].x = maxPoint[3].x + matchResult.cols / 2;
    137     //maxPoint[3].x = maxPoint[3].y + matchResult.rows / 2;
    138 }
    139 //------------------------------------图像投影算法-----------------------------------------------//
    140 //*************@src------------------输入矩阵为单通道********************************************//
    141 //*************@leftUpJumpWave-------上升跳变沿存储**********************************************//
    142 //*************@rightDownJumpWave----下降跳变沿存储**********************************************//
    143 //*************@maxInterval----------允许高电平(像素)最大间隔,也可以说是允许的最大误差********//
    144 //-----------------------------------------------------------------------------------------------//
    145 void projectionAlgorithm(Mat src,vector<int>& UpJumpWave,vector<int>& DownJumpWave,bool Axis,int maxInterval)
    146 {
    147     vector<int> pixNum(src.rows > src.cols ? src.rows : src.cols);
    148     //------对X、Y做直方图类似的投影,统计一行或者一列的非零个数--------//
    149     if (Axis)
    150     {
    151         for (size_t i = 0; i < src.cols; i++)
    152         {
    153             Mat col = src.col(i);//一列数据
    154             pixNum[i] = countNonZero(col) > 1 ? countNonZero(col) : 0;
    155         }
    156     }
    157     else
    158     {
    159         
    160         for (size_t i = 0; i < src.rows; i++)
    161         {
    162             Mat row = src.row(i);//一行数据
    163             pixNum[i] = countNonZero(row) > 1 ? countNonZero(row) : 0;
    164         }
    165     }
    166     if (pixNum.size() < maxInterval) return;//防止有空洞(实际没见过,如果有的话那程序架构会奔溃了)
    167     //-----对上面的数据进行二值化0-1,同时对于不满足maxInterval的数据进行剔除--------//
    168     for (int k = 1; k < pixNum.size()-maxInterval; k++)//去除了第一个和最后一个像素
    169     {
    170         if (pixNum[k] > 0 && pixNum[k + maxInterval] > 0)
    171         {
    172             for (size_t j = k; j < k + maxInterval; j++)
    173             {
    174                 pixNum[j] = 1;
    175             }
    176             k = k + maxInterval-1;
    177         }
    178         else
    179         {
    180             pixNum[k] = 0;
    181         }
    182     }
    183     //----对跳变的电平进行存储,高->低,低->高,-----//
    184     for (size_t i = 1 ; i < pixNum.size()-2; i++)//去除了第一个和最后一个像素
    185     {
    186         if (pixNum[i] == 0 && pixNum[i + 1] == 1) UpJumpWave.push_back(i);
    187         if (pixNum[i] == 1 && pixNum[i + 1] == 0) DownJumpWave.push_back(i);
    188     }
    189     //----对得到的结果进行处理,定位点被误判----//
    190     vector<int>::iterator begin = UpJumpWave.begin();    
    191     if (UpJumpWave[0] < 15)                    UpJumpWave.erase(begin);
    192     vector<int>::iterator end = UpJumpWave.end()-1;
    193     if (UpJumpWave[UpJumpWave.size()-1] > 330) UpJumpWave.erase(end);
    194 }
    195 //-----------------------------------------------------------------------------------//
    196 //************************************检测涂卡区域函数***********************************//
    197 void checkKeypoint(Mat& _src,vector<Point2f>& allPoint,vector<Point2f>& testkeyPoint)
    198 {
    199     Mat src = _src.clone();
    200     Mat show = Mat::zeros(src.size(), CV_8UC3);
    201     morphologyEx(src, src, MORPH_DILATE, Mat::ones(3, 3, CV_8UC1));
    202     for (size_t i = 0; i < allPoint.size(); i++)
    203     {
    204         //------判断检测点的正方形涂卡区的非零个数---------//
    205         if (allPoint[i].x == 0 || allPoint[i].y == 0)
    206         {
    207             allPoint[i].x += 1;
    208             allPoint[i].y += 1;
    209         }
    210         Mat rec = src(Rect(static_cast<int>(allPoint[i].x - 1 ), static_cast<int>(allPoint[i].y - 1 ), 12, 5));
    211         int count = countNonZero(rec);
    212         if (count > 15)
    213         {
    214             testkeyPoint.push_back(allPoint[i]);
    215             rectangle(show, Rect(allPoint[i], Point(allPoint[i].x + 13, allPoint[i].y + 5)), Scalar(0, 0, 255));
    216         }
    217     }
    218 }
    219 #endif

     


    补充:

        1.此代码无法识别旋转的答题卡,只能识别透视的答题卡。原因是模板匹配不具有旋转和尺度的不变性。

        2.随便拿来一张答题卡,主要是改里内部的参数,其中包括:

               A. threshold(grayImage, grayImage,90,255, THRESH_BINARY_INV);//阈值更改,一般不用OTSU算法,因为要求得很准确。

           B.   matchTemplate(src, matchMask, matchResult, TM_CCOEFF_NORMED, Mat());//模板匹配方法更改

           C.   vectorGrayImage[0] = grayImage(Rect(perAncherPoint[0].x+4, 0, 15, standImage.rows));//区域的大小更改,其它三个等同

           D. void projectionAlgorithm(Mat src,vector<int>& UpJumpWave,vector<int>& DownJumpWave,bool Axis,int maxInterval)//maxInterval最大误差更改

           E.   if (UpJumpWave[0] < 15) UpJumpWave.erase(begin);//对误判定位线进行筛选,另一个等同

           F.  if (pixNum[i] == 0 && pixNum[i + 1] == 1) UpJumpWave.push_back(i);//跳变沿的两边宽度可以进行调整

           G.   Mat rec = src(Rect(static_cast<int>(allPoint[i].x - 1 ), static_cast<int>(allPoint[i].y - 1 ), 12, 5));//检测涂卡区域大小更改

           H.   if (count > 15)//涂卡区域的程度判断更改

  • 相关阅读:
    《SpringBoot揭秘 快速构建微服务体系》读后感(二)
    《SpringBoot揭秘 快速构建微服务体系》读后感(一)
    《Java多线程编程核心技术》读后感(十八)
    4.Go-结构体、结构体指针和方法
    3.GO-项目结构、包访问权限、闭包和值传递引用传递
    3.Flask-SQLAlchemy
    3.django Model
    2.深入类和对象
    2.shell编程-函数的高级用法
    mysql命令
  • 原文地址:https://www.cnblogs.com/wjy-lulu/p/6958987.html
Copyright © 2020-2023  润新知