پردازش تصویر در OpenCV
گرادیانهای تصویر (Image Gradients)
هدف#
در این فصل یاد خواهیم گرفت:
- گرادیانهای تصویر، لبهها و غیره را پیدا کنیم
- توابع زیر را خواهیم دید: cv.Sobel()، cv.Scharr()، cv.Laplacian() و غیره
تئوری#
OpenCV سه نوع فیلتر گرادیان یا فیلتر بالاگذر (High-pass filter) ارائه میدهد: Sobel، Scharr و Laplacian. هر کدام از آنها را میبینیم.
۱. مشتقات Sobel و Scharr#
عملگرهای Sobel یک عملیات ترکیبی از هموارسازی گاوسی به علاوه مشتقگیری هستند، بنابراین در برابر نویز مقاومت بیشتری دارند. میتوانید جهت مشتقاتی که باید گرفته شود، عمودی یا افقی، را (از طریق آرگومانهای yorder و xorder به ترتیب) مشخص کنید. همچنین میتوانید اندازه کرنل را با آرگومان ksize مشخص کنید. اگر ksize = -1 باشد، یک فیلتر Scharr 3x3 استفاده میشود که نتایج بهتری نسبت به فیلتر Sobel 3x3 ارائه میدهد. لطفاً کرنلهای استفادهشده را در مستندات ببینید.
۲. مشتقات Laplacian#
لاپلاسین تصویر را با رابطهی زیر محاسبه میکند: که در آن هر مشتق با استفاده از مشتقات Sobel پیدا میشود. اگر ksize = 1 باشد، از کرنل زیر برای فیلترسازی استفاده میشود:
کد#
کد زیر تمام عملگرها را در یک نمودار واحد نشان میدهد. تمام کرنلها اندازه 5x5 دارند. عمق (Depth) تصویر خروجی با مقدار -1 ارسال میشود تا نتیجه در نوع np.uint8 باشد.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('dave.jpg', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
laplacian = cv.Laplacian(img,cv.CV_64F)
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
نتیجه:

یک نکته مهم!#
در مثال آخر ما، نوع داده خروجی cv.CV_8U یا np.uint8 بود. اما مشکل کوچکی در این مورد وجود دارد. انتقال از سیاه به سفید به عنوان شیب مثبت (Positive slope) در نظر گرفته میشود (مقدار مثبت دارد) در حالی که انتقال از سفید به سیاه به عنوان شیب منفی (Negative slope) در نظر گرفته میشود (مقدار منفی دارد). بنابراین وقتی داده را به np.uint8 تبدیل میکنید، تمام شیبهای منفی صفر میشوند. به عبارت ساده، آن لبه را از دست میدهید.
اگر میخواهید هر دو لبه را تشخیص دهید، گزینه بهتر این است که نوع داده خروجی را به فرمهای بالاتری نگه دارید، مانند cv.CV_16S، cv.CV_64F و غیره، مقدار قدرمطلق آن را بگیرید و سپس به cv.CV_8U تبدیل کنید. کد زیر این روش را برای یک فیلتر Sobel افقی و تفاوت در نتایج نشان میدهد.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('box.png', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
# Output dtype = cv.CV_8U
sobelx8u = cv.Sobel(img,cv.CV_8U,1,0,ksize=5)
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx64f = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()
نتیجه را در زیر بررسی کنید:
