EF Code First With Existing DataBase Modify Column

話說之前那一篇的 Entity Framework 觀念其實還是有很多地方是不大清楚的,像是使用 Code First with existing database 時什麼情況會異動到資料庫?什麼情況下需要做 Migrations?什麼情況下會直接吐個 Exception 給你而不是自動幫你多個 Table 或 Column?,所以今天就趁著一點時間來寫一下從接觸至今的處理經驗了…(說的好像經驗老到其實還是個新手) 前置工作 這次的範例中我們會需要一個無辜的資料庫讓我們(恣意蹂躪)測試。 就讓我們把這個偉大的資料庫命名為…Demo! 唔…其實 Products 先不用建起來,我們可以來做個實驗看看什麼時候會觸發 Migrations…好~建立起這個資料庫之後我們就可以馬上來寫 Code 玩玩看啦~! 這裡我們就使用 Console Application 就好。我們建立一個名為 EFMigrationDemo 的專案,並在根目錄處新增一個 Model 資料夾裡面放置我們最重要的類別。 在寫 Code 之前我們要先把 EntityFramework 裝起來~就直接打開 Package Manager Console 吧!請在 Console 中直接輸入: Install-Package EntityFramework 若不放心是否為最新就接著下: Update-Package EntityFramework 這樣就可以保證你所指定的套件是最新的! 前置工作這樣就算是告一段落了~ 寫 Code 囉 我們新增一個類別檔案於 Model 資料夾中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 using System.Data.Entity; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace EFMigrationDemo.Model { public class Customers // 類別名稱與資料庫中的表格名稱一樣 { // 標記該屬性(在資料庫中為欄位)為 Promary key //(若屬性結尾為 ID 依照 Conversion naming 規則 EF 會自己判斷出來) [Key] public int ID { get; set; } // 標記該欄位是不能為Null的 [Required] public string Name { get; set; } [Required] public string Address { get; set; } [Required] public string PhoneNumber { get; set; } [Required] public string Email { get; set; } // 標記這個屬性對應到資料庫中的欄位名稱為 NickName 而非 TheNickName // 而且該欄位的內容長度不超過50 [Column("NickName")] [MaxLength(50, ErrorMessage = "Your Nick Name too long")] public string TheNickName { get; set; } // 標記這個屬性並不會對應到資料庫中的任何欄位 [NotMapped] public string NameplusNickName { get { return Name + NickName; } } // 這是個類別所以還是可以有方法,而且不會被認為是欄位或是預儲程序 public void PrintName() { Console.WriteLine("My name is {0}", this.Name); } } // 繼承 DbContext 的類別可以當成是資料庫來進行操作,預設會去 Config 檔中抓取 connectionString 的 section 裡與類別名稱相同的項目,像是: // <add name="Demo" connectionString="" providerName=""> // 而類別名稱其實也可以不用跟資料庫名稱一樣 public class Demo : DbContext { // 被 DbSet 包起來的類別會被判定為是一個實體(也就是一個表格) // 所以屬性名稱並不會影響到表格名稱 public DbSet<Customers> Customer { get; set; } } } 這樣我們的 Model 類別就算準備好了,接下來就是準備呼叫端了~ ...

November 16, 2012 · 3 分鐘 · 566 字

EF-With-Repository-Pattern

在上一篇中我們有使用到一個 NorthwindReposity 類別來存取資料庫,不過我們也發現到這樣的寫法與呼叫端的耦合度還是有點高。若我要使用的資料所要做的事情是固定的,像是瀏覽產品資訊時的產品資料是從主資料庫中找到產品的資料表並取出指定產品的資料,那麼其實對這個部分來說其實他就只關心產品得資料如何存取和操作而已。一個簡單的想法是,我們讓呼叫端用一個介面來針對他所感興趣的部份去做操作。 實作 我們用的資料庫依然是北風資料庫,而且和上一篇是一樣的!避免頁面切來切去這邊就直接寫囉~ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using System.Data.Entity; using System.ComponentModel.DataAnnotations.Schema; public class Northwind : DbContext { public DbSet<product> Products { get; set; } public DbSet<category> Categories { get; set; } } public class Product { public int ProductID { get; set; } public string ProductName { get; set; } public Decimal? UnitPrice { get; set; } public bool Discontinued { get; set; } [ForeignKey("Category")] public int CategoryID { get; set; } public virtual Category Category { get; set; } } public class Category { public int CategoryID { get; set; } public string CategoryName { get; set; } public string Description { get; set; } public byte[] Picture { get; set; } public virtual ICollection<product> Products { get; set; } } 接下來就是我們上一篇用來存取資料庫的主要類別,也是我們這次的修改標的物。 ...

November 8, 2012 · 4 分鐘 · 721 字

EF Code First With Existing Database

在開發專案時很多時候若有連接到資料庫,通常會把 Model 這個部分先切割出來開發,這樣可以讓 Model 獨立在主 project 之外讓其他 projects 可以一起使用,在維護的時候其實也會比較方便只需要抽換DLL就好(這也算關注點分離嗎),另外就是 Model 天生就比較能夠獨立出來開發。不過在獨立開發 Model 的時候也要注意一些小東西。今天使用 Entity Framework (version 5) 的 Code First with existing database 的方式來做個示範。 實作部分 我們選用的 Database 是赫赫有名的 Northwind 資料庫,我想這個資料庫大家應該都比我還熟了。這次我們選出 Products 和 Categories 這兩個資料表來做示範的 Table 吧~!順便提醒一下用 Code First with existing database 時會有些小地方需要注意! 首先~我們就來把 Northwind database bring online 吧~ 把資料庫 online 後,接下來就切到 Visual Studio 中創一個新的 Class Library 專案吧~ 專案名稱就隨便給個 Project.Model 來代表這個 DLL 是我們的 Model。而因為要用 Entity Framework 的 Code First 功能,我們要引用一個 EF 的 DLL:EntityFramework,若是用 Nuget 來安裝的話會連同 System.Data.Entity 這個 DLL 一起裝,不過若是沒有要用視覺設計工具來輔助的話其實是可以不用這個 DLL 的。 ...

October 5, 2012 · 3 分鐘 · 464 字

Hello World Level jQuery.ajax

通常在寫 ASP.NET Web Form 的時候我們很習慣會把一些 html tag 以 Server control 來使用。 但是這些控制項若放在表單中,每次的POST動作都會把這些控制項的內容和狀態一起送回伺服器端,若是有需要的話倒還好,偏偏很多控制項的狀態是根本不需要維護的。像是 Button 啦~ Literal 啦~這些東西其實大多數的時候是死都不會改變的,不過因為天生架構的關係所以每次 POST 回去都會要把這些東西一起送回 Server,實在有點麻煩。所以若是這個時候可以用jQuery來取代 form 傳回 Server 並處理的話可以減輕不少流量的負擔。 實作 一開始我們先開啟一個空的 Web From 專案~ 然後新增一個Index.aspx頁面並設定為起始頁面。 在body中新增下列標籤: 1 2 3 4 5 6 7 <input type="text" id="textbox1" /><input type="button" id="button1" value="jQuery way" /> <form id="form1" runat="server"> <div> <asp:TextBox ID="formTextbox1" runat="server"></asp:TextBox> <asp:Button ID="formbutton1" runat="server" Text="form way" OnClick="formbutton1_Click" /> </div> </form> 並且在body中新增下列script: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function jqueryAjax() { $.ajax({ url: 'ajaxhandler.ashx', type: 'POST', dataType: 'text', cache: 'false', success: function (result) { $('#textbox1').val(result); } }) }; $(function () { $('#button1').bind('click', jqueryAjax) }); 這段script的作用就在於,把id為button1的按鈕的click事件與jqueryAjax函式進行綁定。而jqueryAjax函式的工作就只是呼叫名為ajaxhendler.ashx的泛型處理常式來回應client端的呼叫。 ...

September 26, 2012 · 1 分鐘 · 161 字

ASP.NET Web Form Single Login

會有這樣的題目主要是因為業主的要求(廢話)。明確的說是這樣的,同一時間同一帳號只能在一個地方登入,之後登入的可以把前面登入的剔除。這個機制其實之前我並沒有想到要怎麼解,或是說沒想到不用資料庫的方式要怎麼解…(超遜的啦!)。不過認真想過後才發現這個機制其實不會太難,或是說我的解法其實很簡單,而且應該也可以用在MVC架構裡。 研究方法 Session ASP.NET提供Session讓我們可對工作狀態進行管理,可以讓我們跨越多個要求儲存與瀏覽器工作階段關連的訊息,Session提供Key-value pairs的方式來存取這些資料。雖然我們很常拿Session來儲存使用者的資訊,像是使用者的帳號或是一些其他資訊,不過Session是每個使用者各自有的,這裡面的資訊並不會共享,因此利用Session來達成需求顯然是不行。我們需要的應用系統的全域變數,或是資料結構來對整個系統的使用者進行管理。這樣的需求我想全域應用程式類別(預設檔名為Global.asax)就是我這邊提出的解答! 簡述關於Global.asax的運作 當ASP.NET應用程式收到第一個要求的時候會先使用ApplicationManager建立應用程式定義域(應用程式定義域可以隔離應用程式間的全域變數,而且能夠允許每個應用程式個別進行卸載。)。在所有核心物件初始化後會藉由建立System.Web.HttpApplication類別的實體來執行應用程式。若應用程式中有Global.asax(繼承自HttpApplication)則會改用衍生自HttpApplication的Global.asax來建立執行應用程式的實體。而當處理要求的時候則會執行下述事件:(注意!這邊僅列達成需求出最主要的兩個) 1 2 3 4 5 6 7 8 9 ///當要求ASP.NET應用程式中的第一個資源(如:網頁)時呼叫, ///這個方法在應用程式生命週期中只會被呼叫一次。 ///我們讓全域變數在這邊進行宣告。 Application_Start(object sender, EventArgs e) { } ///這個方法在MSDN裡面的解釋有點怪,不過在應用程式生命週期中該事件發生也是只有一次, ///就是當IIS重啟或是應用程式正常關閉時就會發起該事件。 ///我們讓全域變數所用到的資源在這裡釋放。 Application_End(object sender, EventArgs e) { } 開始撰寫時我們要先準備一下這個管理使用者的容器類別: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 /// 系統中記錄目前線上全部使用者的識別號的容器。 public sealed class SystemUserPool { private static ConcurrentDictionary<string, string> _UserPool = new ConcurrentDictionary<string, string>(); /// 記錄系統中新登入的使用者。 /// userNumber = 登入者的帳號。 /// userIp = 登入者的 IP 位址。 /// 使用者登入成功與否。 public Boolean AddUser(String userNumber, String userIp) { return _UserPool.TryAdd(userNumber, userIp); } /// 剔除系統中指定的使用者。 /// userNumber = 使用者的帳號。 /// 剔除成功與否。 public Boolean DeleteUser(String userNumber) { string str = String.Empty; return _UserPool.TryRemove(userNumber, out str); } /// 列出目前系統中在線上的使用者。 /// 使用者列表。 public List<string> ListAllUser() { return _UserPool.Keys.ToList<string>(); } /// 判斷使用者是否仍在線上。 /// userNumber = 欲查明的使用者。 /// 若在線上則為 true,反之則為 false。 public Boolean IsOnLine(String userNumber) { return _UserPool.Keys.Contains(userNumber); } /// 判斷使用者是否從同一個地方登入。 /// userNumber = 使用者帳號。 /// userIp = 使用者目前 IP 位址。 /// 若 userIp 與系統中使用者的 IP 位址一致表示從同一個地方登入。 public Boolean IsTheSameIP(String userNumber, String userIp) { String orignalIP = String.Empty; if (_UserPool.TryGetValue(userNumber, out orignalIP)) { if (orignalIP == userIp) return true; return false; } return false;//若沒能取得,表示使用者已經離線。 } /// 更新使用者的 IP 位址。 /// userNumber = 使用者帳號。 /// userIP = 使用者新的 IP 位址。 public void UpdateUserIP(String userNumber, String userIP) { if (!IsOnLine(userNumber)) return; _UserPool[userNumber] = userIP; } /// 把系統使用者儲蓄池清空。 public void ClearUserPool() { _UserPool.Clear(); } } 在Application_Start中就這樣寫: ...

September 3, 2012 · 2 分鐘 · 336 字

Simple usage of Reflection and Dependency Injection

前言 這一篇主要是要記錄一下反射(Reflection)的方便用法,作為以後的備忘。底下的範例會順便用一些Depency Injection的觀念來實作。 根據MSDN的描述 反映 (Reflection) 會提供 Type 型別的物件,用來描述組件、模組和型別。 您可以使用反映來動態建立型別的執行個體、將型別繫結至現有物件,或從現有物件取得型別,並叫用其方法或存取其欄位和屬性。 如果您在程式碼中使用屬性,反映可讓您存取這些屬性。 情境 在動物園裡面有很多不同的動物園區,每個動物園區都可以看到專屬於該園區的動物。當遊客買票進去時,可以依據票的種類去不同的園區觀賞。假設目前我們有兩個園區: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 /// 獅子王動物園區 public class LionKing { private ICollection<string> _Pool; public LionKink() { _Pool = new List<string>{ "Simba", "Nala", "Pumbaa", "丁滿" }; } public void SeeAnimals() { foreach(var name in _pool) Console.WriteLine("在動物園區我看到 {0}."name); } } /// 憤怒鳥動物園區 public class AngryBird { private ICollection<string> _Pool; public AngryBird() { _Pool = new List<string> { "RedBird", "BlueBird", "YellowBird" }; } public void SeeAnimals() { foreach(var name in _pool) Console.WriteLine("在動物園區我看到{0}."name); } } 方法: 通常看到這樣的需求有一種寫法是最簡單直覺的: ...

August 30, 2012 · 3 分鐘 · 473 字

MediaElement Open New Source Behavior

前言 Silverlight 在多媒體的處理中提供了 MediaElement 這個控制項,可以進行影片的播放、聲音的播放等應用。而串流資訊的來源可以是一個網址列,也可以是一個串流來源。但是若來源固定的話這個應用實在很弱,因此大多的時候來源都是可以隨時變動的。今天在做一些測試的時候有個情境是這樣的: 使用者在 ListBox 中選了一個 Item,而選中後會觸發事件讓 MediaElement 的 Source 重新設定來源,然後就可以直接播放新的 Item 的內容 (不同的 Item 表示不同的 Radio 位址)。 問題描述 點選 Listbox 的 Item 後會觸發事件,但是程式執行 MediaElement1.Play() 後卻沒有動作。 分析 根據 MSDN 的官方文件中說明,MediaElement 的 Source 在進行變更之後會針對來源進行驗證或是授權(如果有需要的話),接著就會觸發 MediaOpened 事件,不過接著會進行什麼動作則是依據兩個屬性來決定;AutoPlay, CanPause。 Case 1: 若 AutoPlay 是 True。則MediaElement的Source更改後狀態直接進入 Playing 來播放多媒體。 Case 2: 若 AutoPlay 是 False,CanPause 是 True。則Source更改後狀態會進入 Pause。 Case 3: 若兩個屬性都是 False。則 Source 更改後狀態會進入 Stopped。 因此,若想要使用者點選選單然後立刻播放Radio的話可以這麼做: Solution Solution1: xaml 文件中宣告 MediaElement 與這些屬性 1 MediaElement Height="200" Name="myMediaRadio" Width="300" AutoPlay="True" code-behind檔中這樣寫 ...

May 11, 2012 · 1 分鐘 · 136 字

Regular Expression Cheat Sheet

這篇文章只是要把一些關於 Regular Expression 的資料收集起來方便以後查找~ 畢竟是在.NET平台上面寫所以MSDN的資料當然不能放過:MSND Regular Expression 而「規則運算式物件模型」一文中,最常用到的方法莫過於 Split 和 Matches 還有 Groups,而這些在文章中都有範例可以參考。 當然正則表示式是不分國界的很多語言都對這個東西有支援 Java 也不例外在 Java Gossip 中也針對 [Regular Expression] 開了篇文章來說明。 還有這篇文章裡面有提到一些正規表示式的一些規則和簡單範例。 最後說到字串的處理…MSDN上面也有對於字串處理的一些建議:「在.NET Framework中使用字串的最佳作法」。 常用規則: 字元 描述 \ 將下一個字元標記為一個特殊字元、或一個原義字元、或一個向後參照、或一個八進制轉義符。例如,「n」匹配字元「n」。「\n」匹配一個換行符。序列「\」匹配「\」而「(」則匹配「(」。 ^ 匹配輸入字串的開始位置。如果設定了RegExp物件的Multiline屬性,^也匹配「\n」或「\r」之後的位置。 $ 匹配輸入字串的結束位置。如果設定了RegExp物件的Multiline屬性,$也匹配「\n」或「\r」之前的位置。 * 匹配前面的子運算式零次或多次。例如,zo*能匹配「z」以及「zoo」。*等價於{0,}。 + 匹配前面的子運算式一次或多次。例如,「zo+」能匹配「zo」以及「zoo」,但不能匹配「z」。+等價於{1,}。 ? 匹配前面的子運算式零次或一次。例如,「do(es)?」可以匹配「does」或「does」中的「do」。?等價於{0,1}。 {n} n是一個非負整數。匹配確定的n次。例如,「o{2}」不能匹配「Bob」中的「o」,但是能匹配「food」中的兩個o。 {n,} n是一個非負整數。至少匹配n次。例如,「o{2,}」不能匹配「Bob」中的「o」,但能匹配「foooood」中的所有o。「o{1,}」等價於「o+」。「o{0,}」則等價於「o*」 {n,m} m和n均為非負整數,其中n<=m。最少匹配n次且最多匹配m次。例如,「o{1,3}」將匹配「fooooood」中的前三個o。「o{0,1}」等價於「o?」。請注意在逗號和兩個數之間不能有空格。 ? 當該字元緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})後面時,匹配模式是非貪婪的。非貪婪模式儘可能少的匹配所搜尋的字串,而預設的貪婪模式則儘可能多的匹配所搜尋的字串。例如,對於字串「oooo」,「o+?」將匹配單個「o」,而「o+」將匹配所有「o」。 . 匹配除「\n」之外的任何單個字元。要匹配包括「\n」在內的任何字元,請使用像「(. (pattern) 匹配pattern並獲取這一匹配。所獲取的匹配可以從產生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中則使用$0…$9屬性。要匹配圓括號字元,請使用「(」或「)」。 (?:pattern) 匹配pattern但不獲取匹配結果,也就是說這是一個非獲取匹配,不進行儲存供以後使用。這在使用或字元「( (?=pattern) 正向肯定預查,在任何匹配pattern的字串開始處匹配尋找字串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如,「Windows(?=95 (?!pattern) 正向否定預查,在任何不匹配pattern的字串開始處匹配尋找字串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如「Windows(?!95 (?<=pattern) 反向肯定預查,與正向肯定預查類擬,只是方向相反。例如,「(?<=95 (?<!pattern) 反向否定預查,與正向否定預查類擬,只是方向相反。例如「(?<!95 x|y 匹配x或y。例如,「z [xyz] 字符集合。匹配所包含的任意一個字元。例如,「[abc]」可以匹配「plain」中的「a」。 [^xyz] 負值字符集合。匹配未包含的任意字元。例如,「[^abc]」可以匹配「plain」中的「p」。 [a-z] 字元範圍。匹配指定範圍內的任意字元。例如,「[a-z]」可以匹配「a」到「z」範圍內的任意小寫字母字元。 [^a-z] 負值字元範圍。匹配任何不在指定範圍內的任意字元。例如,「[^a-z]」可以匹配任何不在「a」到「z」範圍內的任意字元。 \b 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如,「er\b」可以匹配「never」中的「er」,但不能匹配「verb」中的「er」。 \B 匹配非單詞邊界。「er\B」能匹配「verb」中的「er」,但不能匹配「never」中的「er」。 \cx 匹配由x指明的控制字元。例如,\cM匹配一個Control-M或Enter符。x的值必須為A-Z或a-z之一。否則,將c視為一個原義的「c」字元。 \d 匹配一個數位字元。等價於[0-9]。 \D 匹配一個非數位字元。等價於[^0-9]。 \f 匹配一個換頁符。等價於\x0c和\cL。 \n 匹配一個換行符。等價於\x0a和\cJ。 \r 匹配一個Enter符。等價於\x0d和\cM。 \s 匹配任何空白字元,包括空格、製表符、換頁符等等。等價於[\f\n\r\t\v]。 \S 匹配任何非空白字元。等價於[^\f\n\r\t\v]。 \t 匹配一個製表符。等價於\x09和\cI。 \v 匹配一個垂直製表符。等價於\x0b和\cK。 \w 匹配包括底線的任何單詞字元。等價於「[A-Za-z0-9_]」。 \W 匹配任何非單詞字元。等價於「[^A-Za-z0-9_]」。 \xn 匹配n,其中n為十六進制轉義值。十六進制轉義值必須為確定的兩個數位長。例如,「\x41」匹配「A」。「\x041」則等價於「\x04&1」。正則運算式中可以使用ASCII編碼。. \num 匹配num,其中num是一個正整數。對所獲取的匹配的參照。例如,「(.)\1」匹配兩個連續的相同字元。 \n 標識一個八進制轉義值或一個向後參照。如果\n之前至少n個獲取的子運算式,則n為向後參照。否則,如果n為八進制數位(0-7),則n為一個八進制轉義值。 \nm 標識一個八進制轉義值或一個向後參照。如果\nm之前至少有nm個獲得子運算式,則nm為向後參照。如果\nm之前至少有n個獲取,則n為一個後跟文字m的向後參照。如果前面的條件都不滿足,若n和m均為八進制數位(0-7),則\nm將匹配八進制轉義值nm。 \nml 如果n為八進制數位(0-3),且m和l均為八進制數位(0-7),則匹配八進制轉義值nml。 \un 匹配n,其中n是一個用四個十六進制數位表示的Unicode字元。例如,\u00A9匹配版權符號(©)。 資料來源 另外,Regex 類別在建構式中可以指定參數: ...

August 16, 2011 · 1 分鐘 · 177 字

Image Porcessing Using TPL Contrast Enhancement

影像處理是很注重效率與正確性的,因此普遍都會建議使用平行計算,或是使用指標的方式來增加效能。在 .NET Framework 下使用 C# 這兩種方式都可以使用,只是若用 System.Threading.Task 類別下的方法對於未來的擴展以及程式碼的可閱讀性會有較佳的表現,畢竟M$提供這麼好的工具不用可惜,再說也不是用 C/C++ 來開發…(不過他們也有平行處理的類別庫可以用)。 底下的範例程式是影像強化中的對比強化,輸入的參數為欲處理的影像以及強化的程度。至於那個強化程度的計算與轉換可以參考其他網路上面的作法。 會用到的命名空間: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices;// 使用Marshal類別來配置Unmanaged記憶體。 using System.Threading.Tasks;// TPL //處理邏輯: public Bitmap Contraster(Bitmap bmp, Int32 level) { Double dlevel = Math.Sqrt((level % 10) + 1); Bitmap newbmp = new Bitmap(bmp.Width, bmp.Height); // orignal bitmap only for reading value, the clone one for writing new value. BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); BitmapData newData = newbmp.LockBits(new Rectangle(0, 0, newbmp.Width, newbmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); Int32 Width = bmp.Width; Int32 Height = bmp.Height; Parallel.For(0, Height, delegate(Int32 height) { Parallel.For(0, Width, delegate(Int32 width) { Int32 Offset = height * bmpData.Stride + width * (bmpData.Stride / bmpData.Width); Double Red = Marshal.ReadByte(bmpData.Scan0, Offset + 2); Double Green = Marshal.ReadByte(bmpData.Scan0, Offset + 1); Double Blue = Marshal.ReadByte(bmpData.Scan0, Offset); Red = ((Red / 255.0 - 0.5) * dlevel + 0.5) * 255; Red = Red > 255 ? 255 : Red; Red = Red < 0 ? 0 : Red; Green = ((Green / 255.0 - 0.5) * dlevel + 0.5) * 255; Green = Green > 255 ? 255 : Green; Green = Green < 0 ? 0 : Green; Blue = ((Blue / 255.0 - 0.5) * dlevel + 0.5) * 255; Blue = Blue > 255 ? 255 : Blue; Blue = Blue < 0 ? 0 : Blue; Marshal.WriteByte(newData.Scan0, Offset + 2, (Byte)Red); Marshal.WriteByte(newData.Scan0, Offset + 1, (Byte)Green); Marshal.WriteByte(newData.Scan0, Offset, (Byte)Blue); }); }); bmp.UnlockBits(bmpData); newbmp.UnlockBits(newData); return newbmp; }

July 21, 2011 · 2 分鐘 · 287 字

Image Processing - Binarization

影像處理是現在多媒體系統或影像監控系統是必備的功能與需求,而在進行諸多處理像是影像辨識等需求時,要對影像進行前置處理其中一樣就是二值化(二值化之前要對影像進行灰階的前置處理),把影像分成黑或白借此把影像的邊界與形狀取出,剔除相對不重要的資訊。而對於影像處理初出茅廬的我來說這就是一個最好的研究課題。這個領域的知識發展由來已久,而這篇的重點,Otsu演算法,更是這方面的研究論文幾乎必定會比較的方法。(ps.這篇編排先把實驗結果拉到前面實屬特例,這是為了要讓讀著馬上比較兩種演算法的不同,但話說在前頭這兩種方法沒有誰一定比較好) 實驗數據與分析: 原圖來源:Google Image 原圖: Modified Sauvola: Otsu: 相關研究: 參考文獻:吳乾彌, 許志豪, “影像二值化演算處理器之軟硬整合設計與實現”, 台科大電子工程, 98年六月 paper 這是我在處理影像二值化中最後決定要引用的方法的參考文獻,之前是用鼎鼎大名的Otsu來當作影像閥值的演算法,當初試過一些照片覺得還不錯,但缺點就是閥值的計算是以整張圖片來統計,因此若有區域或局部性的亮度與整體差距過大就會造成該區域整塊的細節消失,算是美中不足的地方。 Otsu 統計整張影像的灰階值,並以此計算值算出以哪個灰階值做劃分可以讓兩群的灰階值的變異數相加為最小,就口語來說就是找出用哪個數值化劃分可以讓兩個群體的群內差距為最小,亦即兩群的差異最大。 為了解決 Otsu 的痛處就要使用區域可適應性的閥值選擇方法,讓每個區域選擇自己的閥值,因此就找上了 Sauvola ,這篇,或這篇都有談到這個方法,當然這個方法我有稍稍改了一下,讓他對一個 Window 內的像素值賦予一個閥值,而非每個像素都有一個閥值,這樣可以減少不少計算量,換回不少計算時間。(ps.但是區域可適應性閥值選擇方法因為不像全域性計算一次閥值就好,因此會有比較多的計算時間) Ostu 的演算法概略如下: 若灰階影像其像速分布為 [1,2,3…x] (註:1x 表示灰階影像像素的數值,範圍從 0255), 可以計算出不同灰階值其分布機率值$P_i$: $$P_i = n_i / N —(1)$$ 其中 $n_i$ 代表灰階值為 i 的個數,$N = n_1+n_2+n_3+…+n_x$ 為所有灰階值個數的總和。而所有$P_i$的總和為1。 $$\sum_{i=1}^xP_i —(2)$$ 上述有提到二值化是為了找出閥值讓兩個群體之間的差異最大,群內的差異最小。這裡有兩個群體,G1與G2,G1的灰階值分布為[1,2,3…y],G2的灰階值分布為[y+1,y+2,y+3,…x],由此計算出個群集的機率分布W1,W2。 $$W1 = \sum_{i=1}^yP_i —(3)$$ $$W2 = \sum_{i=y+1}^xP_i —(4)$$ 接著計算個群集的平均值 M1,M2。 $$M1 = \sum_{i=1}^y i*P_i —(5)$$ $$M2 = \sum_{i=y+1}^x i*P_i —(6)$$ ...

June 10, 2011 · 2 分鐘 · 417 字

Build Double Assembly In VisualStudio

會興起這樣的念頭是因為我最近的工作中因為負責的子功能多達十項,每一次重新建置組件檔更新到專案中都要複製十次,實在十分繁瑣也因此起了個:「若能把所有專案建置的組件檔都集中在一處然後全部一起複製,接著貼上一個指定的資料夾中這樣就省事多了!」 上網找MSDN的說明找到這個:建置前事件/建置後事件命令列對話方塊 這個MSDN上面的連結主要是說明建置命令的聚集的意含是什麼,對於客製化自己的建置指令非常有用處。 依照MSDN的說明那我的需求要如何達成呢?很容易!在建置後事件命令對話方塊中輸入: 1 copy "$(TargetDir)*.*" "$(SolutionDir)Libary.DLLPool\" 這段指令的意思就是 複製 TargetDir 中的所有檔案 放置到"方案的目錄中的Libary.DLLPool"中(注意!這個路徑的最後面會加上一個反斜線) 這樣的話我的組件檔就會在Libary.DLLPool中也有相同的一份了! Step1.於專案中按下右鍵選擇屬性 Step2.按下在建置後事件頁籤中的"建置後事件命令列"的建置後進行編輯按鈕 Step3.按下聚集按鈕來針對自己的需求進行插入與編輯(ex. copy “$(TargetDir)*.dll” “$(SolutionDir)Libary.DLLPool" ) Step4.編輯完成按下重新建置,建置完成就會多一個組件副本在指定的資料夾中 ===========================2012-02-15 新增================================ 範例: 判斷目前建置的平台為Debug或是Release模式來區分組件複製的位置。 這邊要特別注意 batch 的寫法;要把判斷都寫完然後在用 goto 指令跳到實際執行的指令位置,執行完成後要用另外一個 goto 跳出函式之外。 1 2 3 4 5 6 7 8 9 if $(ConfigurationName)==DeBug goto debug if $(ConfigurationName)==Release goto release :debug copy $(TargetPath) $(SolutionDir)DLLPool\ goto finish :release copy $(TargetPath) $(SolutionDir)DLLPool_Release\ goto finish :finish

April 18, 2011 · 1 分鐘 · 63 字

Add New Agent in NS2

撰寫ns2模擬器的時候,常會需要自行撰寫 agent 來驗證自己的系統是否能達成預期的功效 但是要在ns2中使用本身沒有提供的 agent 是需要在ns2中作些修改才能使用的 因此第一步就是在ns2中新增一個 agent. Step 1. 把自訂的 agent 的資料夾放入「ns2 資料夾」中,名稱自訂(ZoneAgent) 「ns2資料夾」就等於下面這一串 1 /home/program/ns-allinone-2.34/ns-2.34 ↑ 就放在這個資料夾中 Step 2. 修改「ns2 資料夾」中的 Makefile.in 於 OBJ_CC= 該處加上 ZoneAgent/ZoneAgent.o Step 3. 修改「ns2 資料夾」中 /common/packet.h 加上新的封包型態 ns2.32 之前: 在 enum packet_t 加上 PT_HybridZone ps.PT_NTYPE 需放置最後面 class p_info 的 public:p_info(){}內加上 name_[PT_HybridZone]=“PT_HybridZone”; ps.PT_NTYPE 需放置最後面 ns2.32 之後: static packet_t PT_NTYPE 前加上 static const packet_t PT_HybridZone=?? ??是一個尚未用過得數字 但是請注意,這組數字一定要比 PT_NTYPE 的數字還小! 在class p_info 的 public:static void initName(){}內 name_[PT_NTYPE]= “undefined”; 前加上 ...

April 17, 2010 · 1 分鐘 · 108 字

Install-NS2-On-Linux

OS以Ubuntu8.04 、 NS2版本為ns2-allinone-2.33為例 最近開始有學習NS2的需要,便綜合網路上眾多好手的範例加上一點自己的話寫成以下的安裝說明文件,以備未來查看。 首先需要安裝的套件是: 1 sudo apt-get install build-essential autoconf automake libxmu-dev 到網路上抓下ns2-allinone-2.33.tar.gz: 1 wget http://nchc.dl.sourceforge.net/sourceforge/nsnam/ns-allinone-2.33.tar.gz 把檔案移到自己喜歡的目錄去(這邊移到root下的usr/local): 1 mv ns-allinone-2.33.tar.gz /usr/local 解壓縮順便改短一點的名稱: 1 2 3 cd /usr/local sudo tar -xvzf ns-allinone-2.33.tar.gz mv ns-allinone-2.33 ns2 安裝ns2: 1 2 cd ns2 ./install 安裝完會出現說明訊息要求我們設定好Path,編輯.bashrc即可: 1 vim ~/.bashrc 更改設定值(注意有出現"/usr/local/ns2"的路徑要改成自己的安裝路徑) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # LD_LIBRARY_PATH OTCL_LIB=/usr/local/ns2/otcl-1.13, NS2_LIB=/usr/local/ns2/lib X11_LIB=/usr/X11R6/lib USR_LOCAL_LIB=/usr/local/lib export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$OTCL_LIB:$NS2_LIB:$X11_LIB:$USR_LOCAL_LIB: # TCL_LIBRARY TCL_LIB=/usr/local/ns2/tcl8.4.18/library USR_LIB=/usr/lib export TCL_LIBRARY=$TCL_LIB:$USR_LIB # PATH XGRAPH=/usr/local/ns2/bin:/usr/local/ns2/tcl8.4.18/unix:/usr/local/ns2/tk8.4.18/unix NS=/usr/local/ns2/ns-2.33/ NAM=/usr/local/ns2/nam-1.13/ PATH=$PATH:$XGRAPH:$NS:$NAM 存檔完後,執行下列指令讓bashrc修改生效 1 source ~/.bashrc 驗證 直接在命令列輸入「ns」,如果看到提示符號從「$」變為「%」,就代表ns2安裝成功了,在%下輸入「exit」就可以跳出ns2。 ...

September 30, 2009 · 1 分鐘 · 104 字