OpenCV图片校正

news2024/12/26 3:22:04

OpenCV图片校正

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

背景

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

几种校正方法

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

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

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

#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

#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

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.四点透视 + 角度 + 旋转

#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

#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.检测矩形轮廓 + 角度 + 旋转

#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

#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;
}

参考

  • 榴莲小怪兽 opencv-图片矫正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/903449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机场项目:解决飞行物空间大小/纵横比、速度、遮挡等问题引起的实时目标检测问题...

点击蓝字 关注我们 关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;计算机视觉研究院 学习群&#xff5c;扫码在主页获取加入方式 计算机视觉研究院专栏 Column of Computer Vision Institute 最近在arXiv中翻阅到一遍基于Yolov8的飞机实时目标检测论文&#xff…

回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本…

MySQL | JDBC连接数据库

一、什么是JDBC 概念&#xff1a; JDBC&#xff0c;即Java Database Connectivity&#xff0c;java数据库连接。是一种用于执行SQL语句的Java API&#xff0c;它是Java中的数据库连接规范。这个API由 java.sql.*,javax.sql.* 包中的一些类和接口组成&#xff0c;它为Java开发…

八种架构演进

日升时奋斗&#xff0c;日落时自省 目录 1、单机架构 2、应用数据分离架构 3、应用服务集群架构 4、读写分离/主从分离架构 5、冷热分离架构 6、垂直分库架构 7、微服务架构 8、容器编排架构 9、小结 1、单机架构 特征&#xff1a;应用服务和数据库服务器公用一台服务…

HRS--人力资源系统(Springboot+vue)--打基础升级--(四)新增员工信息

今天的更新&#xff1a;在员工信息界面&#xff0c;点新增按钮&#xff0c;弹出一个请填写新增员工信息的窗口&#xff0c;然后保存数据 1. 先来看下窗口设计样式 嗯&#xff0c;感觉这窗口又细又长&#xff0c;果然排版不是我的强项&#xff0c;这里的新增属于管理员的权限界面…

SQL助你面大厂(N日留存率)

在一些游戏中比较关注的就是留存率&#xff0c;随着王者的更新&#xff0c;画质在不断的优化&#xff0c;可是匹配机制确实一成不变&#xff0c;导致很多人逐渐退游&#xff0c;那么我们的王者的官方是怎么知道有这么多人退游么&#xff1f;退游了是相当于删除玩家对应的那条数…

第 7 章 排序算法(4)(插入排序)

7.7插入排序 7.7.1插入排序法介绍: 插入式排序属于内部排序法&#xff0c;是对于欲排序的元素以插入的方式找寻该元素的适当位置&#xff0c;以达到排序的目的。 7.7.2插入排序法思想: 插入排序&#xff08;Insertion Sorting&#xff09;的基本思想是&#xff1a;把n个待排…

【令牌桶算法与漏桶算法】

&#x1f4a7; 令牌桶算法与漏桶算法 \color{#FF1493}{令牌桶算法与漏桶算法} 令牌桶算法与漏桶算法&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专…

机器学习深度学习——NLP实战(情感分析模型——RNN实现)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——NLP实战&#xff08;情感分析模型——数据集&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&…

玩转红黑树:手把手教你实现和理解红黑树

玩转红黑树&#xff1a;手把手教你实现和理解红黑树 引言一、红黑树的定义1.1、理论知识1.2、代码实现1.3、代码优化 二、红黑树的旋转2.1、理论知识2.2、代码实现 三、红黑树插入节点3.1、理论知识3.2、代码实现 四、红黑树删除节点4.1、理论知识4.2、代码实现 五、红黑树的查…

04_17页回收问题和水位线和swap交换空间和oom,内存性能微调

前言 应用程序通过 malloc 函数申请内存的时候&#xff0c;实际上申请的是虚拟内存&#xff0c;此时并不会分配物理内存。 当应用程序读写了这块虚拟内存&#xff0c;CPU 就会去访问这个虚拟内存&#xff0c; 这时会发现这个虚拟内存没有映射到物理内存&#xff0c; CPU 就会…

基于STM32CubeMX和keil采用SPI通信实现轮询方式读写W25Q128获取设备ID

文章目录 前言1. SPI通信1.1 SPI硬件接口与连线1.2 SPI传输协议1.3 SPI的数据传输 2. W25Q1282.1 片选2.2 控制指令2.3 时序图分析 3. STM32CubeMX配置3.1 引脚配置3.2 时钟配置3.3 SPI配置3.4 工程配置 4. 代码编写4.1读ID函数 总结 前言 最近使用通信比较多&#xff0c;包含…

多仓库手机端erp进销存pc/h5开源版开发

多仓库手机端erp进销存pc/h5开源版开发 以下是多仓库手机端ERP进销存PC/H5的功能列表&#xff1a; 仓库管理&#xff1a;包括仓库的新增、编辑、删除、查询等功能&#xff0c;可以管理多个仓库的库存情况。 商品管理&#xff1a;可以对商品进行新增、编辑、删除、查询等操作&a…

【JavaSE】面向对象之多态

文章目录 多态的概念多态实现条件重写向上转型和向下转型向上转型向下转型 避免在构造方法中调用重写的方法 多态的概念 通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态实现条件 必须要…

通过LD_PRELOAD绕过disable_functions

LD_PRELOAD 在UNIX的动态链接库的世界中&#xff0c;LD_PRELOAD就是这样一个环境变量&#xff0c;它可以影响程序的运行时的链接&#xff08;Runtime linker&#xff09;&#xff0c;它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态…

PANGOLIN写字

效果: 代码: #include <iostream> #include <pangolin/pangolin.h>// pangolin 绘制文字demousing namespace std;int main() {//创建一个窗口pangolin::CreateWindowAndBind("PangolinShowText", 640, 480);// 定义字体pangolin::GlFont * text_font …

⛳ TCP 协议详解

目录 ⛳ TCP 协议详解&#x1f3a8; 一、TCP / IP 协议的分层模型&#x1f3d3; 1.1、应用层&#x1f9f8; 1.2、传输层&#x1f52e; 1.3、网络层&#x1f3af; 1.4、链路层 &#x1f3ed; 二、HTTP 报文传输原理&#x1f945; 2.1、HTTP 报文传输&#x1f48e; 2.2、封装和分…

【Linux取经路】解析环境变量,提升系统控制力

文章目录 一、进程优先级1.1 什么是优先级&#xff1f;1.2 为什么会有优先级&#xff1f;1.3 小结 二、Linux系统中的优先级2.1 查看进程优先级2.2 PRI and NI2.3 修改进程优先级2.4 进程优先级的实现原理2.5 一些名词解释 三、环境变量3.1 基本概念3.2 PATH&#xff1a;Linux系…

操作系统练习:在Linux上创建进程,及查看进程状态

说明 进程在执行过程中可以创建多个新的进程。创建进程称为“父进程”&#xff0c;新的进程称为“子进程”。每个新的进程可以再创建其他进程&#xff0c;从而形成进程树。 每个进程都有一个唯一的进程标识符&#xff08;process identifier&#xff0c;pid&#xff09;。在L…

games101-windows环境配置(CMake+vcpkg+VS2019)

下载工具 安装CMake 安装vcpkg 安装vs2019 安装 eigen3 opencv 在vcpkg安装目录下&#xff0c;使用Windows Power Shell运行下面脚本 .\vcpkg.exe install eigen3:x64-windows .\vcpkg.exe install opencv:x64-windows安装过程中可能会用红色字体提示&#xff1a;Failed to…