OpenCV图片校正

OpenCV图片校正

  • 背景
  • 几种校正方法
  • [1.傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转](#1.傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转)
  • [3.四点透视 + 角度 + 旋转](#3.四点透视 + 角度 + 旋转)
  • [4.检测矩形轮廓 + 角度 + 旋转](#4.检测矩形轮廓 + 角度 + 旋转)
  • 参考

背景

遇到偏的图片想要校正成水平或者垂直的。

几种校正方法

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

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

1.傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转

cpp 复制代码
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

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_<float>(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<Vec2f> 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<numLines; l++)
    {
        float rho = lines[l][0], theta = lines[l][1];
        Point pt1, pt2;
        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));
        line(linImg, pt1, pt2, Scalar(255, 0, 0), 3, 8, 0);
    }
    imshow("lines", linImg);
    //imwrite("imageText_line.jpg",linImg);
    if (lines.size() == 3){
        cout << "found three angels:" << endl;
        cout << lines[0][1] * 180 / CV_PI << endl << lines[1][1] * 180 / CV_PI << endl << lines[2][1] * 180 / CV_PI << endl << endl;
    }

    //Find the proper angel from the three found angels
    float angel = 0;
    float piThresh = (float)CV_PI / 90;
    float pi2 = CV_PI / 2;
    for (int l = 0; l<numLines; l++)
    {
        float theta = lines[l][1];
        if (abs(theta) < piThresh || abs(theta - pi2) < piThresh)
            continue;
        else{
            angel = theta;
            break;
        }
    }

    //Calculate the rotation angel
    //The image has to be square,
    //so that the rotation angel can be calculate right
    angel = angel<pi2 ? angel : angel - CV_PI;
    if (angel != pi2){
        float angelT = srcImg.rows*tan(angel) / srcImg.cols;
        angel = atan(angelT);
    }
    float angelD = angel * 180 / (float)CV_PI;
    cout << "the rotation angel to be applied:" << endl << angelD << endl << endl;

    //Rotate the image to recover
    Mat rotMat = getRotationMatrix2D(center, angelD, 1.0);
    Mat dstImg = Mat::ones(srcImg.size(), CV_8UC3);
    warpAffine(srcImg, dstImg, rotMat, srcImg.size(), 1, 0, Scalar(255, 255, 255));
    imshow("result", dstImg);
    //imwrite("imageText_D.jpg",dstImg);

    waitKey(0);

    return 0;
}

opencv4x

cpp 复制代码
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h> 
#include <iostream>

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 = argv[1];
    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_<float>(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);
    normalize(magMat, magMat, 0, 1, NORM_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<Vec2f> 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<numLines; l++)
    {
        float rho = lines[l][0], theta = lines[l][1];
        Point pt1, pt2;
        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));
        line(linImg, pt1, pt2, Scalar(255, 0, 0), 3, 8, 0);
    }
    imshow("lines", linImg);
    //imwrite("imageText_line.jpg",linImg);
    if (lines.size() == 3){
        cout << "found three angels:" << endl;
        cout << lines[0][1] * 180 / CV_PI << endl << lines[1][1] * 180 / CV_PI << endl << lines[2][1] * 180 / CV_PI << endl << endl;
    }

    //Find the proper angel from the three found angels
    float angel = 0;
    float piThresh = (float)CV_PI / 90;
    float pi2 = CV_PI / 2;
    for (int l = 0; l<numLines; l++)
    {
        float theta = lines[l][1];
        if (abs(theta) < piThresh || abs(theta - pi2) < piThresh)
            continue;
        else{
            angel = theta;
            break;
        }
    }

    //Calculate the rotation angel
    //The image has to be square,
    //so that the rotation angel can be calculate right
    angel = angel<pi2 ? angel : angel - CV_PI;
    if (angel != pi2){
        float angelT = srcImg.rows*tan(angel) / srcImg.cols;
        angel = atan(angelT);
    }
    //float angelD = angel * 180 / (float)CV_PI;
    float angelD = angel * 180 / (float)CV_PI;
    cout << "the rotation angel to be applied: "<< angelD << endl << endl;

    //Rotate the image to recover
    Mat rotMat = getRotationMatrix2D(center, angelD, 1.0);
    Mat dstImg = Mat::ones(srcImg.size(), CV_8UC3);
    warpAffine(srcImg, dstImg, rotMat, srcImg.size(), 1, 0, Scalar(255, 255, 255));
    imshow("result", dstImg);
    imwrite("imageText_D.jpg",dstImg);

    waitKey(0);

    return 0;
}

CMakeLists.txt

bash 复制代码
project( main )
cmake_minimum_required(VERSION 3.10) 
#添加头文件路径
include_directories(/usr/local/include /usr/local/include/opencv4 /usr/local/include/opencv4/opencv2)
#添加库文件路径
link_directories(/usr/local/lib)
 
add_executable(main test.cpp)
target_link_libraries( main -lopencv_core  -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs)


3.四点透视 + 角度 + 旋转

cpp 复制代码
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
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<Vec2f> 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;
}

opencv4.x

cpp 复制代码
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
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, COLOR_GRAY2BGR);

    //通过霍夫变换检测直线
    vector<Vec2f> 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, 1000)); //根据先验知识,估计好文本的长宽,再裁剪下来
    imshow("裁剪之后", resulyImage);
    imwrite("recified.jpg", resulyImage);
}


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

4.检测矩形轮廓 + 角度 + 旋转

cpp 复制代码
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#include <algorithm>

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<Point>  contours;
    vector<vector<Point> > 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();
}

opencv4.x

cpp 复制代码
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <opencv2/imgproc/types_c.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;
#include <algorithm>

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<Point>  contours;
    vector<vector<Point> > 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];
    RotatedRect 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);
    imwrite("recified.jpg", RatationedImg);
}

int main()
{
    GetContoursPic("test.jpg", "FinalImage.jpg");
    waitKey();
    return 0;
}

参考

相关推荐
OptimaAI11 分钟前
【 LLM论文日更|检索增强:大型语言模型是强大的零样本检索器 】
人工智能·深度学习·语言模型·自然语言处理·nlp
谢眠15 分钟前
机器学习day4-朴素贝叶斯分类和决策树
人工智能·机器学习
HelpHelp同学19 分钟前
教育机构内部知识库:教学资源的集中管理与优化
人工智能·知识库软件·搭建知识库·知识管理工具
深度学习lover26 分钟前
<项目代码>YOLOv8 番茄识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·番茄识别
IT古董26 分钟前
【机器学习】机器学习中用到的高等数学知识-1.线性代数 (Linear Algebra)
人工智能·python·线性代数·机器学习
飞腾开发者34 分钟前
飞腾平台Arm NN软件栈安装使用指南
linux·运维·人工智能·机器学习·计算机视觉
Watermelo6171 小时前
通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制
人工智能·深度学习·神经网络·mongodb·机器学习·自然语言处理·数据挖掘
AI算法-图哥1 小时前
pytorch量化训练
人工智能·pytorch·深度学习·文生图·模型压缩·量化
大山同学1 小时前
DPGO:异步和并行分布式位姿图优化 2020 RA-L best paper
人工智能·分布式·语言模型·去中心化·slam·感知定位
机器学习之心1 小时前
时序预测 | 改进图卷积+informer时间序列预测,pytorch架构
人工智能·pytorch·python·时间序列预测·informer·改进图卷积