MainWindow.xaml
<Window x:Class="Splash.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OpenCVの文字区域提取" Icon="OpenCV.ico" WindowState="Maximized" WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Margin="4" Padding="16,8" MinWidth="160" HorizontalAlignment="Left" Content="选择图像…" Name="ButtonOpen" Click="ButtonOpen_Click"/>
<TabControl Grid.Row="1" Margin="4">
<TabItem Header="原图" Padding="16,8">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Image Margin="4" Stretch="None" Name="ImageRaw"/>
</ScrollViewer>
</TabItem>
<TabItem Header="二值化图" Padding="16,8">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Image Margin="4" Stretch="None" Name="ImageBinary"/>
</ScrollViewer>
</TabItem>
<TabItem Header="初次膨胀图" Padding="16,8">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Image Margin="4" Stretch="None" Name="ImageDilate1"/>
</ScrollViewer>
</TabItem>
<TabItem Header="初次腐蚀图" Padding="16,8">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Image Margin="4" Stretch="None" Name="ImageErode1"/>
</ScrollViewer>
</TabItem>
<TabItem Header="二次膨胀图" Padding="16,8">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Image Margin="4" Stretch="None" Name="ImageDilate2"/>
</ScrollViewer>
</TabItem>
<TabItem Header="轮廓图" Padding="16,8">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Image Margin="4" Stretch="None" Name="ImageContour"/>
</ScrollViewer>
</TabItem>
</TabControl>
</Grid>
</Window>
MainWindow.xaml.cs
/* ----------------------------------------------------------
* 文件名称:MainWindow.xaml.cs
*
* 作者:秦建辉
*
* 微信:splashcn
*
* 博客:http://www.firstsolver.com/wordpress/
*
* 开发环境:
* Visual Studio V2017
* .NET Framework 4.7.2
* OpenCvSharp 4.0.30319
*
* 版本历史:
* V1.0 2019年01月05日
* OpenCVの文字区域提取
*
* 参考资料:
* http://www.voidcn.com/article/p-rkwiytbi-dk.html
* https://blog.youkuaiyun.com/lgh0824/article/details/76100599
* ---------------------------------------------------------- */
using Com.FirstSolver.Splash;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
namespace Splash
{
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonOpen_Click(object sender, RoutedEventArgs e)
{
try
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog
{
Filter = "Image|*.jpg;*.bmp;*.png;*.tif;*.tga;*.ras;*.jp2;*.j2k;*.jpe",
DereferenceLinks = true
};
this.CenterChild();
if (dlg.ShowDialog(Owner).Value == true)
{ // 读取图像文件
byte[] data;
using (FileStream fs = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
data = new byte[fs.Length];
fs.Read(data, 0, (int)fs.Length);
}
// 显示源图像
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = new MemoryStream(data);
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
ImageRaw.Source = bi;
ThreadPool.QueueUserWorkItem(new WaitCallback((state) => {
// 生成Bgr图像
Mat Bgr = Cv2.ImDecode(data, ImreadModes.Color);
// 转换成灰度图
Mat Gray = Bgr.CvtColor(ColorConversionCodes.BGR2GRAY);
// 形态学变换的预处理,得到可以查找矩形的轮廓
Mat Dilation = Preprocess(Gray);
// 查找和筛选文字区域
RotatedRect[] Regions = FindTextRegion(Dilation);
// 用绿线画出这些找到的轮廓
foreach (RotatedRect rr in Regions)
{
Point2f[] p = rr.Points();
Cv2.Line(Bgr, (int)p[0].X, (int)p[0].Y, (int)p[1].X, (int)p[1].Y, new Scalar(0, 255, 0), 2);
Cv2.Line(Bgr, (int)p[1].X, (int)p[1].Y, (int)p[2].X, (int)p[2].Y, new Scalar(0, 255, 0), 2);
Cv2.Line(Bgr, (int)p[2].X, (int)p[2].Y, (int)p[3].X, (int)p[3].Y, new Scalar(0, 255, 0), 2);
Cv2.Line(Bgr, (int)p[3].X, (int)p[3].Y, (int)p[0].X, (int)p[0].Y, new Scalar(0, 255, 0), 2);
}
// 线程安全性
this.Dispatcher.BeginInvoke(new Action(() => {
ImageContour.Source = Bgr.ToBitmapSource();
}));
}));
}
}
catch (System.Exception exception)
{
MessageBoxPlus.Show(this, exception.Message, "图像文件异常", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// 预处理
private Mat Preprocess(Mat gray)
{ // Sobel算子,x方向求梯度
Mat sobel = new Mat();
Cv2.Sobel(gray, sobel, MatType.CV_8U, 1, 0, 3);
// 二值化
Mat Binary = new Mat();
Cv2.Threshold(sobel, Binary, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
// 膨胀一次,让轮廓突出
Mat ElementDilate = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(24, 4));
Mat Dilate1 = new Mat();
Cv2.Dilate(Binary, Dilate1, ElementDilate, null, 1);
// 腐蚀一次,去掉细节,表格线等。这里去掉的是竖直的线
Mat ElementErode = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(30, 9));
Mat Erode1 = new Mat();
Cv2.Erode(Dilate1, Erode1, ElementErode, null, 1);
// 再次膨胀,让轮廓明显一些
Mat Dilate2 = new Mat();
Cv2.Dilate(Erode1, Dilate2, ElementDilate, null, 3);
// 显示中间图像
this.Dispatcher.BeginInvoke(new Action(() => {
ImageBinary.Source = Binary.ToBitmapSource(); // 二值化图
ImageDilate1.Source = Dilate1.ToBitmapSource(); // 初次膨胀图
ImageErode1.Source = Erode1.ToBitmapSource(); // 初次腐蚀图
ImageDilate2.Source = Dilate2.ToBitmapSource(); // 再次膨胀图
}));
return Dilate2;
}
// 查找和筛选文字区域
private RotatedRect[] FindTextRegion(Mat img)
{ // 查找轮廓
OutputArray hierarchy = new Mat();
Cv2.FindContours(img, out Mat[] contours, hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
// 筛选那些面积小的
List<RotatedRect> regions = new List<RotatedRect>();
foreach (Mat cnt in contours)
{ // 计算当前轮廓的面积
double area = Cv2.ContourArea(cnt);
// 面积小于1000的全部筛选掉
if (area < 1000) continue;
// 轮廓近似,作用很小
double epsilon = 0.001 * Cv2.ArcLength(cnt, true);
Mat approx = new Mat();
Cv2.ApproxPolyDP(cnt, approx, epsilon, true);
// 找到最小矩形,该矩形可能有方向
RotatedRect rect = Cv2.MinAreaRect(approx);
int width = rect.BoundingRect().Width;
int height = rect.BoundingRect().Height;
// 筛选那些太细的矩形,留下扁的
if (height > width * 1.2) continue;
regions.Add(rect);
}
return regions.ToArray();
}
}
}
运行结果