半年前寫過應用裝飾模式於影像處理函式,溫故知新後想嘗試重新翻寫,希望對於正在學習裝飾模式的同好有所助益!
在上一篇 裝飾模式應用: 使用C#,提到三個類別Building、Furniture(Building子類別)、及繼承Furniture子類別,透過裝飾模式方式,讓客戶端可以任意順序裝飾物件,搭配不同資料型態儲存如陣列或List(長度可不固定),這篇將利用裝飾模式應用於影像處理步驟組裝。
1. Line 4: img私有影像成員
2. Line 5: name儲存影像處理方法名稱
3. Line 6~10: 存取name
4. Line 11~21: 存取img
5. Line 22~25: ImageObject創建子
6. Line 27~30: Process()虛擬函式: 影像處理函式
7. Line 31~35: ShowImg()虛擬函式: 顯示影像
8. Line 36: 保護級資料成員 ImageObject componet,用來裝飾(掛載)下一個物件
9. Line 37~40: 裝飾物件函式
1: public class ImageObject
2: {
3: public ImageObject() { }
4: protected Mat img = new Mat();
5: protected string name;
6: public string Name
7: {
8: get { return name; }
9: set { name = value; }
10: }
11: public Mat Image
12: {
13: get
14: {
15: return img;
16: }
17: set
18: {
19: value.CopyTo(img);
20: }
21: }
22: public ImageObject(string name)
23: {
24: this.name = name;
25: }
26:
27: public virtual void Process()
28: {
29: Console.WriteLine("{0} 影像處理", name);
30: }
31: public virtual void ShowImg()
32: {
33: CvInvoke.Imshow(name, img);
34: //CvInvoke.WaitKey();
35: }
36: protected ImageObject component;
37: public void Decorate(ImageObject component)
38: {
39: this.component = component;
40: }
41:
42: }
1. Line9: 將目前影像處理結果(img)複製給下一個裝飾物件(component.Image)
2. Line10: 執行下一個裝飾物件影像處理: component.Process()
1: public class ImageCV:ImageObject
2: {
3: public override void Process()
4: {
5: if(component!=null)
6: {
7: Console.WriteLine("{0} 影像處理", name);
8: // 將目前影像處理結果複製給下一個裝飾物件(component)
9: component.Image = img.Clone();
10: component.Process();
11: }
12: }
13:
14: }
LoadImage類別(繼承ImageObject)
1. Line 5~9: LoadImage創建子,其中輸入filename為影像名稱,name為影像處理名稱。
2. Line 10~20: 覆寫Process(),載入一張影像
3. Line 19: 相當於執行ImageObject.Process(),把目前影像複製給下一個裝飾物件【component.Image = img.Clone();】,並執行下一個裝飾物件Process()
【component.Process();】
1: public class LoadImage : ImageCV
2: {
3: string filename;
4: public LoadImage() { }
5: public LoadImage(string filename)
6: {
7: this.filename = filename; // 影像名稱(完整路徑)
8: this.name = "LoadImage()"; // 影像處理名稱
9: }
10: public override void Process()
11: {
12: img = CvInvoke.Imread(filename);
13: if (img.IsEmpty)
14: {
15: Console.WriteLine("{0} Error, Image is empty! ", name);
16: Console.ReadLine();
17: return;
18: }
19: base.Process();
20: }
21: }
RGB2Gray類別(繼承ImageObject)
1. Line 14: 新增一個local影像變數imgDst,影像大小與img相同,但channel數為1。
2. Line 15: 執行彩色轉灰階 CvtColor(…)
3. Line 16: 將灰階影像在存回原本img。
4. Line 17: 相當於執行ImageObject.Process(),把目前影像複製給下一個裝飾物件【component.Image = img.Clone();】,並執行下一個裝飾物件Process()
【component.Process();】
1: public class RGB2Gray :ImageCV
2: {
3: public RGB2Gray() { name = "RGB2Gray()"; }
4: public override void Process()
5: {
6: if (img.IsEmpty)
7: {
8: Console.WriteLine("{0} Error, Image is empty! ", name);
9: Console.ReadLine();
10: return;
11: }
12: int width = img.Width;
13: int height = img.Height;
14: Mat imgDst = new Mat(width, height, DepthType.Cv8U, 1);
15: CvInvoke.CvtColor(img, imgDst, ColorConversion.Bgr2Gray);
16: img = imgDst.Clone(); // 將灰階影像在存回原本img
17: base.Process();
18: }
19: }
PyrDown類別(繼承ImageObject)
1: public class PyrDown:ImageCV
2: {
3: public PyrDown() { this.name = "PyrDown()"; }
4: public override void Process()
5: {
6: if (img.IsEmpty)
7: {
8: Console.WriteLine("{0} Error, Image is empty! ", name);
9: Console.ReadLine();
10: return;
11: }
12: Mat imgDst = new Mat();
13: CvInvoke.PyrDown(img, imgDst);
14: img = imgDst.Clone();
15: base.Process();
16: }
17:
18: }
PyrUp類別(繼承ImageObject)
1: public class PyrUp : ImageCV
2: {
3: public PyrUp() { this.name = "PyrUp()"; }
4: public override void Process()
5: {
6: if (img.IsEmpty)
7: {
8: Console.WriteLine("{0} Error, Image is empty! ", name);
9: Console.ReadLine();
10: return;
11: }
12: Mat imgDst = new Mat();
13: CvInvoke.PyrUp(img, imgDst);
14: img = imgDst.Clone();
15: //ShowImg();
16: base.Process();
17: }
18: }
Smooth類別(繼承ImageObject)
1: public class Smooth : ImageCV
2: {
3: public Smooth() { this.name = "Smooth()"; }
4: public override void Process()
5: {
6: if (img.IsEmpty)
7: {
8: Console.WriteLine("{0} Error, Image is empty! ", name);
9: Console.ReadLine();
10: return;
11: }
12: Mat imgDst = new Mat();
13: CvInvoke.PyrDown(img, imgDst);
14: CvInvoke.PyrUp(imgDst, imgDst);
15: img = imgDst.Clone();
16: //ShowImg();
17: base.Process();
18: }
19: }
客戶端測試1
假設一個影像處理步驟如下
1: LoadImage loadImage = new LoadImage(@"C:\Emgu\images\lena.jpg");
2: RGB2Gray rgb2gray = new RGB2Gray();
3: PyrDown pyrDown = new PyrDown();
4: PyrUp pyrUp = new PyrUp();
5: Smooth smooth = new Smooth();
6: string name = "object #1";
7: ImageObject imageObject1 = new ImageObject(name);
8:
9: loadImage.Name = name + " : (1)LoadImage()";
10: pyrDown.Name = name + " : (2)pyrDown()";
11: rgb2gray.Name = name + " : (3)rgb2gray()";
12: pyrUp.Name = name + " : (4)pyrUp()";
13: smooth.Name = name + " : (5)Smooth()";
14:
15: List<ImageObject> imgList1 = new List<ImageObject>();
16: imgList1.Add(loadImage);
17: imgList1.Add(pyrDown);
18: imgList1.Add(rgb2gray);
19: imgList1.Add(pyrUp);
20: imgList1.Add(smooth);
21: imgList1.Add(imageObject1);
22:
23: ImgProcList.Run(imgList1);
ImgProcList類別
1: public static class ImgProcList
2: {
3: public static void Run(List<ImageObject> imgList)
4: {
5: // 執行影像處理流程
6: for (int i = 0; i < imgList.Count - 2; i++)
7: imgList[i].Decorate(imgList[i + 1]);
8: imgList[0].Process();
9:
10: // 顯示每個步驟影像處理結果
11: for (int i = 0; i < imgList.Count - 1; i++)
12: {
13: imgList[i].ShowImg();
14: }
15: CvInvoke.WaitKey(0);
16: }
17: }
客戶端測試2
假設一個影像處理步驟如下
1: name = "object #2";
2: ImageObject imageobject2 = new ImageObject(name);
3: List<ImageObject> imgList2 = new List<ImageObject>();
4:
5: loadImage.Name = name + ": (1)loadimage()";
6: imgList2.Add(loadImage);
7: rgb2gray.Name = name + ": (2)rgb2gray()";
8: imgList2.Add(rgb2gray);
9: pyrDown.Name = name + ": (3)pyrdown()";
10: imgList2.Add(pyrDown);
11: pyrUp.Name = name + ": (4)pyrup()";
12: imgList2.Add(pyrUp);
13: imgList2.Add(imageobject2);
14:
15: ImgProcList.Run(imgList2);
參考資料:
1. 裝飾模式應用: 使用C#
3. 大話設計模式, 作者:程杰