Visual Studio Community 2015 工程和代碼:http://pan.baidu.com/s/1jHmlQeE
內容
在這篇文章中將提到以下內容:
- 修改像素值
- 圖像ROI
- 圖像加法
- 圖像減法
- 按位運算
- 圖像混合
准備工作
- 創建工程:參考Emgu學習之(一)——Emgu介紹創建一個WinForm項目
- Form1.cs引用命名空間:
1 using Emgu.CV; 2 using Emgu.CV.Structure; 3 using Emgu.CV.CvEnum;
- 界面:在Form1中添加2行2列的TableLayout容器,然后添加四個Emgu.CV.UI.ImageBox控件(參考Emgu學習之(二)——圖像讀取、顯示、保存),添加后界面如下:
如果你自己在實驗時無法得到圖片顯示的效果,那么可以試試修改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。
運行效果為: