Emgu学习之(三)——操作图像数据
Visual Studio Community 2015 工程和代码:http://pan.baidu.com/s/1jHmlQeE
内容
在这篇文章中将提到以下内容:
- 修改像素值
- 图像ROI
- 图像加法
- 图像减法
- 按位运算
- 图像混合
准备工作
- 创建工程:参考Emgu学习之(一)——Emgu介绍创建一个WinForm项目
- Form1.cs引用命名空间:
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
- 界面:在Form1中添加2行2列的TableLayout容器,然后添加四个Emgu.CV.UI.ImageBox控件(参考Emgu学习之(二)——图像读取、显示、保存),添加后界面如下:
如果你自己在实验时无法得到图片显示的效果,那么可以试试修改ImageBox的SizeMode属性。
- 新建DataAccess类:
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum; namespace AccessingImageData
{
public class DataAccess
{
}
}
修改像素值
可以通过行、列索引直接操作像素值,操作方式如下:
_image[row, cols] = new Bgr(Color.Green);
在DataAccess类中添加ExchangePixelValue方法,这个方法将交换两个区域的像素值。
/// <summary>
/// 交换图像中两个区域的像素值
/// </summary>
/// <param name="image"></param>
public static void ExchangePixelValue(Image<Bgr, Byte> image)
{
for(int i = ; i < ; i ++)
for(int j = ; j < ; j ++)
{
Bgr tmp = image[i, j];
image[i, j] = image[i + , j + ];
image[i + , j + ] = tmp;
}
}
在Form1.cs中调用此方法的效果如下,小狗的脸去到了左上角,左上角的天空去到了小狗的脸位置。
图像ROI
ROI即为region of interest(感兴趣区域)。
“在很多情况下,使用它会提高计算机视觉代码的执行速度,这是因为它允许对图像的某一小部分进行操作,而不是对整个图像进行运算。在OpenCV中,普遍支持ROI,函数的
操作被限于感兴趣区域。”——《学习OpenCV(中文版)》。也就是如果你想只对图片的某个区域操作,你可以设置ROI后直接对图片进行操作,这时候被处理的部分只有ROI
区域。
OpenCV提供了cvSetImageROI()函数设置ROI区域,和cvResetImageROI()函数取消ROI,在Emgu中你可以在CvInvoke类中找到这两个方法,同时你也可以设置Image
类的ROI属性来设置ROI,通过访问IsROISet来获取当前对象是否已经设置了ROI,如果要取消ROI,你需要把对象的ROI属性设置为Rectangle.Empty。Rectangle类来自于
System.Drawing,也就是在Emgu中ROI为Rectangle对象指定的矩形区域。
下面代码显示了设置、取消ROI操作,在DataAccess类中添加SetRoiRed方法:
/// <summary>
/// 设置指定ROI区域为红色
/// </summary>
/// <param name="image"></param>
/// <param name="roi"></param>
public static void SetRoiRed(Image<Bgr, Byte> image, Rectangle roi)
{
image.ROI = roi;
image.SetValue(new Bgr(Color.Red));
//要记得取消ROI的设置,否则后续的操作都会在ROI中进行,包括显示图像
image.ROI = Rectangle.Empty;
}
在Form1中调用这个方法的运行效果为:
图像加法
使用Image.Add()方法,你可以让两个图像相加,或让当前图像加上一个色彩值。另外你也可以使用CvInvoke.Add()方法执行相同的操作,
Image.Add()方法内部就是调用CvInvoke.Add()方法实现的。
Image.Add()有3个实现,每个实现的返回都是一个相同色彩空间、值类型的Image对象:
///<summary> 当前图片与另外一张图片相加,另外一张图片必须与当前图片是相同的类型和尺寸(或相同ROI尺寸) </summary>
///<param name="img2">与当前图片相加的图片</param>
///<returns> 相加的结果</returns>
public Image<TColor, TDepth> Add(Image<TColor, TDepth> img2)
///<summary> 当前图片与另外一张图片相加(ret(I)=src1(I)+src2(I) if mask(I)!=0),另外一张图片必须与当前图片是相同的类型和尺寸(或形同ROI尺寸)</summary>
///<param name="img2">另一张图片</param>
///<param name="mask">掩膜图片</param>
///<returns> 使用掩膜图片相加的结果</returns>
public Image<TColor, TDepth> Add(Image<TColor, TDepth> img2, Image<Gray, Byte> mask)
///<summary> 当前图片加上一个色彩值 </summary>
///<param name="val"> 色彩值 </param>
///<returns> 相加的结果 <paramref name="val"/> from the current image</returns>
public Image<TColor, TDepth> Add(TColor val)
接下来我们演示如何使用这些方法:在DataAccess类中添加JustAdd和AddUsingMask方法,JustAdd方法只是简单的调用Add方法。
而AddUsingMask方法中,我们首先需要创建一张掩膜图片,掩膜图片左半边为白色(255),右半边为黑色(0),在执行加操作时,
白色部分会执行加操作,而黑色部分不执行任何操作,所以resImage的右半边是黑色的,这时把原图的右半边拷贝到resImage的右半
边上,代码如下:
/// <summary>
/// 两张图片相加
/// </summary>
/// <param name="image1">相加的源图片1</param>
/// <param name="image2">相加的源图片2</param>
/// <returns></returns>
public static Image<Bgr, Byte> JustAdd(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
{
return image1.Add(image2);
} /// <summary>
/// 使用掩码图片进行相加操作
/// </summary>
/// <param name="image1"></param>
/// <param name="image2"></param>
/// <returns></returns>
public static Image<Bgr, Byte> AddUsingMask(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
{
var rect = new Rectangle(new Point(, ), new Size(image1.Width / , image1.Height));
using (var mask = new Image<Gray, Byte>(image1.Size))
{
mask.SetZero();//设置所有值为0
mask.ROI = rect;
mask.SetValue();//设置ROI的值为255
mask.ROI = Rectangle.Empty;//去掉ROI
//res(I)=img1(I)+img2(I) if mask(I)!=0
var resImage = image1.Add(image2, mask);
mask._Not();//反转mask的值(255->0, 0->255)
image1.Copy(resImage, mask);//在mask(I) != 0的条件下,把image1的值拷贝到resImage中
return resImage;
}
}
在Form1中调用以上方法:
private void Form1_Load(object sender, EventArgs e)
{
_image = new Image<Bgr, byte>(Properties.Resources.gougou);
imageBox1.Image = _image;
imageBox2.Image = DataAccess.JustAdd(_image, _image);//图片自加
imageBox4.Image = DataAccess.AddUsingMask(_image, _image); //创建掩膜图片
var mask = new Image<Gray, Byte>(_image.Size);
mask.SetZero();//设置所有值为0
mask.ROI = new Rectangle(new Point(, ), new Size(_image.Width / , _image.Height));
mask.SetValue();//设置ROI的值为255
mask.ROI = Rectangle.Empty;//去掉ROI imageBox3.Image = mask;
}
运行效果:
图像减法
使用Image.Sub()方法,你可以让当前图像减去另外一个图像,或让当前图像减去一个色彩值。另外你也可以使用CvInvoke.Subtract()方法执行相同的操作,
Image.Sub()方法内部就是调用CvInvoke.Subtract()方法实现的。
与加法相似,Image.Sub()同样有3个实现,每个实现的返回都是一个相同色彩空间、值类型的Image对象:
///<summary> 当前图片减去一张图片,被减图片必须与当前图片是相同的类型和尺寸(或相同的ROI尺寸) </summary>
///<param name="img2">被减图片</param>
///<returns> 相减的结果</returns>
public Image<TColor, TDepth> Sub(Image<TColor, TDepth> img2)
///<summary> 当前图片减去另外一张图片(ret(I)=src1(I)-src2(I) if mask(I)!=0),被减图片必须与当前图片是相同的类型和尺寸(或相同的ROI尺寸) </summary>
///<param name="img2">被减图片</param>
///<param name="mask">掩膜图片</param>
///<returns> 使用掩膜图片相减的结果</returns>
public Image<TColor, TDepth> Sub(Image<TColor, TDepth> img2, Image<Gray, Byte> mask)
///<summary> 当前图片减去一个色彩值</summary>
///<param name="val">被减去的色彩值</param>
///<returns> 减去色彩值的结果</returns>
public Image<TColor, TDepth> Sub(TColor val)
接下来我们演示如何使用这些方法:在DataAccess类中添加JustSub和SubUsingMask方法,代码如下:
/// <summary>
/// 图像减法
/// </summary>
/// <param name="image1"></param>
/// <param name="image2"></param>
/// <returns></returns>
public static Image<Bgr, Byte> JustSub(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
{
return image1.Sub(image2);
} /// <summary>
/// 在掩码图片的条件下,用image1减去image2的值
/// </summary>
/// <param name="image1"></param>
/// <param name="image2"></param>
/// <returns></returns>
public static Image<Bgr, Byte> SubUsingMask(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
{
var rect = new Rectangle(new Point(, ), new Size(image1.Width / , image1.Height));
using (var mask = new Image<Gray, Byte>(image1.Size))
{
mask.SetZero();//设置所有值为0
mask.ROI = rect;
mask.SetValue();//设置ROI的值为255
mask.ROI = Rectangle.Empty;//去掉ROI
//res(I)=img1(I)-img2(I) if mask(I)!=0
var resImage = image1.Sub(image2, mask);
mask._Not();//反转mask的值(255->0, 0->255)
image1.Copy(resImage, mask);//在mask(I)!= 0的条件下,把image1的值拷贝到resImage中
return resImage;
}
}
在Form1中调用以上方法,在加法演示中我们使图片自加,得到了一张更亮的照片,但是在减法中如果我们使图片自减就会得到一张黑色的图片,
但是为了使读者能清晰的看到减法操作,我们使原图减去一张红色图,所以在以下程序中我们需要创建一张红色图片:
private void Form1_Load(object sender, EventArgs e)
{
_image = new Image<Bgr, byte>(Properties.Resources.gougou);
imageBox1.Image = _image;
using (var colorImage = new Image<Bgr, Byte>(_image.Size))
{//首先创建一张红色图片
colorImage.SetValue(new Bgr(Color.Red));
imageBox2.Image = DataAccess.JustSub(_image, colorImage);
imageBox4.Image = DataAccess.SubUsingMask(_image, colorImage);
} //创建掩膜图片
var mask = new Image<Gray, Byte>(_image.Size);
mask.SetZero();//设置所有值为0
mask.ROI = new Rectangle(new Point(, ), new Size(_image.Width / , _image.Height));
mask.SetValue();//设置ROI的值为255
mask.ROI = Rectangle.Empty;//去掉ROI imageBox3.Image = mask;
}
运行效果:
按位运算
按位运算有与(And)、或(Or)、非(Not)和异或(Xor)运算。要执行这些运算,你可以使用CvInvoke类下的静态方法:BitwiseAnd、BitwiseNot、BitwiseOr、
BitwiseXor。但是与加减法类似,Image类同时也提供Add、Not、Or、Xor方法,同时还提供了_Add、_Not、_Or、_Xor,这些带下划线的方法会在当前对象上进行
运算,而不带下划线的方法则是返回新的对象。同时_Add、_Not、_Or、_Xor不提供掩膜操作。
接下来我们演示如何把OpenCV的logo添加到狗狗图片中,这里我们需要一张和logo图同样的mask图片。首先我们在DataAccess类中添加AddLogo方法:
/// <summary>
/// 在掩膜图片的条件下,在图片中添加logo,掩膜图片为logo图片的二值图
/// </summary>
/// <param name="image"></param>
/// <param name="logo"></param>
/// <param name="mask"></param>
/// <returns></returns>
public static Image<Bgr, Byte> AddLogo(Image<Bgr, Byte> image, Image<Bgr, Byte> logo, Image<Gray, Byte> mask)
{
var resImage = image.Copy(); //设置操作区域,所有的操作都在这个区域中进行
image.ROI = new Rectangle(new Point(, ), logo.Size);
resImage.ROI = image.ROI; using (var colorMask = mask.Convert<Bgr, Byte>())
{
//把Logo区域变成白色(0xFF)
CvInvoke.Add(image, colorMask, resImage, mask);
} CvInvoke.BitwiseAnd(resImage, logo, resImage, mask); //失能操作区域
resImage.ROI = Rectangle.Empty;
return resImage;
}
然后,在Form1中调用AddLogo方法:
图像混合
图像混合和图像加法类似,图像加法是简单的把两张图片相加,而图像混合是将两张图片按照不同的权重相加:
res[m, n] = α·src1[m,n] + β·src1[m,n] + γ(其中α = 1 - β)
实现这个功能的函数为cvAddWeighted方法,以下为调用实例:
var resImage = image1.AddWeighted(image2, 0.5, 0.5, );
这里α = 0.5,β = 0.5, γ = 0。
运行效果为:
Emgu学习之(三)——操作图像数据的更多相关文章
- tensorflow学习笔记三:实例数据下载与读取
一.mnist数据 深度学习的入门实例,一般就是mnist手写数字分类识别,因此我们应该先下载这个数据集. tensorflow提供一个input_data.py文件,专门用于下载mnist数据,我们 ...
- R语言学习 第三篇:数据框
数据框(data.frame)是最常用的数据结构,用于存储二维表(即关系表)的数据,每一列存储的数据类型必须相同,不同数据列的数据类型可以相同,也可以不同,但是每列的行数(长度)必须相同.数据框的每列 ...
- Javascript学习笔记三——操作DOM(二)
Javascript学习笔记 在我的上一个博客讲了对于DOM的基本操作内容,这篇继续巩固一下对于DOM的更新,插入和删除的操作. 对于HTML解析的DOM树来说,我们肯定会时不时对其进行一些更改,在原 ...
- Docker学习第三天(Docker数据卷管理)
1.Docker数据卷管理 在Docker中,要想实现数据的持久化(所谓Docker的数据持久化即数据不随着Container的结束而结束),需要将数据从宿主机挂载到容器中.目前Docker提供了三种 ...
- 第十四节,OpenCV学习(三)图像的阈值分割
图像的阈值处理 图像的阈值分割:图像的二值化(Binarization) 阈值分割法的特点是:适用于目标与背景灰度有较强对比的情况,重要的是背景或物体的灰度比较单一,而且总可以得到封闭且连通区域的边界 ...
- c++学习(三)------static数据与成员函数
疑惑: static类型成员是类的全局变量,所有类的实例都享有这个变量,或者说这个变量不属于任何一个类的实例. static类型变量可以为private,或public或其他(static数据可以被继 ...
- angular学习笔记(三)-视图绑定数据的两种方式
绑定数据有两种方式: <!DOCTYPE html> <html ng-app> <head> <title>2.2显示文本</title> ...
- Emgu 学习(7)threshold ,图像过滤
Threshold 代码如下 static void Main(String[] args) { Mat img = CvInvoke.Imread(@"C:\Users\dell\Pict ...
- Emgu学习之(四)——图像阈值
http://www.cnblogs.com/CoverCat/p/5043833.html Visual Studio Community 2015 工程和代码:http://pan.baidu.c ...
随机推荐
- Vue 错误:Avoid mutating a prop directly
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re- ...
- PHP 获取当前类名、方法名、URL地址
1.PHP获取当前类名.方法名 __CLASS__ 获取当前类名 __FUNCTION__ 当前函数名(confirm) __METHOD__ 当前方法名 (bankcard::confir ...
- unity3d之游戏优化
=============================================================================== 美术规格: 1.单个蒙皮网格渲染器2.一 ...
- SQL Server Metadata
http://www.devart.com/dotconnect/sqlserver/docs/MetaData.htmlhttps://msdn.microsoft.com/en-us/librar ...
- 使用css实现三角符号
关于使用css制作三角符号,网上有很多的例子了,在这里只是为了详细的向各位解释一下三角符号的原理 下图,是一个长宽为100px,边框宽度为100px的一个元素,由此可见,在css中上下左右的边框相交处 ...
- CAS服务器集群和客户端集群环境下的单点登录和单点注销解决方案
CAS的集群环境,包括CAS的客户应用是集群环境,以及CAS服务本身是集群环境这两种情况.在集群环境下使用CAS,要解决两个问题,一是单点退出(注销)时,CAS如何将退出请求正确转发到用户sessio ...
- 在SQL service或Oracle中将数字转换成有千位符号
1.在SQL service中的写法: --Function主体 CREATE FUNCTION [dbo].[FnMoneyStyle](@Number )) RETURNS VARCHAR() A ...
- spring+quarts常见问题
javax/transaction/UserTransactionCaused by: java.lang.NoClassDefFoundError: javax/transaction/UserTr ...
- Mybatis学习---基础知识考核
MyBatis 2.什么是MyBatis的接口绑定,有什么好处 接口映射就是在IBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了Sql ...
- EXCHANGE 2013 TLS传输层安全
默认情况下,SMTP流量是不被加密的,这就导致在公网上进行邮件沟通就像是在广播一样,任何人拦截到该邮件都可以轻而易举的读取其内容.但是现实场景中有许多敏感信息是通过邮件来进行发送的,所以其中一种保护邮 ...