WPF+Emgucv实现在图像上画出感兴趣的区域 并进行掩膜获取 得到图像均值 和简单的 漫水填充

<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;
}
}
}
}
WPF+Emgucv实现在图像上画出感兴趣的区域 并进行掩膜获取 得到图像均值 和简单的 漫水填充的更多相关文章
- Android中Google地图路径导航,使用mapfragment地图上画出线路(google map api v2)详解
在这篇里我们只聊怎么在android中google map api v2地图上画出路径导航,用mapfragment而不是mapview,至于怎么去申请key,manifest.xml中加入的权限,系 ...
- 使用JavaScript在Canvas上画出一片星空
随着Html5的迅猛发展,画布也变得越来越重要.下面我就写一个关于在canvas上画出一片星空的简单的代码的示例. 理论基础 初始化一个canvas,获得一个用于绘制图形的上下文环境context.并 ...
- 【wpf】在win10系统上弹出toast和notification
原文:[wpf]在win10系统上弹出toast和notification 老规矩,先看效果 右下角的notification: 操作中心的notification: 整体效果: 前提条件 1.需要在 ...
- Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 图像属性 图像 ...
- WPF在圆上画出刻度线
思路 我们可以使用Ellipse先画出一个圆当背景,然后用Canvas再叠加画上刻度线,就能得到如下的效果 我们先用Ellipse画一个橙色的圆,然后将Canvas的宽度和高度绑定到Ellipse的宽 ...
- OpenCV之响应鼠标(四):在图像上绘制出矩形并标出起点的坐标
涉及到两方面的内容:1. 用鼠标画出矩形.2.在图像上绘制出点的坐标 用鼠标绘制矩形,涉及到鼠标的操作,opencv中有鼠标事件的介绍.需要用到两个函数:回调函数CvMouseCallback和注册回 ...
- Android教程:在百度地图上画出轨迹
[日期:2013-04-14] 来源:Linux社区 作者:crazyxin1988 [字体:大 中 小] 接着上面的项目<Android访问webservice.客户端登录注册> ...
- canvas上画出坐标集合,并标记新坐标,背景支持放大缩小拖动功能
写在前面:项目需求,用户上传一个区位的平面图片,用户可以在图片上添加新的相机位置,并且展示之前已绑定的相机坐标位置,图片支持放大缩小&拖动的功能.新增坐标,页面展示相对canvas定位,保存时 ...
- 还没被玩坏的robobrowser(4)——从页面上抓取感兴趣的内容
背景 本节的知识实际上是属于Beautiful Soup的内容. robobrowser支持Beautiful Soup,一般来说通过下面3个方法获取页面上感兴趣的内容 find find_all s ...
- [Java]在窗口界面上画出硬盘中图片文件
利用类javax.swing.JPanel来在窗口界面上画图.图片文件通过javax.imageio.ImageIO类来获取. import java.awt.Graphics; import jav ...
随机推荐
- HarmonyOS网络管理开发—Socket连接
简介 Socket连接主要是通过Socket进行数据传输,支持TCP/UDP/TLS协议. 基本概念 ● Socket:套接字,就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象. ● ...
- Mysql之GTID
一.GTID Mysql5.6引入GTID(Global Transaction IDs),多线程复制: 由服务器的UUID和事务ID号组成唯一标识某一个主机的某个事务的ID号: 每一个事务首部都有G ...
- linux 性能自我学习 ———— 关于内存 [七]
前言 内存的基本知识,将在操作系统篇中详细介绍,这里只说明如何排查问题. 正文 内存的分配和回收: 在malloc 是c 标准库中的内存分配函数,对应到系统调用上,有两种实现方式,一种是brk()和 ...
- nginx重新整理——————http 模块中的请求过程[十一]
前言 简单介绍一下http的一些指令. 正文 一般http的嵌套规则是这样的: http{ upstream{} split_clients {} map{} gep{} server{ if(){} ...
- redis 简单整理——redis shell[九]
前言 简单介绍一下redis的shell命令. 正文 redis 提供了一些工具,如redis-cli.redis-server.redis-benchmark等. redis-cli -r 对red ...
- c#代码重构与迭代(一)——循环代码的优化
foreach (var item in list) { Devices _Device = DevicesLogic.GetInstance().GetDevices(item.DeviceID); ...
- Oracle常用的创建表语句
Oracle常用的创建表语句 Oracle常用的创建表语句 指定字段的创建 --指定字段的创建 create table table_name( test_1(字段名1) varchar2(50),( ...
- Flow vs Jenkins 实操对比,如何将Java应用快速发布至ECS
简介:Jenkins 由于其开源特性以及丰富插件能力,长久以来都是中小企业搭建 CICD 流程的首选.不过 Jenkins 存在维护成本高.配置复杂等缺点,云效 Flow 较好地解决了这些问题. 本 ...
- 2019-10-14-云之幻-UWP-视频教程
title author date CreateTime categories 云之幻 UWP 视频教程 lindexi 2019-10-14 21:8:26 +0800 2019-10-14 21: ...
- 什么是SQL 语句中相关子查询与非相关子查询
1.什么是SQL子查询 要理解相关子查询和非相关子查询,我们得首先理解什么是子查询,子查询是指在一个查询语句中嵌套的另一个查询语句. 子查询可以嵌套在其他查询语句中,如 SELECT.INSERT.U ...