Примечания к исследованию OpenCvSharp 12. Использование морфологических операций для извлечения горизонтальных и вертикальных линий + практический бой: извлечение таблиц и текста.

Цель

Функция расширения Dilate(). Используйте
функцию коррозии Erode(). Используйте
GetStructuringElement() для получения структурных элементов.

принцип

Наиболее часто используемые морфологические операции — это расширение и размывание, а другие морфологические операции представляют собой комбинацию операций расширения и размывания.
Расширение добавляет пиксели к границам объектов изображения (белые, ненулевые), эрозия делает обратное. То, как именно добавляются или вычитаются пиксели, зависит от размера и формы структурных элементов (также называемых ядрами) операции. подробности следующим образом:

Расширение

Конкретное значение выходного пикселя — это максимальное значение всех 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");            
}

Supongo que te gusta

Origin blog.csdn.net/TyroneKing/article/details/129859451
Recomendado
Clasificación