تشخیص حرکت با OpenCV
در این بخش قصد داریم به کمک OpenCV کد تشخیص حرکت یا Motion Detection بنویسیم. درمدل تشخیص حرکت یک سیستمی وجود دارد که از تغییرات فریم و جابجایی بیشتر از یک مقدار مشخص در تصویر متوجه حرکت میشود. و بر اساس آن میتواند آن محلی که حرکت اتفاق افتاده است را مشخص کند. و خروجی لازم را برای کاربرد در موارد مختلف به ما میدهد. به عنوان مثال اگر در یک فیلم شخص مورد نظر سر و یا دست خود را حرکت دهد این سیستم سرو دست را به عنوان یک شی که در حال حرکت است تشخیص میدهد.
Preprocessing!
برای نوشتن کد تشخیص حرکت ابتدا کتابخانه OpenCV که به عنوان ابزار Computer Vision میباشد، و کتابخانه numpy را وارد میکنیم.
ابتدا تابع get_fram_gray را تعریف می کنیم. آرگومان تعریف شده در این تابع در واقع cap ایجاد شده با متد VideoCapture موجود در OpenCV است. که webcam به آن متصل است. سپس متد read را روی آن اعمال میکنیم و برابر با frame و ret قرار میدهیم. برای اینکه فقط تصویر شخص مورد نظر را داشته باشیم. و قسمتهای اطراف دیده نشود یک حاشیه دور تصویر شخص با اسلایس زدن از frame ایجاد میکنیم. در ارتفاع برش نمیزنیم ولی در پهنا بین پیکسل 130 تا 550 را برش میزنیم. frame به دست آمده را gray میکنیم و سپس بر روی تصویر خاکستری ایجاد شده متد GaussianBlur را اعمال میکنیم. که اگر noiseهایی که ممکن است وجود داشته باشد حذف و یا کم رنگ شود. که با kernel زیاد یعنی 21 این کار را انجام میدهیم. خروجی این تابع frame و gray خواهد بود.
در ادامه یک حلقه while True برای دریافت تمام frameها ایجاد میکنیم. در این حلقه با فراخوانی تابع get_fram_gray میتوان به frameها دسترسی پیدا کرد. و سپس آنها را نمایش داد. در واقع با تابع imshow هم تصویر اصلی و هم تصویری که خاکستری و blur شده است نمایش داده میشود. در تصویر ایجاد شده با متد GaussianBlur آبجکتهای بزرگ نسبت به noiseهای ریزی که ممکن است در تصویر وجود داشته باشد از اهمیت بیشتری برخوردار هستند.
thresholding در تشخیص حرکت
برای ایجاد thresholding در کد تشخیص حرکت ابتدا VideoCapture را صدا میزنیم و capture را ایجاد میکنیم. قبل از حلقه while، یک frame با فراخوانی تابع get_fram_gray ایجاد میکنیم. و برابر با old_gray قرار میدهیم. سپس وارد حلقه while میشویم و با فراخوانی تابع get_fram_gray یک frame و یک gray ایجاد میکنیم. برای سنجیدن اختلاف بین frame قبلی و frame جدید، از متد absdiff استفاده میکنیم. و مقدار به دست آمده را برابر با frameDelta قرار میدهیم.
کلا detectorهای حوزه تصویر با بررسی اختلاف بین فریمها کار خود را انجام میدهند. به عنوان مثال بررسی میکنند که آیا دستی که در تصویر بوده است جابجایی داشته یا نه. برای آشنایی با متد threshold میتوان به دوره جامع آموزش پردازش تصویر و بینایی کامپیوتر در پایتون مراجعه نمود. به عنوان آرگومان متد threshold مقدار frameDelta ایجاد شده را در قرار میدهیم. و آستانه در نظر گرفتن را بر اساس معیار TRESH_BINARY که ساده ترین معیار میباشد در نظر میگیریم. مقدار به دست آمده را برابر با متغیرهای ret و thresh قرار میدهیم. در نهایت thresh و frameDelta را با متد imshow نمایش میدهیم.
پس از اجرای کد فوق دو تصویر ایجاد میشود که تصویر سمت راست حرکت دست به صورت محو دیده میشود و در تصویر سمت چپ حرکت دست به صورت bold با آستانهگذاری درست دیده میشود. یعنی background سیاه میباشد و دست به صورت سفید دیده میشود. در واقع از thresholding بعدا به عنوان ابزار motion detector استفاده خواهیم کرد.
ایجاد Bounding Box در تشخیص حرکت
ابتدا یک تابع به نام bounding_rec ایجاد میکنیم که frame و thresh را به عنوان آرگومان برای این تابع درنظر میگیریم. سپس با استفاده از متد ones از کتابخانه numpy یک kernel با ابعاد 5 در 5 و uint8 ایجاد میکنیم. در ادامه متد dilate از کتابخانه cv2 را فراخوانی میکنیم و thresh و kernel را به آن میدهیم. و دوباره مقدار آن را برابر با thresh قرار میدهیم. متد dilate باعث میشود اون شی که پراکنده ساخته شده تقریبا کاملتر شود. به عنوان مثال اگر متد dilate را برای تصویر یک دست ایجاد کنیم، تمام فضاهای خالی بین انگشتان دست را پر میکند و یک تصویر بستهتر ایجاد میشود. برای آشنایی بیشتر با متد dilate میتوان به دوره جامع آموزش پردازش تصویر و بینایی کامپیوتر در پایتون مراجعه نمود.
سپس متغیر thresh را به متد findContours میدهیم تا contourهای thresh را بر اساس دو مد cv2.RETR_EXTERNAL و CV2.CHAIN_APPROX_SIMPLE پیدا کند. با ایجاد یک حلقه بر روی contourهای ایجاد شده ارزیابی می کنیم که contourهای بالای 300 پیکسل که از نظر محیطی و مساحتی شیی را دربرمیگیرند در محاسبات بررسی شوند. و اگر کوچکتر بود عبارت continue را قرار میدهیم. یعنی اینکه محاسبات ادامه پیدا نکند و به ابتدای حلقه رود. در حالت بزرگتر از 300 متد boundingRect را که boundingRect احاطه کننده آن contour را ایجاد میکند، فراخوانی میکنیم. و در نهایت یک مستطیل سبز رنگ با متد rectangle دور آن قرار میدهیم.
در ادامه همان حلقه while که در مرحله thresholding در preprocessing ایجاد کرده بودیم را با یک سری تغییرات دوباره ایجاد میکنیم. در واقع بعد از اینکه متد absdiff و threshold را فراخوانی کردیم تابع bounding_rec را که در کد بالا ایجاد کردیم، فراخوانی میکنیم. در نهایت با استفاده از متد imshow تصویر همراه با boundingRect نمایش داده میشود. در اینجا هر تصویر gray که در حلقه ایجاد میشود در انتهای حلقه برابر میشود با old_gray و دوباره در ابتدای حلقه یک gray جدید خواهیم داشت. که بتونیم برای diffrence گرفتن از این دو تصویر جدید و قدیمی استفاده کنیم.