پردازش تصویر در OpenCV
تشخیص لبه Canny
هدف#
در این فصل، درباره موارد زیر یاد خواهیم گرفت:
- مفهوم تشخیص لبه Canny
- توابع OpenCV برای آن: cv.Canny()
تئوری#
تشخیص لبه Canny (Canny Edge Detection) یک الگوریتم محبوب تشخیص لبه است. این الگوریتم توسط John F. Canny در سال ۱۹۸۶ توسعه یافت. این یک الگوریتم چندمرحلهای است و هر مرحله را بررسی خواهیم کرد.
-
کاهش نویز (Noise Reduction)
از آنجایی که تشخیص لبه در برابر نویز موجود در تصویر حساس است، اولین گام حذف نویز از تصویر با یک فیلتر گاوسی 5x5 است. این را قبلاً در فصلهای پیشین دیدهایم.
-
یافتن گرادیان شدت روشنایی تصویر (Finding Intensity Gradient of the Image)
تصویر هموارشده سپس با یک کرنل Sobel در هر دو جهت افقی و عمودی فیلتر میشود تا مشتق اول در جهت افقی () و جهت عمودی () به دست آید. از این دو تصویر، میتوانیم گرادیان لبه و جهت آن را برای هر پیکسل به صورت زیر پیدا کنیم:
جهت گرادیان همیشه عمود بر لبههاست. این جهت به یکی از چهار زاویه نمایانگر جهتهای عمودی، افقی و دو جهت قطری گرد میشود.
-
حذف غیرماکزیمم (Non-maximum Suppression)
پس از به دست آوردن بزرگی و جهت گرادیان، یک پیمایش کامل از تصویر انجام میشود تا هر پیکسل ناخواستهای که ممکن است لبه را تشکیل ندهد حذف شود. برای این کار، در هر پیکسل، بررسی میشود که آیا آن یک ماکزیمم محلی در محله خود در جهت گرادیان است یا خیر. تصویر زیر را بررسی کنید:

نقطه A روی لبه (در جهت عمودی) قرار دارد. جهت گرادیان عمود بر لبه است. نقاط B و C در جهتهای گرادیان هستند. بنابراین نقطه A با نقاط B و C بررسی میشود تا ببینیم آیا یک ماکزیمم محلی تشکیل میدهد. اگر چنین باشد، برای مرحله بعدی در نظر گرفته میشود، در غیر این صورت، حذف میشود (به صفر تبدیل میشود).
به طور خلاصه، نتیجهای که به دست میآورید یک تصویر دودویی با «لبههای نازک» است.
-
آستانهگذاری هیسترزیس (Hysteresis Thresholding)
این مرحله تعیین میکند که کدام لبهها واقعاً لبه هستند و کدامها نیستند. برای این کار، به دو مقدار آستانه، minVal و maxVal نیاز داریم. هر لبهای با گرادیان شدت روشنایی بیشتر از maxVal قطعاً لبه است و آنهایی که زیر minVal هستند قطعاً لبه نیستند و بنابراین حذف میشوند. آنهایی که بین این دو آستانه قرار دارند بر اساس اتصالشان به عنوان لبه یا غیرلبه طبقهبندی میشوند. اگر به پیکسلهای «لبه قطعی» متصل باشند، بخشی از لبهها در نظر گرفته میشوند. در غیر این صورت، آنها نیز حذف میشوند. تصویر زیر را ببینید:

لبه A بالاتر از maxVal است، بنابراین به عنوان «لبه قطعی» در نظر گرفته میشود. اگرچه لبه C زیر maxVal است، اما به لبه A متصل است، بنابراین آن هم به عنوان لبه معتبر در نظر گرفته شده و کل منحنی را دریافت میکنیم. اما لبه B، اگرچه بالای minVal است و در همان ناحیه لبه C قرار دارد، به هیچ «لبه قطعی» متصل نیست، بنابراین حذف میشود. بنابراین بسیار مهم است که minVal و maxVal را به درستی انتخاب کنیم تا نتیجه صحیح به دست آید.
این مرحله همچنین نویزهای پیکسلی کوچک را بر اساس این فرض که لبهها خطوط طولانی هستند حذف میکند.
پس در نهایت آنچه به دست میآوریم لبههای قوی در تصویر است.
تشخیص لبه Canny در OpenCV#
OpenCV تمام موارد بالا را در یک تابع واحد به نام cv.Canny() قرار میدهد. میبینیم چگونه از آن استفاده کنیم. آرگومان اول تصویر ورودی ما است. آرگومانهای دوم و سوم به ترتیب minVal و maxVal ما هستند. آرگومان چهارم aperture_size است. این اندازه کرنل Sobel است که برای یافتن گرادیانهای تصویر استفاده میشود. به طور پیشفرض ۳ است. آرگومان آخر L2gradient است که معادله یافتن بزرگی گرادیان را مشخص میکند. اگر True باشد، از معادله ذکرشده در بالا استفاده میکند که دقیقتر است، در غیر این صورت از این تابع استفاده میکند: . به طور پیشفرض False است.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
نتیجه را در زیر ببینید:

منابع تکمیلی#
- آشکارساز لبه Canny در ویکیپدیا
- آموزش تشخیص لبه Canny توسط Bill Green، ۲۰۰۲.
تمرینها#
- یک برنامه کوچک بنویسید که تشخیص لبه Canny را پیدا کند و مقادیر آستانه آن بتوانند با دو trackbar تغییر یابند. به این ترتیب میتوانید تأثیر مقادیر آستانه را درک کنید.