پردازش تصویر در OpenCV
کانتورها: شروع کار
هدف#
- درک مفهوم کانتور (Contour) چیست.
- یاد گرفتن نحوه پیدا کردن و رسم کانتورها
- با توابع زیر آشنا خواهید شد: cv.findContours()، cv.drawContours()
کانتور چیست؟#
کانتورها را میتوان به سادگی به عنوان منحنیای تعریف کرد که تمام نقاط پیوسته (در امتداد مرز) با رنگ یا شدت یکسان را به هم متصل میکند. کانتورها ابزاری مفید برای تحلیل شکل (shape analysis) و تشخیص و بازشناسی اشیاء (object detection and recognition) هستند.
- برای دقت بهتر، از تصاویر باینری (binary images) استفاده کنید. پس قبل از پیدا کردن کانتورها، آستانهگذاری (threshold) یا تشخیص لبه کنی (Canny edge detection) را اعمال کنید.
- از OpenCV 3.2 به بعد، تابع findContours() دیگر تصویر مبدأ را تغییر نمیدهد.
- در OpenCV، پیدا کردن کانتورها مانند پیدا کردن شیء سفید از پسزمینه سیاه است. پس به یاد داشته باشید، شیء مورد نظر باید سفید و پسزمینه باید سیاه باشد.
بیایید ببینیم چگونه کانتورهای یک تصویر باینری را پیدا کنیم:
import numpy as np
import cv2 as cv
im = cv.imread('test.jpg')
assert im is not None, "file could not be read, check with os.path.exists()"
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
همانطور که میبینید، تابع cv.findContours() سه آرگومان دارد؛ اول تصویر مبدأ، دوم حالت بازیابی کانتور (contour retrieval mode)، و سوم روش تقریب کانتور (contour approximation method). خروجی آن کانتورها و سلسلهمراتب (hierarchy) است. Contours یک لیست Python از تمام کانتورهای موجود در تصویر است. هر کانتور به صورت یک آرایه Numpy از مختصات (x,y) نقاط مرزی شیء ذخیره میشود.
چگونه کانتورها را رسم کنیم؟#
برای رسم کانتورها از تابع cv.drawContours استفاده میشود. همچنین میتوان از این تابع برای رسم هر شکلی استفاده کرد، مشروط بر اینکه نقاط مرزی آن را داشته باشید. آرگومان اول آن تصویر مبدأ است، آرگومان دوم کانتورهایی است که باید به صورت یک لیست Python ارسال شوند، آرگومان سوم ایندکس کانتور است (هنگام رسم یک کانتور منفرد مفید است. برای رسم همه کانتورها، مقدار -1 را ارسال کنید) و آرگومانهای باقیمانده رنگ، ضخامت و غیره هستند.
- برای رسم همه کانتورها در یک تصویر:
cv.drawContours(img, contours, -1, (0,255,0), 3)
- برای رسم یک کانتور منفرد، مثلاً کانتور چهارم:
cv.drawContours(img, contours, 3, (0,255,0), 3)
- اما در بیشتر مواقع، روش زیر مفیدتر است:
cnt = contours[4]
cv.drawContours(img, [cnt], 0, (0,255,0), 3)
روش تقریب کانتور (Contour Approximation Method)#
این آرگومان سوم در تابع cv.findContours است. در واقع چه چیزی را نشان میدهد؟
در بالا گفتیم که کانتورها مرزهای یک شکل با شدت یکسان هستند. این مرز مختصات (x,y) نقاط را ذخیره میکند. اما آیا همه مختصات را ذخیره میکند؟ این موضوع توسط روش تقریب کانتور مشخص میشود.
اگر cv.CHAIN_APPROX_NONE را ارسال کنید، تمام نقاط مرزی ذخیره میشوند. اما آیا واقعاً به همه نقاط نیاز داریم؟ برای مثال، فرض کنید کانتور یک خط مستقیم را پیدا کردهاید. آیا به همه نقاط روی خط نیاز دارید تا آن خط را نشان دهید؟ نه، تنها به دو نقطه انتهایی آن خط نیاز داریم. این همان کاری است که cv.CHAIN_APPROX_SIMPLE انجام میدهد. تمام نقاط اضافی را حذف کرده و کانتور را فشرده میکند و در نتیجه حافظه را ذخیره میکند.
تصویر زیر از یک مستطیل این تکنیک را نشان میدهد. فقط یک دایره روی تمام مختصات موجود در آرایه کانتور رسم کنید (با رنگ آبی). تصویر اول نقاطی را که با cv.CHAIN_APPROX_NONE به دست آمدند (۷۳۴ نقطه) و تصویر دوم نقاطی را که با cv.CHAIN_APPROX_SIMPLE به دست آمدند (تنها ۴ نقطه) نشان میدهد. ببینید چقدر حافظه ذخیره میشود!!!
