OpenCV 分割斜体文字(包括旋转,垂直投影,水平投影,透视变换等)

一,原图
这里写图片描述
二,分割成行
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
三,通过旋转,切割后的效果(无透视变换)
这里写图片描述

四,通过旋转,透视变换,切割后的效果
这里写图片描述
五,源代码

//初始化透视变换全局变量
Mat warpMatrix;

//计算透视变换矩阵
void InitPersTransfor()
{
    vector<Point> not_a_rect_shape;
    not_a_rect_shape.push_back(Point(3, 82));
    not_a_rect_shape.push_back(Point(2, 276));
    not_a_rect_shape.push_back(Point(234, 191));
    not_a_rect_shape.push_back(Point(239, 1));
    // For debugging purposes, draw green lines connecting those points
    // and save it on disk
    const Point* point = &not_a_rect_shape[0];
    int n = (int)not_a_rect_shape.size();
    //Mat draw = ImgCut.clone();
    //polylines(draw, &point, &n, 1, true, Scalar(0, 255, 0), 3, CV_AA);
    //imwrite("draw.jpg", draw);
    //  topLeft, topRight, bottomRight, bottomLeft
    cv::Point2f src_vertices[4];
    src_vertices[0] = not_a_rect_shape[0];
    src_vertices[1] = not_a_rect_shape[1];
    src_vertices[2] = not_a_rect_shape[2];
    src_vertices[3] = not_a_rect_shape[3];

    Point2f dst_vertices[4];
    dst_vertices[0] = Point(2, 2);
    dst_vertices[1] = Point(0, 194);
    dst_vertices[2] = Point(235, 190);
    dst_vertices[3] = Point(239, 1);
    warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
}

//斜体分割
void MyCutImageItalic(Mat src, int iImgNO, CString C_SavePath, bool bSave)
{
    //--------------旋转------------------------------------------------------
    double angle = 19;
    Point2f center(src.cols / 2, src.rows / 2);
    Mat rot = getRotationMatrix2D(center, angle, 1);
    Rect bbox = RotatedRect(center, src.size(), angle).boundingRect();

    rot.at<double>(0, 2) += bbox.width / 2.0 - center.x;
    rot.at<double>(1, 2) += bbox.height / 2.0 - center.y;

    //设置选择背景边界颜色:白色 
    Scalar borderColor = Scalar(255, 255, 255);
    Mat dst;
    warpAffine(src, dst, rot, bbox.size(), INTER_LINEAR, BORDER_CONSTANT, borderColor);

    Mat gray, BinImg;
    if (dst.channels() == 3)
    {
       cvtColor(dst, gray, CV_BGR2GRAY);   //转换成灰度图
    }
    else
    {
        gray = dst;
    }
    //--------------旋转------------------------------------------------------

    //--------------垂直投影--------------------------------------------------
    threshold(gray, BinImg, 166, 255, CV_THRESH_BINARY);                               //整个图像进行二值化
    Mat lineImg(1, BinImg.cols, CV_8UC1, cv::Scalar(0, 0, 0));                        //生成一维图像,高是1,宽是原图列数
    lineImg = VerticalProjection(BinImg);

    //---------------判断峰值的开始结束位置-------------------------
    vector<int> ivecBegin;                                                            //开始
    vector<int> ivecEnd;                                                              //结束
    int icount = 0;                                                                   //统计峰值数
    int ibegin = 666666;                                                              //开始位置
    int iend = 666666;                                                                //结束位置
    int minVal = 2;                                                                   //判断的基准值
    int minSpace = 6;                                                                 //最小间距
    bool bbegin = false;                                                              //判断峰值是否在最左边
    int value = 0;                                                                    //图像像素值
    for (int i = 0; i < lineImg.cols; i++)
    {
        value = lineImg.at<uchar>(0, i);
        //=======排除最左边峰值干扰=============
        if (value > minVal&&i == 0)
        {
            bbegin = true;
        }
        if (value < minVal&&bbegin == true)
        {
            bbegin = false;
        }
        //=======排除最左边峰值干扰=============

        if (value > minVal && ibegin == 666666 && bbegin == false)//bbegin == false可排除最左边未切割干净的字符
        {
            ibegin = i;                                                               //开始位置  
        }
        else if (value<minVal&&ibegin != 666666 && (i - ibegin)>minSpace)
        {
            icount++;                                                                 //统计数加1
            iend = i;                                                                 //结束位置
            ivecBegin.push_back(ibegin);                                              //加入容器
            ivecEnd.push_back(iend);                                                  //加入容器
            ibegin = 666666;                                                          //复位
            iend = 666666;                                                            //复位
        }
        else if (value>minVal&&ibegin != 666666 && i == lineImg.cols - 1)               //峰值在最右边情况
        {
            icount++;                                                                 //统计数加1
            iend = i;                                                                 //结束位置
            ivecBegin.push_back(ibegin);                                              //加入容器
            ivecEnd.push_back(iend);                                                  //加入容器
            ibegin = 666666;                                                          //复位
            iend = 666666;                                                            //复位
        }
        else
        {

        }
    }  //for (int i = 0; i < lineImg.cols; i++)
    //---------------判断峰值的开始结束位置-------------------
    //--------------垂直投影--------------------------------------------------

    for (int i = 0; i < ivecEnd.size(); i++)
    {
        Mat ImgCut = BinImg(Rect(ivecBegin[i] - 1, 0, ivecEnd[i] - ivecBegin[i] + 1, BinImg.rows));                           //选择二值化图像部分
        Mat ImgCutgray = gray(Rect(ivecBegin[i] - 1, 0, ivecEnd[i] - ivecBegin[i] + 1, BinImg.rows));                          //选择灰度图像部分

        //-------------------透视变换校正-----------------------------------------------------
        cv::Mat rotated, rotatedgray;
        //设置选择背景边界颜色:白色 
        Scalar borderColor2 = Scalar(255, 255, 255);
        warpPerspective(ImgCut, rotated, warpMatrix, rotated.size(), INTER_LINEAR, BORDER_CONSTANT, borderColor2);
        warpPerspective(ImgCutgray, rotatedgray, warpMatrix, rotatedgray.size(), INTER_LINEAR, BORDER_CONSTANT, borderColor2);
        //-------------------透视变换校正-----------------------------------------------------

        //-------------------先做一下水平投影,去除多余空白-----------------------------------
        Mat rotatedCopy;
        rotated.copyTo(rotatedCopy);
        Mat HorImg = HorProjection(rotatedCopy);                                            //水平投影
        int ibeginrow = 0;                                                                  //开始行
        int iendrow = 0;                                                                    //结束行
        bool bbeginrow = false;
        bool bendrow = false;
        bool bHorFengzhi = false;                                                           //水平方向是否有峰值
        for (int i = 0; i < HorImg.rows; i++)
        {
            if (HorImg.at<uchar>(i, 0) != 0 && bbeginrow == false) //正向搜索
            {
                ibeginrow = i;
                bbeginrow = true;
            }
            if (HorImg.at<uchar>(HorImg.rows - i - 1, 0) != 0 && bendrow == false) //反向搜索
            {
                iendrow = HorImg.rows - i - 1;
                bendrow = true;
            }
        }
        Mat imgSave = rotatedgray(Rect(0, ibeginrow, ImgCut.cols, iendrow - ibeginrow + 1));

        //-------------------先做一下水平投影,去除多余空白-----------------------------------

        if (bSave == true)
        {
            CString CTemp;
            CTemp.Format(_T("%s%d_%d.jpg"), C_SavePath,iImgNO, i + 1);
            string strImgPath = (CW2A(CTemp.GetString()));
            imwrite(strImgPath, imgSave);
        }
    } //    for (int i = 0; i < ivecEnd.size(); i++)
}

//读取待分割图像
bool CCutImageVS2013Dlg::MyClassifiReadImageItalic()
{
    //初始化透视变换
    InitPersTransfor();

    CWnd* pWnd = AfxGetMainWnd();//
    CString C_SavePath, C_readImgPath;

    int NUM = 0;            //读图图片数量
    pWnd->GetDlgItem(IDC_ImgNum_EDIT)->GetWindowTextW(m_ImgNumVal);//获取控件中的参数值
    NUM = _wtoi(m_ImgNumVal); //传给全局变量
    if (NUM == 0)
    {
        AfxMessageBox(_T("请输入图像数量!"));
        return false;
    }

    Mat image;
    int iImgNO = 1;
    CString C_str;
    int iType = 66;
    //##########判断文件夹存不存在###########################    

    if (nSel == 0)
    {
        C_readImgPath = "D:\\sxl\\处理图片\\斜体分割\\1lineImage\\";  //读取图像路径
        C_SavePath = "D:\\sxl\\处理图片\\斜体分割\\1lineImage处理\\";

        //------------方便查看结果-------------
        C_FilePath = C_SavePath;
        //------------方便查看结果-------------

        //strPath0.Format(_T("%s\\"), C_Str0);
        if (!PathFileExists(C_SavePath))
        {
            system("md D:\\sxl\\处理图片\\斜体分割\\1lineImage处理\\");
        }
    }
    int icount = 0;
    int iID = 0;
    while (iImgNO <= NUM)   //
    {
        //#############################################
        CString C_Status;
        C_Status.Format(_T("正在处理第%d张图,共%d张!"), iImgNO, NUM);
        pWnd->GetDlgItem(IDC_Status_EDIT)->SetWindowTextW(C_Status);
        pWnd->UpdateWindow();
        //#############################################
        if ((iImgNO-icount*21) == 22)
        {
            icount++;
            iID = 0;
        }
        iID++;
        C_str.Format(_T("%s%d_%d.jpg"), C_readImgPath, icount, iID);   //读取图像路径

        string strImgPath = (CW2A(C_str.GetString()));
        image = imread(strImgPath, 0);//读取图片  
        if (image.data == 0)
        {
            AfxMessageBox(_T("没有图片!"));
            return false;
        }
        else
        {
            //#####处理图像####################
            Mat imageCopy;
            image.copyTo(imageCopy);
            MyCutImageItalic(image, iImgNO, C_SavePath, true);

            //#####处理图像####################

        }
        iImgNO++;
    }
    AfxMessageBox(_T("运行结束!"));
    return true;
}


猜你喜欢

转载自blog.csdn.net/sxlsxl119/article/details/80882187