基于OpenCV进行文本分块切割

假设有如下一张图,如何把其中的文本分块切割出来,比如“华普超市朝阳门店”、“2015-07-26”就是两个文本块。

做图像切割有很多种方法,本文描述一种最直观的投影检测法。先来看看什么是投影,简单来说,投影就是在一定方向上有效像素的数量。来看个直观的图像:

这是一张水平投影图与原图的对比,从投影图上能看到多个波峰,文字多的地方,投影就长,行间的空白处,投影为0。 上个示例代码:

public void HorizontalProjection()
{
    //以灰度图方式读入源泉文件
    string filename = "source.jpg";
    var src = IplImage.FromFile(filename, LoadMode.GrayScale);

    //二值化,采用阈值分割法
    Cv.Threshold(src, src, 0, 255, ThresholdType.BinaryInv | ThresholdType.Otsu);

    //存储投影值的数组
    var h = new int[src.Height];

    //对每一行计算投影值
    for(int y = 0;y < src.Height;++y)
    {
        //遍历这一行的每一个像素,如果是有效的,累加投影值
        for(int x = 0;x < src.Width;++x)
        {
            var s = Cv.Get2D(src, y, x);
            if(s.Val0 == 255)
                h[y]++;
        }
    }

    //准备一个图像用于画投影图
    var paintY = Cv.CreateImage(src.Size, BitDepth.U8, 1);
    Cv.Zero(paintY);

    //画图
    var t = new CvScalar(255);
    for(int y = 0;y < src.Height;++y)
    {
        for(int x = 0;x < h[y];++x)
            Cv.Set2D(paintY, y, x, t);
    }

    //显示
    using(var window = new CvWindow("Source"))
    {
        window.Image = src;
        using(var win2 = new CvWindow("Projection"))
        {
            win2.Image = paintY;
            Cv.WaitKey();
        }
    }
}

显然找出波峰对应的y值,就能把行切割开了。 得到一行以后,可以采用类似的思想进行垂直投影,挑了一行测试一下,效果如下:

可以看到效果不是特别好,左右结构的汉字有可能被切开,一个完整的数值也有可能分成多个数字,这种情况需要做一下处理,比如识别的时候要判断如果间距较小就认为仍是同一文本块,或者对图像进行一下横向膨胀处理:

var kernal = Cv.CreateStructuringElementEx(3, 1, 1, 0, ElementShape.Rect);
Cv.Dilate(src, src, kernal, 4);

再计算投影,得到的效果就好多了:

最后上完整代码以及切割效果展示:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using OpenCvSharp;
using OpenCvSharp.Extensions;
using OpenCvSharp.Utilities;

namespace OpenCvTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //打开原文件
            string filename = "source.jpg";
            var src = IplImage.FromFile(filename);

            //转成灰度图
            var gray = Cv.CreateImage(src.Size, BitDepth.U8, 1);
            Cv.CvtColor(src, gray, ColorConversion.BgrToGray);

            //二值化,阈值分割算法
            Cv.Threshold(gray, gray, 0, 255, ThresholdType.BinaryInv | ThresholdType.Otsu);

            //分行
            var rows = GetRowRects(gray);

            //针对每一行再分块
            var items = new List<CvRect>();
            foreach (var row in rows)
            {
                var cols = GetBlockRects(gray.Clone(row), row.Y);
                items.AddRange(cols);
            }

            //把识别出的每一块画到原图上去
            var color = new CvScalar(255, 0, 0);
            foreach (var rect in items)
            {
                Cv.DrawRect(src, rect, color, 1);
            }

            //显示
            using (var window = new CvWindow("Image"))
            {
                window.Image = src;
                Cv.WaitKey();
            }
        }

        /// <summary>
        /// 识别行
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private static List<CvRect> GetRowRects(IplImage source)
        {
            var rows = new List<CvRect>();

            //用于存储投影值
            var projection = new int[source.Height];

            //遍历每一行计算投影值
            for (int y = 0; y < source.Height; ++y)
            {
                for (int x = 0; x < source.Width; ++x)
                {
                    var s = Cv.Get2D(source, y, x);
                    if (s.Val0 == 255)
                        projection[y]++;
                }
            }

            bool inLine = false;
            int start = 0;

            //开始根据投影值识别分割点
            for (int i = 0; i < projection.Length; ++i)
            {
                if (!inLine && projection[i] > 10)
                {
                    //由空白进入字符区域了,记录标记
                    inLine = true;
                    start = i;
                }
                else if ((i - start > 5) && projection[i] < 10 && inLine)
                {
                    //由字符区域进入空白区域了
                    inLine = false;

                    //忽略高度太小的行,比如分隔线
                    if (i - start > 10)
                    {
                        //记录下位置
                        var rect = new CvRect(0, start, source.Width, i - start);
                        rows.Add(rect);
                    }
                }
            }
            
            return rows;
        }

        /// <summary>
        /// 识别块
        /// </summary>
        /// <param name="source"></param>
        /// <param name="rowY"></param>
        /// <returns></returns>
        private static List<CvRect> GetBlockRects(IplImage source, int rowY)
        {
            var blocks = new List<CvRect>();

            //用于存储投影值
            var projection = new int[source.Width];
            
            //先进行横向膨胀
            var kernal = Cv.CreateStructuringElementEx(3, 1, 1, 0, ElementShape.Rect);
            Cv.Dilate(source, source, kernal, 4);

            //遍历每一列计算投影值
            for (int x = 0; x < source.Width; ++x)
            {
                for (int y = 0; y < source.Height; ++y)
                {
                    var s = Cv.Get2D(source, y, x);
                    if (s.Val0 == 255)
                        projection[x]++;
                }
            }

            bool inBlock = false;
            int start = 0;

            //开始根据投影值识别分割点
            for (int i = 0; i < projection.Length; ++i)
            {
                if (!inBlock && projection[i] >= 2)
                {
                    //由空白区域进入字符区域了
                    inBlock = true;
                    start = i;
                }
                else if ((i - start > 10) && inBlock && projection[i] < 2)
                {
                    //由字符区域进入空白区域了
                    inBlock = false;
            
                    //记录位置,注意由于传入的是source只是一行,因此最终的位置信息要+rowY
                    var rect = new CvRect(start, rowY, i - start, source.Height);
                    blocks.Add(rect);
                }
            }

            return blocks;
        }
    }
}

得到的图像如下,大部分效果还行,有些识别错误,将来继续优化吧:

转载请注明出处 http://boytnt.blog.51cto.com/966121/1679697

时间: 2024-08-07 08:34:52

基于OpenCV进行文本分块切割的相关文章

基于opencv图片切割

基于opencv图片切割为n个3*3区块 工作原因,切割图片,任务急,暂留调通的源码,留以后用. package com.rosetta.image.test; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.Rect; import org.opencv.highgui.Highgui; /** * @Author: nya * @Date: 18-8-28 下午5:50 */ pu

Java基于opencv实现图像数字识别(一)

Java基于opencv实现图像数字识别(一) 最近分到了一个任务,要做数字识别,我分配到的任务是把数字一个个的分开:当时一脸懵逼,直接百度java如何分割图片中的数字,然后就百度到了用BufferedImage这个类进行操作:尝试着做了一下,做到灰度化,和二值化就做不下去了:然后几乎就没有啥java的资料了,最多的好像都是c++,惹不起.惹不起...... 我也想尝试着用c++做一下,百度到了c++基于opencv来做图像识别的:但是要下vs啊,十几个g呢,我内存这么小,配置这么麻烦,而且vs

基于OpenCV的图片卡通化处理

学习OpenCV已有一段时间,除了研究各种算法的内容,在空闲之余,根据书本及资料的引导,尝试结合图像处理算法和日常生活联系起来,首先在台式机上(带摄像头)完成一系列视频流处理功能,开发平台为Qt5.3.2+OpenCV2.4.9. 本次试验实现的功能主要有: 调用摄像头捕获视频流: 将帧图像转换为素描效果图片: 将帧图像卡通化处理: 简单地生成"怪物"形象: 人脸肤色变换. 本节所有的算法均由类cartoon中的函数cartoonTransform()来实现: // Frame:输入每

基于Opencv和Mfc的图像处理增强库GOCVHelper(索引)

GOCVHelper(GreenOpen Computer Version Helper )是我在这几年编写图像处理程序的过程中积累下来的函数库.主要是对Opencv的适当扩展和在实现Mfc程序时候的功能增强. 这里将算法库开放源代码,并且编写一系列blog对函数实现进行说明.目的是在于“取之于互联网,用之于互联网”.并且也希望该库能够继续发展下去. 由于算法库基于Opencv和Mfc进行编写,所以要求阅读使用者具备一定基础. 最终提交的是GOCVHelper.h 和GOCVHelper版本号.

基于opencv网络摄像头在ubuntu下的视频获取

 基于opencv网络摄像头在ubuntu下的视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译运行步骤 安装编译opencv-2.3  参考http://blog.csdn.net/xiabodan/article/details/23547847 3  测试代码 编译 g++ cameraCaptrue.cpp -o test `pkg-config --libs --cflags opencv` cameraCaptrue.cpp

Csharp调用基于Opencv编写的类库文件

现在将Csharp调用基于Opencv编写的类库文件(Dll)的方法定下来,我取名叫做GreenOpenCsharpWarper,简称GOCW. 一.CLR编写的DLL部分 1.按照正常方法引入Opencv; 2.提供接口函数,进行图像处理(这里只是实现了cvtColor,实际过程中可以用自己编写的复杂函数) String^  Class1::Method(cli::array<unsigned char>^ pCBuf1){     pin_ptr<System::Byte> p

基于opencv的小波变换

基于opencv的小波变换 提供函数DWT()和IDWT(),前者完成任意层次的小波变换,后者完成任意层次的小波逆变换.输入图像要求必须是单通道浮点图像,对图像大小也有要求(1层变换:w,h必须是2的倍数:2层变换:w,h必须是4的倍数:3层变换:w,h必须是8的倍数......),变换后的结果直接保存在输入图像中.1.函数参数简单,图像指针pImage和变换层数nLayer.2.一个函数直接完成多层次二维小波变换,尽量减少下标运算,避免不必要的函数调用,以提高执行效率.3.变换过程中,使用了一

基于 OpenCV 的人脸识别

基于 OpenCV 的人脸识别 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的图片拼接,到交互艺术展览的技术实现中,都有 OpenCV 的身影. OpenCV 起始于 1999 年 Intel 的一个内部研究项目.从那时起,它的开发就一直很活跃.进化到现在,它已支持如 OpenCL 和 OpenGL 的多种现代技术,也支持如 iOS

每日一练之自适应中值滤波器(基于OpenCV实现)

本文主要介绍了自适应的中值滤波器,并基于OpenCV实现了该滤波器,并且将自适应的中值滤波器和常规的中值滤波器对不同概率的椒盐噪声的过滤效果进行了对比.最后,对中值滤波器的优缺点了进行了总结. 空间滤波器 一个空间滤波器包括两个部分: 一个邻域,滤波器进行操作的像素集合,通常是一个矩形区域 对邻域中像素进行的操作 一个滤波器就是在选定的邻域像素上执行预先定义好的操作产生新的像素,并用新的像素替换掉原来像素形成新的图像. 通常,也可以将滤波器称之为核(kernel),模板(template)或者窗