از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
تشخیص لبه OpenCV در پایتون با ()cv2.Canny
سرفصلهای مطلب
معرفی
تشخیص لبه کاری است که ما به طور طبیعی انجام میدهیم، اما وقتی صحبت از تعریف قوانین برای رایانهها میشود، آسان نیست. در حالی که روشهای مختلفی ابداع شدهاند، روش حاکم در سال 1986 توسط جان اف.
این سریع، نسبتاً قوی است و تقریباً بهترین عملکرد را برای نوع تکنیکی که دارد انجام می دهد. در پایان راهنما، میدانید که چگونه تشخیص لبهها را در زمان واقعی انجام دهید روی ویدئوها، و تولید چیزی در امتداد خطوط:
تشخیص لبه Canny
روش Canny چیست؟ از چهار عملیات مجزا تشکیل شده است:
- صاف کردن گاوسی
- شیب محاسباتی
- سرکوب غیر حداکثری
- آستانه هیسترزیس
صاف کردن گاوسی به عنوان اولین گام برای “آت کردن” تصویر ورودی و کاهش نویز استفاده می شود و خروجی نهایی را بسیار تمیزتر می کند.
گرادیان های تصویر در برنامه های قبلی برای تشخیص لبه استفاده شده است. مهمتر از همه، فیلترهای Sobel و Scharr متکی هستند روی گرادیان های تصویر فیلتر سوبل به دو هسته (Gx و گی)، جایی که Gx تغییرات افقی را تشخیص می دهد، در حالی که گی تغییرات عمودی را تشخیص می دهد:
وقتی آنها را روی یک تصویر می کشید، هر کدام خطوط را در جهت مربوطه خود “برمی گیرند” (تاکید می کنند). هسته های Scharr به همین ترتیب با مقادیر متفاوت کار می کنند:
این فیلترها، هنگامی که روی تصویر پیچیده می شوند، نقشه های ویژگی را تولید می کنند:
اعتبار تصویر: ویکی پدیا
برای این نقشه های ویژگی، می توانید محاسبه کنید قدر گرادیان و جهت گرادیان – یعنی تغییر چقدر شدید است (چقدر احتمال دارد چیزی لبه باشد) و تغییر در کدام جهت است. از آنجایی که Gy نشان دهنده تغییر عمودی (شیب Y) و Gx نشان دهنده تغییر افقی (شیب X) است – می توانید مقدار را با استفاده از قضیه فیثاغورث به سادگی محاسبه کنید تا فرضیه مثلثی که توسط “چپ” تشکیل شده است را بدست آورید. جهت های “درست”:
$$
{G} ={\sqrt {{{G} _{x}}^{2}+{{G} _{y}}^{2}}}
$$
با استفاده از اندازه و جهت، می توانید تصویری با لبه های برجسته تولید کنید:
اعتبار تصویر: ویکی پدیا
با این حال – می توانید ببینید که چقدر سر و صدا از بافت آجرها نیز گرفته شده است! گرادیان های تصویر به نویز بسیار حساس هستند. به همین دلیل است که فیلترهای Sobel و Scharr به عنوان مؤلفه مورد استفاده قرار گرفتند، اما نه تنها رویکرد در روش Canny. صاف کردن گاوسی در اینجا نیز کمک می کند.
سرکوب غیر حداکثری
یک مشکل قابل توجه در مورد فیلتر Sobel این است که لبه ها واقعاً واضح نیستند. اینطور نیست که کسی یک مداد برداشته و خطی بکشد تا یک هنر خطی از تصویر ایجاد کند. لبه ها معمولاً در تصاویر چندان واضح نیستند، زیرا نور به تدریج پخش می شود. با این حال، ما میتوانیم خط مشترک را در لبهها پیدا کنیم و بقیه پیکسلهای اطراف آن را سرکوب کنیم و به جای آن یک خط جدایی تمیز و نازک ایجاد کنیم. این به عنوان سرکوب غیر حداکثری شناخته می شود! پیکسلهای غیرحداکثر (آنهایی که کوچکتر از پیکسلهایی که در یک فیلد محلی کوچک با آن مقایسه میکنیم، مانند یک هسته 3×3) سرکوب میشوند. این مفهوم برای وظایف بیشتر از این قابل استفاده است، اما بیایید فعلاً آن را به این زمینه پیوند دهیم.
آستانه هیسترزیس
بسیاری از غیر لبهها را میتوان و احتمالاً بهدلیل شرایط نور، مواد موجود در تصویر و غیره بهعنوان لبه ارزیابی کرد. به دلیل دلایل مختلفی که این محاسبات اشتباه رخ میدهد – ارزیابی خودکار از اینکه یک لبه قطعاً چیست و چه چیزی نیست، دشوار است. ‘t. با فرض اینکه لبه های “واقعی” شدیدتر از لبه های “جعلی” هستند، می توانید گرادیان ها را در آستانه قرار دهید، و فقط شیب های قوی تر را شامل کنید.
Thresholding تقریباً به همان روش معمول کار می کند – اگر گرادیان زیر یک آستانه پایین تر است، آن را حذف کنید (آن را به صفر برسانید) و اگر بالاتر از یک آستانه بالا است، آن را حفظ کنید. همه چیز بین کران پایین و کران بالا در “منطقه خاکستری” است. اگر هر لبه ای در بین آستانه ها به a متصل شود لبه قطعی (آنهایی که بالاتر از آستانه هستند) – آنها نیز لبه در نظر گرفته می شوند. اگر آنها متصل نباشند، احتمالاً مصنوعات یک لبه اشتباه محاسبه شده هستند.
این آستانه هیسترزیس است! در واقع به تمیز کردن خروجی نهایی و حذف لبه های کاذب کمک می کند روی آنچه شما به عنوان لبه کاذب طبقه بندی می کنید. برای یافتن مقادیر آستانه خوب، معمولاً با مرزهای مختلف پایین و بالایی برای آستانه ها آزمایش می کنید، یا از یک روش خودکار مانند روش Otsu یا روش مثلث استفاده می کنید.
بیایید یک تصویر را بارگذاری کنیم و آن را در مقیاس خاکستری قرار دهیم (Canny، همانطور که Sobel/Scharr به تصاویر نیاز دارد که در مقیاس خاکستری باشند):
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('finger.jpg', cv2.IMREAD_GRAYSCALE)
img_blur = cv2.GaussianBlur(img, (3,3), 0)
plt.imshow(img_blur, cmap='gray')
تصویر نزدیک انگشت به عنوان یک محل آزمایش خوب برای تشخیص لبه عمل می کند – تشخیص اثر انگشت از روی تصویر کار آسانی نیست، اما می توانیم آن را تقریبی کنیم.
تشخیص لبه روی تصاویر با ()cv2.Canny
الگوریتم Canny را می توان با استفاده از OpenCV اعمال کرد Canny()
روش:
cv2.Canny(input_img, lower_bound, upper_bound)
یافتن تعادل مناسب بین کران پایین و کران بالا می تواند مشکل باشد. اگر هر دو کم باشند – لبه های کمی خواهید داشت. اگر کران پایین پایین و بالا بالا باشد – نویز خواهید داشت. اگر هر دو بالا و نزدیک به یکدیگر باشند – لبه های کمی خواهید داشت. نقطه مناسب فاصله کافی بین مرزها دارد و آنها را دارد روی مقیاس مناسب آزمایش کنید!
تصویر ورودی با روش Canny تار می شود، اما اغلب اوقات، شما از محو کردن آن سود خواهید برد. قبل از آن نیز وارد می شود. این روش قبل از انجام بقیه عملیات، یک تاری گاوسی 5×5 را روی ورودی اعمال میکند، اما حتی با این تاری، مقداری نویز همچنان میتواند از آن عبور کند، بنابراین قبل از وارد کردن آن به الگوریتم، تصویر را تار کردهایم:
edge = cv2.Canny(img_blur, 20, 30)
fig, ax = plt.subplots(1, 2, figsize=(18, 6), dpi=150)
ax(0).imshow(img, cmap='gray')
ax(1).imshow(edge, cmap='gray')
این نتیجه در:
ارزش های 20
و 30
در اینجا دلخواه نیستند – من روش را آزمایش کرده ام روی پارامترهای مختلف، و مجموعه ای را انتخاب کرد که به نظر می رسید نتیجه مناسبی ایجاد کند. آیا می توانیم سعی کنیم این را خودکار کنیم؟
آستانه خودکار برای ()cv2.Canny؟
آیا می توانید مجموعه ای بهینه از مقادیر آستانه را پیدا کنید؟ بله، اما همیشه کار نمی کند. شما می توانید محاسبه خود را برای مقداری خوب انجام دهید و سپس محدوده را با a تنظیم کنید sigma
در اطراف آن آستانه:
lower_bound = (1-sigma)*threshold
upper_bound = (1+sigma)*threshold
چه زمانی sigma
، می گویند، 0.33
– حدود خواهد بود 0.66*threshold
و 1.33*threshold
، اجازه می دهد حدود 1/3 در اطراف آن باشد. اگرچه، پیدا کردن threshold
سخت تر است OpenCV روش Otsu (برای تصاویر دو وجهی عالی کار می کند) و روش مثلث را در اختیار ما قرار می دهد. بیایید هر دوی آنها را امتحان کنیم، و همچنین یک میانه ساده از مقادیر پیکسل را به عنوان گزینه سوم در نظر بگیریم:
otsu_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_OTSU)
triangle_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_TRIANGLE)
manual_thresh = np.median(img_blur)
def get_range(threshold, sigma=0.33):
return (1-sigma) * threshold, (1+sigma) * threshold
otsu_thresh = get_range(otsu_thresh)
triangle_thresh = get_range(triangle_thresh)
manual_thresh = get_range(manual_thresh)
print(f"Otsu's Threshold: {otsu_thresh} \nTriangle Threshold: {triangle_thresh} \nManual Threshold: {manual_thresh}")
این نتیجه در:
Otsu's Threshold: (70.35, 139.65)
Triangle Threshold: (17.419999999999998, 34.58)
Manual Threshold: (105.18999999999998, 208.81)
اینها خیلی متفاوت هستند! از مقادیری که قبلا دیدهایم، میتوانیم پیشبینی کنیم که روش مثلث در اینجا بهترین عملکرد را داشته باشد. آستانه دستی چندان آگاه نیست، زیرا فقط مقدار پیکسل میانه را می گیرد و در نهایت آستانه پایه بالایی دارد که در محدوده وسیعی برای این تصویر چند برابر می شود. روش Otsu کمتر از این آسیب می بیند، اما با این وجود رنج می برد.
اگر ما اجرا کنیم Canny()
روش با این محدوده های آستانه:
edge_otsu = cv2.Canny(img_blur, *otsu_thresh)
edge_triangle = cv2.Canny(img_blur, *triangle_thresh)
edge_manual = cv2.Canny(img_blur, *manual_thresh)
fig, ax = plt.subplots(1, 3, figsize=(18, 6), dpi=150)
ax(0).imshow(edge_otsu, cmap='gray')
ax(1).imshow(edge_triangle, cmap='gray')
ax(2).imshow(edge_manual, cmap='gray')
توجه داشته باشید: تابع انتظار آرگومان های متعدد دارد و آستانه های ما یک تاپل واحد هستند. ما میتوانیم تخریب تاپل را با قرار دادن پیشوند با آرگومان های متعدد تبدیل می کند *
. این کارها روی لیست ها و مجموعه ها را نیز ارائه می دهد و راهی عالی برای ارائه چندین آرگومان پس از به دست آوردن آنها با ابزارهای برنامه ای است.
این نتیجه در:
روش مثلث در اینجا خیلی خوب کار کرد! این تضمینی نیست که در موارد دیگر نیز به خوبی کار کند.
تشخیص لبه در زمان واقعی روی ویدیوهایی با ()cv2.Canny
در نهایت، بیایید تشخیص لبه Canny را در زمان واقعی برای یک ویدیو اعمال کنیم! ما ویدیوی در حال پردازش (هر فریم همانطور که انجام شده است) را با استفاده از آن نمایش خواهیم داد cv2.imshow()
که پنجره ای را با قاب مورد نظر ما نمایش می دهد. اگرچه، ما همچنین ویدیو را در یک فایل MP4 ذخیره میکنیم که بعداً قابل بررسی و اشتراکگذاری است.
برای بارگذاری یک ویدیو با استفاده از OpenCV، از VideoCapture()
روش. اگر عبور کنیم 0
– از وب کم فعلی ضبط می کند، بنابراین می توانید کد را اجرا کنید روی وب کم شما نیز! اگر نام فایلی را ارسال کنید، فایل را بارگیری می کند:
def edge_detection_video(filename):
cap = cv2.VideoCapture(filename)
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (int(cap.get(3)), int(cap.get(4))), isColor=False)
while cap.isOpened():
(ret, frame) = cap.read()
if ret == True:
frame = cv2.GaussianBlur(frame, (3, 3), 0)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edge = cv2.Canny(frame, 50, 100)
out.write(edge)
cv2.imshow('Edge detection', edge)
else:
break
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
edge_detection_video('secret_video.mp4')
این VideoWriter
چندین پارامتر را می پذیرد – نام فایل خروجی، FourCC (چهار کد کدک، نشان دهنده کدک مورد استفاده برای رمزگذاری ویدیو)، نرخ فریم و وضوح به صورت یک تایی. برای حدس زدن یا تغییر اندازه ویدیو – از عرض و ارتفاع ویدیوی اصلی استفاده کرده ایم که از طریق VideoCapture
نمونهای که حاوی دادههایی درباره خود ویدیو است، مانند عرض، ارتفاع، تعداد کل فریمها و غیره.
در حالی که کپچر باز است، سعی می کنیم فریم بعدی را با آن بخوانیم cap.read()
، که یک کد نتیجه و فریم بعدی را برمی گرداند. کد نتیجه این است True
یا False
، نشان دهنده وجود فریم بعدی یا عدم وجود آن است. فقط زمانی که یک قاب وجود دارد، ما سعی خواهیم کرد process آن را بیشتر، در غیر این صورت، ما حلقه را بشکنیم. برای هر فریم معتبر، آن را از طریق یک تاری گاوسی اجرا می کنیم، آن را به مقیاس خاکستری تبدیل می کنیم، اجرا می کنیم cv2.Canny()
روی آن را بنویسید و با استفاده از VideoWriter
روی دیسک، و با استفاده از آن نمایش داده شود cv2.imshow()
برای نمایش زنده
در نهایت، ضبط و ویدئونویس را منتشر میکنیم، زیرا هر دو با فایلها کار میکنند روی دیسک، و تمام ویندوزهای موجود را از بین ببرید.
وقتی روش را با a اجرا می کنید secret_video.mp4
ورودی – یک پنجره ظاهر می شود و پس از اتمام آن، یک فایل در دایرکتوری کاری شما:
نتیجه
در این راهنما، نگاهی به روش عملکرد تشخیص لبه Canny، و اجزای تشکیل دهنده آن – صاف کردن گاوسی، فیلترهای سوبل و گرادیان های تصویر، سرکوب غیر حداکثری و آستانه هیسترزیس انداخته ایم. در نهایت، روشهایی را برای جستجوی محدوده آستانه خودکار برای تشخیص لبه Canny با استفاده از آن بررسی کردهایم cv2.Canny()
، و از این تکنیک استفاده کرد روی یک ویدیو، که تشخیص لبهها را در زمان واقعی ارائه میکند و نتایج را در یک فایل ویدیویی ذخیره میکند.
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-03 05:57:03