文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

OpenCV实现倾斜文字校正

2022-11-13 13:52

关注

基于OpenCV的倾斜文字校正,供大家参考,具体内容如下

使用OpenCV里example中的的倾斜文本作为输入,本文的目的即将该倾斜的文本校正成水平方向的文本。

主要思路为:

读取图像-——>Canny边缘检测——->形态学操作-——>提取最小外接矩形——->计算旋转矩阵-——>仿射变换校正文本图像

原始图像:

提取最小外接矩形区域

校正后的图像

主要涉及的API

创建滑动条

这个API可以创建一个滑动条,可以在不改变程序的情况下更改变量的值来显示图像变化的效果。在这个场景中使用创建滑动条来调节Canny边缘检测的阈值,提取合适的边缘。

```cpp
Clicking the label of each trackbar enables editing the trackbar values manually.

@param trackbarname Name of the created trackbar.\\第一个参数为滑动条的名称
@param winname Name of the window that will be used as a parent of the created trackbar. \\ 滑动条所依附的窗口名称
@param value Optional pointer to an integer variable whose value reflects the position of the slider. Upon creation, the slider position is defined by this variable.
\\ 引用值,即拖动滑动条所改变的值,需要提前定义,定义好的值即为滑动条的初始值。
@param count Maximal position of the slider. The minimal position is always 0. \\滑动条的最大位置
@param onChange Pointer to the function to be called every time the slider changes position. This function should be prototyped as void Foo(int,void\*); , where the first parameter is the trackbar position and the second parameter is the user data (see the next parameter). If the callback is the NULL pointer, no callbacks are called, but only value is updated.
\\ 定义回调函数,每次滑动滑动条都会调用这个回调函数。回调函数的格式为 void Foo(int,void*)其中第一个参数为轨迹条的位置,第二个参数是用户数据userdata。
@param userdata User data that is passed as is to the callback. It can be used to handle trackbar events without using global variables.
\\ 用户传递给回调函数的数据userdata,这里使用的是全局变量,因此这一项可以忽略。

CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);
### Canny边缘检测

使用Canny边缘检测算法提取文本图像的边缘

`

```cpp

CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
                         double threshold1, double threshold2,
                         int apertureSize = 3, bool L2gradient = false );

边缘检测的结果,由于测试的样本图像比较理想,调节滑动条的位置影响不是很大。如果图像质量较差,也可以添加图像预处理步骤优化源图像。

形态学处理

使用形态学处理操作连接经过Canny边缘检测处理后字符缝隙,使其变成一个连通域。

步骤:创建结构元素(getStructuringElement)->膨胀处理

Mat src_dilate;
Mat kernel = getStructuringElement(MORPH_RECT, Size(11, 11), Point());
// 创建结构元素的大小,第一个参数为结构元素的形状(矩形MORPH_RECT,十字形结构MORPH_CROSS,椭圆形结构MORPH_ELLIPSE),第二个参数为结构元素的大小,第三个参数为锚点的位置。
dilate(Canny_edge, src_dilate, kernel, Point(-1, -1), 1, BORDER_DEFAULT);
// 膨胀图像,第一个参数为输入图像,第二个为输出图像,第三个参数为结构元素,第四个参数为锚点的位置,第五个参数为迭代的次数,最后一个参数为边界填充的类型

膨胀后的图像显示为:

查找轮廓

vector<vector<Point>> Contours;
// 定义边缘的点集
vector<Vec4i> hierarchy;
// 定义边缘的层次关系
findContours(src_dilate, Contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point());

CV_EXPORTS_W void findContours( InputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset = Point());

最小外接矩形

旋转矩形对象类型 RotatedRect

轮廓的最小外接矩形 minAreaRect


CV_EXPORTS_W RotatedRect minAreaRect( InputArray points );

计算旋转矩阵

Mat Rotation = getRotationMatrix2D(center,RRect_degree,1.0);
Mat output;
warpAffine(src,output,Rotation,src.size(),INTER_CUBIC,BORDER_CONSTANT,Scalar(255,255,255));
// 输入要变换图像,输出图像,定义旋转矩阵,定义插值方式,边界类型(边界类型需要注意)

完整代码

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/calib3d.hpp>

using namespace std;
using namespace cv;

// Define Mat
Mat src, gray_src, dst;
const char *IWindow = "InputWindow";
const char *OWindow = "OutputWindow";
int canny_threshold = 100;
int threshold_level = 255;

void Canny_function(int, void *);

int main(int argc, char **argv) {
    src = imread("D:/Delete/imageTextR.png");
    if (src.empty()) {
        cout << "Could not load image" << endl;
        return -1;
    }
    // Covert to GrayScale
    cvtColor(src, gray_src, COLOR_BGR2GRAY);
    namedWindow(IWindow, WINDOW_AUTOSIZE);
    imshow(IWindow, src);
    namedWindow(OWindow, WINDOW_AUTOSIZE);
    createTrackbar("Canny_Threshold", IWindow, &canny_threshold, threshold_level, Canny_function);

    waitKey();
    return 0;
}

void Canny_function(int, void *) {
    Mat Canny_edge;
    Canny(gray_src, Canny_edge, canny_threshold, canny_threshold * 2, 3, false);
    //Display Canny_edge
    //imshow(OWindow,Canny_edge);

    Mat src_dilate;
    Mat kernel = getStructuringElement(MORPH_RECT, Size(11, 11), Point());
    dilate(Canny_edge, src_dilate, kernel, Point(-1, -1), 1, BORDER_DEFAULT);
    //Display Dilate image
    imshow(OWindow,src_dilate);

    // Find Contour
    vector<vector<Point>> Contours;
    vector<Vec4i> hierarchy;
    findContours(src_dilate, Contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point());

    // Select the Max area Contour
    double MaxAreaRRect = 0;
    int SizeContour = 0;
    for (size_t t = 0; t < Contours.size(); t++) {
        RotatedRect RRect = minAreaRect(Contours[t]);
        double AreaRRect = 0;
        AreaRRect = RRect.size.area();
        MaxAreaRRect = max(MaxAreaRRect, AreaRRect);
    }

    double RRect_degree = 0;
    dst = src.clone(); // 这里涉及是否复制数据的问题
    for (size_t t = 0; t < Contours.size(); t++) {
        RotatedRect RRect = minAreaRect(Contours[t]);
        double AreaRRect = RRect.size.area();
        if (AreaRRect == MaxAreaRRect ) {
            SizeContour = SizeContour + 1;
            // Rotate degree
            RRect_degree = RRect.angle;
            // Draw this rectangle
            Point2f vertex[4];
            RRect.points(vertex);
            for (int i = 0; i < 4; i++) {
                line(dst, Point(vertex[i]), Point(vertex[(i + 1) % 4]), Scalar(0, 255, 0), 2, LINE_8);
            }
        }
    }
    cout << "SizeContour : \t "<< SizeContour <<endl;
    cout << "Rotated Rectangle angle : " << RRect_degree << endl;

    //imshow(OWindow, dst);

    Point2f center(src.cols/2,src.rows/2);
    Mat Rotation = getRotationMatrix2D(center,RRect_degree,1.0);
    Mat output;
    warpAffine(src,output,Rotation,src.size(),INTER_CUBIC,BORDER_CONSTANT,Scalar(255,255,255));


    // Display Rotate Rectangle
    namedWindow("Final_Result",WINDOW_AUTOSIZE);
    imshow("Final_Result", output);


}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯