zoukankan      html  css  js  c++  java
  • Emgu学习之(三)——操作图像数据

    Visual Studio Community 2015 工程和代码:http://pan.baidu.com/s/1jHmlQeE

    内容

    在这篇文章中将提到以下内容:

    • 修改像素值
    • 图像ROI
    • 图像加法
    • 图像减法
    • 按位运算
    • 图像混合

    准备工作

    1 using Emgu.CV;
    2 using Emgu.CV.Structure;
    3 using Emgu.CV.CvEnum;

       

         如果你自己在实验时无法得到图片显示的效果,那么可以试试修改ImageBox的SizeMode属性。

    • 新建DataAccess类:
     1 using Emgu.CV;
     2 using Emgu.CV.Structure;
     3 using Emgu.CV.CvEnum;
     4 
     5 namespace AccessingImageData
     6 {
     7     public class DataAccess
     8     {
     9     }
    10 }

    修改像素值

    可以通过行、列索引直接操作像素值,操作方式如下:

    _image[row, cols] = new Bgr(Color.Green);

    在DataAccess类中添加ExchangePixelValue方法,这个方法将交换两个区域的像素值。

     1      /// <summary>
     2         /// 交换图像中两个区域的像素值
     3         /// </summary>
     4         /// <param name="image"></param>
     5         public static void ExchangePixelValue(Image<Bgr, Byte> image)
     6         {
     7             for(int i = 0; i < 100; i ++)
     8                 for(int j = 0; j < 200; j ++)
     9                 {
    10                     Bgr tmp = image[i, j];
    11                     image[i, j] = image[i + 100, j + 200];
    12                     image[i + 100, j + 200] = tmp;
    13                 }
    14         }

    在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方法:

     1         /// <summary>
     2         /// 设置指定ROI区域为红色
     3         /// </summary>
     4         /// <param name="image"></param>
     5         /// <param name="roi"></param>
     6         public static void SetRoiRed(Image<Bgr, Byte> image, Rectangle roi)
     7         {
     8             image.ROI = roi;
     9             image.SetValue(new Bgr(Color.Red));
    10             //要记得取消ROI的设置,否则后续的操作都会在ROI中进行,包括显示图像
    11             image.ROI = Rectangle.Empty;
    12         }

    在Form1中调用这个方法的运行效果为:

    图像加法

    使用Image.Add()方法,你可以让两个图像相加,或让当前图像加上一个色彩值。另外你也可以使用CvInvoke.Add()方法执行相同的操作,

    Image.Add()方法内部就是调用CvInvoke.Add()方法实现的。

    Image.Add()有3个实现,每个实现的返回都是一个相同色彩空间、值类型的Image对象:

    1     ///<summary> 当前图片与另外一张图片相加,另外一张图片必须与当前图片是相同的类型和尺寸(或相同ROI尺寸) </summary>
    2       ///<param name="img2">与当前图片相加的图片</param>
    3       ///<returns> 相加的结果</returns>
    4       public Image<TColor, TDepth> Add(Image<TColor, TDepth> img2)
    1     ///<summary> 当前图片与另外一张图片相加(ret(I)=src1(I)+src2(I) if mask(I)!=0),另外一张图片必须与当前图片是相同的类型和尺寸(或形同ROI尺寸)</summary>
    2       ///<param name="img2">另一张图片</param>
    3       ///<param name="mask">掩膜图片</param>
    4       ///<returns> 使用掩膜图片相加的结果</returns>
    5       public Image<TColor, TDepth> Add(Image<TColor, TDepth> img2, Image<Gray, Byte> mask)
    1       ///<summary> 当前图片加上一个色彩值 </summary>
    2       ///<param name="val"> 色彩值 </param>
    3       ///<returns> 相加的结果 <paramref name="val"/> from the current image</returns>
    4       public Image<TColor, TDepth> Add(TColor val)

    接下来我们演示如何使用这些方法:在DataAccess类中添加JustAdd和AddUsingMask方法,JustAdd方法只是简单的调用Add方法。

    而AddUsingMask方法中,我们首先需要创建一张掩膜图片,掩膜图片左半边为白色(255),右半边为黑色(0),在执行加操作时,

    白色部分会执行加操作,而黑色部分不执行任何操作,所以resImage的右半边是黑色的,这时把原图的右半边拷贝到resImage的右半

    边上,代码如下:

     1         /// <summary>
     2         /// 两张图片相加
     3         /// </summary>
     4         /// <param name="image1">相加的源图片1</param>
     5         /// <param name="image2">相加的源图片2</param>
     6         /// <returns></returns>
     7         public static Image<Bgr, Byte> JustAdd(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
     8         {
     9             return image1.Add(image2);
    10         }
    11 
    12         /// <summary>
    13         /// 使用掩码图片进行相加操作
    14         /// </summary>
    15         /// <param name="image1"></param>
    16         /// <param name="image2"></param>
    17         /// <returns></returns>
    18         public static Image<Bgr, Byte> AddUsingMask(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
    19         {
    20             var rect = new Rectangle(new Point(0, 0), new Size(image1.Width / 2, image1.Height));
    21             using (var mask = new Image<Gray, Byte>(image1.Size))
    22             { 
    23                 mask.SetZero();//设置所有值为0
    24                 mask.ROI = rect;
    25                 mask.SetValue(255);//设置ROI的值为255
    26                 mask.ROI = Rectangle.Empty;//去掉ROI
    27                 //res(I)=img1(I)+img2(I) if mask(I)!=0
    28                 var resImage = image1.Add(image2, mask);
    29                 mask._Not();//反转mask的值(255->0, 0->255)
    30                 image1.Copy(resImage, mask);//在mask(I) != 0的条件下,把image1的值拷贝到resImage中
    31                 return resImage;
    32             }
    33         }

    在Form1中调用以上方法:

     1         private void Form1_Load(object sender, EventArgs e)
     2         {
     3             _image = new Image<Bgr, byte>(Properties.Resources.gougou);
     4             imageBox1.Image = _image;
     5             imageBox2.Image = DataAccess.JustAdd(_image, _image);//图片自加
     6             imageBox4.Image = DataAccess.AddUsingMask(_image, _image);
     7 
     8             //创建掩膜图片
     9             var mask = new Image<Gray, Byte>(_image.Size);
    10             mask.SetZero();//设置所有值为0
    11             mask.ROI = new Rectangle(new Point(0, 0), new Size(_image.Width / 2, _image.Height));
    12             mask.SetValue(255);//设置ROI的值为255
    13             mask.ROI = Rectangle.Empty;//去掉ROI
    14 
    15             imageBox3.Image = mask;
    16         }

    运行效果:

    图像减法

    使用Image.Sub()方法,你可以让当前图像减去另外一个图像,或让当前图像减去一个色彩值。另外你也可以使用CvInvoke.Subtract()方法执行相同的操作,

    Image.Sub()方法内部就是调用CvInvoke.Subtract()方法实现的。

    与加法相似,Image.Sub()同样有3个实现,每个实现的返回都是一个相同色彩空间、值类型的Image对象:

    1        ///<summary> 当前图片减去一张图片,被减图片必须与当前图片是相同的类型和尺寸(或相同的ROI尺寸) </summary>
    2        ///<param name="img2">被减图片</param>
    3        ///<returns> 相减的结果</returns>
    4        public Image<TColor, TDepth> Sub(Image<TColor, TDepth> img2)
    1       ///<summary> 当前图片减去另外一张图片(ret(I)=src1(I)-src2(I) if mask(I)!=0),被减图片必须与当前图片是相同的类型和尺寸(或相同的ROI尺寸) </summary>
    2        ///<param name="img2">被减图片</param>
    3        ///<param name="mask">掩膜图片</param>
    4        ///<returns> 使用掩膜图片相减的结果</returns>
    5        public Image<TColor, TDepth> Sub(Image<TColor, TDepth> img2, Image<Gray, Byte> mask)
    1        ///<summary> 当前图片减去一个色彩值</summary>
    2        ///<param name="val">被减去的色彩值</param>
    3        ///<returns> 减去色彩值的结果</returns>
    4        public Image<TColor, TDepth> Sub(TColor val) 

    接下来我们演示如何使用这些方法:在DataAccess类中添加JustSub和SubUsingMask方法,代码如下:

     1         /// <summary>
     2         /// 图像减法
     3         /// </summary>
     4         /// <param name="image1"></param>
     5         /// <param name="image2"></param>
     6         /// <returns></returns>
     7         public static Image<Bgr, Byte> JustSub(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
     8         {
     9             return image1.Sub(image2);
    10         }
    11 
    12         /// <summary>
    13         /// 在掩码图片的条件下,用image1减去image2的值
    14         /// </summary>
    15         /// <param name="image1"></param>
    16         /// <param name="image2"></param>
    17         /// <returns></returns>
    18         public static Image<Bgr, Byte> SubUsingMask(Image<Bgr, Byte> image1, Image<Bgr, Byte> image2)
    19         {
    20             var rect = new Rectangle(new Point(0, 0), new Size(image1.Width / 2, image1.Height));
    21             using (var mask = new Image<Gray, Byte>(image1.Size))
    22             {
    23                 mask.SetZero();//设置所有值为0
    24                 mask.ROI = rect;
    25                 mask.SetValue(255);//设置ROI的值为255
    26                 mask.ROI = Rectangle.Empty;//去掉ROI
    27                 //res(I)=img1(I)-img2(I) if mask(I)!=0
    28                 var resImage = image1.Sub(image2, mask);
    29                 mask._Not();//反转mask的值(255->0, 0->255)
    30                 image1.Copy(resImage, mask);//在mask(I)!= 0的条件下,把image1的值拷贝到resImage中
    31                 return resImage;
    32             }
    33         }

    在Form1中调用以上方法,在加法演示中我们使图片自加,得到了一张更亮的照片,但是在减法中如果我们使图片自减就会得到一张黑色的图片,

    但是为了使读者能清晰的看到减法操作,我们使原图减去一张红色图,所以在以下程序中我们需要创建一张红色图片:

     1         private void Form1_Load(object sender, EventArgs e)
     2         {
     3             _image = new Image<Bgr, byte>(Properties.Resources.gougou);
     4             imageBox1.Image = _image;
     5             using (var colorImage = new Image<Bgr, Byte>(_image.Size))
     6             {//首先创建一张红色图片
     7                 colorImage.SetValue(new Bgr(Color.Red));
     8                 imageBox2.Image = DataAccess.JustSub(_image, colorImage);
     9                 imageBox4.Image = DataAccess.SubUsingMask(_image, colorImage);
    10             }
    11 
    12             //创建掩膜图片
    13             var mask = new Image<Gray, Byte>(_image.Size);
    14             mask.SetZero();//设置所有值为0
    15             mask.ROI = new Rectangle(new Point(0, 0), new Size(_image.Width / 2, _image.Height));
    16             mask.SetValue(255);//设置ROI的值为255
    17             mask.ROI = Rectangle.Empty;//去掉ROI
    18 
    19             imageBox3.Image = mask;
    20         }

    运行效果:

    按位运算

    按位运算有与(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方法:

     1         /// <summary>
     2         /// 在掩膜图片的条件下,在图片中添加logo,掩膜图片为logo图片的二值图
     3         /// </summary>
     4         /// <param name="image"></param>
     5         /// <param name="logo"></param>
     6         /// <param name="mask"></param>
     7         /// <returns></returns>
     8         public static Image<Bgr, Byte> AddLogo(Image<Bgr, Byte> image, Image<Bgr, Byte> logo, Image<Gray, Byte> mask)
     9         {
    10             var resImage = image.Copy();
    11 
    12             //设置操作区域,所有的操作都在这个区域中进行
    13             image.ROI = new Rectangle(new Point(0, 0), logo.Size);
    14             resImage.ROI = image.ROI;
    15 
    16             using (var colorMask = mask.Convert<Bgr, Byte>())
    17             {
    18                 //把Logo区域变成白色(0xFF)
    19                 CvInvoke.Add(image, colorMask, resImage, mask);
    20             }
    21           
    22             CvInvoke.BitwiseAnd(resImage, logo, resImage, mask);
    23 
    24             //失能操作区域
    25             resImage.ROI = Rectangle.Empty;
    26             return resImage;
    27         }

    然后,在Form1中调用AddLogo方法:

    图像混合

    图像混合和图像加法类似,图像加法是简单的把两张图片相加,而图像混合是将两张图片按照不同的权重相加:

     res[m, n] = α·src1[m,n] + β·src1[m,n] + γ(其中α = 1 - β)

    实现这个功能的函数为cvAddWeighted方法,以下为调用实例:

    1 var resImage =  image1.AddWeighted(image2, 0.5, 0.5, 0);

    这里α = 0.5,β = 0.5, γ = 0。

    运行效果为:

  • 相关阅读:
    谈一谈对象池SafeObjectPool能干什么
    .net core 程序退出事件
    .NETCore 快速开发做一个简易商城
    Git创建子分支,合并分支并提交
    Vue项目中关闭eslint的方法
    Missing space before function parentheses
    单引号变双引号 格式化去掉分号
    Docker Swarm搭建多服务器下Docker集群
    Asp.Net Core中服务的生命周期选项区别和用法
    KnockoutJS-模板绑定
  • 原文地址:https://www.cnblogs.com/CoverCat/p/5014259.html
Copyright © 2011-2022 走看看