当初用C++写的, 高考答题卡 大概原理是先进行模板匹配 得到四个顶点,然后分割出下图这两根线
读取端点,然后判断目标点上有没有涂黑。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include <stack>
using namespace std;
using namespace cv;
vector<char*> listFiles(const char * dir);
void cuttingX();
void cuttingY();
void HSVthreshold ();//HSV阈值分割
int linex[16]={0};
int liney[22]={0};
//for循环 用%10求余来得到余值加2便可以
/**************请在此处输入正确答案*************************/
int a[60]={15,15,14,14,16,//1-5
15,14,14,16,15,//6-10
16,15,16,14,16,//11-15
15,16,14,15,14,//16-20
13,11,12,10,12,//21-25
12,11,10,12,13,//26-30
11,13,12,10,11,//31-35
12,10,11,8,7,//36-40
5,3,6,4,6,//41-45
6,4,5,4,3,//46-50
6,5,4,5,3,//51-55
5,6,6,3,4 //56-60
};
int main()
{
/***************拍照***************/
VideoCapture capture(0);
waitKey(50);
capture.set(CV_CAP_PROP_FRAME_WIDTH, 1920);
capture.set(CV_CAP_PROP_FRAME_HEIGHT, 1080);
while(1)
{
Mat src;
while (1)
{
capture >> src;
Mat src1=src.clone();
int x0 = src1.cols / 4;
int x1 = src1.cols * 9/10;
int y0 = src1.rows / 4-140;
int y1 = src1.rows * 9/10;
//画线的坐标,起始坐标和终止坐标
cv::Point p0 = cv::Point(x0,y0);
cv::Point p1 = cv::Point(x1, y0);
cv::Point p2 = cv::Point(x1, y1);
cv::line(src1, p0, p1, cv::Scalar(0, 0, 255), 3, 4);
cv::line(src1, p1, p2, cv::Scalar(0, 0, 255), 3, 4);
cv::namedWindow("对齐", CV_WINDOW_NORMAL);
cv::imshow("对齐", src1);
waitKey(10);
if(kbhit( ))
{
int k1=getch();
break;
}}
imwrite("保存的图像.jpg", src);
/***************模板匹配***************/
//Mat src= imread("±£´æ.jpg",1);
Mat mattmp= imread("3.jpg", 1);
Mat imagematch;
Point minLoc;
Point maxLoc01,maxLoc02,maxLoc03,maxLoc04,judje0;
Point anchor01,anchor02,anchor03,anchor04;
double minVal;
double maxVal2;
//Mat src = imread("C:/answercard/1.jpg",0);//¶ÁÈëºÚ°×ÔʼͼÏñ
int srcRows = src.rows;
int srcCols = src.cols;
Mat src01 = src(Rect(0,0,srcCols/2,srcRows/2));
Mat src02 = src(Rect(srcCols/2,0,srcCols/2,srcRows/2));
Mat src03 = src(Rect(0,srcRows/2,srcCols/2,srcRows/2));
Mat src04 = src(Rect(srcCols/2,srcRows/2,srcCols/2,srcRows/2));
//imshow("src01",src01);imshow("src02",src02);imshow("src03",src03);imshow("src04",src04);
matchTemplate( mattmp, src01, imagematch, 5 );
normalize( imagematch, imagematch, 0, 1, NORM_MINMAX, -1, Mat() );
minMaxLoc( imagematch, &minVal, &maxVal2, &minLoc, &maxLoc01, Mat() );
anchor01 = maxLoc01;
maxLoc01.x=maxLoc01.x+28;
maxLoc01.y=maxLoc01.y+25;
circle(src,maxLoc01,3,Scalar(0,255,0),3);
matchTemplate( mattmp, src02, imagematch, 5 );
normalize( imagematch, imagematch, 0, 1, NORM_MINMAX, -1, Mat() );
minMaxLoc( imagematch, &minVal, &maxVal2, &minLoc, &maxLoc02, Mat() );
anchor02 = Point(maxLoc02.x+srcCols/2,maxLoc02.y);
anchor02.x=anchor02.x+28;
anchor02.y=anchor02.y+25;
circle(src,anchor02,3,Scalar(0,255,0),3);
matchTemplate( mattmp, src03, imagematch, 5 );
normalize( imagematch, imagematch, 0, 1, NORM_MINMAX, -1, Mat() );
minMaxLoc( imagematch, &minVal, &maxVal2, &minLoc, &maxLoc03, Mat() );
anchor03 = Point(maxLoc03.x,maxLoc03.y+srcRows/2);
anchor03.x=anchor03.x+28;
anchor03.y=anchor03.y+27;
circle(src,anchor03,3,Scalar(0,255,0),3);
matchTemplate( mattmp, src04, imagematch, 5 );
normalize( imagematch, imagematch, 0, 1, NORM_MINMAX, -1, Mat() );
minMaxLoc( imagematch, &minVal, &maxVal2, &minLoc, &maxLoc04, Mat() );
anchor04 = Point(maxLoc04.x+srcCols/2,maxLoc04.y+srcRows/2);
anchor04.x=anchor04.x+28;
anchor04.y=anchor04.y+27;
circle(src,anchor04,3,Scalar(0,255,0),3);
//cv::namedWindow("原图", CV_WINDOW_NORMAL);
//cv::imshow("原图", src);
imwrite("保存图像.jpg", src);
HSVthreshold ();//HSV阈值分割
src = imread("img.jpg", 0);
/***************区域切割***************/
threshold(src, src, 16, 255, THRESH_BINARY);
// imshow("二值图",src);
GaussianBlur(src, src, Size(9,9), 0);
Mat element = getStructuringElement(MORPH_RECT, Size(3,4));
dilate(src, src, element);
//element = getStructuringElement(MORPH_RECT, Size(5,4));
erode(src, src, element);
GaussianBlur(src, src, Size(7,7), 0);
GaussianBlur(src, src, Size(7,7), 0);
//morphologyEx(img, img, MORPH_OPEN, element);
//morphologyEx(img, img, MORPH_CLOSE, element);
//morphologyEx(img, img, MORPH_OPEN, element);
threshold(src, src, 150, 255, THRESH_BINARY);
// cv::namedWindow("camera", CV_WINDOW_NORMAL);//CV_WINDOW_NORMAL¾ÍÊÇ0
// cv::imshow("camera", src);
IplImage imgTmp = src;
IplImage* image = cvCloneImage(&imgTmp);//以上两行为深拷贝
cvSetImageROI(image,cvRect(maxLoc01.x,anchor02.y-10,anchor02.x-maxLoc01.x,20));//x、y、长、宽
//cvShowImage("imageROIX",image);//X轴
cvSaveImage("imageROIX.jpg",image);
image = cvCloneImage(&imgTmp);
cvSetImageROI(image,cvRect(anchor02.x-10,anchor02.y,20,anchor04.y-anchor02.y));//x、y、长、宽
//cvShowImage("imageROIY",image);//Y轴
cvSaveImage("imageROIY.jpg",image);
cuttingX();
cuttingY();
/****************答案获取************************/
//选择题20道 1.5分 1-20
//21-35 2 36-40是2分一道 7个选项
//41-60 2
//用for循环对于某一点的右下角区域进行考察15*15
//若白色区域大于50 则认为进行了填涂,且为对。且用绿色标注
//否则为错,并用红色对该点进行标注
//用for循环开始判断 x位置为linex【a【题号】】 y的位置为题号%liney【10+2】
cvSetImageROI(image,cvRect(maxLoc01.x,anchor02.y,anchor02.x-maxLoc01.x,anchor04.y-maxLoc02.y));//x、y、长、宽
cvSaveImage("imageROI.jpg",image);
Mat srcXY = imread("imageROI.jpg", 1);
float sum1=0,sum2=0,sum3=0;
for (int tihao=0;tihao<60;tihao++)//判断每一题的正确与错误
{
int whitesum=0;
for (int x=0;x<16;x++)
{
for (int y=0;y<16;y++)
{
Vec3b &p = srcXY.at<Vec3b>(liney[tihao%20+2]+y , (linex[a[tihao]-1]+x) );
//printf(" (%d,%d)处的颜色为(b=%d, g=%d, r=%d) \n",linex[a[tihao]]+x, liney[tihao%10+2]+y,p[0], p[1], p[2]);
//printf("第%d个的答案是%d\r\n",x+y,a[x+y]);
if (p[0]>128)
{
whitesum++;
}
// p[0] = 255;//p1蓝色 用于判断
// p[1] = 255; //P2 绿色
// p[2] = 255; //P3 红色
}
}
judje0.x=linex[a[tihao]-1];
judje0.y=liney[tihao%20+2];
//printf("第%d道题目位置是(%d,%d)\r\n",tihao,judje0.x,judje0.y);
if (whitesum>20)
{
circle(srcXY,judje0,3,Scalar(0,255,0),3);
if (0<=tihao&tihao<=19) sum1++;
if (40<=tihao&tihao<=59) sum3++;
if (20<=tihao&tihao<=39) sum2++;
}
else
{
circle(srcXY,judje0,3,Scalar(0,0,255),3);
}
// printf("一共有%d个白色像素\n",whitesum);
}
imshow("指定点",srcXY);
imwrite("指定点.jpg",srcXY);
//cvSaveImage("imageROI.jpg",srcXY);
printf("该同学1-20得分%.1f\n",(1.5*sum1));
printf("21-40得分%.1f\n",(sum2*2));
printf("41-60得分%.1f\n",sum3*1.5);
printf("总分为%.1f\n",(1.5*sum1+2*sum2+1.5*sum3));
}
}
void HSVthreshold ()//HSV阈值分割
{
Mat img;
Mat bgr;
Mat hsv;
Mat dst;
//输入图像
img = imread("保存图像.jpg", IMREAD_COLOR);
//imshow("原图", img);
//彩色图像的灰度值归一化
img.convertTo(bgr, CV_32FC3, 1.0 / 255, 0);
//颜色空间转换
cvtColor(bgr, hsv, COLOR_BGR2HSV);
dst = Mat::zeros(img.size(), CV_32FC3);
//掩码
Mat mask;
inRange(hsv, Scalar(0, 0 / float(255) , 0 / float(255)) , Scalar(360 , 255 / float(255) , 142 / float(255)), mask);
// hmin,smin/float(smin_Max), vmin / float(vmin_Max)), Scalar(hmax, smax / float(smax_Max), vmax / float(vmax_Max)
//只保留
for (int r = 0; r < bgr.rows; r++)
{
for (int c = 0; c < bgr.cols; c++)
{
if (mask.at<uchar>(r, c) == 255)
{
dst.at<Vec3f>(r, c) = bgr.at<Vec3f>(r, c);
}
}
}
//输出图像
//imshow("3HSV阈值计算", dst);
//保存图像
dst.convertTo(dst, CV_8UC3, 255.0, 0);
imwrite("img.jpg", dst);
dst.release();//释放内存
//waitKey(0);
}
void cuttingX( )
{
//一共有15个点 后期进行位置排序 并且去掉第一个和最后一个
Mat matSrc = imread("imageROIX.jpg", 0);
//threshold(matSrc, matSrc, 135, 254, THRESH_BINARY_INV);
GaussianBlur(matSrc, matSrc, Size(7,7), 0);
vector<vector<Point> > contours;//contours的类型,双重的vector
vector<Vec4i> hierarchy;//Vec4i是指每一个vector元素中有四个int型数据。
//阈值
threshold(matSrc, matSrc, 70, 255, THRESH_BINARY);
//imshow("阈值",matSrc);
//寻找轮廓,这里注意,findContours的输入参数要求是二值图像,二值图像的来源大致有两种,第一种用threshold,第二种用canny
findContours(matSrc.clone(), contours, hierarchy,CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
/// 计算矩
vector<Moments> mu(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = moments(contours[i], false);
}
/// 计算中心矩:
vector<Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
linex[i]=int (mu[i].m10 / mu[i].m00);
//printf("第 %d 个的位置是 %d\r\n",i,linex[i]);
}
/// 绘制轮廓
Mat drawing = Mat::zeros(matSrc.size(), CV_8UC1);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(255);
//drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
int i,j,temp;
for(j=0;j<=17;j++)//外围循环 循环10次
{
for (i=0;i<16-j;i++)//内层循环 循环 10减去外围的循环的第几次
if (linex[i]>linex[i+1])//比较 比较 a[i]是不是大于a[i+1]是的话
{
temp=linex[i]; //就吧a[i]和a[i+1]互相交换了 这样 大的数就 随着循环
linex[i]=linex[i+1]; //排到 后面了
linex[i+1]=temp;}
}
//for(i=0;i<16;i++)
// printf("%d=%d,",i,linex[i] );//去掉前后两个
// printf("\r\n");
// imshow("outImageX",drawing);
}
void cuttingY( )
{
Mat matSrc = imread("imageROIY.jpg", 0);
//threshold(matSrc, matSrc, 120, 254, THRESH_BINARY_INV);
GaussianBlur(matSrc, matSrc, Size(7, 7), 0);
vector<vector<Point> > contours;//contours的类型,双重的vector
vector<Vec4i> hierarchy;//Vec4i是指每一个vector元素中有四个int型数据。
//阈值
threshold(matSrc, matSrc, 70, 255, THRESH_BINARY);
//寻找轮廓,这里注意,findContours的输入参数要求是二值图像,二值图像的来源大致有两种,第一种用threshold,第二种用canny
findContours(matSrc.clone(), contours, hierarchy,CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
/// 计算矩
vector<Moments> mu(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = moments(contours[i], false);
}
/// 计算中心矩:
vector<Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
liney[i]=int (mu[i].m01 / mu[i].m00);
//printf("第%d个的位置是%d\r\n",i,liney[i]);
}
/// 绘制轮廓
Mat drawing = Mat::zeros(matSrc.size(), CV_8UC1);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(255);
//drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
int i,j,temp;
for(j=0;j<=23;j++)//外围循环 循环10次
{ for (i=0;i<22-j;i++)//内层循环 循环 10减去外围的循环的第几次
if (liney[i]>liney[i+1])//比较 比较 a[i]是不是大于a[i+1]是的话
{ temp=liney[i]; //就吧a[i]和a[i+1]互相交换了 这样 大的数就 随着循环
liney[i]=liney[i+1]; //排到 后面了
liney[i+1]=temp;}
}
// for(i=0;i<22;i++)
// printf("%d=%d,",i,liney[i] );//去掉前后两个
// printf("\r\n");
// imshow("outImageY",drawing);
}