|
@@ -53,7 +53,7 @@ public class Card2Controller extends BaseController { |
|
|
long t1 = new Date().getTime(); |
|
|
long t1 = new Date().getTime(); |
|
|
//String sourceimage1 = "D:\\test\\abc\\card\\test4.jpg"; |
|
|
//String sourceimage1 = "D:\\test\\abc\\card\\test4.jpg"; |
|
|
String sourceimage = Constants.PATH + imagefile; |
|
|
String sourceimage = Constants.PATH + imagefile; |
|
|
//表格检测,获取到表格内容,是查找识别区域的部分 |
|
|
|
|
|
|
|
|
//表格检测,获取到表格内容,是查找识别区域的部分,返回的是校正之后的图像 |
|
|
Mat mat = markingArea(sourceimage); |
|
|
Mat mat = markingArea(sourceimage); |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_3.png"; |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_3.png"; |
|
|
Highgui.imwrite(destPath, mat); |
|
|
Highgui.imwrite(destPath, mat); |
|
@@ -84,13 +84,13 @@ public class Card2Controller extends BaseController { |
|
|
public static Mat markingArea(String path){ |
|
|
public static Mat markingArea(String path){ |
|
|
Mat source = Highgui.imread(path, Highgui.CV_LOAD_IMAGE_COLOR); |
|
|
Mat source = Highgui.imread(path, Highgui.CV_LOAD_IMAGE_COLOR); |
|
|
Mat img = new Mat();; |
|
|
Mat img = new Mat();; |
|
|
// 彩色转灰度 |
|
|
|
|
|
Mat result= source.clone(); |
|
|
Mat result= source.clone(); |
|
|
|
|
|
// 彩色转灰度 |
|
|
Imgproc.cvtColor(source, img, Imgproc.COLOR_BGR2GRAY); |
|
|
Imgproc.cvtColor(source, img, Imgproc.COLOR_BGR2GRAY); |
|
|
|
|
|
//此处图像预处理,可以使用方式1也可以使用方式2都可以,自己也可以测试下2种方式的差异 |
|
|
// //方式1:通过高斯滤波然后边缘检测膨胀来链接边缘,将轮廓连通便于轮廓识别 |
|
|
// //方式1:通过高斯滤波然后边缘检测膨胀来链接边缘,将轮廓连通便于轮廓识别 |
|
|
// // 高斯滤波,降噪 |
|
|
// // 高斯滤波,降噪 |
|
|
// Imgproc.GaussianBlur(img, img, new Size(3,3), 2, 2); |
|
|
// Imgproc.GaussianBlur(img, img, new Size(3,3), 2, 2); |
|
|
// |
|
|
|
|
|
// // Canny边缘检测 |
|
|
// // Canny边缘检测 |
|
|
// Imgproc.Canny(img, img, 20, 60, 3, false); |
|
|
// Imgproc.Canny(img, img, 20, 60, 3, false); |
|
|
// // 膨胀,连接边缘 |
|
|
// // 膨胀,连接边缘 |
|
@@ -99,6 +99,9 @@ public class Card2Controller extends BaseController { |
|
|
//方式2:使用形态学梯度算法,此算法来保留物体的边缘轮廓很有效果 |
|
|
//方式2:使用形态学梯度算法,此算法来保留物体的边缘轮廓很有效果 |
|
|
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5)); |
|
|
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5)); |
|
|
Imgproc.morphologyEx(img, img, Imgproc.MORPH_GRADIENT, element); |
|
|
Imgproc.morphologyEx(img, img, Imgproc.MORPH_GRADIENT, element); |
|
|
|
|
|
|
|
|
|
|
|
//图像二值化,使用的是OTSU二值化,阈值为170,也可以使用自适用二值化 |
|
|
|
|
|
// Imgproc.adaptiveThreshold(img,img, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 51, 10); |
|
|
Imgproc.threshold(img,img, 170, 255, Imgproc.THRESH_BINARY|Imgproc.THRESH_OTSU); |
|
|
Imgproc.threshold(img,img, 170, 255, Imgproc.THRESH_BINARY|Imgproc.THRESH_OTSU); |
|
|
|
|
|
|
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_1.png"; |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_1.png"; |
|
@@ -107,10 +110,9 @@ public class Card2Controller extends BaseController { |
|
|
|
|
|
|
|
|
List<MatOfPoint> contours = new ArrayList<>(); |
|
|
List<MatOfPoint> contours = new ArrayList<>(); |
|
|
Mat hierarchy = new Mat(); |
|
|
Mat hierarchy = new Mat(); |
|
|
|
|
|
//轮廓查找,主要就是找最外表格框 |
|
|
Imgproc.findContours(img, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); |
|
|
Imgproc.findContours(img, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 找出轮廓对应凸包的四边形拟合 |
|
|
// 找出轮廓对应凸包的四边形拟合 |
|
|
List<MatOfPoint> squares = new ArrayList<>(); |
|
|
List<MatOfPoint> squares = new ArrayList<>(); |
|
|
List<MatOfPoint> hulls = new ArrayList<>(); |
|
|
List<MatOfPoint> hulls = new ArrayList<>(); |
|
@@ -138,6 +140,7 @@ public class Card2Controller extends BaseController { |
|
|
// 筛选出面积大于某一阈值的,且四边形的各个角度都接近直角的凸四边形 |
|
|
// 筛选出面积大于某一阈值的,且四边形的各个角度都接近直角的凸四边形 |
|
|
MatOfPoint approxf1 = new MatOfPoint(); |
|
|
MatOfPoint approxf1 = new MatOfPoint(); |
|
|
approx.convertTo(approxf1, CvType.CV_32S); |
|
|
approx.convertTo(approxf1, CvType.CV_32S); |
|
|
|
|
|
//此处是筛选表格框,面积大于40000 |
|
|
if (approx.rows() == 4 && Math.abs(Imgproc.contourArea(approx)) > 40000 && |
|
|
if (approx.rows() == 4 && Math.abs(Imgproc.contourArea(approx)) > 40000 && |
|
|
Imgproc.isContourConvex(approxf1)) { |
|
|
Imgproc.isContourConvex(approxf1)) { |
|
|
double maxCosine = 0; |
|
|
double maxCosine = 0; |
|
@@ -145,7 +148,7 @@ public class Card2Controller extends BaseController { |
|
|
double cosine = Math.abs(getAngle(approxf1.toArray()[j%4], approxf1.toArray()[j-2], approxf1.toArray()[j-1])); |
|
|
double cosine = Math.abs(getAngle(approxf1.toArray()[j%4], approxf1.toArray()[j-2], approxf1.toArray()[j-1])); |
|
|
maxCosine = Math.max(maxCosine, cosine); |
|
|
maxCosine = Math.max(maxCosine, cosine); |
|
|
} |
|
|
} |
|
|
// 角度大概72度 |
|
|
|
|
|
|
|
|
// 考虑到图片倾斜等情况,角度大概72度 |
|
|
if (maxCosine < 0.3) { |
|
|
if (maxCosine < 0.3) { |
|
|
MatOfPoint tmp = new MatOfPoint(); |
|
|
MatOfPoint tmp = new MatOfPoint(); |
|
|
contourHull.convertTo(tmp, CvType.CV_32S); |
|
|
contourHull.convertTo(tmp, CvType.CV_32S); |
|
@@ -165,7 +168,6 @@ public class Card2Controller extends BaseController { |
|
|
// 找到这个最大的四边形对应的凸边框,再次进行多边形拟合,此次精度较高,拟合的结果可能是大于4条边的多边形 |
|
|
// 找到这个最大的四边形对应的凸边框,再次进行多边形拟合,此次精度较高,拟合的结果可能是大于4条边的多边形 |
|
|
MatOfPoint contourHull = hulls.get(index); |
|
|
MatOfPoint contourHull = hulls.get(index); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MatOfPoint2f tmp = new MatOfPoint2f(); |
|
|
MatOfPoint2f tmp = new MatOfPoint2f(); |
|
|
contourHull.convertTo(tmp, CvType.CV_32F); |
|
|
contourHull.convertTo(tmp, CvType.CV_32F); |
|
|
Imgproc.approxPolyDP(tmp, approx, 3, true); |
|
|
Imgproc.approxPolyDP(tmp, approx, 3, true); |
|
@@ -190,6 +192,7 @@ public class Card2Controller extends BaseController { |
|
|
if (getSpacePointToPoint(p1, p2) > 2 * maxL) { |
|
|
if (getSpacePointToPoint(p1, p2) > 2 * maxL) { |
|
|
lines.add(new double[]{p1.x, p1.y, p2.x, p2.y}); |
|
|
lines.add(new double[]{p1.x, p1.y, p2.x, p2.y}); |
|
|
logger.info("p1x:"+p1.x+" p1y:"+p1.y+" p2x:"+p2.x+" p2y:"+p2.y); |
|
|
logger.info("p1x:"+p1.x+" p1y:"+p1.y+" p2x:"+p2.x+" p2y:"+p2.y); |
|
|
|
|
|
//画出4条边线,真正识别过程中这些都是可以注释掉的,只是为了方便观察 |
|
|
Core.line(source, new Point(p1.x, p1.y), new Point( p2.x, p2.y), new Scalar(255, 0, 0),4); |
|
|
Core.line(source, new Point(p1.x, p1.y), new Point( p2.x, p2.y), new Scalar(255, 0, 0),4); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@@ -221,11 +224,11 @@ public class Card2Controller extends BaseController { |
|
|
double space1 = getSpacePointToPoint(p1, p2); |
|
|
double space1 = getSpacePointToPoint(p1, p2); |
|
|
double space2 = getSpacePointToPoint(p2, p3); |
|
|
double space2 = getSpacePointToPoint(p2, p3); |
|
|
double space3 = getSpacePointToPoint(p3, p0); |
|
|
double space3 = getSpacePointToPoint(p3, p0); |
|
|
|
|
|
|
|
|
|
|
|
// 使用最宽和最长的边作为进行图像矫正目标图像的长宽 |
|
|
double imgWidth = space1 > space3 ? space1 : space3; |
|
|
double imgWidth = space1 > space3 ? space1 : space3; |
|
|
double imgHeight = space0 > space2 ? space0 : space2; |
|
|
double imgHeight = space0 > space2 ? space0 : space2; |
|
|
logger.info("imgWidth:"+imgWidth+" imgHeight:"+imgHeight); |
|
|
logger.info("imgWidth:"+imgWidth+" imgHeight:"+imgHeight); |
|
|
// 如果提取出的图片宽小于高,则旋转90度 |
|
|
|
|
|
|
|
|
// 如果提取出的图片宽小于高,则旋转90度,因为示例中的矩形框是宽>高的,如果宽小于高应该是图片旋转了 |
|
|
if (imgWidth > imgHeight) { |
|
|
if (imgWidth > imgHeight) { |
|
|
logger.info("----in"); |
|
|
logger.info("----in"); |
|
|
double temp = imgWidth; |
|
|
double temp = imgWidth; |
|
@@ -246,12 +249,14 @@ public class Card2Controller extends BaseController { |
|
|
// new Point(imgWidth*0.4, imgHeight*0.4), |
|
|
// new Point(imgWidth*0.4, imgHeight*0.4), |
|
|
// new Point(imgWidth*1.6, imgHeight*0.4), |
|
|
// new Point(imgWidth*1.6, imgHeight*0.4), |
|
|
// new Point(imgWidth*1.6, imgHeight*1.6)); |
|
|
// new Point(imgWidth*1.6, imgHeight*1.6)); |
|
|
|
|
|
|
|
|
|
|
|
//quadMat目标图像的点设置,以之前取出的最长的长宽作为新图像的长宽,创建一个图层 |
|
|
MatOfPoint2f quadMat = new MatOfPoint2f(new Point(0, 0), |
|
|
MatOfPoint2f quadMat = new MatOfPoint2f(new Point(0, 0), |
|
|
new Point(imgWidth, 0), |
|
|
new Point(imgWidth, 0), |
|
|
new Point(imgWidth, imgHeight), |
|
|
new Point(imgWidth, imgHeight), |
|
|
new Point(0, imgHeight)); |
|
|
new Point(0, imgHeight)); |
|
|
|
|
|
|
|
|
// 提取图像 |
|
|
|
|
|
|
|
|
// 提取图像,使用warpPerspective做图像的透视变换 |
|
|
Mat transmtx = Imgproc.getPerspectiveTransform(cornerMat, quadMat); |
|
|
Mat transmtx = Imgproc.getPerspectiveTransform(cornerMat, quadMat); |
|
|
Imgproc.warpPerspective(result, quad, transmtx, quad.size()); |
|
|
Imgproc.warpPerspective(result, quad, transmtx, quad.size()); |
|
|
return quad; |
|
|
return quad; |
|
@@ -341,14 +346,28 @@ public class Card2Controller extends BaseController { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//答题卡识别 |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 答题卡识别,增加注释 |
|
|
|
|
|
* 已得到矫正后的图像后,进行后续答案识别算法过程 |
|
|
|
|
|
* @Author Songer |
|
|
|
|
|
* @param mat |
|
|
|
|
|
* @return String |
|
|
|
|
|
* @Date 2018年12月19日 |
|
|
|
|
|
* 更新日志 |
|
|
|
|
|
* 2018年12月19日 王嵩 首次创建 |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
public String cardResult(Mat mat){ |
|
|
public String cardResult(Mat mat){ |
|
|
|
|
|
//设置剪切的边距,目的是裁剪表格边框,防止边框影响轮廓查找,这里设置为20像素 |
|
|
int cutsize = 20; |
|
|
int cutsize = 20; |
|
|
Mat img_cut = mat.submat(cutsize,mat.rows()-cutsize,cutsize,mat.cols()-cutsize);new Mat(); |
|
|
Mat img_cut = mat.submat(cutsize,mat.rows()-cutsize,cutsize,mat.cols()-cutsize);new Mat(); |
|
|
Mat img_gray = img_cut.clone(); |
|
|
Mat img_gray = img_cut.clone(); |
|
|
|
|
|
//图像灰度化 |
|
|
Imgproc.cvtColor(img_cut, img_gray, Imgproc.COLOR_BGR2GRAY); |
|
|
Imgproc.cvtColor(img_cut, img_gray, Imgproc.COLOR_BGR2GRAY); |
|
|
|
|
|
//图像二值化,注意是反向二值化以及OTSU算法 |
|
|
Imgproc.threshold(img_gray,img_gray, 170, 255, Imgproc.THRESH_BINARY_INV|Imgproc.THRESH_OTSU); |
|
|
Imgproc.threshold(img_gray,img_gray, 170, 255, Imgproc.THRESH_BINARY_INV|Imgproc.THRESH_OTSU); |
|
|
Mat temp = img_gray.clone(); |
|
|
Mat temp = img_gray.clone(); |
|
|
|
|
|
//此处使用的是方式2,形态学梯度算法保留填图选项的边框 |
|
|
// //方式1:通过高斯滤波然后边缘检测膨胀来链接边缘,将轮廓连通便于轮廓识别 |
|
|
// //方式1:通过高斯滤波然后边缘检测膨胀来链接边缘,将轮廓连通便于轮廓识别 |
|
|
// // 高斯滤波,降噪 |
|
|
// // 高斯滤波,降噪 |
|
|
// Imgproc.GaussianBlur(temp, temp, new Size(3,3), 2, 2); |
|
|
// Imgproc.GaussianBlur(temp, temp, new Size(3,3), 2, 2); |
|
@@ -357,15 +376,14 @@ public class Card2Controller extends BaseController { |
|
|
// // 膨胀,连接边缘 |
|
|
// // 膨胀,连接边缘 |
|
|
// Imgproc.dilate(temp, temp, new Mat(), new Point(-1,-1), 3, 1, new Scalar(1)); |
|
|
// Imgproc.dilate(temp, temp, new Mat(), new Point(-1,-1), 3, 1, new Scalar(1)); |
|
|
|
|
|
|
|
|
//方式2:使用形态学梯度算法,此算法来保留物体的边缘轮廓很有效果 |
|
|
|
|
|
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5)); |
|
|
|
|
|
Imgproc.morphologyEx(temp, temp, Imgproc.MORPH_GRADIENT, element); |
|
|
|
|
|
|
|
|
//方式2:使用形态学梯度算法,此算法来保留物体的边缘轮廓很有效果 |
|
|
|
|
|
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5)); |
|
|
|
|
|
Imgproc.morphologyEx(temp, temp, Imgproc.MORPH_GRADIENT, element); |
|
|
Imgproc.threshold(temp, temp, 170, 255, Imgproc.THRESH_BINARY|Imgproc.THRESH_OTSU); |
|
|
Imgproc.threshold(temp, temp, 170, 255, Imgproc.THRESH_BINARY|Imgproc.THRESH_OTSU); |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_4.png"; |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_4.png"; |
|
|
Highgui.imwrite(destPath, temp); |
|
|
Highgui.imwrite(destPath, temp); |
|
|
// Highgui.imwrite("D:\\test\\abc\\card\\card3.png", temp); |
|
|
// Highgui.imwrite("D:\\test\\abc\\card\\card3.png", temp); |
|
|
|
|
|
|
|
|
// logger.info(temp.cols()); |
|
|
|
|
|
|
|
|
//按比例截取,此处是根据原答题卡的各列的比例截取成4列,学号那列还要横向分隔一下,上半部分是学号下半部分是答题卡 |
|
|
Mat cut1 = temp.submat(0,temp.rows(),0,(int)(0.275*temp.cols())); |
|
|
Mat cut1 = temp.submat(0,temp.rows(),0,(int)(0.275*temp.cols())); |
|
|
Mat cut_gray1 = img_gray.submat(0,img_gray.rows(),0,(int)(0.275*img_gray.cols())); |
|
|
Mat cut_gray1 = img_gray.submat(0,img_gray.rows(),0,(int)(0.275*img_gray.cols())); |
|
|
|
|
|
|
|
@@ -389,6 +407,7 @@ public class Card2Controller extends BaseController { |
|
|
// Highgui.imwrite("D:\\test\\abc\\card\\card_cut5.png", cut5); |
|
|
// Highgui.imwrite("D:\\test\\abc\\card\\card_cut5.png", cut5); |
|
|
|
|
|
|
|
|
List<String> resultList = new ArrayList<String>(); |
|
|
List<String> resultList = new ArrayList<String>(); |
|
|
|
|
|
//按列处理 |
|
|
List<String> list1 = processByCol(cut1,cut_gray1,img_cut,5); |
|
|
List<String> list1 = processByCol(cut1,cut_gray1,img_cut,5); |
|
|
List<String> list2 = processByCol(cut2,cut_gray2,img_cut,5); |
|
|
List<String> list2 = processByCol(cut2,cut_gray2,img_cut,5); |
|
|
List<String> list3 = processByCol(cut3,cut_gray3,img_cut,4); |
|
|
List<String> list3 = processByCol(cut3,cut_gray3,img_cut,4); |
|
@@ -427,16 +446,20 @@ public class Card2Controller extends BaseController { |
|
|
List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); |
|
|
List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); |
|
|
List<MatOfPoint> answerList = new ArrayList<MatOfPoint>(); |
|
|
List<MatOfPoint> answerList = new ArrayList<MatOfPoint>(); |
|
|
Mat hierarchy = new Mat(); |
|
|
Mat hierarchy = new Mat(); |
|
|
|
|
|
//进行轮廓查找,注意因为该答题卡特征是闭合填图区域,所以用这种方式,如果是非闭合填涂区域,则不适用 |
|
|
Imgproc.findContours(cut1.clone(), contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); |
|
|
Imgproc.findContours(cut1.clone(), contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); |
|
|
// logger.info(contours.size()); |
|
|
// logger.info(contours.size()); |
|
|
// logger.info("-----w------"+(temp.width()*80/2693-20)); |
|
|
// logger.info("-----w------"+(temp.width()*80/2693-20)); |
|
|
// logger.info("-----h------"+(temp.height()*80/2764-20)); |
|
|
// logger.info("-----h------"+(temp.height()*80/2764-20)); |
|
|
for(int i = 0;i<contours.size();i++){ |
|
|
for(int i = 0;i<contours.size();i++){ |
|
|
MatOfPoint mop= contours.get(i); |
|
|
MatOfPoint mop= contours.get(i); |
|
|
|
|
|
//每个轮廓给出外包矩形,便于操作 |
|
|
Rect rect = Imgproc.boundingRect(mop); |
|
|
Rect rect = Imgproc.boundingRect(mop); |
|
|
// logger.info("-----------"+rect.width+" "+rect.height); |
|
|
// logger.info("-----------"+rect.width+" "+rect.height); |
|
|
//一个填图区域大概占整个表格的w:80/2733 h:0/2804,,所以排除掉太小的轮廓和过大的轮廓 |
|
|
//一个填图区域大概占整个表格的w:80/2733 h:0/2804,,所以排除掉太小的轮廓和过大的轮廓 |
|
|
|
|
|
// //绘制每个轮廓图 |
|
|
// Imgproc.drawContours(cut1, contours, i, new Scalar(170), 2); |
|
|
// Imgproc.drawContours(cut1, contours, i, new Scalar(170), 2); |
|
|
|
|
|
//此处是为了排除杂点,较小或较大的轮廓都是非填图选项,可以排除,可以按实际情况灵活变动限制条件,最好输出出轮廓便于观察 |
|
|
if(rect.width>(temp.width()*80/2693-20) && rect.height>(temp.height()*80/2764-20) && rect.width<temp.width()*0.05 && rect.height<temp.height()*0.05){ |
|
|
if(rect.width>(temp.width()*80/2693-20) && rect.height>(temp.height()*80/2764-20) && rect.width<temp.width()*0.05 && rect.height<temp.height()*0.05){ |
|
|
// if(rect.width>50&&rect.height>50&&rect.area()>2500&&rect.area()<10000){ |
|
|
// if(rect.width>50&&rect.height>50&&rect.area()>2500&&rect.area()<10000){ |
|
|
// Core.rectangle(img_cut, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y |
|
|
// Core.rectangle(img_cut, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y |
|
@@ -462,6 +485,8 @@ public class Card2Controller extends BaseController { |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
//每4/5个一组组成新list,并按x坐标排序 |
|
|
//每4/5个一组组成新list,并按x坐标排序 |
|
|
|
|
|
//该方式依赖于预处理后的图片没有干扰轮廓。如果图像处理不佳,干扰项没排除,那么此处的分组就会错乱。 |
|
|
|
|
|
//提供一个优化思路:对于闭合填涂区域的可以使用水平垂直投影进行坐标定位点的判定。 |
|
|
int queno = 1; |
|
|
int queno = 1; |
|
|
int totoSize = answerList.size(); |
|
|
int totoSize = answerList.size(); |
|
|
for (int i = 0; i < totoSize; i+=answerCols) { |
|
|
for (int i = 0; i < totoSize; i+=answerCols) { |
|
@@ -471,7 +496,7 @@ public class Card2Controller extends BaseController { |
|
|
} |
|
|
} |
|
|
List<MatOfPoint> newList = answerList.subList(i,toIndex); |
|
|
List<MatOfPoint> newList = answerList.subList(i,toIndex); |
|
|
Collections.sort(newList, new Comparator<MatOfPoint>() { |
|
|
Collections.sort(newList, new Comparator<MatOfPoint>() { |
|
|
//按照y坐标升序排列 |
|
|
|
|
|
|
|
|
//按照x坐标升序排列 |
|
|
@Override |
|
|
@Override |
|
|
public int compare(MatOfPoint o1, MatOfPoint o2) { |
|
|
public int compare(MatOfPoint o1, MatOfPoint o2) { |
|
|
Rect rect1 = Imgproc.boundingRect(o1); |
|
|
Rect rect1 = Imgproc.boundingRect(o1); |
|
@@ -490,12 +515,14 @@ public class Card2Controller extends BaseController { |
|
|
Imgproc.drawContours(cut_gray, newList, j, new Scalar(170), 2); |
|
|
Imgproc.drawContours(cut_gray, newList, j, new Scalar(170), 2); |
|
|
//掩模提取出轮廓 |
|
|
//掩模提取出轮廓 |
|
|
Mat mask = Mat.zeros(cut_gray.size(), CvType.CV_8UC1); |
|
|
Mat mask = Mat.zeros(cut_gray.size(), CvType.CV_8UC1); |
|
|
|
|
|
//绘制轮廓便于观察 |
|
|
Imgproc.drawContours(mask, newList, j, new Scalar(255), -1); |
|
|
Imgproc.drawContours(mask, newList, j, new Scalar(255), -1); |
|
|
Mat dst = new Mat(); |
|
|
Mat dst = new Mat(); |
|
|
Core.bitwise_and(cut_gray, mask, dst); |
|
|
Core.bitwise_and(cut_gray, mask, dst); |
|
|
//获取填涂百分比 |
|
|
|
|
|
|
|
|
//获取填涂百分比,填涂区域的二值化后取出非0点/掩模的轮廓面积 |
|
|
double p100 = Core.countNonZero(dst) * 100 / Core.countNonZero(mask); |
|
|
double p100 = Core.countNonZero(dst) * 100 / Core.countNonZero(mask); |
|
|
String anno = index2ColName(j); |
|
|
String anno = index2ColName(j); |
|
|
|
|
|
//认为非0像素超过80%的算是填涂 |
|
|
if(p100>80){ |
|
|
if(p100>80){ |
|
|
resultChoose += anno; |
|
|
resultChoose += anno; |
|
|
logger.info(p100+" 第"+queno+"行:选项("+anno+")填涂"); |
|
|
logger.info(p100+" 第"+queno+"行:选项("+anno+")填涂"); |
|
@@ -503,7 +530,7 @@ public class Card2Controller extends BaseController { |
|
|
logger.info(p100+" 第"+queno+"行:选项("+anno+")未填涂"); |
|
|
logger.info(p100+" 第"+queno+"行:选项("+anno+")未填涂"); |
|
|
} |
|
|
} |
|
|
// Highgui.imwrite("D:\\test\\abc\\card\\card_x"+i+j+".png", dst); |
|
|
// Highgui.imwrite("D:\\test\\abc\\card\\card_x"+i+j+".png", dst); |
|
|
if(i==0&&j==0){ |
|
|
|
|
|
|
|
|
if(i==0&&j==0){//输出一下第一个掩模 |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_5.png"; |
|
|
String destPath = Constants.PATH + Constants.DEST_IMAGE_PATH + "cardResult_5.png"; |
|
|
Highgui.imwrite(destPath, dst); |
|
|
Highgui.imwrite(destPath, dst); |
|
|
} |
|
|
} |
|
|