آستانه گذاری ساده-تطبیقی -Otsu، موضوع این مقاله است و مرسوم ترین خصوصیت تصویر برای آستانه گذاری، سطح خاکستری پیکسل است:
برای تجزیه و تحلیل اگر T آستانه باشد:
g(x,y) = 0 if f(x,y) < T و g(x,y) = 1 if f(x,y) ≥ T
اگر دو تا آستانه داشته باشیم T1 < T1 :
g(x,y) = 0 if f(x,y) < T1 OR f(x,y) > T2 and g(x,y) = 1 if T1 ≤ f(x,y) ≤ T2
مشکل اصلی این است که چگونه یک یا تعدادی آستانه مناسب، برای جدا کردن یک یا چند شی دلخواه از پس زمینه، انتخاب کنیم.
در بسیاری از موارد عملی، آستانه گذاری ساده، قادر به جداسازی اشیاء مد نظر نیست، همانطور که در تصاویر بالا نشان داده شده است.
cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
src : آرایه ورودی همان تصویر ورودی( تک کاناله(خاکستری )، ۸ بیتی یا ۳۲ بیتی شناور)
dst : آرایه یا تصویر خروجی که هم اندازه و هم نوع با تصویر ورودی است.
thresh : مقدار آستانه
maxval : حداکثر مقدار برای استفاده دو نو آستانه گذاری ساده THRESH_BINARY
و THRESH_BINARY_INV
.
type : نوع آستانه گذاری
OpenCV سبک های مختلف آستانه گذاری ساده را فراهم می کند و توسط پارامتر چهارم تابع تعیین می شود:
ساده ترین شکل آستانه گذاری ، آستانه گذاری باینری نامیده می شود.
اگر (src (x، y بزرگتر از thresh باشد، مقدار (dst (x، y(تصویر مقصد) به maxValue تنظیم میشود، در غیر اینصورت آن را صفر تنظیم می کند.
ifsrc(x,y) &amp;amp;amp;amp;amp;amp;amp;gt; thresh dst(x,y) =maxValue else dst(x,y) =0
مشخص است. اگر مقدار پیکسل بیشتر از یک مقدار آستانه باشد، مقدار (ممکن است سفید باشد) تعیین می شود، در غیر اینصورت یک مقدار دیگر (ممکن است سیاه باشد) تعیین می شود.
دو خروجی به دست می آید.یکی از آنها retval است که بعدا توضیح داده خواهد شد.خروجی دوم تصویر آستانه ای است. بنابراین حتما باید از دو متغیر استفاده کنیم.
کد:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('threshold.png') #&amp;amp;amp;amp;amp;amp;amp;lt;strong&amp;amp;amp;amp;amp;amp;amp;gt;thresh&amp;amp;amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;amp;amp;lt;/strong&amp;amp;amp;amp;amp;amp;amp;gt;= 0 و&amp;amp;amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;amp;amp;lt;strong&amp;amp;amp;amp;amp;amp;amp;gt;maxValue&amp;amp;amp;amp;amp;amp;amp;lt;/strong&amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;nbsp;= 255. ret,thresh1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY) #با تغییر آستانه به ۳۵، شدت نور بالاتر از ۳۵ نمایش داده میشود. ret,thresh2 = cv2.threshold(img,35,255,cv2.THRESH_BINARY) ret,thresh3 = cv2.threshold(img,65,255,cv2.THRESH_BINARY) #با آستانه ۱۲۷ و بیشترین مقدار ۱۲۵ ، شدت نور بالاتر از ۱۲۷ و با رنگ ۱۲۸ نمایش داده میشود. ret,thresh4 = cv2.threshold(img,127,128,cv2.THRESH_BINARY) ret,thresh5 = cv2.threshold(img,0,128,cv2.THRESH_BINARY) titles = ['Original Image','THRESH1','THRESH12','THRESH3','THRESH4','THRESH6'] images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] for i in range(6): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('threshold.png') ret,thresh1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY) # تغییر بیشترین مقدار به ۱۲۸، مقدار بخش آستانه گذاری شده را به ۱۲۸ تنظیم می کند. ret,thresh2 = cv2.threshold(img,0,128,cv2.THRESH_BINARY) ret,thresh3 = cv2.threshold(img,0,201,cv2.THRESH_BINARY) ret,thresh4 = cv2.threshold(img,0,66,cv2.THRESH_BINARY) ret,thresh5 = cv2.threshold(img,0,33,cv2.THRESH_BINARY) titles = ['Original Image','THRESH255','THRESH128','THRESH201','THRESH6','THRESH33'] images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] for i in range(6): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
Inverse Binary Thresholding ( type = THRESH_BINARY_INV )
معکوس کردن آستانه گذاری دوتایی (فقط دو مقار سفید و سیاه را داریم) است.
اگر پیکسل منبع مربوطه بیشتر از آستانه باشد پیکسل مقصد به صفر تنظیم می شود و در غیر این صورت، به maxValue تنظیم می شود.
if src(x,y) &amp;amp;amp;amp;amp;amp;gt; thresh dst(x,y) = 0 else dst(x,y) =maxValue
کد:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('threshold.png') ret,thresh1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY) ret,thresh2 = cv2.threshold(img,0,128,cv2.THRESH_BINARY_INV) titles = ['Original Image','THRESH_BINARY','THRESH_BINARY_INV'] images = [img, thresh1, thresh2] for i in range(3): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
در این نوع آستانه، پیکسل مقصد به آستانه (thresh) تنظیم می شود اگر مقدار پیکسل منبع بیشتر از آستانه باشد. در غیر این صورت آن را به مقدار پیکسل منبع تنظیم می کنیم. maxValue نادیده گرفته شده است.
الگوریتم:
if src(x,y) &amp;amp;amp;amp;amp;amp;amp;gt; thresh dst(x,y) =thresh else dst(x,y) =src(x,y)
توجه داشته باشید که تمام مقادیر بالای آستانه (۱۲۷) به ۱۲۷ تنظیم شده و تمام مقادیر کمتر یا برابر ۱۲۷ بدون تغییر هستند.
maxValue نادیده گرفته شده است
کد:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('threshold.png',0) ret,thresh1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY) ret,thresh2 = cv2.threshold(img,0,127,cv2.THRESH_BINARY_INV) ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC) titles = ['Original Image','THRESH_BINARY','THRESH_BINARY_INV','THRESH_TRUNC'] images = [img, thresh1, thresh2,thresh3] for i in range(4): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
در این نوع آستانه، اگر مقدار پیکسل منبع بیشتر از آستانه باشد، مقدار پیکسل مقصد به مقدار پیکسل منبع مربوطه تنظیم می شود. در غیر این صورت صفر است. maxValue نادیده گرفته شده است.
الگوریتم:
if src(x,y) &amp;amp;amp;amp;amp;amp;gt; thresh dst(x,y) =src(x,y) else dst(x,y) =&amp;amp;amp;amp;amp;amp;gt;0
کد:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('threshold.png',0) ret,thresh1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY) ret,thresh2 = cv2.threshold(img,0,127,cv2.THRESH_BINARY_INV) ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC) ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO) titles = ['Original Image','THRESH_BINARY','THRESH_BINARY_INV','THRESH_TRUNC','THRESH_TOZERO'] images = [img, thresh1, thresh2,thresh3,thresh4] for i in range(5): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
در این نوع آستانه، اگر مقدار پیکسل منبع بیشتر از آستانه باشد، مقدار پیکسل مقصد به مقدار صفر تنظیم می شود . در غیر این صورت آن را به مقدار پیکسل منبع تنظیم می کنیم. maxValue نادیده گرفته شده است.
الگوریتم:
if src(x,y) &amp;amp;amp;amp;amp;amp;gt; thresh dst(x,y) =0 else dst(x,y) =src(x,y)
کد:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('threshold.png',0) ret,thresh1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY) ret,thresh2 = cv2.threshold(img,0,127,cv2.THRESH_BINARY_INV) ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC) ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO) ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)titles = ['Original Image','THRESH_BINARY','THRESH_BINARY_INV','THRESH_TRUNC','THRESH_TOZERO','THRESH_TOZERO_INV'] images = [img, thresh1, thresh2,thresh3,thresh4,thresh5] for i in range(6): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([])plt.show()
در روش Simple Thresholding، تصاویری که دارای روشنایی متفاوت در قسمتهای مختلف می باشند خروجی جالبی ندارند. ولی در آستانه گذاری تطبیقی یا سازگاری، بر خلاف آستانه گذاری ساده، مقدار آستانه در هر مکان پیکسل، بستگی به شدت روشنایی پیکسله ای همسایه دارد. در واقع مقدار آستانه برای ناحیه ی کوچکی از تصویر محاسبه می شود و ما دارای آستانه های متفاوت زیادی می باشیم.پیش فرض این است که مناطق کوچکتر تصویر، احتمال بیشتری برای روشنایی یکنواخت دارند، بنابراین برای آستانه گذاری بیشتر مناسب هستند.
**** برای محاسبه استانه محلی مراحل زیر طی می شود:****
۱) یک بلوک یا ناحیه bxb، اطراف مکان پیکسل توسط کاربر انتخاب می شود.b=3,5,7,…
2) میانگین وزنی ناحیه bxb را محاسبه می کنیم. Opencv دو روش پیشنهاد می دهد:
— محاسبه میانگین ناحیه پیکسل ناحیه bxb
— محاسبه میانگین وزنی گوسی مکان پیکسل ناحیه bxb
۳) برای محاسبه مقدار آستانه، میانگین از یک مقدر ثابت(C) کم می شود:
T= M- C
توابع ساده و سریع عبارتند از:
میانگین (mean) توزیع شدت محلی:
مقدار میانه(median )
یا میانگین حداقل و حداکثر مقادیر
اندازه همسایگی، باید به اندازه کافی بزرگ باشد تا پیکسل های شی و پس زمینه کافی را پوشش دهد، در غیر این صورت یک آستانه ضعیف انتخاب میشود.از سوی دیگر، انتخاب مناطق که بیش از حد بزرگ هستند می تواند فرض تقریبا یکنواخت را نقض کند.(b)
از آنجا که این تصویر حاوی گرادیان روشنایی قوی است، آستانه سراسری، نتایج بسیار بدی را به وجود می آورد، همانطور که دیده می شود:
import cv2 import numpy as np img = cv2.imread('sample.jpg') ret,threshold1 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV) cv2.imshow('threshold ',threshold1) cv2.waitKey (0) cv2.destroyAllWindows ()
در آستانه گذاری تطبیقی ، مانند آستانه گذاری ساده، از مقدار سراری برای آستانه استفاده نمی شود. چون عکس هایی که دارای روشنایی متفاوت در قسمت های مختلف می باشند خروجی جالبی ندارند.
بنابراین در این روش ، آستانه برای یک قسمت کوچکی از تصویر حساب می شود وبا این مقادیر ، به نتایج بهتری در تصاویر با نورهای متفاوت می رسیم.
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod,
thresholdType, blockSize, C[, dst]) -> dst
این تابع تنها یک خروجی دارد و ورودی های آن به صورت زیر است:
src: تصویر ورودی
maxval: حداکثر مقدار ، که می تواند به خروجی اختصاص داده شود.
Adaptive Method : نوع آستانه
cv2.ADAPTIVE_THRESH_MEAN_C: مقدار آستانه، برای وزن متوسط تنظیم شده است.
cv2.ADAPTIVE_THRESH_GAUSSIAN_C: مقدار آستانه ، مجموعه ای از مقادیر ناحیه ای است که وزن آنها یک پنجره گاوسی است.
blockSize : اندازه ناحیه ی همسایگی را تعیین میکند.
C: c، یک ثابت است که از میانگین یا میانگین وزنی کم میشود.
dst :تصویر خروجی(باینری)
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('sample.jpg',0) img = cv2.medianBlur(img,5) ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,2) th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2) titles = ['Original Image', 'Global Thresholding (v = 127)','Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding'] images = [img, th1, th2, th3] for i in xrange(4): plt.subplot(2,2,i+1),plt.imshow(images[i],'gray') plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
تفاوت آستانه ساده با تطبیقی در این تصویر مشهود است.
آستانه گذاری ساده، از آستانه ثابت برای تمام پیکسل های تصویر استفاده می کند، بنابراین، تنها اگر هیستوگرام شدت تصویر ورودی، دارای پیک های جدا شده، مربوط به شی(ها) و پس زمینه مورد نظر باشد، کار می کند. از این رو، نمی تواند با تصاویری که حاوی، gradient روشنایی قوی باشد، مقابله کند.
از سوی دیگر، آستانه گذاری تطبیقی محلی، یک آستانه منحصربه فردی را برای هر پیکسل بر اساس محدوده ای از میزان شدت در همسایگی محلی انتخاب می کند. این آستانه گذاری، برای تصاویری که هیستوگرام شدت روشنایی، دارای قله های متمایز نباشد(مانند:بخش بندی متن از تصویر)، مناسب است.
این روش در ناحیه اطراف متن نتیجه بخش است. زیرا پیکسل های پیش زمینه و پس زمینه در محدوده همسایگی هر پیکسل به اندازه کافی وجود دارد.
با این حال در margin(ناحیه اطراف متن)، میانگین ناحیه محلی به عنوان یک آستانه مناسب نیست، زیرا محدوده مقادیر شدت در همسایگی محلی، بسیار کوچک است و میانگین نزدیک به پیکسل مرکزی است. این وضعیت را می توان بهبود داد، اگر آستانه مورد استفاده mean نباشد مگر (mean-C)، که C یک ثابت است.
بنابراین تمام پیکسل هایی که در محدوده ی یکسان وجود دارند (به عنوان مثال در امتداد حاشیه) به پس زمینه تنظیم می شود.
نتیجه ۷ × ۷ همسایگی و C = 7 نشان داده شده است :
تاثیر انتخاب cوb های متفاوت در تصویر خروجی.
میتوان به اهمیت این دو ورودی پی برد.
در این روش قرار است از خروجی دوم روش آستانه گیری ساده، retVal استفاده کنیم. چرا ما از این روش استفاده می کنیم؟?؟
در آستانه گیری ساده ما از یک مقدار سراری استفاده کردیم. ولی از کجا بدونیم این مقدار، یک مقدار خوب و بهینه است؟ بله. با آزمون و خطا.
روش آستانه گذاری Otsu ، تصویر را به گونه ای تقسیم بندی می کند که تصویر با بهترین مقدار آستانه به دو کلاس سیاه و سفید یا پیش زمینه و پس زمینه یا پس زمینه و شی تقسیم می شود:
در یک تصویر bimodal (تصویری که هیستوگرام آن دارای دو قله می باشد)، تقریبا در وسط قله ها میتوان آستانه بهینه را انتخاب کرد.
برای همین ما از تابع cv2.threshold() استفاده می کنیم ولی یک فلگ اضافی ، cv2.THRESH_OTSU را داریم . این روش مقدار بهینه آستانه را در retVal قرار می دهد و مقدار threshold برابر ۰ می شود.
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('bimodal_hsv_noise.png',0) # global thresholding ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) # Otsu's thresholding ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) # Otsu's thresholding after Gaussian filtering blur = cv2.GaussianBlur(img,(5,5),0) ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) # plot all the images and their histograms images = [img, 0, th1, img, 0, th2, blur, 0, th3] titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)', 'Original Noisy Image','Histogram',"Otsu's Thresholding", 'Gaussian filtered Image','Histogram',"Otsu's Thresholding"] for i in xrange(3): plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray') plt.title(titles[i*3]), plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256) plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray') plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([]) plt.show()
اینجا پیکسل های سفید پس زمینه و سیاه، شی یا پیش زمینه را نشان می دهند. در یک تصویر bimodal (تصویری که هیستوگرام آن دارای دو قله میباشد)، تقریبا در وسط قله ها می توان آستانه بهینه را انتخاب کرد.
آستانه گذاری دوسطحی یک مقدار آستانه دارد و پیکسلهای تصویر به دو گروه یا کلاس تقسیم می شوند. انتخاب آستانه در این گروه تصاویر بسیار ساده از آستانه گذاری چند سطحی می باشد.
تصویری که بیش از یک آستانه دارد، آستانه گذاری چند سطحی می باشد.
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('sample.jpg',0) ret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) ret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) blur = cv2.GaussianBlur(img,(5,5),0) ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) images = [img, 0, th1, img, 0, th2, blur, 0, th3] titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)', 'Original Noisy Image','Histogram',"Otsu's Thresholding", 'Gaussian filtered Image','Histogram',"Otsu's Thresholding"] for i in xrange(3): plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray') plt.title(titles[i*3]), plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256) plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray') plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([]) plt.show()
🆔@image_Process
https://t.me/image_Process