امروزه با کمک پردازش تصویر و هوش مصنوعی میتوان تحلیلهای عمیقی انجام داد. تشخیص چهره، تشخیص چشم، تشخیص خودرو و … از این دسته از تحلیلها میباشند. اما پردازش تصویر چگونه انجام میگیرد یا چگونه میتوان آن را پیاده سازی کرد. امروز در همین خصوص، وارد مبحث عملیات مورفولوژی تصاویر در OpenCV خواهیم شد.
ماژول opencv
Opencv یکی از ماژولهای پردازش تصویر و هوش مصنوعی میباشد. با کمک این ماژول میتوان تصاویر را دریافت و بسیاری از عملیاتهای پردازش تصویر را پیاده سازی کرد. جالب است بدانید با opencv میتوان تشخیص چهره، تشخیص چشم و یا حتی تشخیص اعداد را نیز انجام داد. علاوه بر تصاویر، پردازش ویدئو نیز میتوان با کمک این ماژول انجام داد. در این مقاله، عملیات مورفولوژی تصاویر را با کمک OPENCV و زبان برنامهنویسی پایتون پیادهسازی خواهیم کرد. البته بعضی از قسمتها به موجب نیاز، از ماژول Numpy نیز استفاده میگردد. Pdf آموزش Numpy از این لینک میتوانید تهیه نمایید.
عملیات مورفولوژی چیست؟
عملیاتهای مورفولوژی در پردازش تصویر به مجموعهای از عملیات غیرخطی گفته میشود که با جایگاه پیکسلها در تصاویر در ارتباط است (بر اساس جایگاه پیکسل فعلی، مقدار پیکسل جدید تعیین میگردد.). این نوع عملیاتها بر روی تصاویر باینری مناسبتر هستند. البته در تصاویر grayscale نیز میتوان از این عملیاتها استفاده کرد. از جمله مهمترین عملیاتهای مورفولوژی میتوان به موارد زیر اشاره نمود:
- Erosion
- Dilation
- Opening
- Closing
Erosion
واژه erosion به معنی فرسایش میباشد. عملیات erosion نیز سطح بیرونی یک تصویر را فرسایش میکند (یا حذف میکند). سطح بیرونی به چه معناست؟ پاسخ به این سوال را با دیدن نحوه انجام عملیات erosion و پیاده سازی چندین مثال خواهیم داد. برای انجام erosion نیاز به kernel داریم. میزان فرسایش (erosion) با توجه به سایز kernel میباشد. تصویر زیر را ببینید:

اگر عملیات erosion با kernel به سایز 30*30 انجام دهیم تصویر خروجی به صورت زیر میشود:

از تصویر مشخص است که سطح بیرونی نقاط سفید رنگ حذف شده است. حال اگر kernel را 5*5 قرار دهیم خروجی به صورت زیر میشود:

تصویر بالا تقریباً مشابه تصویر ورودی است. بدلیل کوچک بودن سایز kernel عملیات erosion تغییر چندانی در تصویر ایجاد نکرده است(تغییری که با چشم قابل تشخیص باشد).
حال نوبت به آن رسیده تا در زمینه چگونگی عملیات Erosion بگوییم. Kernel بر روی پیکسلهای تصویر چرخشی دارد. اگر تمامی پیکسلهای که در Kernel قرار میگیرند عدد 1 باشند پیکسل خروجی نیز 1 میشود در غیر این صورت پیکسل خروجی صفر (0) خواهد بود.

عملیات Erosion
تابع Erode
برای پیادهسازی عملیات Erosion در Opencv از تابع Erode استفاده میشود. این تابع ورودیهای زیر را دریافت میکند:
- Src: تصویر ورودی
- Kernel: ابعاد kernel
- Iterations: تعداد تکرار عملیات Erosion در تصویر
شایان ذکر است که ورودیهای دیگری نیز توسط تابع دریافت میگردد. برای دیدن دیگر ورودیهای این تابع به لینک مراجعه نمایید.
فراخوانی تصویر
ما قبل از هر اقدامی نیاز است ماژولهای مورد نیاز را فراخوانی نماییم:
import cv2
import numpy as np
در این قسمت تصویر ورودی را ابتدا میخوانیم:
image = cv2.imread(binary.jpg',0)
cv2.imshow("Original", image)
تصویر ورودی به صورت زیر میباشد:

تصویر ورودی عدد 5 میباشد. این تصویر به صورت grayscale خواندیم.
پیادهسازی Erosion در opencv
نوبت به پیادهسازی Erosion در Opencv رسیده است. ابتدا یک Kernel تعریف میکنیم. این Kernel را با کمک ماژول Numpy ایجاد میکنیم.
kernel = np.ones((30,30), np.uint8)
تمامی عناصر kernel مقدار یک است به همین دلیل از np.ones استفاده کردهایم. یک kernel به ابعاد 30*30 ساختهایم. تابع Erode را صدا میزنیم:
erosion = cv2.erode(image, kernel, iterations = 1)
مقادیری ورودی تابع Erode به ترتیب image (تصویر ورودی) و kernel و 1 (تعداد تکرار عملیات) میباشد. برای نمایش خروجی کد زیر را مینویسیم:
cv2.imshow('Erosion', erosion)
تصویر خروجی به صورت زیر است:

همانطور که میبینید سطح سفید رنگ بسیار کوچک شده است.
عملیات dilation
عملیات dilation برعکس erosion است. در این عملیات شی درون تصویر بزرگتر خواهد شد. نحوه رفتار این عملیات نیز بسیار ساده است. ابتدا یک kernel دریافت میکند. Kernel بر روی پیکسلهای تصویر چرخشی دارد. اگر تمامی پیکسلهای که در Kernel قرار میگیرند عدد صفر (0) باشند پیکسل خروجی نیز صفر (0) میشود در غیر این صورت پیکسل خروجی 1 (یک) خواهد بود.
تابع dilate
برای پیادهسازی عملیات dilation در opencv از تابع dilate استفاده میکنند ورودیهای این تابع به صورت زیر است:
- Src: تصویر ورودی
- Kernel: ابعاد kernel
- Iterations: تعداد تکرار عملیات dilation در تصویر
شایان ذکر است که ورودیهای دیگری نیز توسط تابع دریافت میگردد. برای دیدن دیگر ورودیهای این تابع به لینک مراجعه نمایید.
پیادهسازی عملیات dilation
برای پیاده سازی عملیات dilation از تصویر و kernel بخش قبل استفاده میکنیم. کد زیر را ببینید:
dilation = cv2.dilate(image, kernel, iterations = 1)
cv2.imshow('Dilation', dilation)
مقادیری ورودی تابع dilate به ترتیب image (تصویر ورودی) و kernel و 1 (تعداد تکرار عملیات) میباشد. خروجی این کد به صورت زیر است:

همانطور که در تصویر خروجی مشخص است عدد 5 درون تصویر بسیار بزرگتر یا ضخیمتر شده است.
عملیاتهای opening و Closing
دو عملیات جدیدی که میخواهیم در این قسمت توضیح دهیم opening و closing میباشد. ابتدا با این عملیاتها آشنا شویم:
- Opening: اگر در تصویر ورودی ابتدا Erosion و سپس dilation را اعمال کنیم عملیات opening را انجام دادهایم.
- Closing: اگر در تصویر ورودی ابتدا dilation و سپس Erosion را اعمال کنیم عملیات closing را انجام دادهایم.
یکی از کاربردهای دو فرآیند بالا حذف نویز میباشد. تابع morphologyEx در opencv این دو عملیات را پیاده سازی کرده است. ورودیهای این تابع به صورت زیر است:
- Src: تصویر ورودی
- نوع عملیات: cv2.MORPH_OPEN برای عملیات opening و cv2.MORPH_CLOSE برای closing استفاده میشود.
- Kernel
ابتدا یک مثال برای عملیات closing پیاده سازی خواهیم کرد. در این مثال تصویر ورودی به صورت زیر است:

لوگو وبسایت کدگیت به همراه نویز(دایرههای کوچک مشکی رنگ) را به عنوان ورودی تعیین کردهایم. برویم سراغ پیاده سازی:
import cv2
import numpy as np
تصویر ورودی را فراخوانی و kernel را ایجاد کردیم. حال برای عملیات closing باید از تابع morphologyEx کمک بگیریم:
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Closing',closing)
مقادیری ورودی تابع morphologyEx به ترتیب image (تصویر ورودی) و cv2.MORPH_CLOSE (نوع عملیات) و kernel میباشد. خروجی بالا به صورت زیر است:

همانطور که در تصویر مشخص است نقطههای سیاه رنگ از بین رفتهاند. برای opening تصویر ورودی زیر را آماده کردهایم:

کد عملیات Closing را ببینید:
image = cv2.imread('opencv_inv.png', 0)
cv2.imshow('Opening image Original', image)
در تابع morphologyEx مشابه مثال قبل ورودیها را تعیین کردیم تنها تفاوت در نوع عملیات است که مقدار آن را cv2.MORPH_OPEN قرار دادیم. خروجی این قسمت به صورت زیر است:

قسمتی از حروف از بین رفته است که نشان میدهد این عملیات برای تصویر ما مناسب نیست. شما میتوانید با تست تصاویر بیشتر، با این عملیات بیشتر آشنا شوید.
نکته: در تمامی پیاده سازی این مقاله kernel استفاده شده همگی عناصر آن عدد یک (1) بودهاند و تنها ابعاد آنها متفاوت میباشد.