از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
OpenCV Thresholding در پایتون با cv2.threshold()
سرفصلهای مطلب
معرفی
Thresholding یک تکنیک ساده و کارآمد برای انجام تقسیم بندی اولیه در یک تصویر و باینریزه کردن آن (تبدیل آن به یک تصویر باینری) است که در آن پیکسل ها یا 0
یا 1
(یا 255
اگر از اعداد صحیح برای نشان دادن آنها استفاده می کنید).
به طور معمول، میتوانید از آستانهگذاری برای انجام بخشبندی ساده پسزمینه-پیشزمینه در یک تصویر استفاده کنید، و به انواع مختلف خلاصه میشود. روی یک تکنیک ساده برای هر پیکسل:
if pixel_value > threshold:
pixel_value = MAX
else:
pixel_value = 0
این ضروری است process به عنوان شناخته شده است آستانه باینری. اکنون – راه های مختلفی وجود دارد که می توانید این ایده کلی را تغییر دهید، از جمله معکوس کردن عملیات (تغییر کردن >
با a امضا کنید <
علامت)، تنظیم pixel_value
به threshold
به جای حداکثر مقدار/0 (معروف به کوتاه کردن)، حفظ مقدار pixel_value
خودش اگر بالاتر از threshold
یا اگر زیر آن باشد threshold
.
همه اینها به راحتی در OpenCV پیاده سازی شده اند:
cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
cv2.THRESH_TRUNC
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
… به ترتیب. اینها روشهای نسبتاً سادهای هستند، زیرا نسبتاً ساده هستند، زمینه را در تصاویر در نظر نمیگیرند، اطلاعاتی در مورد اشکال رایج دارند و غیره. برای این ویژگیها – ما باید از نظر محاسباتی بسیار گرانتر و قدرتمندتر استفاده کنیم تکنیک.
اکنون، حتی با روش های “ساده لوحانه” – مقداری برای یافتن آستانه های خوب، می توان اکتشافی را به کار برد، که شامل روش Otsu و روش مثلث می شود:
cv2.THRESH_OTSU
cv2.THRESH_TRIANGLE
توجه داشته باشید: آستانه OpenCV یک تکنیک ابتدایی است و به تغییرات نور و گرادیان، ناهمگونی رنگ و غیره حساس است. بهترین کاربرد آن است. روی تصاویر نسبتاً تمیز، پس از محو کردن آنها برای کاهش نویز، بدون تفاوت رنگ زیاد در اشیایی که می خواهید قطعه بندی کنید.
راه دیگر برای غلبه بر برخی از مسائل مربوط به آستانه اولیه با یک مقدار آستانه، استفاده از آستانه تطبیقی که یک مقدار آستانه اعمال می کند روی هر منطقه کوچک در یک تصویر، به جای جهانی.
آستانه گذاری ساده با OpenCV
آستانه گذاری در OpenCV پایتون API از طریق انجام می شود cv2.threshold()
روش – که یک تصویر (آرایه NumPy، نشان داده شده با اعداد صحیح)، آستانه، حداکثر مقدار و روش آستانه را می پذیرد (چگونه threshold
و maximum_value
استفاده می شود):
img = cv2.imread('objects.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blurred = cv2.GaussianBlur(img, (7, 7), 0)
ret, img_masked = cv2.threshold(blurred, 220, 255, cv2.THRESH_BINARY)
کد بازگشتی فقط آستانه اعمال شده است:
print(f"Threshold: {ret}")
در اینجا، از آنجایی که آستانه است 220
و ما از آن استفاده کرده ایم THRESH_BINARY
روش – هر مقدار پیکسل بالا 220
افزایش خواهد یافت 255
، در حالی که هر پیکسل زیر است 220
به پایین خواهد آمد 0
، ایجاد یک تصویر سیاه و سفید، با یک “ماسک”، پوشش اشیاء پیش زمینه.
چرا 220؟ دانستن اینکه تصویر چگونه به نظر می رسد به شما امکان می دهد تا حدس هایی تقریبی در مورد آستانه ای که می توانید انتخاب کنید، انجام دهید. در عمل، به ندرت می خواهید یک آستانه دستی تنظیم کنید، و ما انتخاب آستانه خودکار را در یک لحظه پوشش خواهیم داد.
بیایید نتیجه را ترسیم کنیم! پنجرههای OpenCV ممکن است کمی مشکل باشند، بنابراین تصویر اصلی، تصویر تار و نتایج را با استفاده از Matplotlib ترسیم میکنیم:
fig, ax = plt.subplots(1, 3, figsize=(12, 8))
ax(0).imshow(img)
ax(1).imshow(blurred)
ax(2).imshow(img_masked)
روش های آستانه گذاری
همانطور که قبلا ذکر شد، روشهای مختلفی وجود دارد که میتوانید از مقدار آستانه و حداکثر در یک تابع استفاده کنید. در ابتدا نگاهی به آستانه باینری انداختهایم. بیایید لیستی از روش ها ایجاد کنیم و آنها را یکی یکی اعمال کنیم و نتایج را ترسیم کنیم:
methods = (cv2.THRESH_BINARY, cv2.THRESH_BINARY_INV, cv2.THRESH_TRUNC, cv2.THRESH_TOZERO, cv2.THRESH_TOZERO_INV)
names = ('Binary Threshold', 'Inverse Binary Threshold', 'Truncated Threshold', 'To-Zero Threshold', 'Inverse To-Zero Threshold')
def thresh(img_path, method, index):
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blurred = cv2.GaussianBlur(img, (7, 7), 0)
ret, img_masked = cv2.threshold(blurred, 220, 255, method)
fig, ax = plt.subplots(1, 3, figsize=(12, 4))
fig.suptitle(names(index), fontsize=18)
ax(0).imshow(img)
ax(1).imshow(blurred)
ax(2).imshow(img_masked)
plt.tight_layout()
for index, method in enumerate(methods):
thresh('coins.jpeg', method, index)
THRESH_BINARY
و THRESH_BINARY_INV
معکوس یکدیگر هستند و یک تصویر را باینری می کنند 0
و 255
، اختصاص دادن آنها به پس زمینه و پیش زمینه و بالعکس.
THRESH_TRUNC
“دوتایی می کند“تصویر بین threshold
و 255
.
THRESH_TOZERO
و THRESH_TOZERO_INV
دوتایی کردن بین 0
و مقدار پیکسل فعلی (src(x, y)
). بیایید نگاهی به تصاویر به دست آمده بیندازیم:
این روشها به اندازه کافی شهودی هستند – اما، چگونه میتوانیم یک مقدار آستانه خوب را خودکار کنیم، و اصلاً مقدار «آستانه خوب» به چه معناست؟ اکثر نتایج تا کنون دارای ماسک های غیر ایده آل بودند، با علامت ها و لکه هایی در آنها. این به دلیل تفاوت در سطوح بازتابنده سکه ها اتفاق می افتد – به دلیل تفاوت در روش انعکاس نور برجستگی ها، رنگ آنها یکنواخت نیست.
ما میتوانیم تا حدی با یافتن آستانه جهانی بهتر با آن مبارزه کنیم.
آستانه گذاری خودکار/بهینه شده با OpenCV
OpenCV از دو روش موثر جستجوی آستانه جهانی – روش Otsu و روش Triangle استفاده می کند.
روش Otsu فرض می کند که کار می کند روی دوحالته تصاویر. تصاویر دو حالته تصاویری هستند که هیستوگرام رنگی آنها فقط شامل دو پیک است (یعنی فقط دو مقدار پیکسل مجزا دارند). با توجه به اینکه قله ها هر کدام متعلق به کلاسی مانند “پس زمینه” و “پیش زمینه” هستند – آستانه ایده آل درست در وسط آنها قرار دارد.
اعتبار تصویر: https://scipy-lectures.org/
میتوانید برخی از تصاویر را با تاریهای گاوسی دووجهیتر کنید، اما نه همه.
یک الگوریتم جایگزین الگوریتم مثلث است که فاصله بین حداکثر و حداقل هیستوگرام سطح خاکستری را محاسبه کرده و یک خط ترسیم می کند. نقطه ای که آن خط حداکثر دورتر از بقیه هیستوگرام باشد به عنوان آستانه انتخاب می شود:
هیچ رقابتی بین آنها وجود ندارد – هر کدام کار می کنند روی انواع مختلفی از تصاویر، بنابراین بهتر است آنها را امتحان کنید و ببینید کدامیک نتیجه بهتری را نشان می دهد. هر دوی اینها یک تصویر در مقیاس خاکستری را فرض می کنند، بنابراین باید تصویر ورودی را از طریق خاکستری به خاکستری تبدیل کنیم. cv2.cvtColor()
:
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
ret, mask1 = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)
ret, mask2 = cv2.threshold(blurred, 0, 255, cv2.THRESH_TRIANGLE)
masked = cv2.bitwise_and(img, img, mask=mask1)
بیایید تصویر را با هر دو روش اجرا کنیم و نتایج را تجسم کنیم:
methods = (cv2.THRESH_OTSU, cv2.THRESH_TRIANGLE)
names = ('Otsu Method', 'Triangle Method')
def thresh(img_path, method, index):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
ret, img_masked = cv2.threshold(blurred, 0, 255, method)
print(f"Threshold: {ret}")
fig, ax = plt.subplots(1, 3, figsize=(12, 5))
fig.suptitle(names(index), fontsize=18)
ax(0).imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax(1).imshow(cv2.cvtColor(gray, cv2.COLOR_BGR2RGB))
ax(2).imshow(cv2.cvtColor(img_masked, cv2.COLOR_BGR2RGB))
for index, method in enumerate(methods):
thresh('coins.jpeg', method, index)
در اینجا، روش مثلث از روش Otsu بهتر عمل می کند، زیرا تصویر دو حالته نیست:
import numpy as np
img = cv2.imread('coins.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
histogram_gray, bin_edges_gray = np.histogram(gray, bins=256, range=(0, 255))
histogram_blurred, bin_edges_blurred = np.histogram(blurred, bins=256, range=(0, 255))
fig, ax = plt.subplots(1, 2, figsize=(12, 4))
ax(0).plot(bin_edges_gray(0:-1), histogram_gray)
ax(1).plot(bin_edges_blurred(0:-1), histogram_blurred)
با این حال، واضح است که چگونه روش مثلث توانسته با تصویر کار کند و نتیجه رضایتبخشتری ایجاد کند.
محدودیت های OpenCV Thresholding
آستانه گذاری با OpenCV ساده، آسان و کارآمد است. با این حال، نسبتاً محدود است. به محض معرفی عناصر رنگارنگ، پسزمینههای غیریکنواخت و تغییر شرایط نوری – آستانه جهانی به عنوان یک مفهوم بسیار سفت و سخت میشود.
تصاویر معمولاً برای اینکه یک آستانه کافی نباشد بسیار پیچیده هستند و تا حدی می توان از طریق آن به آن پرداخت آستانه تطبیقی، جایی که بسیاری از آستانه های محلی به جای یک آستانه جهانی استفاده می شود. در حالی که محدود است، آستانه تطبیقی بسیار انعطاف پذیرتر از آستانه جهانی است.
نتیجه
در سالهای اخیر، تقسیمبندی باینری (مانند کاری که در اینجا انجام دادیم) و تقسیمبندی چند برچسبی (جایی که میتوانید تعداد دلخواه کلاسها را کدگذاری کنید) با موفقیت با شبکههای یادگیری عمیق مدلسازی شدهاند که بسیار قدرتمندتر و انعطافپذیرتر هستند. علاوه بر این، آنها می توانند زمینه جهانی و محلی را در تصاویری که در حال تقسیم بندی هستند رمزگذاری کنند. نقطه ضعف این است – برای آموزش آنها به داده ها و همچنین زمان و تخصص نیاز دارید.
برای روی-the-fly، آستانه ساده، می توانید از OpenCV استفاده کنید. برای تقسیم بندی دقیق و در سطح تولید، باید از شبکه های عصبی استفاده کنید.
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-03 10:09:06