基于opencv实现字符分割(直方图投影法)

一 实现步骤

1. 使用imread()加载原图

2. 对原图进行二值化

3. 统计垂直方向和水平方向的直方图

4. 把直方图投影到Mat上

5. 从直方图分割字符

二 关键代码

1 加载原图
void MainWindow::on_tbImagePath_clicked()
{

    //选择原图
    QFileDialog dlg(this, tr("打开文件"), homeDir(),
                    "Image Files(*.bmp *.jpg *.jpeg *png);;BMP Files(*.bmp);;JPG Files(*.jpg *.jpeg);;PNG File(*.png);;All Files(*)");

    dlg.setFileMode(QFileDialog::ExistingFile);
    dlg.setAcceptMode(QFileDialog::AcceptOpen);

    QString sText;
    if (dlg.exec() == QDialog::Accepted)
    {
        sText = dlg.selectedFiles()[0];
        ui->leImagePath->setText(sText);
    }

    //1. 加载原图
    if (!sText.isEmpty())
    {
        m_matSrc = imread(sText.toLatin1().data(), IMREAD_COLOR);

        showImage();
    }
}
2 对原图进行二值化

void MainWindow::showImage()
{
    if (m_matSrc.empty()) return;

    m_pScene->clear();

    //1. 转成灰度图
    Mat matGray;
    cvtColor(m_matSrc, matGray, CV_RGB2GRAY);

    //2. 把灰度图转成二值图
    threshold(matGray, m_matBin, m_iThreshold, 255, THRESH_BINARY);
    imwrite("/home/mark/Desktop/matBin.bmp", m_matBin);

    int w = m_matSrc.cols;
    int h = m_matSrc.rows;
    if (m_pScene->width() != w || m_pScene->height() != h)
    {
        m_pScene->setSceneRect(0, 0, w, h);
        ui->graphicsView->setScene(m_pScene);
    }

    QPixmap pix;
    if (m_bShowWho)
    {
        pix = mat2Pixmap(&m_matSrc);
    }
    else
    {
        pix = mat2Pixmap(&m_matBin);
    }
    m_pScene->addPixmap(pix);

    //投影直方图 分割字符
    shadowHistogram();
}

3 统计垂直方向和水平方向的直方图

//水平方向投影直方图
void MainWindow::hShadowHistogram(int *&pHorizontal)
{
    if (m_matBin.empty()) return;

    int w = m_matBin.cols;
    int h = m_matBin.rows;
    pHorizontal = new int[h]; //创建一个用于储存每行黑色像素个数的数组
    memset(pHorizontal, 0, sizeof(int) * m_matBin.rows);

    //遍历每一行的图像灰度值,查找每一列0的值
    for (int iRow = 0; iRow < h; iRow++)
    {
        for (int iCol = 0; iCol < w; iCol++)
        {
            uchar perPixelValue = m_matBin.at(iRow, iCol);
            if (perPixelValue == 0)//如果是白底黑字
            {
                pHorizontal[iRow]++;
            }
        }
    }

    //新建一个Mat用于储存投影垂直方向直方图并将背景置为黑
    Mat matHorizontal(h, w, CV_8UC1, Scalar(0));

    //将直方图的曲线设为白色
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < pHorizontal[i]; j++)
        {
            //matHorizontal.at(i, w - 1 - j) = 255; //直方图设置为白色
            matHorizontal.at(i, j) = 255; //直方图设置为白色
        }
    }

    namedWindow("H");
    imshow("H", matHorizontal);
}

//垂直方向投影直方图
void MainWindow::vShadowHistogram(int *&pVertical)
{
    if (m_matBin.empty()) return;

    int w = m_matBin.cols;
    int h = m_matBin.rows;
    pVertical = new int[w];   //创建一个用于储存每列黑色像素个数的数组
    memset(pVertical, 0, sizeof(int) * m_matBin.cols);

    //遍历每一列的图像灰度值,查找每一行0的值
    for (int iCol = 0; iCol < w; iCol++)
    {
        for (int iRow = 0; iRow < h; iRow++)
        {
            uchar perPixelValue = m_matBin.at(iRow, iCol);
            if (perPixelValue == 0)//如果是白底黑字
            {
                pVertical[iCol]++;
            }
        }
    }

    //新建一个Mat用于储存投影垂直方向直方图并将背景置为黑
    Mat matVertical(h, w, CV_8UC1, Scalar(0));

    //将直方图的曲线设为白色
    for (int i = 0; i < w; i++)
    {
        for (int j = 0; j < pVertical[i]; j++)
        {
            //matVertical.at(h - 1 - j, i) = 255; //直方图设置为白
            matVertical.at(j, i) = 255; //直方图设置为白色色
        }
    }

    namedWindow("V");
    imshow("V", matVertical);
}
4 把直方图投影到Mat上

//投影直方图
void MainWindow::shadowHistogram()
{
    if (m_matBin.empty()) return;

    Mat matDraw = m_matSrc.clone();

    //1 投影直方图
    int* pHorizontal = NULL;
    int* pVertical = NULL;
    hShadowHistogram(pHorizontal); //投影水平方向直方图
    vShadowHistogram(pVertical); //投影垂直方向直方图

    //2 根据直方图 分割字符
    int w = m_matBin.cols;
    int h = m_matBin.rows;

    QList hLine;
    bool bBlock = false;//是否遍历到了字符区内

    if (pHorizontal)
    {
        int iStartY = 0;  //记录进入字符区的索引
        int iEndY = 0;    //记录进入空白区域的索引

        for (int i = 0; i < h; i++)
        {
            if (!bBlock && pHorizontal[i] != 0)//进入字符区了
            {
                bBlock = true;
                iStartY = i;

                QLine line1(QPoint(0, iStartY), QPoint(w, iStartY));
                hLine << line1;
                m_pScene->addLine(line1, QPen(QColor(255, 0, 0)));

                line(matDraw, Point(0, iStartY), Point(w-1, iStartY), Scalar(0, 0, 255));
            }
            else if (bBlock && pHorizontal[i] == 0)
            {
                iEndY = i;
                bBlock = false;

                QLine line2(QPoint(0, iEndY), QPoint(w, iEndY));
                hLine << line2;
                m_pScene->addLine(line2, QPen(QColor(255, 0, 0)));

                line(matDraw, Point(0, iEndY), Point(w-1, iEndY), Scalar(0, 0, 255));
            }
        }
    }

    QList vLine;
    bBlock = false;//是否遍历到了字符区内

    if (pVertical)
    {
        int iStartX = 0;  //记录进入字符区的索引
        int iEndX = 0;    //记录进入空白区域的索引

        for (int i = 0; i < w; i++)
        {
            if (!bBlock && pVertical[i] != 0)//进入字符区了
            {
                bBlock = true;
                iStartX = i;

                QLine line1(QPoint(iStartX, 0), QPoint(iStartX, h));
                vLine << line1;
                m_pScene->addLine(line1, QPen(QColor(0, 255, 0)));

                line(matDraw, Point(iStartX, 0), Point(iStartX, h-1), Scalar(0, 255, 0));
            }
            else if (bBlock && pVertical[i] == 0)
            {
                iEndX = i;
                bBlock = false;

                QLine line2(QPoint(iEndX, 0), QPoint(iEndX, h));
                vLine << line2;
                m_pScene->addLine(line2, QPen(QColor(0, 255, 0)));

                line(matDraw, Point(iEndX, 0), Point(iEndX, h-1), Scalar(0, 255, 0));
            }
        }
    }

    imwrite("/home/mark/Desktop/matDraw.bmp", matDraw);

    MYDELETES(pHorizontal);
    MYDELETES(pVertical);
}

三 测试结果

测试结果1:

基于opencv实现字符分割(直方图投影法)_第1张图片

测试结果2

基于opencv实现字符分割(直方图投影法)_第2张图片

四 源码链接

https://download.csdn.net/download/cwj066/10859928

你可能感兴趣的:(opencv)