8、Opencv实现文档扫描

目的:在图片中放入一张A4纸(以倾斜角度),通过图片扫描的手法我们可以实现文档的俯视图(即正对着看图像)

#include 
#include  // 说是说gui 具体什么gui 不清楚
#include  // 图像头文件
#include  // 图像处理头文件
using namespace std;
using namespace cv;

// 变量
Mat imgOriginal, imgGray, imgCanny, imgThre, imgBlur, imgDil;
vector initalPoints; // 初始点
vector finalPoints; // 初始点经过固定排序之后的点
float w = 420, h = 596;
// 函数
Mat preProcessing(Mat& img); //图像预处理函数
vector getContours(Mat imgDil,Mat& imgOriginal);// 获取轮廓函数

void drawPoints(vector points, Scalar color,Mat imgOriginal);// 描点函数

vector reorder(vector initalPoints); // 修改点的顺序,改为想要的 0 1 2 3

Mat getWarp(Mat imgOriginal, vector finalPoints, float w,float h);

int main()
{
    string path = "resources/paper.jpg"; // 导入图形的时候,先要在右边点击显示所有文件!!!
    Mat imgOriginal = imread(path); // 在opencv 中所有的图像信息都使用Mat 
    
    //resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5); // 将原始图像缩小0.5倍

    // pre-processing 
    imgThre = preProcessing(imgOriginal);
    // get-contours - biggest
    initalPoints = getContours(imgThre, imgOriginal);
    for (int i = 0; i < initalPoints.size(); i++)
    {
        cout << initalPoints[i].x << "," << initalPoints[i].y << endl;
    }
    finalPoints = reorder(initalPoints);
    //drawPoints(finalPoints, Scalar(0, 255, 0), imgOriginal);
    // warp image 
    Mat imgWarp = getWarp(imgOriginal, finalPoints, w, h);

    // crop  因为图像边缘存在非文档的东西
    // 此类东西容易影响文档扫描的精度
    int cropNum = 5;
    Rect roi(cropNum, cropNum,w-2* cropNum,h-2* cropNum);
    Mat imgCrop = imgWarp(roi);

    imshow("Image", imgOriginal);
    imshow("Image Dilation", imgThre);
    imshow("Image Warp", imgWarp);
    imshow("Image Crop", imgCrop);
    waitKey(0); // 延时,0即相当于无穷大
}
/*image pre-process isn't error**/
Mat preProcessing(Mat& img)
{
    cvtColor(img, imgGray, COLOR_BGR2GRAY); // OPencv 中含有BGR 协议而不是RGB协议、
    // 高斯滤噪 模糊
    GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0); // 该Size 图像具体哪里来的,不是很清楚
    // Canny 边缘检测, 在进行Canny边缘检测之前我们一般会进行高斯滤波
    Canny(imgBlur, imgCanny, 25, 75); // 后面两个参数即Canny 边缘检测的阈值

    // How to dilate and erode an image 
    // dilate : increase the thickness to convenietly detect  即增加边线的厚度,使其显示的更加明显
    // erode :  decrease the thickness
    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); // 更改Size 的大小便更改了放大倍数 注意Size 只能选择奇数
    dilate(imgCanny, imgDil, kernel); // 使用getStructruingElement 创建kernel
    return imgDil;
}

vector getContours(Mat imgDil,Mat& imgOriginal)
{
    vector> contours;
    vector hierarchy; // Vec4i 即代表该向量内有4个 int 变量typedef    Vec   Vec4i;   这四个向量每一层级代表一个轮廓
    findContours(imgDil, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); // CV_CHAIN_APPROX_SIMPLE - 简单的链式接近法
    vector> conpoly(contours.size());// conpoly(paprameter1) ,paprameter1便代表vector对象的行数,而其列数中的vector 是使用了point点集但其只包含图形的拐角点集
    vector biggest; // 定义最大轮廓
    // 为了滤除微小噪声,因此计算area 的面积
    int maxArea = 0; // 遍历临时变量,用以查找最大轮廓
    for (int i = 0; i < contours.size(); i++) // 关于contours.size()为什么是返回二维数组的行,因为 vector::size()函数只接受vector 对象的调用而contours的所有行(不管列)均为其对象
    {
        int area = contourArea(contours[i]);
        //cout << area << endl;

        // area > 1000 的目的是为了滤除图像中的微小瑕疵
        if (area > 1000)
        {
            

            float peri = arcLength(contours[i], true);// 该函数计算轮廓的长度,后面的bool值表面轮廓曲线是否闭合若为true 则轮廓曲线闭合
           
            approxPolyDP(contours[i], conpoly[i], 0.02 * peri, true); //  conpoly[i]是输出array   0.02*peri 这个参数理解不了就不要理解!!! 最后一个参数仍然是询问是否闭合
            
            // 判断图像中最大的矩形
            if (area > maxArea&& conpoly[i].size()==4) // 若面积最大,则将其点值赋值给biggest 其中 biggest 为 point 向量
            {
                //drawContours(imgOriginal, conpoly, i, Scalar(255, 0, 255), 2); // 只需要绘制最大图形即可
                maxArea = area;
                biggest = { conpoly[i][0],conpoly[i][1],conpoly[i][2],conpoly[i][3] };
                
            }

           
            
            //rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
        }
        
    }
    return biggest;
}
void drawPoints(vector points, Scalar color, Mat imgOriginal)
{
    for (int i = 0; i < points.size(); i++)
    {
        circle(imgOriginal,points[i],10,color,FILLED);
        putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color,4);
    }
}
// vector 容器pushback 函数是在vector 容器的尾部添加元素
vector reorder(vector initalPoints)
{
    vector tempPoints; // 设置中间变量
    vector sumPoints, subPoints;
    int sumMinIndex, sumMaxIndex; //  查找sum的最大(3)最小值(0)索引
    int subMinIndex, subMaxIndex; // 查找sub的最大()最小值()索引
    for (int i = 0; i < 4; i++)
    {
        sumPoints.push_back(initalPoints[i].x + initalPoints[i].y); // 和值最大为第四个点
        //和值最小为第一个点 
        // 而 2 3 点则是通过y - x 进行判断
        subPoints.push_back(initalPoints[i].x - initalPoints[i].y);
    }
    sumMinIndex = min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin(); // 0
    subMaxIndex = max_element(subPoints.begin(), subPoints.end()) - subPoints.begin(); //1
    subMinIndex = min_element(subPoints.begin(), subPoints.end()) - subPoints.begin();
    sumMaxIndex = max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin();// 3
    tempPoints.push_back(initalPoints[sumMinIndex]); // 0 即最小元素 
    tempPoints.push_back(initalPoints[subMaxIndex]); 
    tempPoints.push_back(initalPoints[subMinIndex]);
    tempPoints.push_back(initalPoints[sumMaxIndex]);
    return tempPoints;
}
Mat getWarp(Mat imgOriginal, vector finalPoints, float w, float h)
{
    Point2f src1[4] = { finalPoints[0],finalPoints[1],finalPoints[2],finalPoints[3] };
    Point2f src2[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };
    Mat martix = getPerspectiveTransform(src1, src2);
    Mat imgWarp;
    warpPerspective(imgOriginal, imgWarp, martix,Size(w,h));
    return imgWarp;
}

运行结果:

8、Opencv实现文档扫描_第1张图片

你可能感兴趣的:(OPENCV,opencv,c++,计算机视觉)