پردازش تصویر در OpenCV

تبدیلات مورفولوژیکی (Morphological Transformations)

۴ دقیقه مطالعهOpenCV 5.xبه‌روزرسانی:

هدف#

در این فصل،

  • عملیات مورفولوژیکی مختلفی مانند فرسایش (Erosion)، انبساط (Dilation)، بازشدن (Opening)، بسته‌شدن (Closing) و غیره را یاد خواهیم گرفت.
  • توابع مختلفی مانند: cv.erode()، cv.dilate()، cv.morphologyEx() و غیره را خواهیم دید.

تئوری#

تبدیلات مورفولوژیکی عملیات‌های ساده‌ای هستند که بر اساس شکل تصویر انجام می‌شوند. معمولاً بر روی تصاویر دودویی (binary) اعمال می‌شوند. دو ورودی نیاز دارند: یکی تصویر اصلی ما و دیگری عنصر ساختاری (structuring element) یا کرنل (kernel) نام دارد که ماهیت عملیات را تعیین می‌کند. دو عملگر مورفولوژیکی پایه، فرسایش و انبساط هستند. سپس اشکال متنوع آن‌ها مانند بازشدن (Opening)، بسته‌شدن (Closing)، گرادیان (Gradient) و غیره نیز وارد می‌شوند. آن‌ها را یکی‌یکی با کمک تصویر زیر می‌بینیم:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۱

۱. فرسایش (Erosion)#

ایده اصلی فرسایش دقیقاً مانند فرسایش خاک است؛ مرزهای شیء پیش‌زمینه (foreground) را می‌ساید (همیشه سعی کنید پیش‌زمینه را به رنگ سفید نگه دارید). پس چه اتفاقی می‌افتد؟ کرنل از روی تصویر می‌گذرد (مانند کانولوشن دوبعدی). یک پیکسل در تصویر اصلی (یا ۱ یا ۰) تنها در صورتی ۱ در نظر گرفته می‌شود که تمام پیکسل‌های زیر کرنل ۱ باشند، در غیر این صورت فرسوده می‌شود (به صفر تبدیل می‌شود).

پس اتفاقی که می‌افتد این است که تمام پیکسل‌های نزدیک به مرز بسته به اندازه کرنل حذف می‌شوند. بنابراین ضخامت یا اندازه شیء پیش‌زمینه کاهش می‌یابد یا به سادگی ناحیه سفید در تصویر کاهش می‌یابد. برای حذف نویزهای سفید کوچک (همان‌طور که در فصل فضای رنگی دیدیم)، جداسازی دو شیء متصل و غیره مفید است.

در اینجا، به عنوان مثال، از یک کرنل 5x5 پر از یک‌ها استفاده می‌کنم. بیایید ببینیم چگونه کار می‌کند:

import cv2 as cv
import numpy as np

img = cv.imread('j.png', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۲

۲. انبساط (Dilation)#

این دقیقاً برعکس فرسایش است. در اینجا، یک عنصر پیکسل اگر حداقل یک پیکسل زیر کرنل '1' باشد، '1' است. بنابراین ناحیه سفید در تصویر یا اندازه شیء پیش‌زمینه افزایش می‌یابد. معمولاً، در مواردی مانند حذف نویز، فرسایش پس از انبساط دنبال می‌شود. چون فرسایش نویزهای سفید را حذف می‌کند، اما شیء ما را هم کوچک می‌کند. پس آن را منبسط می‌کنیم. از آنجایی که نویز رفته، دیگر برنمی‌گردد، اما مساحت شیء ما افزایش می‌یابد. همچنین در اتصال قطعات شکسته یک شیء مفید است.

dilation = cv.dilate(img,kernel,iterations = 1)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۳

۳. بازشدن (Opening)#

بازشدن نام دیگری برای فرسایش پس از انبساط است. در حذف نویز مفید است، همان‌طور که در بالا توضیح دادیم. در اینجا از تابع cv.morphologyEx() استفاده می‌کنیم.

opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۴

۴. بسته‌شدن (Closing)#

بسته‌شدن عکس بازشدن است، یعنی انبساط پس از فرسایش. برای بستن حفره‌های کوچک داخل اشیاء پیش‌زمینه، یا نقاط سیاه کوچک روی شیء مفید است.

closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۵

۵. گرادیان مورفولوژیکی (Morphological Gradient)#

این اختلاف بین انبساط و فرسایش یک تصویر است.

نتیجه مانند طرح کلی (outline) شیء به نظر می‌رسد.

gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۶

۶. کلاه‌بالایی (Top Hat)#

این اختلاف بین تصویر ورودی و بازشدن تصویر است. مثال زیر برای یک کرنل 9x9 انجام شده است.

tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۷

۷. کلاه‌سیاه (Black Hat)#

این اختلاف بین بسته‌شدن تصویر ورودی و تصویر ورودی است.

blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)

نتیجه:

تبدیلات مورفولوژیکی (Morphological Transformations) — تصویر ۸

عنصر ساختاری (Structuring Element)#

در مثال‌های قبلی، عناصر ساختاری را با کمک NumPy به صورت دستی ایجاد کردیم. شکل آن‌ها مستطیلی بود. اما در برخی موارد، ممکن است به کرنل‌های بیضوی/دایره‌ای شکل نیاز داشته باشید. برای این منظور، OpenCV تابعی به نام cv.getStructuringElement() دارد. کافی است شکل و اندازه کرنل را ارسال کنید تا کرنل مورد نظر را دریافت کنید.

# Rectangular Kernel
>>> cv.getStructuringElement(cv.MORPH_RECT,(5,5))
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]], dtype=uint8)

# Elliptical Kernel
>>> cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
array([[0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0]], dtype=uint8)

# Cross-shaped Kernel
>>> cv.getStructuringElement(cv.MORPH_CROSS,(5,5))
array([[0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0]], dtype=uint8)

# Diamond-shaped Kernel
>>> cv.getStructuringElement(cv.MORPH_DIAMOND,(5,5))
array([[0, 0, 1, 0, 0],
       [0, 1, 1, 1, 0],
       [1, 1, 1, 1, 1],
       [0, 1, 1, 1, 0],
       [0, 0, 1, 0, 0]], dtype=uint8)

منابع تکمیلی#

  1. عملیات مورفولوژیکی در HIPR2