答題卡改卷

 當初用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);

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章