问题:如果文字大小存在比较大的差异时,怎么办?
答:这里给出另外一种策略,不是使用投影直方图,而是使用膨胀以及寻找连通区域。进行分割。
1)对图像二值化
2)对二值化之后的图像进行膨胀操作(dilate)
3)在2)得到的结果上寻找联通区域的边界(findContours)。
4)利用3)得到的结果画出方框。
本文是对这里的文章的另一种实现。使用C++。

  • 首先,读取图片
Mat img = imread(IMG_PATH);
    if (img.empty())
    {
        cerr<<"can not read image"<<endl;
    }
    imshow("original image", img);
    Mat img_erode = img.clone();
  • 第一步:1)对图像二值化
  • 第二步:2)对二值化之后的图像进行膨胀操作(dilate)
void step_1_erode(Mat& img){

    cvtColor(img,img,CV_BGR2GRAY,1);
    threshold(img,img,90,255,THRESH_OTSU);
    img = 255 - img;
    imshow("after otsu",img);

    Mat ker = Mat::ones(3,7,CV_8U);
    //Mat ker = getStructuringElement(MORPH_ELLIPSE,Size(3,3));
    dilate(img,img,ker,Point(0,1));
    imshow("after dilate",img);

}

腐蚀之后的结果:

opencv汉字提取 opencv文字区域提取_二值化


问题:getStructuringElement是一个什么样的存在?

答:获取形态学滤波中使用的“结构元”

问题:dilate表示什么意思?

答:对图像进行“膨胀”操作

问题:是否有其他形态学算法?

答:有,比如腐蚀(erode),开(open),。。。

还有一个高大上的函数“morphologyEx”,只需要在其中选择参数就可以实现各种形态学操作。

  • 第三步:3)在2)得到的结果上寻找联通区域的边界(findContours)。
  • 第四步:4)利用3)得到的结果画出方框。
void step_2_find_conection(const Mat& original_img, Mat& img_erode){

    // find contours of img
    vector<vector<Point>> contours;
    findContours(img_erode,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);

    Scalar color = Scalar(0,0,255);
    // 在原图上画出边界
    Mat img1 = original_img.clone();
    drawContours(img1,contours,-1,color);
    imshow("img with contours",img1);

    // 在原图上画出,包括边界的最大矩形
    Mat img2 = original_img.clone();
    Rect ri;
    vector<vector<Point>>::iterator itcon;
    for (itcon = contours.begin(); itcon != contours.end(); itcon++)
    {
        ri = boundingRect(*itcon);
        if (ri.height >10 && (ri.width * 1.0 / ri.height) > 0.2)
            rectangle(img2,ri,color);
    }
    imshow("img with rectangle",img2);


    // 如果有旋转的话,需要使用下面的方法画出旋转的方框
    Mat img3 = original_img.clone();
    vector<RotatedRect> minRect(contours.size());
    for (int i = 0; i < contours.size(); i++){
        minRect[i] = minAreaRect(Mat(contours[i]));
        Point2f rect_points[4];
        minRect[i].points(rect_points);
        for (int j = 0; j < 4; j++)
            line(img3,rect_points[j],rect_points[(j+1)%4],color,1,8);
    }
    imshow("img with rotated rectangle",img3);

}

找出的边界结果:

opencv汉字提取 opencv文字区域提取_#include_02


最后的方框结果:

opencv汉字提取 opencv文字区域提取_#include_03

问题:vector< vector> contours; 是什么鬼?好可怕

答:见这里。

问题:如果文字存在旋转,怎么办?比如下面这张图

opencv汉字提取 opencv文字区域提取_二值化_04


答:在代码中有一段“// 如果有旋转的话,需要使用下面的方法画出旋转的方框”,这一段就是处理旋转的情况的。

对上图的处理结果如下:

opencv汉字提取 opencv文字区域提取_opencv_05

放大招,整体代码如下:

// csdn_code.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;


#define IMG_PATH  "..//figures//222_angle.jpg"

void step_1_erode(Mat& img){

    cvtColor(img,img,CV_BGR2GRAY,1);
    threshold(img,img,90,255,THRESH_OTSU);
    img = 255 - img;
    imshow("after otsu",img);

    Mat ker = Mat::ones(3,7,CV_8U);
    //Mat ker = getStructuringElement(MORPH_ELLIPSE,Size(3,3));
    dilate(img,img,ker,Point(0,1));
    imshow("after dilate",img);

}


void step_2_find_conection(const Mat& original_img, Mat& img_erode){

    // find contours of img
    vector<vector<Point>> contours;
    findContours(img_erode,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);

    Scalar color = Scalar(0,0,255);
    // 在原图上画出边界
    Mat img1 = original_img.clone();
    drawContours(img1,contours,-1,color);
    imshow("img with contours",img1);

    // 在原图上画出,包括边界的最大矩形
    Mat img2 = original_img.clone();
    Rect ri;
    vector<vector<Point>>::iterator itcon;
    for (itcon = contours.begin(); itcon != contours.end(); itcon++)
    {
        ri = boundingRect(*itcon);
        if (ri.height >10 && (ri.width * 1.0 / ri.height) > 0.2)
            rectangle(img2,ri,color);
    }
    imshow("img with rectangle",img2);


    // 如果有旋转的话,需要使用下面的方法画出旋转的方框
    Mat img3 = original_img.clone();
    vector<RotatedRect> minRect(contours.size());
    for (int i = 0; i < contours.size(); i++){
        minRect[i] = minAreaRect(Mat(contours[i]));
        Point2f rect_points[4];
        minRect[i].points(rect_points);
        for (int j = 0; j < 4; j++)
            line(img3,rect_points[j],rect_points[(j+1)%4],color,1,8);
    }
    imshow("img with rotated rectangle",img3);

}

int main()
{

    Mat img = imread(IMG_PATH);
    if (img.empty())
    {
        cerr<<"can not read image"<<endl;
    }
    imshow("original image", img);
    Mat img_erode = img.clone();

    step_1_erode(img_erode);

    step_2_find_conection(img,img_erode);

    waitKey();
    system("pause");
    return 0;
}