opencv关于答题卡比对结果

功能实现:1.采集图片 2.预处理 3.具体问题具体分析

CMakeLists.txt

cmake_minimum_required(VERSION 3.1)
project(OMR VERSION 1.0.0)
add_executable(OMR main.cpp)
find_package(OpenCV REQUIRED)
target_link_libraries(OMR ${OpenCV_LIBS})

main.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

Mat img, imgGray, imgBlur, imgCanny, imgContours, imgWarp, imgWarpGrade, imgThre, imgWarpGray, imgWarpGradeGray, imgGradeThre;
vector<Point> docPoint_max, docPoint_grade;
vector<Mat> boxes;

/*获取所需轮廓区域*/
vector<vector<Point>> getContours(Mat image)
{
    //四个角点,闭合的轮廓
    //让涂卡去从众多矩阵中脱颖而出(面积,周长)
    //两个ROI:1.涂卡区 2.成绩区
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);

    vector<vector<Point>> coatedArea;
    vector<vector<Point>> gradeArea;
    vector<vector<Point>> recCnt(contours.size());      //轮廓的角点坐标
    int maxArea = 0;
    for (int i = 0; i < contours.size(); i++)
    {
        //遍历每个轮廓
        double area = contourArea(contours[i], false);  //面积绝对值
        //cout << area << endl;
        if (area > 300)
        {
            double peri = arcLength(contours[i], true); //闭合曲线的长度
            approxPolyDP(contours[i], recCnt[i], 0.1 * peri, true);

            if (recCnt[i].size() == 4 && area > maxArea)
            {
                //drawContours(img, recCnt, i, Scalar(0, 255, 0));
                //imshow("drawContours", img);
                maxArea = area;
                coatedArea.push_back(recCnt[i]);
            }
        }
    }
    return coatedArea;
}

/*画点*/
void drawPoints(Mat image, vector<Point> cPoint, Scalar color)
{
    for (int i = 0; i < cPoint.size(); i++)
    {
        circle(image, cPoint[i], 5, color, FILLED);
        putText(image, to_string(i), cPoint[i], FONT_HERSHEY_PLAIN, 5, color, 3);
    }
    
}

/*给点重新排序*/
vector<Point> reorder(vector<Point> points)
{
    vector<Point> newPoints;
    vector<int> sumPoints, subPoints;

    for (int i = 0; i < points.size(); i++)
    {
        sumPoints.push_back(points[i].x + points[i].y);
        subPoints.push_back(points[i].x - points[i].y);
    }
    
    newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]);
    newPoints.push_back(points[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]);
    newPoints.push_back(points[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]);
    newPoints.push_back(points[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]);

    return newPoints;
}

/*放射变换*/
Mat getWrap(Mat image, vector<Point> points, float w, float h)
{
    Mat imgWrap;
    Point2f src[4] = {points[0], points[1], points[2], points[3]};
    Point2f dst[4] = {{0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h}};
    Mat matrix = getPerspectiveTransform(src, dst);
    warpPerspective(img, imgWrap, matrix, Point(w, h));
    return imgWrap;
}

/*图形分割*/
vector<Mat> splitBox(Mat img)
{
    vector<Mat> boxes;
    Mat img_cut, roi_img;
    int width = img.cols / 5;
    int height = img.rows / 5;
    //cout << "width:" << width << " height: " << height << endl;
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            Rect rect(i * height, j * height, 60, 60);
            img_cut = Mat(img, rect);
            roi_img = img_cut.clone();
            boxes.push_back(roi_img);
        }
        
    }
    
    for (int i = 0; i < boxes.size(); i++)
    {
        string num = to_string(i);
        //imshow(num, boxes[i]);
    }

    return boxes;
}

/*答案显示*/
Mat showAnswer(Mat img, vector<int> index, vector<int> grading, vector<int> ans, int question, int choice)
{
    int secW = img.cols / choice;
    int secH = img.rows / question;
    Scalar color;
    for (int i = 0; i < question; i++)
    {
        int sx = (index[i] * secW) + (secW / 2);
        int sy = (i * secH) + (secH / 2);
        if (grading[i] == 1)
        {
            color = Scalar(0, 255, 0);
        }
        else
        {
            color = Scalar(0, 0, 255);
            circle(img, Point((ans[i] * secW) + (secW / 2), sy), 10, Scalar(0, 255, 0), FILLED);
        }
        circle(img, Point(sx, sy), 20, color, FILLED);    
    }

    return img;    
}

/*反向仿射变换*/
Mat getreWrap(Mat image, vector<Point> points, float w, float h, float sizeW, float sizeH)
{
    Mat imgWrap;
    Point2f src[4] = {points[0], points[1], points[2], points[3]};
    Point2f dst[4] = {{0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h}};
    Mat matrix = getPerspectiveTransform(dst, src);
    warpPerspective(image, imgWrap, matrix, Point(sizeW, sizeH));
    return imgWrap;
}

int main(int argc, char const *argv[])
{   
    img = imread("../omr1.JPG");
    cout << img.size << endl;
    resize(img, img, Size(500, 500));
    //imshow("img", img);

    cvtColor(img, imgGray, COLOR_RGB2GRAY);
    //imshow("imgGray", imgGray);

    GaussianBlur(imgGray, imgBlur, Size(5, 5), 0, 0);
    //imshow("imgBlur", imgBlur);

    Canny(imgBlur, imgCanny, 50, 25, 3);
    imshow("imgCanny", imgCanny);

    
    //ROI: resgion of interest
    vector<vector<Point>> contours;
    vector<vector<Point>> cPoint;
    vector<Vec4i> hierarchy;
    //深拷贝不会影响原图
    imgContours = img.clone();
    //findContours(imgCanny, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    //drawContours(imgContours, contours, -1, Scalar(0, 255, 0), 4);
    //imshow("imgContours", imgContours);
    
    //找到涂卡去的矩形轮廓
    cPoint = getContours(imgCanny);
    drawContours(imgContours, cPoint, -1, Scalar(0, 255, 0), 2);
    imshow("imgContours", imgContours);

    //1.重新排序四个角点
    cout << cPoint[0] << endl;
    cout << cPoint[1] << endl;
    // drawPoints(img, cPoint[0], Scalar(0, 0, 200));
    // drawPoints(img, cPoint[1], Scalar(0, 0, 200));
    // imshow("img", img);

    docPoint_grade = reorder(cPoint[0]);
    docPoint_max = reorder(cPoint[1]);
    drawPoints(imgContours, docPoint_max, Scalar(0, 0, 200));
    drawPoints(imgContours, docPoint_grade, Scalar(0, 0, 200));
    imshow("imgContours", imgContours);

    //2.仿射变换
    imgWarp = getWrap(img, docPoint_max, 300, 300);
    //imshow("imgWrap", imgWarp);

    imgWarpGrade = getWrap(img, docPoint_grade, 180, 100);
    //imshow("imgWarpGrade", imgWarpGrade);

    //3.图像二值化
    cvtColor(imgWarp, imgWarpGray, COLOR_RGB2GRAY);
    threshold(imgWarpGray, imgThre, 180, 255, THRESH_BINARY_INV);
    imshow("imgThre", imgThre);
    cvtColor(imgWarpGrade, imgWarpGradeGray, COLOR_RGB2GRAY);
    threshold(imgWarpGradeGray, imgGradeThre, 180, 255, THRESH_BINARY_INV);
    imshow("imgGradeThre", imgGradeThre);

    //4.图形分割
    boxes = splitBox(imgThre);

    //5.计算答案下标,对比正确答案
    int totalPixels;
    int myPixelVal[5][5] = {0};
    for (int i = 0; i < 25; i++)
    {
        totalPixels = countNonZero(boxes[i]);
        myPixelVal[i % 5][i / 5] = totalPixels;
    }
    
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            cout << "[" << i << "," << j << "] " << myPixelVal[i][j] << " ";
        }
        cout << endl;        
    }
    
    vector<vector<int>> brr;
    for (int i = 0; i < 5; i++)
    {
        vector<int> arr;
        for (int j = 0; j < 5; j++)
        {
            arr.push_back(myPixelVal[i][j]);
        }
        brr.push_back(arr);
    }

    vector<int> index;
    for (int i = 0; i < brr.size(); i++)
    {
        auto location = max_element(brr[i].begin(), brr[i].end());
        index.push_back((int)(location - brr[i].begin()));
        cout << *location << " " << index[i] << endl;
    }
    
    //标准答案
    vector<int> ans = {1, 2, 0, 1, 4};
    vector<int> grading;
    int rightnum = 0;
    for (int i = 0; i < 5; i++)
    {
        if (ans[i] == index[i])
        {
            rightnum++;
            grading.push_back(1);
        }
        else
        {
            grading.push_back(0);
        }
        cout << grading[i] << endl;
    }
    
    //计算分数
    int score = (double)rightnum / 5 * 100;
    cout << "score: " << score << endl;

    //6.显示答案及分数
    Mat imgAnswser = imgWarp.clone();
    imgAnswser = showAnswer(imgAnswser, index, grading, ans, 5, 5);
    imshow("imgAnswser", imgAnswser);

    Mat imgDrawing(imgAnswser.size(), imgAnswser.type(), Scalar(0, 0, 0));
    imgDrawing = showAnswer(imgDrawing, index, grading, ans, 5, 5);
    imshow("imgDrawing", imgDrawing);


    Mat imgGradeDrawing(imgWarpGrade.size(), imgWarpGrade.type(), Scalar(0, 0, 0));
    putText(imgGradeDrawing, to_string(score) + "%", Point(20, 80), FONT_HERSHEY_PLAIN, 5, Scalar(0, 0, 255), 4);
    imshow("imgGradeDrawing", imgGradeDrawing);

    //7.反向仿射变换
    Mat imgReWarp = getreWrap(imgDrawing, docPoint_max, 300, 300, 500, 500);
    Mat imgGradeReWarp = getreWrap(imgGradeDrawing, docPoint_grade, 180, 100, 500, 500);
    imshow("imgReWarp", imgReWarp);
    imshow("imgGradeReWarp", imgGradeReWarp);

    //8.融合
    Mat imgFinal;
    addWeighted(img, 1, imgReWarp, 1, 0, imgFinal);

    addWeighted(imgFinal, 1, imgGradeReWarp, 1, 0, imgFinal);
    imshow("imgFinal", imgFinal);

    
    waitKey();
    return 0;
}

结果展示:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值