<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid>
<UniformGrid Columns="2">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<InkCanvas Name="ink" Background="Transparent" Cursor="Pen" ForceCursor="True">
<Image Name="ImgShow" Source="/temp.png" IsHitTestVisible="False">
</Image>
</InkCanvas>
</ScrollViewer>
<UniformGrid Rows="2">
<GroupBox Header="Mask" >
<Image x:Name="imgMask"></Image>
</GroupBox>
<GroupBox Header="Result">
<Image x:Name="imgResult"></Image>
</GroupBox>
</UniformGrid>
</UniformGrid>
</Grid>
<StackPanel Grid.Row="1" Margin="20">
<DockPanel >
<Grid Grid.Row="1" VerticalAlignment="Center" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" VerticalAlignment="Center" Content="繪制墨跡" Click="RadioButton_Click" IsChecked="True"/>
<RadioButton Grid.Column="1" Content="按點擦除" Click="RadioButton_Click"/>
<RadioButton Grid.Column="2" Content="按線擦除" Click="RadioButton_Click"/>
<RadioButton Grid.Column="3" Content="選中墨跡" Click="RadioButton_Click"/>
<RadioButton Grid.Column="4" Content="停止操作" Click="RadioButton_Click"/>
</Grid>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20 0 0 0">顏色選擇:</TextBlock>
<Border CornerRadius="5" x:Name="colorchk" Background="Black" Width="50" HorizontalAlignment="Left" MouseLeftButtonDown="Grid_MouseLeftButtonDown" ></Border>
</DockPanel>
<DockPanel>
<Button Margin="20 0 0 0" Width="100" Height="30" Click="Button_Click">Roi截取</Button>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20 0 0 0">興趣區域平均值:<Run Foreground="#e03997" Name="txt_meanValue"></Run></TextBlock>
<Slider Value="120" x:Name="threshould_slider" Width="200" TickPlacement="Both" TickFrequency="1" Maximum="255" IsSnapToTickEnabled="True"></Slider>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">閾值<Run Text="{Binding ElementName=threshould_slider,Path=Value}"></Run></TextBlock>
<Button Margin="20 0 0 0" Width="100" HorizontalAlignment="Left" Height="30" Click="floodfill_Click">漫水填充</Button>
</DockPanel>
</StackPanel>
</Grid>
</Grid>
//cs代碼
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using Emgu.CV.Util;
using Color = System.Drawing.Color;
using Point = System.Drawing.Point;
using Rectangle = System.Drawing.Rectangle;
namespace MaskGetMean
{
///
/// MainWindow.xaml 的交互邏輯
///
public partial class MainWindow : Window
{
//聲明一個 DrawingAttributes 類型的變量
DrawingAttributes drawingAttributes;
///
/// 默認圖像路徑。可拖拽圖像進窗體
///
string imgpath = (AppDomain.CurrentDomain.BaseDirectory + "temp.png");
public MainWindow()
{
InitializeComponent();
//創建 DrawingAttributes 類的一個實例
drawingAttributes = new DrawingAttributes();
//將 InkCanvas 的 DefaultDrawingAttributes 屬性的值賦成創建的 DrawingAttributes 類的對象的引用
//InkCanvas 通過 DefaultDrawingAttributes 屬性來獲取墨跡的各種設置,該屬性的類型為 DrawingAttributes 型
ink.DefaultDrawingAttributes = drawingAttributes;
//設置 DrawingAttributes 的 Color 屬性設置顏色
}
/// <summary>
/// 拖拽
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainWindow_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effects = DragDropEffects.Link; //WinForm中為e.Effect = DragDropEffects.Link
else e.Effects = DragDropEffects.None; //WinFrom中為e.Effect = DragDropEffects.None
}
private void MainWindow_Drop(object sender, DragEventArgs e)
{
string fileName = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
if (fileName.EndsWith(".jpg") || fileName.EndsWith(".png") || fileName.EndsWith(".bmp"))
{
imgpath = fileName;
ImgShow.Source = new BitmapImage(new Uri(imgpath));
//The Imageviewer 直接彈出新的窗口
Image<Bgr, byte> loadImg = new Image<Bgr, byte>(imgpath);
ImageViewer viewer = new ImageViewer(loadImg, "Loaded Image");
viewer.Show();
//1.使用HistogramViewer不需要事先拉到設計窗口中,他是彈出窗口,你只需要直接使用便可以
HistogramViewer.Show(loadImg[0], 32); //image[0] 顯示Blue,bin = 32
HistogramViewer.Show(loadImg, 32); //顯示所有信道
//獲得文件名后的操作...
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//原始圖像
var OldImage = CvInvoke.Imread(imgpath);
//獲取圖像原始大小和顯示大小的比例
double a = OldImage.Width / ImgShow.Source.Width;
//獲取畫的坐標線集合
var list = ink.Strokes;
//裝輪廓集合
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
//獲取畫的坐標點集合
foreach (var item in list)
{ //單個輪廓坐標集合
VectorOfPoint vectorOfPoint = new VectorOfPoint();
foreach (var stylusPoint in item.StylusPoints)
{
vectorOfPoint.Push(new System.Drawing.Point[] { new System.Drawing.Point((int)(stylusPoint.X * a), (int)(stylusPoint.Y * a)) });
}
if (vectorOfPoint.Size <= 0)
break;
contours.Push(vectorOfPoint);
}
//准備掩膜圖像
Image<Gray, byte> mask = new Image<Gray, byte>(OldImage.Width, OldImage.Height);
//設置全黑
mask.SetZero();
//把輪廓全部繪制成白色在mask上變成感興趣的區域 類似roi
for (int i = 0; i < contours.Size; i++)
{
CvInvoke.DrawContours(mask, contours, i, new MCvScalar(255, 255, 255), -1, LineType.AntiAlias, null, int.MaxValue);
}
//准備結果圖像
Image<Bgr, byte> Result = new Image<Bgr, byte>(OldImage.Width, OldImage.Height);
//顯示
imgMask.Source = BitmapToBitmapImage(mask.ToBitmap());
//CvInvoke.Imshow("DrawContours", mask);
//獲取感興趣的區域到結果圖
OldImage.CopyTo(Result, mask);
//顯示
//CvInvoke.Imshow("mask", Result);
imgResult.Source = BitmapToBitmapImage(Result.ToBitmap());
txt_meanValue.Text = CvInvoke.Mean(Result).V0.ToString();
}
/// <summary>
/// 左鍵選擇畫筆顏色
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
System.Windows.Forms.ColorDialog colorDialog = new System.Windows.Forms.ColorDialog();
if (colorDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
System.Drawing.SolidBrush sb = new System.Drawing.SolidBrush(colorDialog.Color);
SolidColorBrush solidColorBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(sb.Color.A, sb.Color.R, sb.Color.G, sb.Color.B));
colorchk.Background = solidColorBrush;
drawingAttributes.Color = new System.Windows.Media.Color() { A = sb.Color.A, B = sb.Color.B, G = sb.Color.G, R = sb.Color.R };
}
}
/// <summary>
/// Bitmap轉BitmapImage 用於Image控件成像
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static BitmapSource BitmapToBitmapImage(System.Drawing.Bitmap bitmap)
{
if (bitmap == null)
return null;
try
{
using (System.Drawing.Bitmap source = bitmap)
{
IntPtr ptr = source.GetHbitmap(); //obtain the Hbitmap
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
System.Windows.Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr); //release the HBitmap
return bs;
}
}
catch (Exception)
{
return null;
}
}
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
/// <summary>
/// ink畫板事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
if ((sender as RadioButton).Content.ToString() == "繪制墨跡")
{
ink.ForceCursor = true;
ink.EditingMode = InkCanvasEditingMode.Ink;
}
else
{
ink.ForceCursor = false;
if ((sender as RadioButton).Content.ToString() == "按點擦除")
{
ink.EditingMode = InkCanvasEditingMode.EraseByPoint;
}
else if ((sender as RadioButton).Content.ToString() == "按線擦除")
{
ink.EditingMode = InkCanvasEditingMode.EraseByStroke;
}
else if ((sender as RadioButton).Content.ToString() == "選中墨跡")
{
ink.EditingMode = InkCanvasEditingMode.Select;
}
else if ((sender as RadioButton).Content.ToString() == "停止操作")
{
ink.EditingMode = InkCanvasEditingMode.None;
}
}
}
/// <summary>
/// 漫水填充
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void floodfill_Click(object sender, RoutedEventArgs e)
{
int threshould = (int)threshould_slider.Value;
var color = System.Drawing.Color.White;
//原始圖像
var OldImage = CvInvoke.Imread(imgpath);
//獲取圖像原始大小和顯示大小的比例
double a = OldImage.Width / ImgShow.Source.Width;
//獲取畫的坐標線集合
var list = ink.Strokes;
//裝輪廓集合
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
//獲取畫的坐標點集合
foreach (var item in list)
{ //單個輪廓坐標集合
VectorOfPoint vectorOfPoint = new VectorOfPoint();
foreach (var stylusPoint in item.StylusPoints)
{
vectorOfPoint.Push(new System.Drawing.Point[] { new System.Drawing.Point((int)(stylusPoint.X * a), (int)(stylusPoint.Y * a)) });
}
if (vectorOfPoint.Size <= 0)
break;
contours.Push(vectorOfPoint);
}
if (contours.Size == 0) return;
//清空點
ink.Strokes.Clear();
//計算點的閾值平均值(不太好用 也就是沒寫好)
//var GrayOldImage = OldImage.Clone();
//if (GrayOldImage.NumberOfChannels == 3)
// CvInvoke.CvtColor(GrayOldImage, GrayOldImage, ColorConversion.Rgb2Gray);
//int rows = GrayOldImage.Rows, cols = GrayOldImage.Cols, step = GrayOldImage.Step;
//threshould = 0;
//long count = 0;
//for (int i = 0; i < contours.Size; i++)
//{
// for (int j = 0; j < contours[i].Size; j++)
// {
// unsafe
// {
// byte* dataptr = (byte*)GrayOldImage.DataPointer;
// ///單通道圖像遍歷方式
// int index = contours[i][j].X * step + contours[i][j].Y;
// value += dataptr[index];
// }
// }
// count += contours[i].Size;
//}
//value = value / count;
var data = FloodFill(OldImage.ToBitmap(), contours[0][0], color, threshould);
if (data != null)
{
VectorOfVectorOfPoint NewVec = new VectorOfVectorOfPoint();
CvInvoke.FindContours(data.ToImage<Gray, byte>(), NewVec, null, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxTc89Kcos);
CvInvoke.DrawContours(OldImage, NewVec, 0, new MCvScalar(0, 255, 0), 1, LineType.AntiAlias, null, 1);
//顯示
imgMask.Source = BitmapToBitmapImage(OldImage.ToBitmap());
//顯示
imgResult.Source = BitmapToBitmapImage(OldImage.ToBitmap());
txt_meanValue.Text = CvInvoke.Mean(OldImage).V0.ToString();
//-----------獲取roi---------------
//准備掩膜圖像
Image<Gray, byte> mask = new Image<Gray, byte>(OldImage.Width, OldImage.Height);
//設置全黑
mask.SetZero();
//把輪廓全部繪制成白色在mask上變成感興趣的區域 類似roi
for (int i = 0; i < NewVec.Size; i++)
{
CvInvoke.DrawContours(mask, NewVec, i, new MCvScalar(255, 255, 255), -1, LineType.AntiAlias, null, int.MaxValue);
}
//准備結果圖像
Image<Bgr, byte> Result = new Image<Bgr, byte>(OldImage.Width, OldImage.Height);
//獲取感興趣的區域到結果圖
OldImage.CopyTo(Result, mask);
//顯示
//CvInvoke.Imshow("mask", Result);
imgResult.Source = BitmapToBitmapImage(Result.ToBitmap());
}
}
/// <summary>
///
/// </summary>
/// <param name="src">原圖</param>
/// <param name="location">檢測點</param>
/// <param name="fillColor">填充顏色</param>
/// <param name="threshould"閾值></param>
/// <returns>填充圖,非填充部分為默認值</returns>
unsafe public Bitmap FloodFill(Bitmap src, Point location, Color fillColor, int threshould)
{
try
{
Bitmap srcbmp = src;
Color backColor = srcbmp.GetPixel(location.X, location.Y);
Bitmap dstbmp = new Bitmap(src.Width, src.Height);
int w = srcbmp.Width; int h = srcbmp.Height;
Stack<Point> fillPoints = new Stack<Point>(w * h);
System.Drawing.Imaging.BitmapData bmpData = srcbmp.LockBits(new Rectangle(0, 0, srcbmp.Width, srcbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Drawing.Imaging.BitmapData dstbmpData = dstbmp.LockBits(new Rectangle(0, 0, dstbmp.Width, dstbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int stride = bmpData.Stride; int stridedst = dstbmpData.Stride; byte* srcbuf = (byte*)bmpData.Scan0.ToPointer();
int* dstbuf = (int*)dstbmpData.Scan0.ToPointer();
int cr = backColor.R, cg = backColor.G, cb = backColor.B, ca = backColor.A;
byte fcr = fillColor.R, fcg = fillColor.G, fcb = fillColor.B; int fc = fillColor.ToArgb();
if (location.X < 0 || location.X >= w || location.Y < 0 || location.Y >= h) return null; fillPoints.Push(new Point(location.X, location.Y)); int[,] mask = new int[w, h];
while (fillPoints.Count > 0)
{
Point p = fillPoints.Pop();
mask[p.X, p.Y] = 1;
dstbuf[p.X + p.Y * w] = fc;
if (p.X > 0 && (mask[p.X - 1, p.Y] != 1) && Math.Abs(cb - srcbuf[4 * (p.X - 1) + p.Y * stride]) + Math.Abs(cg - srcbuf[4 * (p.X - 1) + 1 + p.Y * stride]) + Math.Abs(cr - srcbuf[4 * (p.X - 1) + 2 + p.Y * stride]) < threshould && Math.Abs(ca - srcbuf[4 * (p.X - 1) + 3 + p.Y * stride]) < threshould)
{
dstbuf[(p.X - 1) + p.Y * w] = fc;
fillPoints.Push(new Point(p.X - 1, p.Y)); mask[p.X - 1, p.Y] = 1;
}
if (p.X < w - 1 && (mask[p.X + 1, p.Y] != 1) && Math.Abs(cb - srcbuf[4 * (p.X + 1) + p.Y * stride]) + Math.Abs(cg - srcbuf[4 * (p.X + 1) + 1 + p.Y * stride]) + Math.Abs(cr - srcbuf[4 * (p.X + 1) + 2 + p.Y * stride]) < threshould && Math.Abs(ca - srcbuf[4 * (p.X + 1) + 3 + p.Y * stride]) < threshould)
{ dstbuf[(p.X + 1) + p.Y * w] = fc; fillPoints.Push(new Point(p.X + 1, p.Y)); mask[p.X + 1, p.Y] = 1; }
if (p.Y > 0 && (mask[p.X, p.Y - 1] != 1) && Math.Abs(cb - srcbuf[4 * p.X + (p.Y - 1) * stride]) + Math.Abs(cg - srcbuf[4 * p.X + 1 + (p.Y - 1) * stride]) + Math.Abs(cr - srcbuf[4 * p.X + 2 + (p.Y - 1) * stride]) < threshould && Math.Abs(ca - srcbuf[4 * p.X + 3 + (p.Y - 1) * stride]) < threshould)
{ dstbuf[p.X + (p.Y - 1) * w] = fc; fillPoints.Push(new Point(p.X, p.Y - 1)); mask[p.X, p.Y - 1] = 1; }
if (p.Y < h - 1 && (mask[p.X, p.Y + 1] != 1) && Math.Abs(cb - srcbuf[4 * p.X + (p.Y + 1) * stride]) + Math.Abs(cg - srcbuf[4 * p.X + 1 + (p.Y + 1) * stride]) + Math.Abs(cr - srcbuf[4 * p.X + 2 + (p.Y + 1) * stride]) < threshould && Math.Abs(ca - srcbuf[4 * p.X + 3 + (p.Y + 1) * stride]) < threshould)
{ dstbuf[p.X + (p.Y + 1) * w] = fc; fillPoints.Push(new Point(p.X, p.Y + 1)); mask[p.X, p.Y + 1] = 1; }
}
fillPoints.Clear(); srcbmp.UnlockBits(bmpData); dstbmp.UnlockBits(dstbmpData); return dstbmp;
}
catch
{
return null;
}
}
}
}