الگوریتم Canny
لبه یاب کنی توسط جان اف کنی در سال 1986 ایجاد شد و هنوز یک لبه یاب استاندارد و با دقت و کیفیت بالا میباشد.الگوریتم لبه یابی کنی یکی از بهترین لبه یابها تا به امروز است. در ادامه روش کار این الگوریتم و هم چنین کد الگوریتم Canny در Visual Basic را بررسی خواهیم کرد. این الگوریتم لبه یابی از سه بخش اصلی زیر تشکیل شده:
- تضعیف نویز
- پیدا کردن نقاطی که بتوان آنها را به عنوان لبه در نظر گرفت
- حذب نقاطی که احتمال لبه بودن آنها کم است
معیارهایی که در لبه یا کنی مطرح است:
1 -پایین آوردن نرخ خطا- یعنی تا حد امکان هیچ لبه ای در تصویر نباید گم شود و هم چنین هیچ چیزی که لبه نیست نباید به جای لبه فرض شود. لبه هان پیدا شده تا حد ممکن به لبه ها اصلی
نزدیک باشند.
2 -لبه در مکان واقعی خود باشد- یعنی تا حد ممکن لبه ها کمترین فاصله را با مکان واقعی خود داشته باشند.
3 -بران هر لبه فقط یک پاسخ داشته باشیم.
4 -لبه ها کمترین ضخامت را داشته باشند- (در صورت امکان یک پیکسل).
لبه یاب کنی بخاطر توانایی در تولید لبه های نازک تا حد یک ییکسل برای لبه های پیوسته معروف شده است. این لبه یاب شامل چهار مرحله و چهار ورودی زیر است:
یک تصویر ورودی
یک پارامتر به نام سیگما جهت مقدار نرم کنندگی تصویر
یک حد آستانه بالا (Th)
یک حد آستانه پایین (Tl)
مراحل الگوریتم Canny:
1- در ابتدا باید تصویر رنگی را به جهت لبه یابی بهتر به یک تصویر سطح خاکسترن تبدیب کرد.
2- نویز را از تصویر دریافتی حذف کرد. بدلیل اینکه فیلتر گاوسین از یک ماسک ساده برای حذف نویز استفاده می کند لبه یاب کنی در مرحله اول برای حذف نویز آن را بکار میگیرد.
3- در یک تصویر سطح خاکستر جایی را که بیشترین تغییرات را داشته باشند به عنوان لبه در نظر گرفته می شوند و این مکانها با گرفتن گرادیان تصویر با استفاده عملگر سوبل بدست می آیند. سپس لبه های مات یافت شده به لبه های تیزتر تبدیل می شوند.
4- برخی از لبه های کشف شده واقعا لبه نیستند و در واقع نویز هستند که باید آنها توسط حد آستانه هیسترزیس فیلتر شوند.هیسترزیس از دو حد آستانه بالاتر (Th) و حد آستانه پایین تر (Tl) استفاده کرده و کنی پیشنهاد می کند که نسبت استانه بالا به پایین سه به یک باشد.
این روش بیشتر به کشف لبه های ضعیف به درستی می پردازد و کمتر فریب نویز را می خورد و از بقیه روش ها بهتر است.
الگوریتم Canny در Visual Basic:
کد زیر یک کد تکمیل نشده است.تکمیل آن به عنوان تمرین به خواننده واگذار می شود.
Imports System.Drawing Imports System.Drawing.Imaging Public Class clsEdges Public Sub EdgeDetectDifference(ByVal b As Bitmap, ByVal threshold As Byte) ' first we create a clone o the image we want to find the edges on Dim b2 As Bitmap = b.Clone ' we create bitmapdata of the images at the same time locking them Dim bmData1 As BitmapData = b.LockBits(New Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) Dim bmData2 As BitmapData = b2.LockBits(New Rectangle(0, 0, b2.Width, b2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) ' the stride describes the distance between image bytes Dim stride As Integer = bmData2.Stride ' scan0 is some sort of OS handle or something to identify the actual data in the memory Dim scan01 As IntPtr = bmData1.Scan0 Dim scan02 As IntPtr = bmData2.Scan0 ' we need to know how big the data is so that we can create the correct size for the file Dim bytes As Integer = b.Height * b.Width * 3 ' we create the byte arrays so that we can edit them Dim p01(bytes - 1) As Byte Dim p02(bytes - 1) As Byte ' put the images into the byte arrays System.Runtime.InteropServices.Marshal.Copy(scan01, p01, 0, bytes) System.Runtime.InteropServices.Marshal.Copy(scan02, p02, 0, bytes) ' the nWidth describes the width of the actual image multiplied by three for each byte in the pixel (3 bytes per pixel 24 bits ;)) Dim nWidth As Integer = b2.Width * 3 ' for some reason although the original code show a formula to come up with the offset this doesn't work very well. ' I found that it is just easier to make the offset 0 and so all bits are handled. Basically the problem comes when ' using this on files that don't have Dim nOffset As Integer = 0 Dim nPixel As Integer = 0, npixelmax As Integer = 0 Dim pos1 As Integer = stride + 3 Dim pos2 As Integer = stride + 3 Dim p2minusplus As Integer, p2plusminus As Integer, p2plusplus As Integer, p2minusminus As Integer Dim p2minusstride As Integer, p2plusstride As Integer Dim p2plus As Integer, p2minus As Integer For y As Integer = 1 To b.Height - 1 For x As Integer = 1 To nWidth - 3 p2minusplus = pos2 - stride + 3 p2plusminus = pos2 + stride - 3 p2plusplus = pos2 + stride + 3 p2minusminus = pos2 - stride - 3 p2minusstride = pos2 - stride p2plusstride = pos2 + stride p2minus = pos2 - 3 p2plus = pos2 + 3 If p2minusplus <= p02.Length - 1 And p2minusplus >= 0 And p2plusminus <= p02.Length - 1 And p2plusminus >= 0 And _ p2plusplus <= p02.Length - 1 And p2plusplus >= 0 And p2minusminus <= p02.Length - 1 And p2minusminus >= 0 And _ p2minusstride <= p02.Length - 1 And p2minusstride >= 0 And p2plusstride <= p02.Length - 1 And p2plusstride >= 0 And _ p2plus <= p02.Length - 1 And p2plus >= 0 And p2minus <= p02.Length - 1 And p2minus >= 0 And pos1 < p01.Length Then npixelmax = Math.Abs(CInt(p02(p2minusplus)) - CInt(p02(p2plusminus))) nPixel = Math.Abs(CInt(p02(p2plusplus)) - CInt(p02(p2minusminus))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(p2minusstride)) - CInt(p02(p2plusstride))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(p2plus)) - CInt(p02(p2minus))) If nPixel > npixelmax Then npixelmax = nPixel If npixelmax < CInt(threshold) Then npixelmax = 0 p01(pos1) = CByte(npixelmax) End If pos1 += 1 pos2 += 1 Next pos1 += nOffset pos2 += nOffset Next System.Runtime.InteropServices.Marshal.Copy(p01, 0, scan01, bytes) b.UnlockBits(bmData1) b2.UnlockBits(bmData2) End Sub Public Sub EdgeDetectHomogenity(ByVal b As Bitmap, ByVal threshold As Byte) Dim b2 As Bitmap = b.Clone Dim bmData1 As BitmapData = b.LockBits(New Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) Dim bmData2 As BitmapData = b2.LockBits(New Rectangle(0, 0, b2.Width, b2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) Dim stride As Integer = bmData2.Stride Dim scan01 As IntPtr = bmData1.Scan0 Dim scan02 As IntPtr = bmData2.Scan0 Dim bytes As Integer = b.Height * b.Width * 3 Dim p01(bytes - 1) As Byte Dim p02(bytes - 1) As Byte System.Runtime.InteropServices.Marshal.Copy(scan01, p01, 0, bytes) System.Runtime.InteropServices.Marshal.Copy(scan02, p02, 0, bytes) Dim nWidth As Integer = b2.Width * 3 Dim nOffset As Integer = 0 Dim nPixel As Integer = 0, npixelmax As Integer = 0 Dim pos1 As Integer = stride + 3 Dim pos2 As Integer = stride + 3 Dim p2plusminus As Integer, p2plusstride As Integer, p2plusplus As Integer, p2minusstride As Integer, _ p2minusminus As Integer, p2minusplus As Integer For y As Integer = 1 To b.Height - 1 For x As Integer = 1 To nWidth - 3 p2plusminus = pos2 + stride - 3 p2plusstride = pos2 + stride p2plusplus = pos2 + stride + 3 p2minusstride = pos2 - stride p2minusminus = pos2 - stride - 3 p2minusplus = pos2 - stride + 3 If p2plusminus < p02.Length And p2plusminus >= 0 And p2plusstride < p02.Length And p2plusstride >= 0 And _ p2plusplus < p02.Length And p2plusplus >= 0 And p2minusstride < p02.Length And p2minusstride >= 0 And _ p2minusstride < p02.Length And p2minusstride >= 0 And p2minusminus < p02.Length And p2minusminus >= 0 And _ p2minusplus < p02.Length And p2minusplus >= 0 Then npixelmax = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2plusminus))) nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2plusstride))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2plusplus))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2minusstride))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2plusstride))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2minusminus))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2minusstride))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2)) - CInt(p02(p2minusplus))) If nPixel > npixelmax Then npixelmax = nPixel If npixelmax < threshold Then npixelmax = 0 p01(pos1) = CByte(npixelmax) End If pos1 += 1 pos2 += 1 Next pos1 += nOffset pos2 += nOffset Next System.Runtime.InteropServices.Marshal.Copy(p01, 0, scan01, bytes) b.UnlockBits(bmData1) b2.UnlockBits(bmData2) End Sub Public Function EdgeEnhance(ByVal b As Bitmap, ByVal threshold As Byte) As Boolean Dim b2 As Bitmap = b.Clone Dim bmData1 As BitmapData = b.LockBits(New Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) Dim bmData2 As BitmapData = b2.LockBits(New Rectangle(0, 0, b2.Width, b2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb) Dim stride As Integer = bmData2.Stride Dim scan01 As IntPtr = bmData1.Scan0 Dim scan02 As IntPtr = bmData2.Scan0 Dim bytes As Integer = b.Height * b.Width * 3 Dim p01(bytes - 1) As Byte Dim p02(bytes - 1) As Byte System.Runtime.InteropServices.Marshal.Copy(scan01, p01, 0, bytes) System.Runtime.InteropServices.Marshal.Copy(scan02, p02, 0, bytes) Dim nWidth As Integer = b2.Width * 3 Dim nOffset As Integer = 0 Dim nPixel As Integer = 0, npixelmax As Integer = 0 Dim pos1 As Integer = stride + 3 Dim pos2 As Integer = stride + 3 Dim p2minusplus As Integer, p2plusminus As Integer, p2plusplus As Integer, p2minusminus As Integer Dim p2minusstride As Integer, p2plusstride As Integer Dim p2plus As Integer, p2minus As Integer For y As Integer = 1 To b.Height - 1 For x As Integer = 1 To nWidth - 3 p2minusplus = pos2 - stride + 3 p2plusminus = pos2 + stride - 3 p2plusplus = pos2 + stride + 3 p2minusminus = pos2 - stride - 3 p2minusstride = pos2 - stride p2plusstride = pos2 + stride p2minus = pos2 - 3 p2plus = pos2 + 3 If p2minusplus <= p02.Length - 1 And p2minusplus >= 0 And p2plusminus <= p02.Length - 1 And p2plusminus >= 0 And _ p2plusplus <= p02.Length - 1 And p2plusplus >= 0 And p2minusminus <= p02.Length - 1 And p2minusminus >= 0 And _ p2minusstride <= p02.Length - 1 And p2minusstride >= 0 And p2plusstride <= p02.Length - 1 And p2plusstride >= 0 And _ p2plus <= p02.Length - 1 And p2plus >= 0 And p2minus <= p02.Length - 1 And p2minus >= 0 And pos1 < p01.Length Then npixelmax = Math.Abs(CInt(p02(pos2 - stride + 3)) - CInt(p02(pos2 + stride - 3))) nPixel = Math.Abs(CInt(p02(pos2 + stride + 3)) - CInt(p02(pos2 - stride - 3))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2 - stride)) - CInt(p02(pos2 + stride))) If nPixel > npixelmax Then npixelmax = nPixel nPixel = Math.Abs(CInt(p02(pos2 + 3)) - CInt(p02(pos2 - 3))) If nPixel > npixelmax Then npixelmax = nPixel If npixelmax > threshold And npixelmax > p01(pos1) Then p01(pos1) = CByte(Math.Max(CInt(p01(pos1)), npixelmax)) End If End If pos1 += 1 pos2 += 1 Next pos1 += nOffset pos2 += nOffset Next System.Runtime.InteropServices.Marshal.Copy(p01, 0, scan01, bytes) b.UnlockBits(bmData1) b2.UnlockBits(bmData2) Return True End Function End Class
این کد کامل نیست!
دانلود کد فوق از طریق لینک زیر:
CannyInVisualBasic
رمز فایل : behsanandish.com