မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Visual Studio C# ကို အသံုးျပဳၿပီး Image Processing အတြက္ pixel နဲ႔ Color တန္ဖိုးေတြကို လ်င္လ်င္ျမန္ျမန္ Detect လုပ္ႏိုင္မရ္႕ Lockbits( ) and and direct memory access in unsafe context Mehtod အေၾကာင္းေလးကို ေလ့လာၾကည့္ပါ့မရ္။ Processing အတြက္ကိုေတာ့ ေရွ႕သင္ခန္းစာမွာ ေလ့လာခဲ့ၿပီးျဖစ္တဲ့ Image Color Channel ခြဲထုတ္တာကိုပဲ သံုးထားပါတရ္။ ဒီေကာင္ကေတာ့ က်ေနာ္တို႔ ေလ့လာၾကည့္မရ္႕ Fast Image Porcessing Method ေတြက တတိယေျမာက္ Method ျဖစ္ပါတရ္။ ေရွ႕မွာ Method ႏွစ္ခု ေလ့လာခဲ့ၿပီးျဖစ္လို႔ ဒီသင္ခန္းစာက နားလည္ရ လြယ္သြားမရ္လို႔ ထင္ပါတရ္။ ေရွ႕မွာ ေလ့လာခဲ့ၿပီးျဖစ္တဲ့... (၁) Processing using GetPixel and SetPixel (၂) Processing using Bitmap.Lockbits and Marshal.Copy Method ႏွစ္ခုထက္စာရင္ Processing Time ကို ပိုမိုေလွ်ာ့ခ်ေပးႏိုင္ပါတရ္။ က်ေနာ့္ ေလ့က်င့္ခန္းေတြကေတာ့ စတင္ ေလ့လာသူမ်ားအတြက္သာ ရည္ရြယ္ပါတရ္။ ဒါေၾကာင့္ သိေနၿပီးသား သူမ်ားအေနနဲ႔ က်ေနာ္ရဲ႕ တင္ျပပံုမွားယြင္းတာမ်ား ရွိခဲ့ရင္ နားလည္ေပးၾကဖို႔နဲ႔ ျပန္လည္ေထာက္ျပေပးႏိုင္ဖို႔ ေမွ်ာ္လင့္ပါတရ္ဗ်။
ဒီမွာ Processing Method ကို မေလ့လာမွီ က်ေနာ္အေနနဲ႔ အနည္းငရ္ ေဆြးေႏြးေပးစရာေလး ရွိေနပါတရ္။ ဒါကေတာ့ ဒီေန႔ သင္ခန္းစာမွာ Memory Address ကိုတိုက္ရိုက္ Processing လုပ္မရ္႕ Pointer(*) လို Unsafe context ကို အသံုးျပဳမွာပါ။ Pointer မွာ အသံုးျပဳမည့္ (*)သေကၤတသည္ အေျမွာက္လို(4*5)လို သေကၤတမ်ိဳးနဲ႔ ေရာေထြးႏိုင္တာ ေၾကာင့္ Unsafe context ျဖစ္တရ္၊ ေနာက္ၿပီး Memory Address ကိုတိုက္ရိုက္ သြား Processing လုပ္မွာ ျဖစ္တဲ့အတြက္ Unsafe context ျဖစ္တရ္လို႔ ဆိုလိုပါတရ္။ ဒီလို Unsafe ျဖစ္တဲ့ သေကၤတေတြကို အသံုးျပဳႏိုင္ဖို႔ VS C# မွာ Unsafe ဆိုတဲ့ Keyword ကို ထည့္သြင္းေပးထားပါတရ္။ ဒါေပမဲ့ မိမိ Project မွာ Unsafe context ကို အသံုးျပဳပါ့မရ္ဆုိတာကိုေတာ့ Configuration အနည္းငယ္ ျပန္ေပးရပါတရ္။
အေပၚကပံု(2)မွာ ျမင္ေတြ႔ရတဲ့အတိုင္း မိမိ Project ရဲ႕ Properties ထဲကို ၀င္လိုက္ပါ(မိမိ Project => MenuBar (Project) => မိမိ Project Name Properties...)။ ၿပီးရင္ေတာ့ ေပၚလာတဲ့ Box ရဲ႕ Build TabBar ကိုေရြးခ်ယ္ၿပီး ၄င္းအတြင္းမွ Allow unsafe code ဆိုတဲ့ CheckBox ကို အမွန္ျခစ္ ေရြးခ်ယ္ေပးရပါ့မရ္ (က်ေနာ္ ပံုမွာ၀ိုင္းျပထားပါတရ္)။ ဒါဆိုရင္ေတာ့ က်ေနာ္တို႔ Project မွာ Unsafe Keyword ကို အသံုးျပဳႏုိင္ပါၿပီဗ်။
ဒီေန႔ ေလ့က်င့္ခန္းအတြက္ GUI နဲ႔ Tools ေတြရဲ႕ Properties ေတြကိုေတာ့ အရင္အတိုင္းပဲ ျပန္သံုးထားပါတရ္။ cbProcessingMethod မွာ Lockbits() and Unsafe context Method အတြက္ Item တိုးေပးဖို႔နဲ႔ Calling Method က Index No ကို ျပန္ေခၚယံုပါပဲဗ်ာ။ အခုမွ စတင္ေလ့လာမိသူမ်ားအေနနဲ႔ ေရွ႕က သင္ခန္းစာေတြကို ျပန္ဖတ္ေစခ်င္ပါတရ္။ ဒါကေတာ့ က်ေနာ္တို႔ Project မွာ အသံုးျပဳမရ္႕ Library File ေတြပါ။ ကဲ... ေအာက္မွာ Lockbits() and Unsafe Method ေလးကို ေလ့လာၾကည့္လိုက္ၾကပါအံုးဗ်ာ။ ရွင္းျပရမရ္ဆိုရင္ေတာ့... Coding စစခ်င္းမွာ Unsafe keyword ကို “{....}” နဲ႔ စထားပါတရ္။ Processing လုပ္မရ္႕ Coding ေတြထဲမွာ Unsafe context ေတြကို သံုးပါ့မရ္ဆိုတဲ့ သေဘာပါ။
1. Load Image to Bitmap
Bitmap cloneImg = new Bitmap(inputImg);
ပထမအဆင့္မွာ Image တစ္ခုကို Bitmap ထဲသို႔ ဆြဲတင္တာျဖစ္ပါတရ္။

2. Lock Bitmap in Rectangle by BitmapData
Rectangle ret = new Rectangle(0, 0, cloneImg.Width, cloneImg.Height);
BitmapData bmpData = cloneImg.LockBits(ret, ImageLockMode.ReadWrite, cloneImg.PixelFormat);
ဒီအဆင့္မွာေတာ့ LockBits() Method ကိုအသံုးျပဳၿပီး Bitmap Image တစ္ခုကို Lock ခ်တာျဖစ္ပါတရ္။ စစခ်င္း inputImg ရဲ႕ Width and Height အတိုင္း Rectangle တစ္ခုကို တည္ေဆာက္ပါတရ္။ ေနာက္တစ္ဆင့္မွာေတာ့ ၄င္း Rectangle ထဲသုိ႔ Image ကို Lock ခ်ပါတရ္။ ဒီမွာ LockBits() Method ကို Parameter(၃)ခုသံုးထားပါတရ္။ ပထမ Parameter က Destination Rectangle ျဖစ္ပါတရ္။ ဒုတိယ parameter ကေတာ့ LockMode ပါ။ ဒီမွာ မိတ္ေဆြတို႔အတြက္ ေရြးခ်ယ္စရာ properties ေတြ အမ်ားႀကီးရွိပါတရ္။ ဥပမာအေနနဲ႔ read အတြက္ bitmap တစ္ခု၊ write အတြက္ bitmap တစ္ခုအေနနဲ႔ ခြဲသံုးႏိုင္ပါတရ္။ ဒီေလ့က်င့္ခန္းမွာ က်ေနာ္ကေတာ့ Bitmap တစ္ခုတည္းကိုပဲ Read, Write လုပ္ၿပီး Return ျပန္ေပးမွာျဖစ္လို႔ LockMode ကို ReadWrite သံုးထားတာပါ။ ေနာက္ဆံုး တတိယ parameter ကေတာ့ pixel format သတ္မွတ္ေပးတာပါ။ တနည္းအားျဖင့္ Image တစ္ခုကို 8bpp, 16bpp, 24bpp, 32pbb,... အစရွိတဲ့ Bits Per Pixel သတ္မွတ္တာပါ။ PixelFormat.Format24bppRgb အခုလို ပုံစံမ်ိဳးနဲ႔လည္း မိမိႏွစ္သတ္ရာ Format ကုိ ေရြးခ်ယ္ၿပီး lock လုပ္ႏိုင္ပါတရ္။ ဘာမွ မေရြးခ်ယ္ပဲ inputImg ရဲ႕ Pixel Format အတိုင္း lock လုပ္ခ်င္တရ္ဆိုရင္ေတာ့ inputImg.PixelFormat ဆိုၿပီး ေပးႏိုင္ပါတရ္။

3. Calculate PixelFormat(BytesPerPixel) and TotleByte
int bytePP = Bitmap.GetPixelFormatSize(cloneImg.PixelFormat) /8;
ဒါကေတာ့ pixel တစ္ခုစီတိုင္းမွာ အသံုးျပဳမရ္ Byte အေရအတြက္ကို တြက္ယူပါတရ္။ 8 နဲ႔စားထားတာကေတာ့ 8bits = 1byte ျဖစ္လို႔ပါ။ ဥပမာ- က်ေနာ္တို႔က 24bppRgb PixelFormat ကိုေရြးမိရင္ 24/8=3 ျဖစ္တဲ့အတြက္ 1 pixel မွာ 3 byte ေနရာယူ အလုပ္ လုပ္ေနမွာပါ။ Blue = 1byte(8bits), Green = 1byte(8bits) and Red = 1byte(8bits) ကိုယ္စီနဲ႔ အလုပ္ လုပ္မွာပါ။ ဒါမွမဟုတ္ 32bppArgb PixelFormat ကုိ ေရြးမိရင္ေတာ့ 32/8=4 ျဖစ္တဲ့အတြက္ pixel တစ္ကြက္မွာ 4 byte ေနရာယူ ေနမွာပါ။ ဒါဆိုရင္ေတာ့ Alpha = 1byte(8bits), Blue = 1byte(8bits), Green = 1byte(8bits) and Red = 1byte(8bits) ကိုယ္စီနဲ႔ အလုပ္ လုပ္သြားမွာျဖစ္ပါတရ္။

4. Store Scaning Data in Pointer Direct Memory
 byte* ptrFirstPixel = (byte*)bmpData.Scan0; 
ဒီေကာင္ကေတာ့ lock ခ်ထားတဲ့ Image ႀကီးတစ္ခုလံုးကို Scan ဖတ္လိုက္တာပါ။ Scan0 ရဲ႕ သေဘာကေတာ့ Target Image ရဲ႕ left-top corner ကေနၿပီး right-buttom အထိ တစ္ဆင့္ခ်င္းစီ Scan ဖတ္သြားမွာပါ။ ေရွ႕သင္ခန္းစာမွာတုန္းက byte[]array ကိုအသံုးျပဳၿပီး 2-Dimension ကို 1-Dimension ေျပာင္းယူပါတရ္။ ဒီသင္ခန္းစာမွာေတာ့ byte* Pointer ကို အသံုးျပဳၿပီးေတာ့ Memory Address ထဲကို တိုက္ရိုက္သြားသိမ္းပါတရ္ဗ်။ ေနာက္တစ္ဆင့္မွာေတာ့ မိမိလိုခ်င္တဲ့ Memory Address ကုိ တိုက္ရိုက္ညႊန္းၿပီး Processing လုပ္ယူမွာ ျဖစ္ပါတရ္ဗ်ာ။

5. Pre-Processing Get Pixel Processing လုပ္သြားတဲ့ ပံုစံကေတာ့ Stride by Stride အလုပ္ လုပ္သြားတာပါ။ ဒီမွာ For Loop ႏွစ္ခုကို သံုးထား ပါတရ္။ ပထမ loop က Stride (or) Row ကို မွတ္သားခ်င္တဲ့အတြက္ပါ။ ဒီမွာ ptrFirstPixel + (y * bmpData.Stride)လုပ္ၿပီး row ဖတ္ထားတာကေတာ့ Stride တစ္ေၾကာင္း Processing လုပ္ၿပီးတိုင္း ေနာက္တစ္ၾကာင္းကို ကူးႏိုင္ရန္ Memory Address ေနရာကို ညႊန္းယူတာပါ။ Pointer နဲ႔ Memory Address ေတြျဖစ္လို႔ Trace လိုက္ၿပီး ရွင္းဖိ႔ုမလြယ္ပါဘူး။ ပံုေသနည္းလို႔ မွန္ထားရင္လည္း အဆင္ေျပပါတရ္။ အၾကမ္းဖ်ဥ္း ေျပာရရင္ေတာ့ Stride တစ္ေၾကာင္းလံုးမွာရွိတဲ့ Pixel and Color Value ေတြကို Address တစ္ခုစီမွာ သိမ္းပါတရ္။ ဥပမာ- Stride 5 ေၾကာင္းရွိရင္ Address ငါးခုရွိတရ္ဆိုတဲ့ သေဘာပါ။ Stride ဆိုတာကေတာ့ row တစ္ row စီမွာရွိတဲ့ byte အေရအတြက္ပါ။ Stride = bytesPerPixel * inputImg.Height; လို႔ ေရွ႕သင္ခန္းစာမွာ ရွင္းျပခဲ့ၿပီးပါၿပီ။ ဒုတိယ loop ကေတာ့ Stride ရဲ႕ byte Index ေတြထဲမွာရွိတဲ့ BGRA တန္ဖိုးေတြကို ဆြဲထုတ္ႏိုင္ေအာင္ပါ။ row ဆိုတာကေတာ့ Memory Address ပါ။ ဒါေၾကာင့္ loop တခါပါတ္တိုင္း Memory Address ထဲကို တိုက္ရိုက္သြားဖတ္ၿပီး BGRA တန္ဖိုးေတြကို ဆြဲထုတ္ယူႏိုင္တာပဲ ျဖစ္ပါတရ္။ x = x + bytePP ေပးထားတာကေတာ့ BGRA ေလးလံုး တစ္ Group ျဖစ္တဲ့အတြက္ Group ခုန္သြားႏိုင္ေအာင္ပါ။ row[x]; row[x + 1]; row[x + 2]; ဒီေကာင္ေတြကေတာ့ Group တစ္ခါခုန္တိုင္း BGRA တန္ဖိုးေတြကို asign လုပ္ႏိုင္ေအာင္ပါ။ ဒီေလ့က်င့္ခန္းမွာေတာ့ Alpha တန္ဖိုးကို က်ေနာ္ ခ်န္လွန္ထားခဲ့ပါတရ္။ စားေရးၿပီး ရွင္းျပရ တာေတာ့ အနည္းငယ္ ခဲယဥ္းပါတရ္ဗ်ာ။ မိတ္ေဆြတို႔ Trace လုိက္ၾကည့္ရင္ေတာ့ Coding Flow ရဲ႕ အလုပ္ လုပ္သြားပံုကို ေသခ်ာနားလည္သြားမွာပါ။

6. Processing Color Channel ဒီေကာင္ကေတာ့ Color Channel အတြက္ Processing လုပ္ထားတာပါ။ ရိုးရိုးရွင္းရွင္းပါပဲ။ မိမိ ေျပာင္းလဲ ခ်င္ေသာ Color Channel ကိုခ်န္ၿပီး က်န္ Channel ေတြကို သုည(0) assign လုပ္လိုက္တာပါ။ နားလည္မရ္လို႔ ေမွ်ာ္လင့္ပါတရ္ဗ်။

7. Unlock bitmap
 cloneImg.UnlockBits(bmpData); 
ဒါကေတာ့ Processing လုပ္ငန္းစဥ္ အားလံုးၿပီးသြားၿပီျဖစ္တဲ့အတြက္ Lock ခ်ထားတဲ့ Image ကို unLock ျပန္လုပ္တာပါ။ 8. Return Bitmap Image
 return cloneImg; 
ေနာက္ဆံုးအဆင့္ကေတာ့ Processing လုပ္ၿပီးသား Image တစ္ခုကို Method သို႔ ျပန္လည္အသံုးျပဳ/ေဖာ္ျပႏိုင္ရန္ Return ျပန္ေပးထားတာ ျဖစ္ပါတရ္။ Coding အလုပ္ လုပ္သြားပံုကိုေတာ့ က်ေနာ္ တတ္ႏိုင္သမွ် အေသးစိတ္ရွင္းထားပါတရ္။ က်ေနာ္ တင္ျပပံု မွားယြင္းခဲ့တာ ရွိခဲ့ရင္လည္း နားလည္ေပးၾကဖို႔ ေတာင္းပန္ပါရေစဗ်ာ။
အခု ဆက္လက္ၿပီး Calling Method ကို ေလ့လာၾကည့္ၾကပါ့မရ္။ btnProcessing Button ကို D_Click ေပးၿပီး ေအာက္ပါ Coding ကို ေရးယူလိုက္ပါဗ်ာ။ Coding မွာ ခက္ခဲတာ ဘာမွမပါ ပါဘူးဗ်ာ။ Stopwatch သံုးထားတာကေတာ့ Processing Time ကို မွတ္ထားေစခ်င္လို႔ပါ။ cbProcessingMethod မွ ေရြးခ်ယ္လိုက္ေသာ Index အေပၚမူတည္ၿပီး Processing Method ေတြကို ေခၚယူမွာ ျဖစ္ပါတရ္။ ဒီေလာက္ဆိုရင္ေတာ့ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမတို႔အေနနဲ႔ Fast Image Processing အတြက္ Lockbits() and Direct Memory Accress in Unsafe context Method ကို အသံုးျပဳၿပီး Processing လုပ္တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ မေန႔က က်ေနာ္တို႔ ေလ့လာခဲ့တဲ့ Lockbits( ) and Marshal.Copy( ) Method သင္ခန္းစာေလးကို နည္းနည္းေလး ျပဳျပင္ၿပီး ထပ္ေလ့လာၾကည့္ပါ့မရ္။ Processing အတြက္ကိုေတာ့ ေရွ႕သင္ခန္းစာမွာ ေလ့လာခဲ့ၿပီးျဖစ္တဲ့ Image Color Channel ခြဲထုတ္တာကိုပဲ သံုးထားပါတရ္။ Processing Time ကေတာ့ ေရွ႕ Version ထက္စာရင္ အနည္းငရ္ ေလွ်ာ့ခ်ေပးႏိုင္ပါတရ္။ ေရွ႕သင္ခန္းစာမွာတုန္းက Image ကို Scan ဖတ္လို႔ရတဲ့ Data ေတြကို byte[ ]Array ထဲထည့္တာဟာ Processing လုပ္ရလြယ္ေအာင္ 2-Dimension ကေန 1-Dimension သို႔ ေျပာင္းလဲတာပါလို႔ ေျပာခဲ့ပါတရ္။ ဒါေပမဲ့ Color Channel ေျပာင္းလဲယူတဲ့ ေနရာမွာေတာ့ 2-Dimension ပံု Stride by Stride အလုပ္လုပ္ၿပီး row ကိုဖမ္းကာ Processing လုပ္တဲ့ ပံုစံနဲ႔ရွင္းခဲ့ပါတရ္။ ဒါကလည္း ပံုနဲ႔အတူ ရွင္းျပထားတဲ့ LockBits( )Method ရဲ႕ 1 Pixel မွာ ARGB အလုပ္လုပ္သြားပံုကို ထင္သာျမင္ေအာင္ ရွိေအာင္ပါ။ ဒါေၾကာင့္ For Loop တစ္ခုအတြက္ Processing Time ပိုၾကာသြားေစပါတရ္။ ဒီေန႔သင္ခန္းစာမွာေတာ့ ဒါေလးကို ျပဳျပင္ၿပီး ထက္ေလ့လာၾကည့္မွာျဖစ္ပါတရ္။ အလုပ္လုပ္ပံု ေက်ာရိုးကေတာ့ အတူတူပါပဲဗ်ာ။
က်ေနာ္ GUI ေတြနဲ႔ Tool properties ေတြအေၾကာင္း မရွင္းျပေတာ့ပါဘူး။ ေရွ႕သင္ခန္းစာအတုိင္းျဖစ္လို႔ ျပန္လည္ ေလ့လာႏိုင္ပါတရ္ဗ်။ Processing Method နဲ႔ Method Calling ပံုစံကိုပဲ ရွင္းျပပါေတာ့မရ္။ ဒါကေတာ့ က်ေနာ္တုိ႔ Project မွာ အသံုးျပဳမရ္႕ Library File ေတြပါ။ ေအာက္က ကုဒ္ေလးကေတာ့ Lockbits( ) and Marshal.Copy( ) Method ကိုအသံုးျပဳၿပီး Color Channel ကို ေျပာင္းလဲယူတဲ့ ကုဒ္ေလးပါ။ ရွင္းျပရမရ္ဆိုရင္ေတာ့...
1. Load Image to Bitmap
Bitmap cloneImg = new Bitmap(inputImg);
ပထမအဆင့္မွာ Image တစ္ခုကို Bitmap ထဲသို႔ ဆြဲတင္တာျဖစ္ပါတရ္။

2. Lock Bitmap in Rectangle by BitmapData
Rectangle ret = new Rectangle(0, 0, cloneImg.Width, cloneImg.Height);
BitmapData bmpData = cloneImg.LockBits(ret, ImageLockMode.ReadWrite, cloneImg.PixelFormat);
ဒီအဆင့္မွာေတာ့ LockBits() Method ကိုအသံုးျပဳၿပီး Bitmap Image တစ္ခုကို Lock ခ်တာျဖစ္ပါတရ္။ စစခ်င္း inputImg ရဲ႕ Width and Height အတိုင္း Rectangle တစ္ခုကို တည္ေဆာက္ပါတရ္။ ေနာက္တစ္ဆင့္မွာေတာ့ ၄င္း Rectangle ထဲသုိ႔ Image ကို Lock ခ်ပါတရ္။ ဒီမွာ LockBits() Method ကို Parameter(၃)ခုသံုးထားပါတရ္။ ပထမ Parameter က Destination Rectangle ျဖစ္ပါတရ္။ ဒုတိယ parameter ကေတာ့ LockMode ပါ။ ဒီမွာ မိတ္ေဆြတို႔အတြက္ ေရြးခ်ယ္စရာ properties ေတြ အမ်ားႀကီးရွိပါတရ္။ ဥပမာအေနနဲ႔ read အတြက္ bitmap တစ္ခု၊ write အတြက္ bitmap တစ္ခုအေနနဲ႔ ခြဲသံုးႏိုင္ပါတရ္။ ဒီေလ့က်င့္ခန္းမွာ က်ေနာ္ကေတာ့ Bitmap တစ္ခုတည္းကိုပဲ Read, Write လုပ္ၿပီး Return ျပန္ေပးမွာျဖစ္လို႔ LockMode ကို ReadWrite သံုးထားတာပါ။ ေနာက္ဆံုး တတိယ parameter ကေတာ့ pixel format သတ္မွတ္ေပးတာပါ။ တနည္းအားျဖင့္ Image တစ္ခုကို 8bpp, 16bpp, 24bpp, 32pbb,... အစရွိတဲ့ Bits Per Pixel သတ္မွတ္တာပါ။ PixelFormat.Format24bppRgb အခုလို ပုံစံမ်ိဳးနဲ႔လည္း မိမိႏွစ္သတ္ရာ Format ကုိ ေရြးခ်ယ္ၿပီး lock လုပ္ႏိုင္ပါတရ္။ ဘာမွ မေရြးခ်ယ္ပဲ inputImg ရဲ႕ Pixel Format အတိုင္း lock လုပ္ခ်င္တရ္ဆိုရင္ေတာ့ inputImg.PixelFormat ဆိုၿပီး ေပးႏိုင္ပါတရ္။

3. Calculate PixelFormat(BytesPerPixel) and TotleByte
int bytePP = Bitmap.GetPixelFormatSize(cloneImg.PixelFormat) /8;
ဒီမွာေတာ့ pixel တစ္ခုစီတိုင္းမွာ အသံုးျပဳမရ္ Byte အေရအတြက္ကို တြက္ယူပါတရ္။ 8 နဲ႔စားထားတာကေတာ့ 8bits = 1byte ျဖစ္လို႔ပါ။ ဥပမာ- က်ေနာ္တို႔က 24bppRgb PixelFormat ကိုေရြးမိရင္ 24/8=3 ျဖစ္တဲ့အတြက္ 1 pixel မွာ 3 byte ေနရာယူ အလုပ္ လုပ္ေနမွာပါ။ Blue = 1byte(8bits), Green = 1byte(8bits) and Red = 1byte(8bits) ကိုယ္စီနဲ႔ အလုပ္ လုပ္မွာပါ။ ဒါမွမဟုတ္ 32bppArgb PixelFormat ကုိ ေရြးမိရင္ေတာ့ 32/8=4 ျဖစ္တဲ့အတြက္ pixel တစ္ကြက္မွာ 4 byte ေနရာယူ ေနမွာပါ။ ဒါဆိုရင္ေတာ့ Alpha = 1byte(8bits), Blue = 1byte(8bits), Green = 1byte(8bits) and Red = 1byte(8bits) ကိုယ္စီနဲ႔ အလုပ္ လုပ္သြားမွာ ျဖစ္ပါတရ္။ နားလည္မရ္လို႔ ထင္ပါတရ္ဗ်။

4. Calculate totalBytes and Create byte Array for Image's Pixel
int totleByte = bmpData.Stride * bmpData.Height;
byte[] pixels = new byte[totleByte];
ပထမကေတာ့ Processing လုပ္မရ္႕ Image တစ္ခုလံုးမွာရွိတဲ့ totalByte ကိုရွာတာပါ။ ဒီမွာေတာ့ Stride ဆိုတာကို သိဖို႔လိုလာပါၿပီ။ အေပၚပံုမွာ ျမင္ရတဲ့အတိုင္း row တစ္ row လံုးမွာ ရွိတဲ့ byte အရည္အတြက္ဟာ Stride ပါပဲ။ Stride = bytePP * inputImg.Width; ပါ။ bytePP က Step:3 မွာရွာခဲ့ၿပီးပါၿပီ 4 ဆိုပါစို႔။ ဥပမာ- က်ေနာ္တို႔ Processing လုပ္ေနတဲ့ Image က 5x5 resolution ပါ။ ဒီေတာ့ width = 5; height = 5; ေပါ။ ဒါဆို Stride = 4*5 = 20bytes ရရွိမွာပါ။ ဒါဆို Stride တစ္ခု (သို႔မဟုတ္) row တစ္ေၾကာင္းဟာ 20bytes ရွိေနတရ္လို႔ သိရပါ့မရ္။ ဒီေတာ့ Image တစ္ခုလံုးမွာ ရွိေနတဲ့ totalByte ကို သိဖို႔ မခက္ခဲေတာ့ပါဘူး။ ၄င္း Stride နဲ႔ Image ရဲ႕ Heigh နဲ႔ကို ေျမွာက္လိုက္ယံုပါပဲ။ ဒါဆို totalByte = 20 * 5 = 100bytes ပါ။ ဒုတိယကေတာ့ အေပၚမွာ က်ေနာ္တို႔ Processing လုပ္ဖို႔အတြက္ Image တစ္ခုလံုးမွာရွိတဲ့ Byte အေရအတြက္ကို ရၿပီဆိုရင္ေတာ့ ဒီအဆင့္မွာ ၄င္း Length အတိုင္း byte[ ] Array တစ္ခုကို ေဆာက္ယူတာျဖစ္ပါတရ္။ ေနာက္ပိုင္းမွာ ဒီ byte[] array ထဲကို Image ရဲ႕ Byte ေတြကို တန္းစီထည့္ပါ့မရ္။

5. Store Scaning Data in Pointer Memory
IntPtr ptrFirstPixel = bmpData.Scan0;
ဒီေကာင္ကေတာ့ lock ခ်ထားတဲ့ Image ႀကီးတစ္ခုလံုးကို Scan ဖတ္လိုက္တာပါ။ Scan0 ရဲ႕ သေဘာကေတာ့ Target Image ရဲ႕ left-top corner ကေနၿပီး right-buttom အထိ တစ္ဆင့္ခ်င္းစီ Scan ဖတ္သြားမွာပါ။ Direct Memory Access သေဘာ အလုပ္ လုပ္မွာ ျဖစ္တဲ့အတြက္ Memory Address ကိုဖတ္ကာ Int Pointer ထဲကို ေခတၱသိမ္းထားပါတရ္။ ေနာက္တစ္ဆင့္မွာေတာ့ Scan ဖတ္လို႔ရတဲ့ Data ေတြကို Processing လုပ္ဖို႔အတြက္ Step:4 မွာ ဖန္တီးခဲ့တဲ့ byte[] array ထဲကုိ ကူးထည့္ပါ့မရ္။

6. Copy Scaning Data to byte[ ] pixels
//Marshal.Copy(IntPtr source, byte[] destination, int startIndex, int length);
Marshal.Copy(ptrFirstPixel, pixels, 0, pixels.Length);
ဒီအဆင့္မွာေတာ့ Marshal.Copy()Method ကို အသံုးျပဳၿပီး Pointer ထဲက Scan ဖတ္ထားတဲ့ Data ေတြကို Processing လုပ္ဖို႔အတြက္ byte[] array ထဲကို ထည့္ပါတရ္။ Parameter ေလးခုကို Description ေတြနဲ႔အတူ က်ေနာ္ ျပထားပါတရ္။ ပထမ parameter က source ျဖစ္လို႔ Scan ဖတ္ထားတဲ့ Pointer Data ကိုေရြးေပးထားပါတရ္။ ဒုတိယ parameter က destination ပါ။ ဒါေၾကာင့္ သြားသိမ္းခ်င္တဲ့ byte[] array ကို ညႊန္းေပးထားပါတရ္။ တတိယ parameter ကေတာ့ byte[]array ထဲကို ဘရ္ Index က စထည့္မွာလဲဆိုတဲ့ Index ရဲ႕ ညႊန္းခ်က္ပါ။ က်ေနာ္တို႔ကေတာ့ Index 0 to Length အထိ ထည့္မွာျဖစ္လို႔ စမွတ္ကို 0 လို႔ ေပးထားပါတရ္။ ေနာက္ဆံုး parameter ကေတာ့ byte[]array ရဲ႕ length ပါ။ byte[]array ထဲကို Data ေတြသိမ္းၿပီး Processing လုပ္တာကေတာ့ ယခင္ 2-Dimension အလုပ္လုပ္ေနရာကေန ပိုမိုလြယ္ကူသြားေအာင္ 1-Dimension ေျပာင္းလိုက္တာပါ။ ဒီေတာ့ Scan ဖတ္ထားတဲ့ Data ေတြကို
byte[] pixels = byte[100];
pixels = {0, 1, 2,.......,100};
ပံုစံမ်ိဳး အလုပ္လုပ္သြားမွာပါ။ နားလည္မရ္လို႔ ထင္ပါတရ္ဗ်ာ။

7. Pre-Processing Get Pixel
for (int x = 0; x < pixels.Length; x = x + bytePP)
{
   //Drag and Mark old RGB Color
       int oldBlue = pixels[x];
          int oldGreen = pixels[x + 1];
   int oldRed = pixels[x + 2];

   // Processing Color Channel 
   // ---------             
}
Processing လုပ္သြားတဲ့ ပံုစံကေတာ့ က်ေနာ္တို႔ Data ေတြအားလံုး Store လုပ္ထားတဲ့ byte[ ]array ရဲ႕ first Index ကစၿပီး Last Index အထိ Loop ပါတ္ထားပါတရ္။ Loop တစ္ခါပါတ္တိုင္း BGRA ေလးလံုး တစ္ Group ၿပီး တစ္ Group ခုန္ကာ Color Detection လုပ္သြားတာပါ။ RGBA Color ေတြကို ဖမ္းသြားတာကလည္း ေရွ႕သင္ခန္းစာထက္စာရင္ ပိုလြယ္ကူသြားမရ္လို႔ ထင္ပါတရ္။ byte[]array ရဲ႕ Index ကိုပဲ Loop ဆံုးသည့္တိုင္ေအာင္ တိုက္ရိုက္ဆြဲတင္လိုက္တာပါ။ ဒီေလ့က်င့္ခန္းမွာေတာ့ Alpha တန္ဖိုးကို က်ေနာ္ ခ်န္လွန္ထားခဲ့ပါတရ္။ မိတ္ေဆြတို႔ Trace လုိက္ၾကည့္ရင္ေတာ့ Coding Flow ရဲ႕ အလုပ္လုပ္သြားပံုကို ေသခ်ာနားလည္ သြားမွာပါ။

8. Processing Color Channel
// Processing Color Channel 
switch (channel) {
     case "Red":
         pixels[x] = 0;
         pixels[x + 1] = 0;
         pixels[x + 2] = (byte)oldRed;
         break;
     case "Green":
         pixels[x] = 0;
         pixels[x + 1] = (byte)oldGreen;
         pixels[x + 2] = 0;
         break;
     case "Blue":
         pixels[x] = (byte)oldBlue;
         pixels[x + 1] = 0;
         pixels[x + 2] = 0;
         break;
     default:
         break;
}
ဒီေကာင္ကေတာ့ Color Channel အတြက္ Processing လုပ္ထားတာပါ။ ရိုးရိုးရွင္းရွင္းပါပဲ။ မိမိ ေျပာင္းလဲခ်င္ေသာ Color Channel ကိုခ်န္ၿပီး က်န္ Channel ေတြကို သုည(0) assign လုပ္လိုက္တာပါ။ နားလည္မရ္လို႔ ေမွ်ာ္လင့္ပါတရ္ဗ်။

9. Re-Enter modified bytes to Image's Pixels
//Marshal.Copy(byte[] source, int startIndex, IntPtr destination, int length);
Marshal.Copy(pixels, 0, ptrFirstPixel, pixels.Length);
ဒီေကာင္ကေတာ့ processing လုပ္ၿပီးသား Data ကို မူလေနရာျဖစ္တဲ့ Pointer ထဲကို ျပန္လည္ ထည့္သြင္းဖို႔အတြက္ သံုးထားတာပါ။ Description ေတြ အေပၚမွာ ေဖာ္ျပထားတာျဖစ္တဲ့အတြက္ ၾကည့္တာနဲ႔ နားလည္ႏိုင္မွာပါ။
10. Unlock bitmap
cloneImg.UnlockBits(bmpData);
ဒါကေတာ့ Processing လုပ္ငန္းစဥ္ အားလံုးၿပီးသြားၿပီျဖစ္တဲ့အတြက္ Lock ခ်ထားတဲ့ Image ကို unLock ျပန္လုပ္တာပါ။

11. Return Bitmap Image
return cloneImg;
ေနာက္ဆံုးအဆင့္ကေတာ့ Processing လုပ္ၿပီးသား Image တစ္ခုကို Method သို႔ ျပန္လည္အသံုးျပဳ/ေဖာ္ျပႏိုင္ရန္ Return ျပန္ေပးထားတာျဖစ္ပါတရ္။ Coding အလုပ္ လုပ္သြားပံုကိုေတာ့ က်ေနာ္ တတ္ႏိုင္သမွ် အေသးစိတ္ရွင္းထားပါတရ္။ က်ေနာ္ တင္ျပပံု မွားယြင္းခဲ့တာ ရွိခဲ့ရင္လည္း နားလည္ေပးၾကဖို႔ ေတာင္းပန္ပါရေစဗ်ာ။
အခု ဆက္လက္ၿပီး Calling Method ကို ေလ့လာၾကည့္ၾကပါ့မရ္။ btnProcessing Button ကို D_Click ေပးၿပီး ေအာက္ပါ Coding ကို ေရးယူလိုက္ပါဗ်ာ။ Coding မွာ ခက္ခဲတာ ဘာမွမပါ ပါဘူးဗ်ာ။ Stopwatch သံုးထားတာကေတာ့ Processing Time ကို မွတ္ထားေစခ်င္လို႔ပါ။ cbProcessingMethod မွ ေရြးခ်ယ္လိုက္ေသာ Index အေပၚမူတည္ၿပီး Processing Method ေတြကို ေခၚယူမွာ ျဖစ္ပါတရ္။ ဒီေလာက္ဆိုရင္ေတာ့ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမတို႔အေနနဲ႔ Fast Image Processing အတြက္ Lockbits() and Marshal.Copy ကို အသံုးျပဳၿပီး Processing လုပ္တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ က်န္ရွိေနေသးတဲ့ Fast Image Processing Method ႏွစ္ခုကိုေတာ့ ေနာက္ သင္ခန္းစာမွာ ဆက္လက္ ေဖာ္ျပေပးသြားပါ့မရ္။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Fast Image Processing using Lockbits() and Marshal.Copy() Method

Posted by ေတဇာလင္း Sunday, 29 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Visual Studio C# ကို အသံုးျပဳၿပီး Image Processing အတြက္ pixel နဲ႔ Color တန္ဖိုးေတြကို လ်င္လ်င္ျမန္ျမန္ Detect လုပ္ႏိုင္မရ္႕ Lockbits( ) and Marshal.Copy( ) Mehtod အေၾကာင္းေလးကို ေလ့လာၾကည့္ပါ့မရ္။ Processing အတြက္ကိုေတာ့ ေရွ႕သင္ခန္းစာမွာ ေလ့လာခဲ့ၿပီးျဖစ္တဲ့ Image Color Channel ခြဲထုတ္တာကိုပဲ သံုးထားပါတရ္။ ေရွ႕သင္ခန္းစာမွာတုန္းကေတာ့ Detection and Procesing အတြက္ GetPixel( ) နဲ႔ SetPixel( ) Method ကို သံုးျပထားပါတရ္။ ဒီ Method ႏွစ္ခုက Image Processing မွာ Pixel and Color Detection အတြက္ အရိုးရွင္းဆံုးနဲ႔ အေျခခံအက်ဆံုး Method ေတြပါ။ Processing Time လည္း အရမ္းေႏွးပါတရ္။ ဒါေၾကာင့္ Image Processing ကုိေလ့လာတဲ့အခါမွာ အသံုးျပဳဖို႔ အဆင္မေျပလွပါဘူး။ ဥပမာအေနနဲ႔ ေရွ႕သင္ခန္းစာ Color Channel - 1024x768 Resolution ရွိတဲ့ Image တစ္ခုကို Channel ခြဲတဲ့အခါမွာ Processing Time 2045ms (2 Second သာသာေပါ့။ မိမိ ကြန္ပ်ဴတာရဲ႕ Processing Speed ေပၚမွာလည္း မူတည္ပါေသးတရ္ဗ်)ေလာက္ ၾကာပါတရ္။ ဒါဆို Resolution မ်ားမ်ားနဲ႔ Advanced Processing အတြက္ ဘရ္လိုမွ အဆင္ေျပႏိုင္ဖို႔ မရွိပါဘူး။ ဒီျပႆနာကို တနည္းတဖံု ေျဖရွင္းႏိုင္ဖို႔အတြက္ Lockbits( ) Method ကို ေလ့လာရတာပါ။
စိတ္၀င္စားတဲ့ မိတ္ေဆြ၊ ညီအစ္ကုိ၊ ေမာင္ႏွမမ်ား က်ေနာ္နဲ႔အတူ ေလ့လာၾကည့္ၾကအံုးစို႔ဗ်ာ။
နားလည္ႏိုင္ၾကလိမ့္မရ္လို႔ ေမ်ာ္လင့္ပါတရ္ဗ်။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Spliting RGB-Color Channel of Image

Posted by ေတဇာလင္း Tuesday, 24 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Visual Studio C# ကို အသံုးျပဳၿပီး Image တစ္ခုရဲ႕ Color Channel ေတြကို ဘရ္လိုခြဲၾကည့္ႏိုင္မလဲ ဆိုတာေလးနဲ႔ ပါတ္သတ္ၿပီး ေလ့လာၾကည့္ၾကမွာ ျဖစ္ပါတရ္။ Image တစ္ခုဟာ 2D Array ပံုစံ pixel ေတြနဲ႔ ဖြဲ႔စည္းထားပါတရ္။ Array တစ္ကြက္စီက pixel တစ္ခုကို ကိုယ္စားျပဳပါတရ္။ ၄င္း pixel တစ္ကြက္ခ်င္းစီမွာရွိတဲ့ တန္ဖိုးေတြကေရာ ဘာေတြျဖစ္မလဲ။ Color တန္ဖိုးေတြေပါ့ဗ်ာ။ ေယဘုယ် အေနနဲ႔ေတာ့ Color တန္ဖိုးတစ္ခုစီဟာ Red, Green, Blue ဆိုတဲ့ Primitive Color သံုးခုနဲ႔ Alpha (Depth, Light) တန္ဖိုးတို႔နဲ႔ ဖြဲ႔စည္းထားတာပါ။ ဒီေလ့က်င့္ခန္းရဲ႕ ရည္ရြယ္ခ်က္ကေတာ့ Image တစ္ခုရဲ႕ piexl တစ္ကြက္စီတိုင္းမွာ ရွိေနတဲ့ Color တန္းဖိုးေတြကို သိဖို႔နဲ႔ မိမိလိုခ်င္တဲ့ Color Channel ကို ဆြဲထုတ္တတ္ဖို႔ ရည္ရြယ္ပါတရ္။ ေနာက္ပိုင္း Image Processing ပိုင္းကို ေလ့လာတဲ့အခ်ိန္ Color တန္ဖိုး အခ်ိန္းအေျပာင္းေတြ လုပ္ၿပီး Image Transform လုပ္တဲ့အခါ လြယ္ကူသြားေအာင္လို႔ပါဗ်ာ။ အခုအခ်ိန္ထိေတာ့ အားလံုးက အေျခခံေလးေတြပါပဲ။ က်ေနာ့္ ေလ့က်င့္ခန္းေတြက စတင္ေလ့လာသူမ်ားအတြက္သာ ရည္ရြယ္လို႔ သိၿပီးသား သူေတြအတြက္ နားလည္ေပးၾကဖို႔ ႀကိဳတင္ ေတာင္းပန္ထားပါရေစဗ်ာ။ ဒီေန႔ ေလ့က်င့္ခန္းအတြက္ က်ေနာ္ Button (၂)ခု၊ ComboBox (၁)ခုနဲ႔ PictureBox (၂)ခုကို အသံုးျပဳထားပါတရ္။ Tools ေတြရဲ႕ Properties ေတြကိုေတာ့ ေအာက္ပါ Table အတိုင္း ျပင္ယူလိုက္ၾကပါဗ်ာ။
PictureBox အတြင္းသို႔ Image ဆြဲတင္ျခင္းအပိုင္းနဲ႔ Image ကို Save ယူျခင္းအပိုင္းကိုေတာ့ ေရွ႕ သင္ခန္းစာေတြမွာ ေလ့လာခဲ့ၿပီးျဖစ္ပါတရ္။ ဒါေၾကာင့္ coding ေတြကို က်ေနာ္ မရွင္းေတာ့ပါဘူး။ ယခု ေလ့က်င့္ခန္းမွာေတာ့ မိတ္ေဆြ ဆြဲတင္ထားတဲ့ Image ကို ComboBox ထဲက Color Channel ကိုေရြးခ်ယ္ၿပီး မိမိ ေျပာင္းလဲေစခ်င္တဲ့ Color Channel အတိုင္းေျပာင္းယူတဲ့ ပံုစံနဲ႔ ေလ့လာၾကည့္မွာပါ။ GUI ပံုစံကိုေတာ့ ေအာက္ကပံုမွာ ၾကည့္ႏိုင္ပါတရ္။
စဥ္းစားပံုကေတာ့ မိတ္ေဆြေရြးခ်ယ္လိုက္တဲ့ Image ရဲ႕ Pixel တစ္ကြက္ခ်င္းစီထဲမွာရွိတဲ့ ARGB Color တန္ဖိုးေတြကို ရွာပါတရ္။ ရွာလို႔ရတဲ့ Color တန္ဖိုးေတြထဲကမွ မိမိ ေျပာင္းလဲေစခ်င္ေသာ Color Channel ကိုသာခ်န္ၿပီး က်န္တဲ့ Color တန္ဖုိးေတြကို သုည(0) ေပးၿပီး ျပန္ Set လုပ္လိုက္တာပါပဲဗ်ာ။ က်ေနာ္တို႔ လိုခ်င္တဲ့ Color Channel အေပၚမူတည္ၿပီး Pixel တစ္ကြက္ခ်င္းစီမွာရွိတဲ့ Color တန္ဖိုးေတြက ေျပာင္းလဲသြားမွာပါ။ ဥပမာ ARGB(255, 100, 45, 72) ထဲက က်ေနာ္တို႔က Blue Channel ေျပာင္းခ်င္တရ္ဆိုပါစို႔။ Pixel တစ္ကြက္ခ်င္းစီမွ ရလာတဲ့ ARGB Color တန္ဖိုးေတြထဲက ARGB(255, 0, 0, 72) Red နဲ႔ Green ကို သုည(0) ေပးၿပီး က်န္ Apha နဲ႔ Blue တန္ဖိုးကို ျပန္ Set လုပ္လိုက္တာပါ။ ComboBox ရဲ႕ SelectedIndex အခ်ိန္းအေျပာင္းေပၚ မူတည္ၿပီး Image ရဲ႕ Color Channel ေျပာင္းလဲမႈကို ခ်က္ခ်င္းျပခ်င္တာျဖစ္တဲ့အတြက္ Calling Mehtod ကို ComboBox ရဲ႕ SelectedIndexChanged_Event ထဲမွာ ၀င္ေရးထားပါတရ္။ Channel အခ်ိန္းအေျပာင္း လုပ္ငန္းစဥ္ကိုေတာ့ သီးသန္႔ Method ခြဲၿပီးေရးထားပါတရ္။ Method ထဲကို Image နဲ႔ Channel ကို Parameter Passing လုပ္ထားပါတရ္။ တြက္ခ်က္ၿပီးရင္ေတာ့ ေနာက္ဆံုး Result အျဖစ္ Image ကို Return ျပန္ေပးမွာျဖစ္ပါတရ္။ Result ကို ပံု(၂)မွာၾကည့္ပါ။
မိတ္ေဆြတို႔ ေသခ်ာနားလည္သြားေအာင္ GetChannelImage() Mehtod ရဲ႕ Coding ကိုေအာက္မွာ ေဖာ္ျပေပးထားပါတရ္။
ဒီမွာ အလုပ္ လုပ္သြားတဲ့ပံုစံကေတာ့ ပထမအဆင့္မွာ မိတ္ေဆြတို႔ ပို႔ေပးလိုက္တဲ့ Image ကို Image ရဲ႕ အေျခခံအက်ဆံုး Format ျဖစ္တဲ့ Bitmap အျဖစ္ ေျပာင္းလဲယူပါတရ္။ ဒုတိယအဆင့္မွာေတာ့ For Loop ကို အသံုးျပဳၿပီး Image ရဲ႕ Pixel တစ္ကြက္ခ်င္းစီကို loop ပါတ္ၿပီး ဖတ္ပါတရ္။ တစ္ဆက္တည္းမွာပဲ GetPixel()Method ကိုအသံုးျပဳၿပီး Pixel တစ္ကြက္ခ်င္းစီမွာရွိတဲ့ Color တန္ဖိုးေတြကို မွတ္ယူပါတရ္။ တတိယအဆင့္မွာေတာ့ မိတ္ေဆြတို႔ ေရြးခ်ယ္လိုက္တဲ့ Color Channel အေပၚမူတည္ၿပီး Pixel တစ္ကြက္ခ်င္းစီရဲ႕ New Color တန္ဖိုးေတြကို ျပန္ Set လုပ္ပါတရ္။ Pixel အားလံုး Color Change ၿပီးသြားၿပီဆိုရင္ေတာ့ Loop ကထြက္သြားမွာပါ။ ေနာက္ဆံုးအဆင့္မွာေတာ့ က်ေနာ္တို႔ Color ခ်ိန္းၿပီးသား Image တစ္ခုကို Bitmap ထဲထည့္ၿပီး Image Type အျဖစ္ Return ျပန္ေပးလိုက္တာပဲျဖစ္ပါတရ္ဗ်ာ။ နားလည္းႏိုင္ၾကလိမ့္မရ္လို႔ ေမွ်ာ္လင့္ပါတရ္။
GetChannelImage() Method ဖန္တီးျခင္းအပိုင္းကို သိၿပီဆိုရင္ေတာ့ ၄င္း Method ကို ဘရ္လို ျပန္ေခၚသံုးမလဲဆုိတာေလးကို ေလ့လာၾကည့္ပါ့မရ္။ ComboBox ကို D_Click ေပးၿပီး SelectedIndexChanged_Event ထဲမွာ ၀င္ေရးလိုက္ပါဗ်ာ။
ခက္ခက္ခဲခဲ မရွိပါဘူးဗ်ာ။ Prarmeter Passing လုပ္ေပးရမဲ့ Image နဲ႔ Channel ကို Initial လုပ္ၿပီး Method ကို ေခၚလိုက္တာပါ။ Method က Image Return ျပန္ေပးမွာျဖစ္တဲ့အတြက္ Image အေနနဲ႔ ျပန္ဖမ္းယံုပါပဲ။ ရလာတဲ့ Image ကိုေတာ့ ဒုတိယ PictureBox ျဖစ္တဲ့ picChannelImg ထဲကို Add ေပးလိုက္တာပါ။
မိတ္ေဆြတို႔ ပိုၿပီး နားလည္သြားေအာင္ Coding အျပည့္အစံုကို ေအာက္မွာ ေလ့လာၾကည့္လိုက္ၾကပါအံုးဗ်ာ။
ကဲ... ဒီေလာက္ဆိုရင္ေတာ့ မိတ္ေဆြတို႔အေနနဲ႔ Image တစ္ခုရဲ႕ မိမိစိတ္ႀကိဳက္ Color Channel ခြဲၿပီး ၾကည့္တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ က်ေနာ့္ အားနည္းခ်က္မ်ားရွိခဲ့ရင္လည္း ျပန္လည္ ေထာက္ျပေပးၾကပါဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Reduce/Compress Image Quality

Posted by ေတဇာလင္း Monday, 23 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Visual Studio C# ကို အသံုးျပဳၿပီး Image တစ္ခုရဲ႕ Size/Length ကို ဘရ္လို ေလွ်ာ့ခ်ႏိုင္မလဲ ဆိုတာေလးနဲ႔ ပါတ္သတ္ၿပီး ေလ့လာၾကည့္ၾကမွာျဖစ္ပါတရ္။ Image ရဲ႕ Quality ကို ေကာင္းေအာင္ ႀကိဳးစားေနၾကတဲ့ေခာတ္မွာ Quality ေလ်ာ့ခ်မရ္ဆိုေတာ့ ထူးထူးဆန္းဆန္းျဖစ္ေကာင္း ျဖစ္ေနပါလိမ့္မရ္။ ဒါေပမဲ့ Mega pixel ေကာင္းေကာင္းရဲ႕ Record ယူထားတဲ့ ဓါတ္ပံုတစ္ပံုဟာ အနည္းဆံုး 1MB နဲ႔ 6MB ေလာက္ထိရွိပါတရ္။ ဒါဆိုရင္ ဓါတ္ပံုေတြ အရမ္းမ်ားလာရင္ မိတ္ေဆြအတြက္ Stroage Space မွာ ျပႆနာရွိလာမွာပါ။ ဒါေၾကာင့္ ဒီ Problem ကို ေျဖရွင္းတဲ့အခါမွာ တစ္ေထာင့္တစ္ေနရာက အေထာက္အကူ ျဖစ္လိုျဖစ္ငွား ဒီေလ့က်င့္ခန္းကို ေဆြးေႏြးေပးျခင္းျဖစ္ပါတရ္။ ေျဖရွင္းၾကတဲ့ နည္းလမ္းေပါင္းစံုရွိၾကမွာပါ။ အဲ့ဒီ နည္းလမ္းေတြထဲက တစ္ခုေပါ့ဗ်ာ။ ဒီလို ေျဖရွင္းမွ ေျပလည္ႏိုင္မရ္လို႔ေတာ့ မဟုတ္ပါဘူး။ က်ေနာ့္ ေဆြးေႏြးေပးမႈမွာလည္း အားနည္းခ်က္ေတြ အမ်ားႀကီးရွိပါတရ္။ ဒါအျပင္ က်ေနာ့္ ေလ့က်င့္ခန္းေတြက စတင္ေလ့လာသူမ်ားအတြက္သာ ရည္ရြယ္လို႔ သိၿပီးသားသူေတြအတြက္ နားလည္ေပးၾကဖို႔ ႀကိဳတင္ေတာင္းပန္ထားပါရေစဗ်ာ။ ေလ့က်င့္ခန္းေတြကေတာ့ အခန္းဆက္ ပံုစံမ်ိဳးျဖစ္ေနလို႔ ေရွ႕အပိုင္းေတြမွာ သံုးခဲ့တာေလးေတြပါ ျပန္လည္အသံုးျပဳသြားမွာျဖစ္ပါတရ္။ ဒါေၾကာင့္ ေရွ႕သင္ခန္းစာရဲ႕ GUI မွာပဲ က်ေနာ္တို႔ လိုအပ္တဲ့ Tool ေလးေတြကို ထပ္ျဖည့္ၿပီး ေလ့လာၾကည့္ၾကပါ့မရ္။ ဒီေန႔ ေလ့က်င့္ခန္းအတြက္ က်ေနာ္ TrackBar (၁)ခု၊ Button (၁)ခုနဲ႔ Label (5)ခုကို အသံုးျပဳထားပါတရ္။ Tools ေတြရဲ႕ Properties ေတြကိုေတာ့ ေအာက္ပါ Table အတိုင္း ျပင္ယူလိုက္ၾကပါဗ်ာ။
PictureBox အတြင္းသို႔ Image ဆြဲတင္ျခင္းအပိုင္းနဲ႔ PictureBox အတြင္းမွ Image Information ၾကည့္တဲ့အပိုင္းကိုေတာ့ ေရွ႕ သင္ခန္းစာေတြမွာ ေလ့လာခဲ့ၿပီးျဖစ္ပါတရ္။ ဒါေၾကာင့္ coding ေတြကို က်ေနာ္ မရွင္းေတာ့ပါဘူး။ ယခု ေလ့က်င့္ခန္းမွာေတာ့ မိတ္ေဆြ ဆြဲတင္ထားတဲ့ Image ဟာ Quality ဘရ္ေလာက္ထိ ေလွ်ာ့ခ်ရင္ Image ရဲ႕ Size ဘရ္ေလာက္ထိ ေလွ်ာ့က်သြားႏိုင္လဲ ဆိုတာေလးကို ေလ့လာၾကည့္မွာပါ။ GUI ပံုစံကိုေတာ့ ေအာက္ကပံုမွာ ၾကည့္ႏိုင္ပါတရ္။
စဥ္းစားပံုကေတာ့ မိတ္ေဆြေရြးခ်ယ္လိုက္တဲ့ Image ရဲ႕ ကနဦး Quality ကို 100 လို႔ သတ္မွတ္လိုက္ပါတရ္။ ဒီ 100 Quality ကိုမွ မိမိစိတ္ႀကိဳက္ေလွ်ာ့ခ်ၿပီး Image ရဲ႕ Size/Length ကို ေလွ်ာ့ခ်ယူမွာပါ။ ဒီလို Quality ကို စိတ္ႀကိဳက္ ခ်ိန္ယူႏိုင္ဖို႔အတြက္ေတာ့ က်ေနာ္ TrackBar ကို သံုးထားတာပါ။ TrackBar ရဲ႕ BoundRate ကေတာ့ 0 to 100 ပါ။ TrackBar ရဲ႕ Rate အတိုး/အေလွ်ာ့ေပၚမူတည္ၿပီး Image ရဲ႕ Size/Length အခ်ိန္းအေျပာင္းကို ျပခ်င္တာျဖစ္တဲ့အတြက္ Calling Mehtod ကို TrackBar ရဲ႕ Scroll_Event ထဲမွာ ၀င္ေရးထားပါတရ္။ Reduce လုပ္ငန္းစဥ္ကိုေတာ့ သီးသန္႔ Method ခြဲၿပီးေရးထားပါတရ္။ Method ထဲကို Image နဲ႔ Quality ကို Parameter Passing လုပ္ထားပါတရ္။ တြက္ခ်က္ၿပီး ေနာက္ဆံုး Result အျဖစ္ Image ကို Return ျပန္ေပးမွာျဖစ္ပါတရ္။ ReduceImage ရဲ႕ Information ျပန္ၾကည့္တဲ့အပိုင္းကိုေတာ့ ေရွ႕သင္ခန္းစာက လုပ္ငန္းစဥ္အတိုင္း ျပန္သံုးထားပါတရ္။ Result ကို ပံု(၂)မွာၾကည့္ပါ။
မိတ္ေဆြတို႔ ေသခ်ာနားလည္သြားေအာင္ CompressImageQuality() Mehtod ရဲ႕ Coding ကိုေအာက္မွာ ေဖာ္ျပေပးထားပါတရ္။
ဒီမွာ အလုပ္ လုပ္သြားတာကေတာ့ Image.Save(savePath, TypeEncoder, EncodeImageQuality) Method ပါ။ ပထမ Parameter ျဖစ္တဲ့ Image ကို Save ဖို႔ File Path ကိုေတာ့ Physical Drives ကို တိုက္ရိုက္မညႊန္းေပးပဲ MemoryStream ထဲမွာ ေခတၱသိမ္းထားတဲ့ ပံုစံနဲ႔ ေရးထားပါတရ္။ ဒုတိယ Parameter ျဖစ္တဲ့ Image TypeEncoder အပိုင္းမွာေတာ့ Linq Method ကို အသံုးျပဳၿပီး Jpeg Format နဲ႔ Encode လုပ္ထားပါတရ္။ Linq မသံုးခ်င္ရင္ေတာ့ For/Foreach Loop တစ္ခုခုကိုအသံုးျပဳၿပီး ImageCodecInfo ရဲ႕ GetImageDecoders() ထဲက Jpeg Format ကို loop ပါတ္ ဆြဲထုတ္တဲ့နည္းနဲ႔ယူႏိုင္ပါတရ္ဗ်။ ဒီမွာ Jpeg Format နဲ႔ Encode လုပ္ရတာကေတာ့ Image ရဲ႕ Quality ကို အရမ္းႀကီးမဆံုးရႈံးရပဲနဲ႔ Image ရဲ႕ Size/Length ကို ေလွ်ာ့ခ်ႏိုင္လို႔ပါ။ Jpeg မဟုတ္ပဲ မိတ္ေဆြတို႔ ေရြးခ်ယ္လိုက္တဲ့ File Type အတိုင္း Endoce လုပ္ခ်င္ရင္ေတာ့ ေအာက္မွာ က်ေနာ္ ဘေလာ့ထားတဲ့ cmd line ကို သံုးႏိုင္ပါတရ္။ ဒါဆိုရင္ေတာ့ မိတ္ေဆြတို႔ Png ဆြဲတင္ရင္ Png နဲ႔ Encode လုပ္သလို Gif ဆြဲတင္ရင္ Gif နဲ႔ Encode လုပ္မွာပါ။ Jpeg Format နဲ႔ Endoce လုပ္သေလာက္ေတာ့ Image ရဲ႕ Size/Length က်သြားမွာ မဟုတ္ဘူးဆိုတာေတာ့ သတိထားရမွာပါ။ ေနာက္ဆံုး Parameter ျဖစ္တဲ့ EncodeImageQuality အပိုင္းမွာေတာ့ EncoderParameters Class ကုိသံုးထားပါတရ္။ ဒီေကာင္ရဲ႕ အဓိကအလုပ္ကေတာ့ Image အား မိမိတို႔ ေလွ်ာ့ခ်ေစခ်င္တဲ့ Quality ကို သတ္မွတ္တာပါ။ Parameter သံုးခုလံုးကို ၿခံဳငံုၿပီးေျပာရရင္ Image အား ပထမ Parameter ျဖစ္တဲ့ Path ထဲကို ဒုတိယ Parameter မွာ ေရြးခ်ယ္ထားတဲ့ Type နဲ႔ တတိယ Parameter မွာ သတ္မွတ္ေပးလိုက္တဲ့ Quality အတိုင္း Save ပါလို႔ ခိုင္းလိုက္တာပါပဲဗ်ာ။

byte[] Array ထဲ သိမ္းထားလိုက္တာကေတာ့ Image Type အေနနဲ႔ တိုက္ရိုက္ Return ျပန္လို႔မရလို႔ တဆင့္ခံအေနနဲ႔ ေခတၱသိမ္းလို္က္တာပါ။ ေနာက္ဆံုးမွာေတာ့ byte[] Array ကို MemoryStream ထဲ ျဖတ္ၿပီး က်ေနာ္တို႔ လိုခ်င္တဲ့ Image Type ေျပာင္းကာ Return ျပန္ေပးလို္က္တာပဲျဖစ္ပါတရ္ဗ်ာ။ က်ေနာ္က ကုဒ္ အလုပ္ လုပ္သြားတဲ့ သေဘာတရားကိုပဲ ရွင္းျပခဲ့တာပါ။ Encode/Decode အပိုင္းကို အေသးစိတ္ သိခ်င္ရင္ေတာ့ ထပ္ေလ့လာၾကည့္ၾကဖို႔ တိုက္တြန္းခ်င္ပါတရ္ဗ်ာ။

Reduce Method ဖန္တီးျခင္းအပိုင္းကို သိၿပီဆိုရင္ေတာ့ ၄င္း Method ကို ဘရ္လို ျပန္ေခၚသံုးမလဲဆုိတာေလးကို ေလ့လာၾကည့္ပါ့မရ္။ TrackBar ကို D_Click ေပးၿပီး Scroll_Event ထဲမွာ ၀င္ေရးလိုက္ပါဗ်ာ။
ခက္ခက္ခဲခဲ မရွိပါဘူးဗ်ာ။ Prarmeter Passing လုပ္ေပးရမဲ့ Image နဲ႔ Quality ကို Initial လုပ္ၿပီး Method ကို ေခၚလိုက္ယံုပါပဲ။ Method က Image Return ျပန္ေပးမွာျဖစ္တဲ့အတြက္ Image အေနနဲ႔ ျပန္ဖမ္းယံုပါပဲ။ ရလာတဲ့ Image ကိုေတာ့ ဒုတိယ PictureBox ျဖစ္တဲ့ picReduceImg ထဲကို Add ေပးလိုက္တာပါ။ ေလွ်ာ့က်သြားတဲ့ Image ရဲ႕ Information ထုတ္ၾကည့္တာကေတာ့ ေရွ႕သင္ခန္းစာမွာ ေလ့လာခဲ့တာနဲ႔ အတူတူပါပဲဗ်ာ။ က်ေနာ္ျပထားတဲ့ ဥပမာမွာဆုိရင္ 5.46MB ရွိတဲ့ Image တစ္ခုကို Quality ကို 56 အထိ ေလွ်ာ့ခ်လိုက္တဲ့အခါ 480KB ေလာက္ထိ ေလွ်ာ့က်သြားတာ ေတြ႔ရမွာပါ။ ပံုကို ဖြင့္ၾကည့္တဲ့အခါမွာလည္း အရမ္းႀကီး Quality က်ၿပီး ၀ါးသြားတာမ်ိဳးမရွိပါဘူးဗ်။
မိတ္ေဆြတို႔ ပိုၿပီး နားလည္သြားေအာင္ Coding အျပည့္အစံုကို ေအာက္မွာ ေလ့လာၾကည့္လိုက္ၾကပါအံုးဗ်ာ။
ကဲ... ဒီေလာက္ဆိုရင္ေတာ့ မိတ္ေဆြတို႔အေနနဲ႔ Image တစ္ခုရဲ႕ Quality ကို C# ကိုအသံုးျပဳၿပီး ေလွ်ာ့ခ်တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ က်ေနာ့္ အားနည္းခ်က္မ်ားရွိခဲ့ရင္လည္း ျပန္လည္ ေထာက္ျပေပးၾကပါဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Get Image Information by Image Type

Posted by ေတဇာလင္း Friday, 20 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Visual Studio C# ကို အသံုးျပဳၿပီး Image တစ္ခုရဲ႕ Information(Width, Height, Type, Size)ေတြကို ဘရ္လို ထုတ္ၾကည့္ႏိုင္မလဲ ဆိုတာေလးနဲ႔ ပါတ္သတ္ၿပီး ေလ့လာၾကည့္ၾကမွာျဖစ္ပါတရ္။ က်ေနာ့္ ေလ့က်င့္ခန္းေတြက စတင္ေလ့လာသူမ်ားအတြက္သာ ရည္ရြယ္လို႔ သိၿပီးသားသူေတြအတြက္ နားလည္ေပးၾကဖို႔ ႀကိဳတင္ေတာင္းပန္ထားပါရေစဗ်ာ။ ေလ့က်င့္ခန္းေတြကေတာ့ အခန္းဆက္ ပံုစံမ်ိဳးျဖစ္ေနလို႔ ေရွ႕အပိုင္းေတြမွာ သံုးခဲ့တာေလးေတြပါ ျပန္လည္အသံုးျပဳသြားမွာျဖစ္ပါတရ္။ ဒါေၾကာင့္ ေရွ႕သင္ခန္းစာရဲ႕ GUI မွာပဲ က်ေနာ္တို႔ လိုအပ္တဲ့ Tool ေလးေတြကို ထပ္ျဖည့္ၿပီး ေလ့လာၾကည့္ၾကပါ့မရ္။ ဒီေန႔ ေလ့က်င့္ခန္းအတြက္ က်ေနာ္ Button (၁)ခုနဲ႔ Label (4)ခုကို အသံုးျပဳထားပါတရ္။ Tools ေတြရဲ႕ Properties ေတြကိုေတာ့ ေအာက္ပါ Table အတိုင္း ျပင္ယူလိုက္ၾကပါဗ်ာ။
PictureBox အတြင္းသို႔ Image ဆြဲတင္ျခင္းအပိုင္းကိုေတာ့ ေရွ႕သင္ခန္းစာျဖစ္တဲ့ C# - Image Loading to PictureBox သင္ခန္းစာမွာ ေလ့လာခဲ့ၿပီးျဖစ္ပါတရ္။ ဒါေၾကာင့္ Image ဆြဲတင္တဲ့ coding ေတြကို က်ေနာ္ မရွင္းေတာ့ပါဘူး။ ယခု ေလ့က်င့္ခန္းမွာေတာ့ မိတ္ေဆြ ဆြဲတင္ထားတဲ့ Image ဟာ Width ဘရ္ေလာက္၊ Height ဘရ္ေလာက္၊ ဘာ Type အမ်ိဳးအစားလဲ၊ Size ဘရ္ေလာက္ရွိလဲ အစရွိတဲ့ Information ေတြကို ထုတ္ၾကည့္မွာျဖစ္ပါတရ္။ GUI ပံုစံကိုေတာ့ ေအာက္ကပံုမွာ ၾကည့္ႏိုင္ပါတရ္။
ပံုမွန္အားျဖင့္ဆိုရင္ File (or) Image ေတြရဲ႕ Information ေတြ ထုတ္ၾကည့္တဲ့အခါမွာ File (or) Image ရဲ႕ Location/Path ကိုေရြးေပးၿပီး FileInfo နဲ႔ ၾကည့္ၾကတာ မ်ားပါတရ္။ က်ေနာ့္လက္ရွိ သင္ခန္းစာေတြကေတာ့ Image ဘက္ကို အသားေပးထားတဲ့အတြက္ေၾကာင့္ Image Type ျဖစ္ေနတဲ့ File တနည္းအားျဖင့္ Image ပါလို႔ အတည္ျပဳၿပီးသား File ရဲ႕ Information ကိုပဲ ထုတ္ၾကည့္တဲ့ ပံုစံနဲ႔ ခ်ဥ္းကပ္ထားပါတရ္။ FileInfo လို File(txt, doc, etc)တိုင္းရဲ႕ Information ကိုေတာ့ ၾကည့္ႏိုင္မွာမဟုတ္ပါဘူး။ အားသာခ်က္အေနနဲ႔ကေတာ့ Image ရဲ႕ Path ကို String Type နဲ႔ Static ပံုစံ အေသလမ္းေၾကာင္း ေပးစရာမလိုေတာ့ပဲ Image Type အျဖစ္ သတ္မွတ္ထားတဲ့ မည္သည့္ File ကိုမဆို Information ထုတ္ၾကည့္ႏိုင္မွာျဖစ္ပါတရ္။ PictureBox နဲ႔ ဥပမာေပးရရင္ PictureBox အတြင္းသို႔ ဆြဲတင္မရ္႕ Image ကို Physical Drives ေတြထဲက ျဖစ္ႏိုင္သလို Database လို ေနရာကလည္း ေရာက္လာႏိုင္တာကိုးဗ်။ FileInfo ကို အသံုးျပဳလို႔ Physical Drives ေတြထဲက PictureBox ကိုဆြဲတင္ရင္ File Path ကို String ပံုစံ အဆင္ေျပေပမဲ့ Database ထဲကဆြဲတင္မရ္ဆိုရင္ေတာ့ String ပံုစံ အဆင္ေျပမွာမဟုတ္ေတာ့ပါဘူးဗ်ာ။ စာနဲ႔ ေရးၿပီးရွင္းရတာေတာ့ နားလည္ရခက္ပါတရ္။ ဒါေၾကာင့္ Image Type ကိုအသံုးျပဳၿပီး Image တစ္ခုရဲ႕ Information ကို ထုတ္ၾကည့္တရ္လို႔ပဲ မွတ္ထားလိုက္ပါဗ်ာ။ Result ကို ပံု(၂)မွာၾကည့္ပါ။
မိတ္ေဆြတို႔ ေသခ်ာနားလည္သြားေအာင္ Coding အျပည့္အစံုကို က်ေနာ္ ေအာက္မွာ ေဖာ္ျပေပးလိုက္ပါတရ္။

နားလည္ႏိုင္ၾကလိမ့္မရ္လို႔ ယံုၾကည္ပါတရ္ဗ်။ ဒီေလ့က်င့္ခန္းမွာေတာ့ Method မခြဲေတာ့ပဲ btnImgInfo ဆုိတဲ့ Button ရဲ႕ Click_Event မွာပဲ Coding ကိုတခါတည္းေရးထားပါတရ္။ ပထမဆံုး PictureBox ထဲမွာ ရွိတဲ့ Image ကို Image Type ထဲထည့္လိုက္ပါတရ္။ Image အျဖစ္သိၿပီဆိုရင္ေတာ့ Width နဲ႔ Height Properties ေတြကို အသံုးျပဳၿပီး Image ရဲ႕ Width နဲ႔ Height ကို ဆြဲထုတ္ပါတရ္။ Type ကိုဆြဲထုတ္တဲ့ ေနရာမွာေတာ့ Image ရဲ႕ Type ေတြဟာ ImageFormat အေနနဲ႔ ရွိေနတာပါ။ က်ေနာ္တို႔ လိုခ်င္တာက String ပံုစံေလ။ ဒီေတာ့ ImageFormatConverter() ရဲ႕ ConvertToString() Method ကိုအသံုးျပဳၿပီး ImageFormat ကေနၿပီး String အျဖစ္ေျပာင္းလဲယူလုိက္တာျဖစ္ပါတရ္။ Image ရဲ႕ Size ကို သိဖို႔အတြက္ကေတာ့ Image ကို MemoryStream() ထဲမွာ ေခတၱသြား Save ေပးရပါတရ္။ MemoryStream ထဲေရာက္ၿပီဆိုရင္ေတာ့ ၄င္း Image ရဲ႕ Size/Length ကိုဖတ္လို႔ရပါၿပီ။ ဒါေပမဲ့ ဖတ္လို႔ရတဲ့ Image ရဲ႕ Size က byte အေနနဲ႔ ထြက္လာမွာပါ။ ဒါကိုမွ က်ေနာ္တို႔ ေဖာ္ျပေစခ်င္တဲ့ KB (or) MB အျဖစ္ ေျပာင္းလဲ ေဖာ္ျပေပးလိုက္တာပဲျဖစ္ပါတရ္။ 1024 နဲ႔ စားတာကေတာ့ 1024 byte မွာ 1 KB ရွိသလို၊ 1024 KB မွာ 1MB ရွိလို႔ပါ။ MB တြက္တဲ့ေနရာမွာ string.Format("{0:F2}", imgSizeKB / 1024) သံုးထားတာကေတာ့ ဒႆ မ(၂)ေနရာျဖတ္ခ်င္လို႔ပါ။ ဒႆ မ(၁)လံုးျဖတ္ခ်င္ရင္ 0:F1, ႏွစ္လံုးျဖတ္ခ်င္ရင္ 0:F2 စသည္ျဖင့္ မိမိျဖတ္ခ်င္တဲ့ ဒႆ မအေရအတြက္အတိုင္း F level ကို တိုးယူႏိုင္ပါတရ္။ Coding ေတြက အခက္ခဲႀကီးေတြ မဟုတ္လို႔ ဒီေလာက္ဆုိရင္ေတာ့ မိတ္ေဆြတို႔အေနနဲ႔ Image တစ္ခုရဲ႕ Information ေတြကို ဆြဲထုတ္ၾကည့္တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Image Type Conversion

Posted by ေတဇာလင္း Thursday, 19 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Visual Studio C# ကို အသံုးျပဳၿပီး Image Type တစ္ခုကေနၿပီး အျခား Image Type တစ္ခုကို ဘရ္လို ေျပာင္းလဲႏိုင္မလဲဆိုတာေလးကို ေလ့လာၾကည့္ၾကမွာျဖစ္ပါတရ္။ က်ေနာ့္ ေလ့က်င့္ခန္းေတြက စတင္ေလ့လာသူမ်ားအတြက္သာ ရည္ရြယ္လို႔ သိၿပီးသားသူေတြအတြက္ နားလည္ေပးၾကဖို႔ ႀကိဳတင္ေတာင္းပန္ထားပါရေစဗ်ာ။ ဒီေန႔ ေလ့က်င့္ခန္းမွာေတာ့ က်ေနာ္ Button (၂)ခုနဲ႔ PictureBox (၁)ခုကို အသံုးျပဳထားပါတရ္။ Tools ေတြရဲ႕ Properties ေတြကိုေတာ့ ေအာက္ပါ Table အတိုင္း ျပင္ယူလိုက္ၾကပါဗ်ာ။
PictureBox အတြင္းသို႔ Image ဆြဲတင္ျခင္းအပိုင္းကိုေတာ့ ေရွ႕သင္ခန္းစာမွာ ေလ့လာခဲ့ၿပီးျဖစ္ပါတရ္။ ယခု ေလ့က်င့္ခန္းမွာေတာ့ မိတ္ေဆြ ဆြဲတင္ထားတဲ့ Image ကို မိမိစိတ္ႀကိဳက္ Type ေျပာင္းၿပီး ျပန္လည္ Save ယူတဲ့ ပံုစံကို ေလ့လာၾကည့္မွာပါ။ GUI ပံုစံကိုေတာ့ ေအာက္ကပံုမွာ ၾကည့္ႏိုင္ပါတရ္။
Image ကို Save တဲ့အခါမွာေတာ့ က်ေနာ္ SaveFileDialog ကိုသံုးထားပါတရ္။ မိတ္ေဆြအေနနဲ႔ မိမိႏွစ္သက္ရာ Path/Location နဲ႔ Type ကို ေရြးေပးၿပီး Image Type ကို ေျပာင္းလဲ Save လုပ္ႏိုင္မွာျဖစ္ပါတရ္။ ပံု(၂)ကိုၾကည့္ပါ။
မိတ္ေဆြတို႔ ေသခ်ာနားလည္သြားေအာင္ Coding အျပည့္အစံုကို က်ေနာ္ ေအာက္မွာ ေဖာ္ျပေပးလိုက္ပါတရ္။
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;

namespace ImgProcessing
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        private void btnLoadImage_Click(object sender, EventArgs e)
        {
            LoadPicture(picOriginalImg);
        }

        public void LoadPicture(PictureBox pic)
        {
            OpenFileDialog openFD = new OpenFileDialog();
            openFD.Title = "Insert an Image";
            openFD.Filter = "JPEG Images|*.jpg|PNG Images |*.png";
            if (openFD.ShowDialog() == DialogResult.OK) {
                pic.Image = Image.FromFile(openFD.FileName);
            }
        }  

        private void btnChangeImgType_Click(object sender, EventArgs e)
        {
            ChangeImageType(picOriginalImg.Image);             
        }

        public void ChangeImageType(Image img)
        {
            SaveFileDialog saveFD = new SaveFileDialog();
            saveFD.Title = "Save an Image";
            saveFD.Filter = "Bitmap(*.bmp)|*.Bmp|Joint Photographic Experts Group(*.jpeg)|*.Jpeg|Portable Network Graphics(*.png)|*.Png|
                                   Tagged Image File Format(*.tiff)|*.Tiff|Graphics Interchange Format(*.gif)|*.Gif";

            if (saveFD.ShowDialog() == DialogResult.OK) {
                FileStream fileLocation = (FileStream)saveFD.OpenFile();
                switch (saveFD.FilterIndex) {
                    case 1: img.Save(fileLocation, ImageFormat.Bmp); break;
                    case 2: img.Save(fileLocation, ImageFormat.Jpeg); break;
                    case 3: img.Save(fileLocation, ImageFormat.Png); break;
                    case 4: img.Save(fileLocation, ImageFormat.Tiff); break;
                    case 5: img.Save(fileLocation, ImageFormat.Gif); break;
                }
            }          
        }
    }
}
နားလည္ႏိုင္ၾကလိမ့္မရ္လို႔ ယံုၾကည္ပါတရ္ဗ်။ ဒီေလ့က်င့္ခန္းမွာေတာ့ ChangeImageType( ) ကို Method ခြဲေရးၿပီး Image ကို Parameter Passing လုပ္ထားပါတရ္။ SaveFileDialog သံုးတာကလည္း ေရွ႕ေလ့က်င့္ခန္းမွာ ေလ့လာခဲ့ၿပီးျဖစ္တဲ့ OpenFileDialog သံုးတာနဲ႔ အတူတူပါပဲ။ ေျပာင္းလဲလို႔ရမရ္႕ Image Type ေတြကိုေတာ့ Filter မွာ ထည့္ေပးထားပါတရ္။ ဒီ Filter ကို ေအာက္မွာ Index ခြဲၿပီးျပန္ေခၚသံုးထားတာပါ။ ဒီအတြက္ FilterIndex ဆိုတဲ့ Properties ကို အသံုးျပဳထားပါတရ္။ ပထမဆံုး Index က 1 ျဖစ္ၿပီး 1 တိုးကာ N ႀကိမ္ေျမာက္အထိ ထည့္သြင္းႏိုင္ပါတရ္ဗ်။ မိတ္ေဆြတို႔ ေရြးခ်ယ္လိုက္တဲ့ FilterIndex အေပၚမူတည္ၿပီး Image ကို Type ေျပာင္းၿပီး Save ေပးသြားမွာပါ။ က်ေနာ္ Switch() statement ကို အသံုးျပဳထားပါတရ္။ ဒီမွာ FileStream နဲ႔ OpenFile() ေခၚသံုးထားတာကေတာ့ SaveFileDialog ကေရြးခ်ယ္ေပးလိုက္တဲ့ Path/Location ကိုဖမ္းခ်င္လို႔ပါ။ Coding ေတြက အခက္ခဲႀကီးေတြ မဟုတ္လို႔ ဒီေလာက္ဆုိရင္ေတာ့ မိတ္ေဆြတို႔အေနနဲ႔ Image Type Conversion အေၾကာင္းေလးကို နားလည္ၾကမရ္လို႔ ထင္ပါတရ္ဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Image Loading to PictureBox

Posted by ေတဇာလင္း Wednesday, 18 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ VS C# ကိုအသံုးျပဳၿပီး မိမိစိတ္ႀကိဳက္ Image တစ္ခုကို Picture Box ေပၚသို႔ ဘရ္လိုဆြဲတင္ႏိုင္မလဲဆိုတာေလးကို ေလ့လာၾကည့္ၾကပါ့မရ္။ က်ေနာ့္ ေလ့က်င့္ခန္းေတြကေတာ့ စတင္ေလ့လာသူမ်ားအတြက္သာ ရည္ရြယ္လို႔ သိၿပီး၊ တတ္ၿပီးသား သူေတြအတြက္ သီးခံေပးၾကဖို႔ ႀကိဳတင္ ေတာင္းပန္ထားပါရေစဗ်ာ။ ဒီေလ့က်င့္ခန္းမွာ က်ေနာ္ Button တစ္ခု၊ PictureBox တစ္ခုသံုးထားပါတရ္။ Tools ေတြရဲ႕ Properties ေတြကိုေတာ့ ေအာက္က Table အတိုင္း ျပင္ယူလုိက္ပါဗ်ာ။
Image ဆြဲတင္တာကိုေတာ့ က်ေနာ္ Method ခြဲၿပီးေရးေပးထားပါတရ္။ PictureBox က parameter passing လုပ္ေပးရမွာျဖစ္ၿပီး void Method သံုးထားျခင္းေၾကာင့္ PictureBox ကို Auto Return ျပန္ေပးပါလိမ့္မရ္။ Result ကိုေတာ့ ေအာက္ကပံုမွာၾကည့္ႏိုင္ပါတရ္ဗ်ာ။
မိတ္ေဆြတို႔ ေသခ်ာနားလည္သြားေအာင္ Coding အျပည့္အစံုကို က်ေနာ္ ေအာက္မွာ ေဖာ္ျပေပးလိုက္ပါတရ္။
using System;
using System.Drawing;
using System.Windows.Forms;

namespace ImgProcessing
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        private void btnLoadImage_Click(object sender, EventArgs e)
        {
            LoadPicture(picOriginalImg);
        }

        public void LoadPicture(PictureBox pic)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "Insert an Image";
            ofd.Filter = "JPEG Images|*.jpg|PNG Images |*.png";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                pic.Image = Image.FromFile(ofd.FileName);
            }
        }
    }
}
နားလည္ႏိုင္ၾကလိမ့္မရ္လို႔ ထင္ပါတရ္ဗ်။ LoadPicture( ) Method မွာ parameter အျဖစ္ PictureBox ကို passing လုပ္ၿပီး pic အျဖစ္ ေၾကျငာထားပါတရ္။ ဒါေၾကာင့္ pic အေျပာင္းအလဲေပၚမူတည္ၿပီး Call Method ရဲ႕ PictureBox မွာ ေျပာင္းလဲသြားမွာျဖစ္ပါတရ္။ ဒါအျပင္ မိမိစိတ္ႀကိဳက္ Image ကို ေရြးတင္ႏိုင္ဖို႔အတြက္ OpenFileDialog Box ကို သံုးထားပါတရ္။ တတ္လာတဲ့ Dialog Box ရဲ႕ Tiltle ကိုေတာ့ "Insert an Image" လို႔ ေပးထားပါတရ္။ မိမိစိတ္ႀကိဳက္ Image အမ်ိဳးအစားကိုလည္း စစ္ထုတ္ဆြဲႏိုင္ပါေသးတရ္။ ဒီေလ့က်င့္ခန္းမွာေတာ့ က်ေနာ္ Filter အျဖစ္ jpg နဲ႔ png အမ်ိဳးအစား(၂)ခုကိုသာ ဆြဲတင္ႏိုင္ေအာင္ Filter ခံထားပါတရ္ဗ်။ မိတ္ေဆြတို႔အေနနဲ႔ေတာ့ စိတ္ႀကိဳက္ စစ္ထုတ္ႏိုင္ပါတရ္။ if()statement စစ္ထားတာကေတာ့ မိတ္ေဆြအေနနဲ႔ Image တစ္ခုကို ေရြးခ်ယ္ဆြဲတင္ျခင္း ရွိ/မရွိကို စစ္ထားတာပါ။ Image ကို စိတ္ႀကိဳက္ေရြးၿပီး OK ေပးလိုက္တာနဲ႔ ၄င္း Image ကို pic ဆိုတဲ့ PictureBox အေပၚကို ဆြဲတင္ထားတာျဖစ္ပါတရ္။ ဒီေလာက္ဆိုရင္ေတာ့ မိတ္ေဆြတို႔အေနနဲ႔ Image တစ္ခုကို PictureBox ေပၚသို႔ ဆြဲတင္တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

C# - Selection Sort Algorithm

Posted by ေတဇာလင္း Sunday, 15 January 2017 0 comments

မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး မဂၤလာပါ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ Microsoft Visual Studio C# ကို အသံုးျပဳၿပီးေတာ့ Sorting Algorithms ေတြထဲက တစ္ခုျဖစ္တဲ့ Selection Sort Algorithm အေၾကာင္း ေလးကို ေလ့လာၾကည့္မွာ ျဖစ္ပါတရ္ဗ်ာ။ Selection Sort ရဲ႕ အလုပ္လုပ္သြားတဲ့ လုပ္ငန္းစဥ္ မူၾကမ္းကို ေအာက္ကပံုမွာ ေလ့လာႏိုင္ပါတရ္ဗ်ာ။
အလုပ္လုပ္သြားတဲ့ လုပ္ငန္းစဥ္က ရိုးရွင္းပါတရ္။ အထက္ပါပံုကို ၾကည့္လိုက္တာနဲ႔ နားလည္ႏိုင္ၾကမွာပါ။ ပိုၿပီးနားလည္သြားေအာင္ က်ေနာ္ အနည္းငရ္ ရွင္းျပပါ့မရ္။ Selection Sort Algorithm ရဲ႕ Step တစ္ခုမွာ အဆင့္(4)ဆင့္ခြဲၿပီး အလုပ္လုပ္ပါတရ္။ ငယ္စဥ္ႀကီးလိုက္(ASC)အတြက္ က်ေနာ္ ဥပမာ ျပပါ့မရ္။ 1. ပထမအဆင့္မွာေတာ့ Unsorted Array ရဲ႕ ပထမ Index ကို key အျဖစ္သတ္မွတ္ပါတရ္။ 2. ဒုတိယအဆင့္အေနနဲ႔ key မွလြဲ၍ က်န္ Arrary အစုထဲမွ အငယ္ဆံုးကိန္း(Min)ကို ရွာပါတရ္။ 3. ဒီအဆင့္မွာေတာ့ အေပၚမွာ က်ေနာ္တို႔ ရွာခဲ့တဲ့ အငယ္ဆံုးကိန္း(Min)နဲ႔ key ကို ႏႈိင္းယွဥ္ပါတရ္။ key ကႀကီးေနရင္ Min နဲ႔ Key ကို ေနရာခ်င္း ခ်ိန္းပါတရ္။ ငယ္ေနရင္ေတာ့ ဒီအတိုင္း ဆက္ထားပါတရ္။ 4. အဆင့္တစ္ဆင့္ၿပီးတိုင္း Array ရဲ႕ ထိပ္ပိုင္းက်တဲ့ Index ေတြဟာ Sorted ျဖစ္ေနပါလိမ့္မရ္ဗ်။ နားလည္ႏိုင္ၾကလိမ့္မရ္လို႔ ထင္ပါတရ္။ အေပၚကပံုကိုၾကည့္ပါ။ Step 1 ၿပီးလို႔ Step 2 ေျပာင္းတဲ့ အခ်ိန္မွာ Array ရဲ႕ First Index ဟာ Sorted ျဖစ္ေနပါၿပီ။ ဒီလိုပါပဲ key ကလည္း ဒုတိယ Index ကို ခုန္သြားပါလိမ့္မရ္။ ေနာက္တစ္ဆင့္ Setp 2 ၿပီးလို႔ Step 3 ေျပာင္းတဲ့အခါမွာလည္း Array ရဲ႕ ေရွ႕ Index ႏွစ္ခုဟာ Sorted ျဖစ္ေနတာ ေတြ႔ရပါလိမ့္မရ္။ key ကလည္း ေနာက္ Index တစ္ခုကို ခုန္သြား မွာပါ။ ဒီလုပ္ငန္းစဥ္အတိုင္းပဲ Array ရဲ႕ ေနာက္ဆံုး Index အထိ loop ပါတ္ ေျဖရွင္းသြားမွာ ျဖစ္ပါတရ္။
စိတ္၀င္စားတဲ့ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား က်ေနာ္နဲ႔အတူ ေလ့လာၾကည့္ၾကစို႔ဗ်ာ။
ွPDF ရဲ႕ SelectionSort() Function မွာ က်ေနာ္ ေဖာ္ျပခဲ့တဲ့ ပံုစံမွားေနပါတယ္ဗ်။ ေအာက္က ပံုစံအတိုင္း ျပင္ေရးလိုက္ပါဗ်။ အားလံုးအဆင္ေျပ ေခ်ာေမြ႕နိုင္ၾကပါေစ။