博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《图像处理实例》 之 答题卡检测
阅读量:4287 次
发布时间:2019-05-27

本文共 17959 字,大约阅读时间需要 59 分钟。

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

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


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

第一步:定位点检测

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

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

          方法二:轮廓区域检测

          方法三:模板匹配

          方法四:特征检测匹配

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

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

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

1 //--------------------------------注释代码部分为未用掩码操作--------------------------// 2 void FindAnchorPoint(const Mat& src,const Mat& matchMask,vector
& 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
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
vectorMask;//注意此处如果用vector
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
& UpJumpWave,vector
& DownJumpWave,bool Axis,int maxInterval) 8 { 9 vector
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 else20 {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 else41 {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
::iterator begin = UpJumpWave.begin(); 53 if (UpJumpWave[0] < 15) UpJumpWave.erase(begin);54 vector
::iterator end = UpJumpWave.end()-1;55 if (UpJumpWave[UpJumpWave.size()-1] > 330) UpJumpWave.erase(end);56 }

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

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

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

 上代码:

1 //-----------------------------------------------------------------------------------// 2 //************************************检测涂卡区域函数***********************************// 3 void checkKeypoint(Mat& _src,vector
& allPoint,vector
& 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
(allPoint[i].x - 1 ), static_cast
(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 
2 #include
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
& anchorPoint); 12 void projectionAlgorithm(Mat src, vector
& UpJumpWave, vector
& DownJumpWave, bool Axis, int maxInterval); 13 void checkKeypoint( Mat& _src, vector
& allPoint, vector
& 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
stdAncherPoint(4); 30 vector
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
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
> upJumpWave(4); 48 vector
> 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
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
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
& 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
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
vectorMask;//注意此处如果用vector
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
& UpJumpWave,vector
& DownJumpWave,bool Axis,int maxInterval)146 {147 vector
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 else158 {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 else179 {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
::iterator begin = UpJumpWave.begin(); 191 if (UpJumpWave[0] < 15) UpJumpWave.erase(begin);192 vector
::iterator end = UpJumpWave.end()-1;193 if (UpJumpWave[UpJumpWave.size()-1] > 330) UpJumpWave.erase(end);194 }195 //-----------------------------------------------------------------------------------//196 //************************************检测涂卡区域函数***********************************//197 void checkKeypoint(Mat& _src,vector
& allPoint,vector
& 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
(allPoint[i].x - 1 ), static_cast
(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)//涂卡区域的程度判断更改

转载地址:http://eitgi.baihongyu.com/

你可能感兴趣的文章
论文笔记 | FLAT: Chinese NER Using Flat-Lattice Transformer
查看>>
论文笔记 | Multi-Grained Named Entity Recognition
查看>>
论文解读 | 百度 ERNIE: Enhanced Representation through Knowledge Integration
查看>>
论文笔记:Event Detection without Triggers
查看>>
论文笔记 | Attention Is All Y ou Need for Chinese Word Segmentation
查看>>
论文笔记|DOC: Deep Open Classification of Text Documents
查看>>
论文笔记|Undersensitivity in Neural Reading Comprehension
查看>>
论文笔记丨FewRel 2.0: Towards More Challenging Few-Shot Relation Classification
查看>>
论文笔记 _ Discourse-Aware Neural Extractive Text Summarization
查看>>
论文笔记 | Simple and Effective Text Matching with Richer Alignment Features
查看>>
论文笔记:A Gated Self-attention Memory Network for Answer Selection
查看>>
论文笔记 | Simplify the Usage of Lexicon in Chinese NER
查看>>
论文解读 | Unsupervised Data Augmentation for Consistency Training
查看>>
论文笔记|Distantly Supervised Named Entity Recognition using Positive-Unlabeled Learning
查看>>
论文笔记:Span-Based Event Coreference Resolution
查看>>
NAACL2021阅读理解论文整理
查看>>
论文笔记 | Leveraging Graph to Improve Abstractive Multi-Document Summarization
查看>>
NAACL2021丨Knowledge Guided Metric Learning for Few-Shot Text Classification
查看>>
论文笔记|Deep Open Intent Classification with Adaptive Decision Boundary
查看>>
【论文笔记】
查看>>