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。

    运行效果为:

  • 相关阅读:
    Maximum Depth of Binary Tree
    Single Number
    Merge Two Sorted Lists
    Remove Nth Node From End of List
    Remove Element
    Remove Duplicates from Sorted List
    Add Two Numbers
    编译视频直播点播平台EasyDSS数据排序使用Go 语言 slice 类型排序的实现介绍
    RTMP协议视频直播点播平台EasyDSS在Linux系统中以服务启动报错can’t evaluate field RootPath in type*struct排查
    【解决方案】5G时代RTMP推流服务器/互联网直播点播平台EasyDSS实现360°全景摄像机VR直播
  • 原文地址:https://www.cnblogs.com/CoverCat/p/5014259.html
Copyright © 2011-2022 走看看