Цель
Функция расширения 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");
}