图像旋转校正的尝试

参考了https://blog.csdn.net/MengchiCMC/article/details/77981112,进行测试。

主要代码如下:
image_rotate_calib.hpp

#ifndef image_rotate_calib_hpp
#define image_rotate_calib_hpp
#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstdio>
#include <list>
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
void image_rotate_line();
#endif /* image_rotate_calib_hpp */

image_rotate_calib.cpp

#include "image_rotate_calib.hpp"
using namespace std;
using namespace cv;

/**
 * @brief calculate the angle of two lines by using vector angle formula: cos(thea) = (a*b) / (|a||b|)
 * @param line1
 * @param line2
 * @return result ranges from 0 to pi
 */
double angle_of_lines(const Vec4i& line1, const Vec4i& line2)
{
    double moduleLine1 = sqrt(pow(line1[0] - line1[2], 2) + pow(line1[1] - line1[3], 2));
    double moduleLine2 = sqrt(pow(line2[0] - line2[2], 2) + pow(line2[1] - line2[3], 2));
    double dotProduct = (line1[0] - line1[2]) * (line2[0] - line2[2]) + (line1[1] - line1[3]) * (line2[1] - line2[3]);
    return acos(dotProduct / moduleLine1 / moduleLine2) * 180 / CV_PI;
}

/**
 * @brief comparison function for sort, sort vector<Vec4i> from small to large accodoring to x of the midpoint of each element
 * @param line1
 * @param line2
 * @return
 */
bool getMinMidX(const cv::Vec4i& line1, const cv::Vec4i& line2)
{
    return (line1[0] + line1[2]) < (line2[0] + line2[2]); // Although middle point compared, there is no need to divide 2
}

/**
 * @brief comparison function for sort, sort vector<Vec4i> from large to small accodoring to x of the midpoint of each element
 * @param line1
 * @param line2
 * @return
 */
bool getMaxMidX(const cv::Vec4i& line1, const cv::Vec4i& line2)
{
    return (line1[0] + line1[2]) > (line2[0] + line2[2]);
}

/**
 * @brief comparison function for sort, sort vector<Vec4i> from small to large accodoring to y of the midpoint of each element
 * @param line1
 * @param line2
 * @return
 */
bool getMinMidY(const cv::Vec4i& line1, const cv::Vec4i& line2)
{
    return (line1[1] + line1[3]) < (line2[1] + line2[3]);
}

/**
 * @brief comparison function for sort, sort vector<Vec4i> from large to small accodoring to y of the midpoint of each element
 * @param line1
 * @param line2
 * @return
 */
bool getMaxMidY(const cv::Vec4i& line1, const cv::Vec4i& line2)
{
    return (line1[1] + line1[3]) > (line2[1] + line2[3]);
}

/**
 * @brief rotation angle in degrees for correcting tilt
 * @param line: for cv::Vec4i& line, [0] is always smaller than [2]
 * @return The symbol of the result represnts the direction of rotation to correct tilt.
 *         Positive values mean counter-clockwise rotation (the coordinate origin is assumed to be the top-left corner).
 */
double angleForCorrect(const cv::Vec4i& line)
{
    Vec4i unitXVector(0, 0, 1, 0);
    double angle = angle_of_lines(unitXVector, line);  // here angle belongs to [0, pi/2]
    // @attention: the increment direction of X and Y axis of OpenCV is different from usual rectangular coordinate system. The origin point is in the upper left corner of the image
    if (angle < 45)
    {
        // consider in the horizontal direction
        if (line[1] > line[3])
        {
            angle = -angle;
        }
    }
    else
    {
        // consider in the vertical direction
        if (line[1] > line[3])
        {
            angle = 90 - angle;
        }
        else
        {
            angle = angle - 90;
        }
    }
    
    return angle;
}

/**
 * @brief rotate iamge according to angle
 * @param src
 * @param dst
 * @param angle: rotation angle in degrees. Positive values mean counter-clockwise rotation (the
 coordinate origin is assumed to be the top-left corner).
 */
void rotateIamge(cv::Mat& src, cv::Mat& dst, double angle)
{
    cv::Point2f center(src.cols / 2, src.rows / 2);
    cv::Mat rot = getRotationMatrix2D(center, angle, 1);
    cv::Rect box = RotatedRect(center, src.size(), angle).boundingRect(); // get circumscribed rectangle
    cv::warpAffine(src, dst, rot, box.size());
}

void image_rotate_line()
{
    Mat imgOri = imread("/Users/TEST/test.jpeg");
    Mat imgScale;
    float scaleFactor = 400.0/imgOri.cols;
    resize(imgOri, imgScale, Size(imgOri.cols * scaleFactor, imgOri.rows * scaleFactor));
    
    Mat imgGray;
    cvtColor(imgScale, imgGray, COLOR_BGR2GRAY);
    
    Mat imgCanny;
    Canny(imgScale, imgCanny, 100, 200);
    imshow("canny", imgCanny);
    
    vector<Vec4i> lineAll;
    HoughLinesP(imgCanny, lineAll, 1, CV_PI/180, 30, 50, 4);
    Mat imgAllLines;
    imgScale.copyTo(imgAllLines);
    for (int i = 0, steps = lineAll.size(); i < steps; i++)
    {
        line(imgAllLines, Point(lineAll[i][0], lineAll[i][1]), Point(lineAll[i][2], lineAll[i][3]), Scalar(255, 255, 255), 3, 8);
    }
    imshow("All lines detected", imgAllLines);
    
    list<Vec4i> linesList;
    for(vector<Vec4i>::iterator itor = lineAll.begin(); itor != lineAll.end(); itor++)
    {
        linesList.push_back(*itor);
    }
    
    vector<Vec4i> lineFiltered;
    for(list<Vec4i>::iterator itorOuter = linesList.begin(); itorOuter != linesList.end();)
    {
        for(list<Vec4i>::iterator itorInner = linesList.begin(); itorInner != linesList.end(); itorInner++)
        {
            // 选出相互垂直的线
            if(abs(angle_of_lines(*itorOuter, *itorInner) - 90) < 1)
            {
                // record these two lines
                lineFiltered.push_back(*itorOuter);
                lineFiltered.push_back(*itorInner);
                itorInner = linesList.erase(itorInner);
                itorOuter = linesList.erase(itorOuter);
                break;
            }
            
            if (itorInner == --linesList.end())
            {
                if (linesList.size() > 2)
                {
                    itorOuter = linesList.erase(itorOuter);  // erase current element when there is no other line perpendicular to it.
                }
                else
                {
                    itorOuter = linesList.end();
                    break;
                }
            }
        }
    }
    
    Mat imgLinesFiltered;
    imgScale.copyTo(imgLinesFiltered);
    // draw lines after filtering
    for (int i = 0, steps = lineFiltered.size(); i < steps; i++)
    {
        line(imgLinesFiltered, Point(lineFiltered[i][0], lineFiltered[i][1]), Point(lineFiltered[i][2], lineFiltered[i][3]), Scalar(255, 0, 0), 3, 8);
    }
    imshow("Lines after filtering", imgLinesFiltered);
 
    double correctAngle = 0.0;  // average tilt angle of PCB
    if (lineFiltered.size() > 0)
    {
        // find edge lines of PCB
        std::vector<Vec4i> lineEdge;
        sort(lineFiltered.begin(), lineFiltered.end(), getMinMidX);  // get the line at the far left of the image
        lineEdge.push_back(lineFiltered[0]);
        sort(lineFiltered.begin(), lineFiltered.end(), getMaxMidX);  // get the line at the far right of the image
        lineEdge.push_back(lineFiltered[0]);
        sort(lineFiltered.begin(), lineFiltered.end(), getMinMidY);  // get the line at the top of the image
        lineEdge.push_back(lineFiltered[0]);
        sort(lineFiltered.begin(), lineFiltered.end(), getMaxMidY);  // get the line at the buttom of the image
        lineEdge.push_back(lineFiltered[0]);
        
        Mat imgLinesEdge;
        imgScale.copyTo(imgLinesEdge);
        // draw lines after filtering
        for (int i = 0, steps = lineEdge.size(); i < steps; i++)
        {
            line(imgLinesEdge, Point(lineEdge[i][0], lineEdge[i][1]), Point(lineEdge[i][2], lineEdge[i][3]), Scalar(0, 0, 255), 3, 8);
        }
        imshow("PCB edge lines", imgLinesEdge);
        
        for (int i = 0, step = lineEdge.size(); i < step; i++)   // calcualte averge tilt angle of PCB edge lines
        {
            correctAngle += angleForCorrect(lineEdge[i]);
        }
        correctAngle /= lineEdge.size();
    }
    
    Mat dst;
    rotateIamge(imgOri, dst, correctAngle);
    
    imshow("rotated", dst);
}

运行结果展示:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值