C++ opencv-3.4.1 图片矫正

对于倾斜的图片通过矫正可以得到水平的图片。一般有如下几种基于opencv的组合方式进行图片矫正。

  1. 傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转
  2. 边缘检测 + 霍夫变换 + 直线+角度 + 旋转
  3. 四点透视 + 角度 + 旋转
  4. 检测矩形轮廓 + 角度 + 旋转

傅里叶 + 霍夫变换

#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;

// 二值化阈值
#define GRAY_THRESH 150

// 直线上点的个数
#define HOUGH_VOTE 50

int main(int argc, char **argv)
{
    //Read a single-channel image
    const char* filename = "31.png";
    Mat srcImg = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
    if (srcImg.empty())
        return -1;
    imshow("source", srcImg);

    Point center(srcImg.cols / 2, srcImg.rows / 2);

    //Expand image to an optimal size, for faster processing speed
    //Set widths of borders in four directions
    //If borderType==BORDER_CONSTANT, fill the borders with (0,0,0)
    Mat padded;
    int opWidth = getOptimalDFTSize(srcImg.rows);
    int opHeight = getOptimalDFTSize(srcImg.cols);
    copyMakeBorder(srcImg, padded, 0, opWidth - srcImg.rows, 0, opHeight - srcImg.cols, BORDER_CONSTANT, Scalar::all(0));

    Mat planes[] = { Mat_(padded), Mat::zeros(padded.size(), CV_32F) };
    Mat comImg;
    //Merge into a double-channel image
    merge(planes, 2, comImg);

    //Use the same image as input and output,
    //so that the results can fit in Mat well
    dft(comImg, comImg);

    //Compute the magnitude
    //planes[0]=Re(DFT(I)), planes[1]=Im(DFT(I))
    //magnitude=sqrt(Re^2+Im^2)
    split(comImg, planes);
    magnitude(planes[0], planes[1], planes[0]);

    //Switch to logarithmic scale, for better visual results
    //M2=log(1+M1)
    Mat magMat = planes[0];
    magMat += Scalar::all(1);
    log(magMat, magMat);

    //Crop the spectrum
    //Width and height of magMat should be even, so that they can be divided by 2
    //-2 is 11111110 in binary system, operator & make sure width and height are always even
    magMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));

    //Rearrange the quadrants of Fourier image,
    //so that the origin is at the center of image,
    //and move the high frequency to the corners
    int cx = magMat.cols / 2;
    int cy = magMat.rows / 2;

    Mat q0(magMat, Rect(0, 0, cx, cy));
    Mat q1(magMat, Rect(0, cy, cx, cy));
    Mat q2(magMat, Rect(cx, cy, cx, cy));
    Mat q3(magMat, Rect(cx, 0, cx, cy));

    Mat tmp;
    q0.copyTo(tmp);
    q2.copyTo(q0);
    tmp.copyTo(q2);

    q1.copyTo(tmp);
    q3.copyTo(q1);
    tmp.copyTo(q3);

    //Normalize the magnitude to [0,1], then to[0,255]
    normalize(magMat, magMat, 0, 1, CV_MINMAX);
    Mat magImg(magMat.size(), CV_8UC1);
    magMat.convertTo(magImg, CV_8UC1, 255, 0);
    imshow("magnitude", magImg);
    //imwrite("imageText_mag.jpg",magImg);

    //Turn into binary image
    threshold(magImg, magImg, GRAY_THRESH, 255, CV_THRESH_BINARY);
    imshow("mag_binary", magImg);
    //imwrite("imageText_bin.jpg",magImg);

    //Find lines with Hough Transformation
    vector lines;
    float pi180 = (float)CV_PI / 180;
    Mat linImg(magImg.size(), CV_8UC3);
    HoughLines(magImg, lines, 1, pi180, HOUGH_VOTE, 0, 0);
    int numLines = lines.size();
    for (int l = 0; l

如果检测不出来,通过修改两个参数进行调节:
GRAY_THRESH 150 :二值化的阈值
HOUGH_VOTE 50 : 霍夫变换时每条线上的最少点个数

边缘检测 + 霍夫变换

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include 
using namespace cv;
using namespace std;


// 直线上点的个数
#define HOUGH_VOTE 50

//度数转换
double DegreeTrans(double theta)
{
    double res = theta / CV_PI * 180;
    return res;
}


//逆时针旋转图像degree角度(原尺寸)    
void rotateImage(Mat src, Mat& img_rotate, double degree)
{
    //旋转中心为图像中心    
    Point2f center;
    center.x = float(src.cols / 2.0);
    center.y = float(src.rows / 2.0);
    int length = 0;
    length = sqrt(src.cols*src.cols + src.rows*src.rows);
    //计算二维旋转的仿射变换矩阵  
    Mat M = getRotationMatrix2D(center, degree, 1);
    warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色  
}

//通过霍夫变换计算角度
double CalcDegree(const Mat &srcImage, Mat &dst)
{
    Mat midImage, dstImage;

    Canny(srcImage, midImage, 50, 200, 3);
    cvtColor(midImage, dstImage, CV_GRAY2BGR);

    //通过霍夫变换检测直线
    vector lines;
    HoughLines(midImage, lines, 1, CV_PI / 180, HOUGH_VOTE);//第5个参数就是阈值,阈值越大,检测精度越高
    //cout << lines.size() << endl;

    //由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢
    //所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。
    float sum = 0;
    //依次画出每条线段
    for (size_t i = 0; i < lines.size(); i++)
    {
        float rho = lines[i][0];
        float theta = lines[i][1];
        Point pt1, pt2;
        //cout << theta << endl;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        //只选角度最小的作为旋转角度
        sum += theta;

        line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA); //Scalar函数用于调节线段颜色

        imshow("直线探测效果图", dstImage);
    }
    float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好

    cout << "average theta:" << average << endl;

    double angle = DegreeTrans(average) - 90;

    rotateImage(dstImage, dst, angle);
    //imshow("直线探测效果图2", dstImage);
    return angle;
}


void ImageRecify(const char* pInFileName, const char* pOutFileName)
{
    double degree;
    Mat src = imread(pInFileName);
    imshow("原始图", src);
    Mat dst;
    //倾斜角度矫正
    degree = CalcDegree(src, dst);
    rotateImage(src, dst, degree);
    cout << "angle:" << degree << endl;
    imshow("旋转调整后", dst);

    Mat resulyImage = dst(Rect(0, 0, dst.cols, 500)); //根据先验知识,估计好文本的长宽,再裁剪下来
    imshow("裁剪之后", resulyImage);
    imwrite("recified.jpg", resulyImage);
}


int main()
{
    ImageRecify("31.png", "FinalImage.jpg");
    waitKey();
    return 0;
}

矩形轮廓 + 角度 + 旋转

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include 
using namespace cv;
using namespace std;
#include 

bool x_sort(const Point2f & m1, const Point2f & m2)
{
    return m1.x < m2.x;
}


//第一个参数:输入图片名称;第二个参数:输出图片名称
void GetContoursPic(const char* pSrcFileName, const char* pDstFileName)
{
    Mat srcImg = imread(pSrcFileName);
    imshow("原始图", srcImg);
    Mat gray, binImg;
    //灰度化
    cvtColor(srcImg, gray, COLOR_RGB2GRAY);
    imshow("灰度图", gray);
    //二值化
    threshold(gray, binImg, 150, 200, CV_THRESH_BINARY);
    imshow("二值化", binImg);

    vector  contours;
    vector > f_contours;
    
    //注意第5个参数为CV_RETR_EXTERNAL,只检索外框  
    findContours(binImg, f_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓

    int max_area = 0;
    int index;
    for (int i = 0; i < f_contours.size(); i++)
    {
        double tmparea = fabs(contourArea(f_contours[i]));
        if (tmparea > max_area)
        {
            index = i;
            max_area = tmparea;
        }

    }

    contours = f_contours[index];
    CvBox2D rect = minAreaRect(Mat(contours));


    float angle = rect.angle;
    cout << "before angle : " << angle << endl;
    if (angle < -45)
        angle = (90 + angle);
    else
        angle = -angle;
    cout << "after angle : " << angle << endl;
    
    //新建一个感兴趣的区域图,大小跟原图一样大  
    Mat RoiSrcImg(srcImg.rows, srcImg.cols, CV_8UC3); //注意这里必须选CV_8UC3
    RoiSrcImg.setTo(0); //颜色都设置为黑色  
    //imshow("新建的ROI", RoiSrcImg);
    //对得到的轮廓填充一下  
    drawContours(binImg, f_contours, 0, Scalar(255), CV_FILLED);

    //抠图到RoiSrcImg
    srcImg.copyTo(RoiSrcImg, gray);


    //再显示一下看看,除了感兴趣的区域,其他部分都是黑色的了  
    namedWindow("RoiSrcImg", 1);
    imshow("RoiSrcImg", RoiSrcImg);

    //创建一个旋转后的图像  
    Mat RatationedImg(RoiSrcImg.rows, RoiSrcImg.cols, CV_8UC1);
    RatationedImg.setTo(0);
    //对RoiSrcImg进行旋转  
    Point2f center = rect.center;  //中心点  
    Mat M2 = getRotationMatrix2D(center, angle, 1);//计算旋转加缩放的变换矩阵 
    warpAffine(RoiSrcImg, RatationedImg, M2, RoiSrcImg.size(), 1, 0, Scalar(0));//仿射变换 
    imshow("旋转之后", RatationedImg);
}

void main()
{
    GetContoursPic("34.png", "FinalImage.jpg");
    waitKey();
}

参考:

OpenCV实现基于傅里叶变换的旋转文本校正
OpenCV-Python的文本透视矫正与水平矫正
penCV探索之路(十六):图像矫正技术深入探讨
OpenCV探索之路(二十二):制作一个类“全能扫描王”的简易扫描软件
OpenCV—python 图像矫正(基于傅里叶变换—基于透视变换
python+opencv实现基于傅里叶变换的旋转文本校正
Python+OpenCV实现旋转文本校正

你可能感兴趣的:(C++ opencv-3.4.1 图片矫正)