မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမမ်ား အားလံုးပဲ မဂၤလာပါဗ်ာ။ ဒီေန႔ေတာ့ က်ေနာ္တို႔ 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 လုပ္တတ္ၿပီလို႔ ထင္ပါတရ္ဗ်ာ။ မိတ္ေဆြ၊ ညီအစ္ကို၊ ေမာင္ႏွမအားလံုး ေလ့လာျခင္းျဖင့္ ေက်နပ္ႏိုင္ၾကပါေစ။

0 Responses so far.

Post a Comment