OpenCvSharp学习笔记12--利用形态学操作提取水平和垂直线+实战:表格及文字提取

目的

Dilate()膨胀函数使用
Erode()腐蚀函数使用
GetStructuringElement()获取结构元素

原理

最常用的形态学运算是膨胀及腐蚀,其它形态学运算是膨胀与腐蚀运算的组合结果。
膨胀是在图像对象(白色,非0)边界增加像素、腐蚀则相反。具体如何增加或减少像素取决于运算的结构元素(也称为核)的大小与形状。具体如下:

膨胀

具体输出的像素值为结构元素中所有1位置对应于输入图像的像素值的最大值,位置由锚点决定。下图为官网示图:用结构元素(核)扫描输入图像,取对应结构元素中为1的任意输入图像中的最大值,填致输出图像对应的锚点位置。
二值图的膨胀运算

二值图的膨胀运算

灰度图的膨胀运算

灰度图的膨胀运算

腐蚀

相对膨胀运算是取最大值,而腐蚀运算是取最小值,官网示图如下:
二值图的腐蚀运算

二值图的腐蚀运算

灰度图的腐蚀运算

灰度图的腐蚀运算

结构元素

结构元素是形态学运算的重要组成部分。一般情况下结构元素是由0和1(击中击不中有-1)构成的任意形态和大小的矩阵,通常比输入图像小的多。

官网图例

乐谱
这是一张官网处理处理的图像,需要提取水平线及音乐符号。
未平滑的水平线

未平滑的水平线

提取的音符

提取的音符

官网代码示例

/// <summary>
/// 提取音符
/// </summary>
/// <exception cref="Exception"></exception>
private void ExtractNote()
{
    
    
    var fileName = ImagePath.Music;
    using var src = Cv2.ImRead(fileName, ImreadModes.Color);
    if (src.Empty()) throw new Exception($"图像打开有误:{
      
      fileName}");

    Mat gray = new Mat();
    if (src.Channels() == 3)
    {
    
    
        Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
    }
    else
    {
    
    
        gray = src;
    }
    Utils.ShowWaitDestroy(gray, "gray");
    Mat binMat = new Mat();
    //原图为白底黑字,先Gray取反,则Binary
    Cv2.AdaptiveThreshold(~gray, binMat, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 15, -2);
    Utils.ShowWaitDestroy(binMat, "binary");

    //创建两张图像副本用于提取水平线和垂直线
    Mat horizontal = binMat.Clone();
    Mat vertical = binMat.Clone();

    int times = 30;
    //水平轴大小,该值太小的话,文字或符号中的水平线也会被提取
    int horizontalSize = horizontal.Cols / times;
    //水平线提取的结构元素
    Mat horizontalStructure = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(horizontalSize, 1));

    Cv2.Erode(horizontal, horizontal, horizontalStructure);
    Cv2.Dilate(horizontal, horizontal, horizontalStructure);
    //水平线
    Utils.ShowWaitDestroy(horizontal, "horizontal");

    //垂直轴大小
    int verticalSize = vertical.Rows / times;
    //垂直线提取的结构元素
    Mat verticalStructure = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(1, verticalSize));
    //先腐蚀,再膨胀,等价于开运算
    Cv2.MorphologyEx(vertical, vertical, MorphTypes.Open, verticalStructure);

    //垂直线
    Utils.ShowWaitDestroy(vertical, "vertical");

    // Inverse vertical image
    Cv2.BitwiseNot(vertical, vertical);
    Utils.ShowWaitDestroy(vertical, "vertical_bit");

    var vClone = vertical.Clone();

    // Extract edges and smooth image according to the logic
    // 1. extract edges
    // 2. dilate(edges)
    // 3. src.copyTo(smooth)
    // 4. blur smooth img
    // 5. smooth.copyTo(src, edges)

    // 1、提取边缘
    Mat edges = new Mat();
    Cv2.AdaptiveThreshold(vertical, edges, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 3, -2);
    Utils.ShowWaitDestroy(edges, "edges");

    // 2、膨胀边缘
    Mat kernel = Mat.Ones(2, 2, MatType.CV_8UC1);
    Cv2.Dilate(edges, edges, kernel);
    Utils.ShowWaitDestroy(edges, "Dilate edges");

    // 3、待平滑图
    Mat smooth = new Mat();
    Cv2.CopyTo(vertical, smooth);

    // 4、均值平滑
    Cv2.Blur(smooth, smooth, new Size(2, 2));
    Utils.ShowWaitDestroy(smooth, "Blur smooth");

    // 5、复制带边缘掩膜的平滑结果
    Cv2.CopyTo(smooth, vertical, edges);

    Utils.ShowWaitDestroy(vertical, "smooth-final");
    Cv2.DestroyAllWindows();
}

实战:表格及文字提取

根据上面的思路,自己做了一张带文字的表格图片,如下:
含有文字的表格
实现目标:生成两张图像,一张只有表格,另一张只有文字
提取的表格
提取的文字

实战代码示例

/// <summary>
/// 提取表格及文字
/// </summary>
/// <exception cref="Exception"></exception>
private void ExtractTableAndText()
{
    
    
    var fileName = ImagePath.TableAndText;
    using var src = Cv2.ImRead(fileName, ImreadModes.Color);
    if (src.Empty()) throw new Exception($"图像打开有误:{
      
      fileName}");

    Mat gray = new Mat();
    if (src.Channels() == 3)
    {
    
    
        Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
    }
    else
    {
    
    
        gray = src;
    }
    Utils.ShowWaitDestroy(gray, "gray");
    Mat binMat = new Mat();
    //原图为白底黑字,先Gray取反,则Binary
    Cv2.AdaptiveThreshold(~gray, binMat, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 15, -2);
    Utils.ShowWaitDestroy(binMat, "二值图");
    
    //创建两张图像副本用于提取水平和垂直线
    Mat horizontal = binMat.Clone();
    Mat vertical = binMat.Clone();

    //水平轴大小,该值太小的话,文字中的水平线也会被提取
    int horizontalSize = 120;//比文字Max(宽、高)大,比单元格Min(高、宽)略小
    //水平线提取的结构元素
    Mat horizontalStructure = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(horizontalSize, 1));
    //先腐蚀,再膨胀,等价于开运算
    Cv2.MorphologyEx(horizontal, horizontal, MorphTypes.Open, horizontalStructure);
    Utils.ShowWaitDestroy(horizontal, "horizontal");

    //垂直轴大小
    int verticalSize = horizontalSize;// vertical.Rows / times;
    //垂直线提取的结构元素
    Mat verticalStructure = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(1, verticalSize));
    //开运算
    Cv2.MorphologyEx(vertical, vertical, MorphTypes.Open, verticalStructure);
    Utils.ShowWaitDestroy(vertical, "vertical");

    //表格=水平线 + 垂直线
    Mat Table = horizontal.BitwiseOr(vertical);

    
    //将表格稍微膨胀
    using var dilateTable = new Mat();
    Cv2.Dilate(Table, dilateTable,null);

    //源二值图-膨胀后的表格,即为文字内容
    Mat Text = binMat - dilateTable;

    //平滑边缘
    SmoothEdges(Table);

    //平滑边缘
    SmoothEdges(Text);
}

/// <summary>
/// 平滑边缘
/// </summary>
/// <param name="binSrc"></param>
private void SmoothEdges(Mat binSrc)
{
    
    
    // Inverse vertical image
    Cv2.BitwiseNot(binSrc, binSrc);
    Utils.ShowWaitDestroy(binSrc, "binSrc_bit");

    // Extract edges and smooth image according to the logic
    // 1. extract edges
    // 2. dilate(edges)
    // 3. src.copyTo(smooth)
    // 4. blur smooth img
    // 5. smooth.copyTo(src, edges)

    // 1、提取边缘
    Mat edges = new Mat();
    Cv2.AdaptiveThreshold(binSrc, edges, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 3, -2);
    Utils.ShowWaitDestroy(edges, "edges");

    // 2、边缘膨胀
    Mat kernel = Mat.Ones(2, 2, MatType.CV_8UC1);
    Cv2.Dilate(edges, edges, kernel);
    Utils.ShowWaitDestroy(edges, "Dilate edges");

    // 3、复制源图
    Mat smooth = new Mat();
    Cv2.CopyTo(binSrc, smooth);

    // 4、对源图副本平滑
    Cv2.Blur(smooth, smooth, new Size(2, 2));
    Utils.ShowWaitDestroy(smooth, "Blur smooth");

    // 5、复制带边缘掩膜的平滑结果
    Cv2.CopyTo(smooth, binSrc, edges);

    Utils.ShowWaitDestroy(binSrc, "smooth -final");            
}

猜你喜欢

转载自blog.csdn.net/TyroneKing/article/details/129859451