close

半年前寫過應用裝飾模式於影像處理函式,溫故知新後想嘗試重新翻寫,希望對於正在學習裝飾模式的同好有所助益!

在上一篇 裝飾模式應用: 使用C#,提到三個類別Building、Furniture(Building子類別)、及繼承Furniture子類別,透過裝飾模式方式,讓客戶端可以任意順序裝飾物件,搭配不同資料型態儲存如陣列或List(長度可不固定),這篇將利用裝飾模式應用於影像處理步驟組裝。

image

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

假設一個影像處理步驟如下

image

   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: }

image

客戶端測試2

假設一個影像處理步驟如下


 image

   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);

image

image

 

 

參考資料:

1. 裝飾模式應用: 使用C#

2. 應用裝飾模式於影像處理函式

3. 大話設計模式, 作者:程杰

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 me1237guy 的頭像
    me1237guy

    天天向上

    me1237guy 發表在 痞客邦 留言(0) 人氣()