نمایش/عدم نمایش سایدبار
رفتن به بالای صفحه
أَللّهُمَّ ارْزُقْنی شَفاعَةَ الْحُسَیْنِ یَومَ الْوُرُودِ

آموزش ماژول High-level GUI در OpenCV

502

به نام خدا : در این مطلب به معرفی ماژول High-level GUI و توابعش میپردازیم، ایجاد پنجره و نمایش تصاویر و انتخاب ROI و کنترل موس و کیبورد و… از کارهایی هستش که این ماژول انجام میده.

آموزش ماژول High-level GUI در OpenCV

 

اصطلاحات مورد نیاز این مطلب

1) ROI چیست : در OpenCV، کلمه ROI مخفف عبارت "region of interest" به معنی "منطقه مورد نظر/علاقه" هستش؛ قسمت از یه تصویر که بُرِش خورده میشه و بهش میگن ROI.

 

2) backend چیست و انواع آن ( QT ،GTK ،Win32 و... )

خلاصه بگم که یه Backend داریم و یه Frontend؛ حالا Backend یعنی قسمت از پروژه که کاربر باهاش کاری نداره، نمیبینه و... یعنی کدهای پروژه، Frontend هم یعنی قسمت GUI، قسمت گرافیکی پروژه که کاربر میبینه و ازشون استفاده میکنه و... مثلا تو کدنویسی وب، HTML و CSS میشن Frontend و PHP میشه Backend.

Frontend vs Backend

 

کتابخانه مورد نیاز این مطلب :

 

توضیح توابع ماژول High-level GUI

1 تا 8) توابع namedWindow و setWindowTitle و moveWindow و resizeWindow و getWindowProperty و setWindowProperty و destroyWindow و destroyAllWindows

1) تابع namedWindow : به کمک تابع namedWindow میتونید یه پنجره ایجاد کنید تا کنترل ها و تصاویر و... رو داخلش نمایش بدید.

توضیح پارامترها :

  • winname : نام پنجره؛ اگه پنجره با این نام قبلا ایجاد شده باشه، این تابع کاری نمیکنه؛ برای عنوان ( title ) پنجره، از همین پارامتر استفاده میشه؛ اگه میخواید عنوان پنجره رو یه مقدار دیگه تنظیم کنید، به تابع setWindowTitle مراجعه کنید.
  • flags : پرچم های پنجره؛ به کمک نوع شمارشی cv::WindowFlags مقدار دهی میشه.

 

نوع شمارشی cv::WindowFlags :

گزینه WINDOW_FREERATIO، مختص Qt backend هستش.

 

2) تابع setWindowTitle : به کمک این تابع میتونید عنوان پنجره ( ای که به کمک تابع namedWindow ایجاد کردید ) رو تغییر بدید؛ در پارامتر winname، نام پنجره مدنظر، و در پارامتر title، عنوان جدید پنجره فوق رو قرار بدید.

 

3) تابع moveWindow : جابجا کردن پنجره مشخص شده؛ پارامتر winname، نام پنجره مدنظر که میخواید جابجاش کنید، پارامتر های x و y هم مختصات جدید پنجره هستند ( مختصات بالا-چپ پنجره )

 

4) تابع resizeWindow : تغییر اندازه پنجره مشخص شده؛ اگر هنگام ایجاد پنجره ( به کمک تابع namedWindow )، مقدار پارامتر flags رو، روی WINDOW_AUTOSIZE تنظیم کرده باشید ( که مقدار پیشفرضش هم همینه )، تابع resizeWindow، نمیتونه اندازه پنجره رو تغییر بده.

 

5 و 6) توابع getWindowProperty و setWindowProperty : -

 

7 و 8) توابع destroyWindow و destroyAllWindows : نابود کردن پنجره مشخص شده یا تمام پنجره ها.

9 تا 13) توابع createTrackbar و getTrackbarPos و setTrackbarPos و setTrackbarMin و setTrackbarMax

9) تابع createTrackbar : برای ایجاد یه Trackbar افقی از این تابع استفاده میکنیم ( تو QT کنترلی شبیه این با نام Horizontal Slider هستش )؛ قبل از استفاده از این تابع، باید به کمک تابع cv::namedWindow، یک پنجره ایجاد کنید ( این Trackbar، داخل پنجره ای که ایجاد کردید قرار میگیره )؛ شروع این کنترل همیشه از 0 هستش و نمیشه شروعشو تغیر داد! اما میشه پایان ( مقدار حداکثر ) شو تعیین کرد!؛ اگه میخواید مقدار شروع / حداقل رو تعیین کنید، باید از تابع setTrackbarMin استفاده کنید.

توضیح پارامترها :

  • trackbarname : نام کنترل.
  • winname : نام پنجره ای که قراره این کنترل داخلش نمایش داده بشه.
  • value : مقدار فعلی کنترل ( value ≤ count )؛ مقدار های آینده هم داخل این پارامتر قرار میگیره!
  • count : مقدار حداکثر کنترل.
  • onChange : تابعی که وقتی مقدار کنترل تغییر کرد، فراخونی میشه.
  • userdata : داده های کاربر؛ داده هایی که میخواید به تابعی که در پارامتر onChange تعریف کردید، ارسال بشه.

خب برا کار با این کنترل چند تا مثال در ادامه قرار میدم :

 

کد نمونه 1 : تو این کد مقدار کنترل بین مقدار 0 تا 50 میتونه تغییر کنه؛ زمانی که مقدار کنترل تغیر کنه، تابع trackbar1_changed اجرا میشه؛ اگه کد رو اجرا کنید میبینید که trackbar1_pos و pos همیشه برابر هستند؛ فلذا وقتی مقدار کنترل تغییر بکنه، متغییر trackbar1_pos هم بروزرسانی میشه.

 

کد نمونه 2 : خب حالا میخوایم از پارامتر userdata استفاده کنیم و یه داده دلخواهی رو به تابع trackbar2_changed ارسال کنیم. ( البته نمیدونم اسم تابع درست باشه یا نه؛ چون معمولا به این مدل توابع میگن وقفه/interrupt، رخداد/event و در اینجا میگن Callback فک کنم؛ در هر صورت کاری به اسمش نداریم ).

توجه : داخل تابع اصلیتون ( اونجایی که تابع createTrackbar رو فراخونی میکنید و یه وقفه برا trackbar ایجاد میکنید ) بهتره که حلقه while قرار بدید که شامل تابع cv::waitKey هم باشه ( مقدارش بزرگتر از 1 باشه ) یا اگه حلقه while ندارید همین تابع رو بدون پارامتر ( یا با مقدار 0 ) انتهای تابع قرار بدید ( تا به مدت بی نهات، صبر کنه! )؛ این طوری تابع فوق، هیچ وقت بسته نمیشه و متغییرهاش نابود نمیشن، فلذا در این حالت وقتی وقفه فوق اجرا میشه، برنامه کرش نمیکنه!؛ حالا بنظرتون چرا برنامه کرش میکنه؟ بله درسته، متغیر user_data داخل تابع trackbar_test2 تعریف شده و وقتی این تابع تموم بشه، متغییر فوق هم نابود میشه، و وقتی وقفه trackbar2_changed رخ میده، میبینه که همچین متغییری ( user_data ) وجود خارجی نداره و این باعث کرش کردن برنامه میشه؛ برای حل این مشکل همونطور که گفتم یا باید از تابع cv::waitKey استفاده کنید، یا این که متغییر user_data رو سراسری ( خارج از تابع ) تعریف کنید.

 

10) تابع getTrackbarPos : خواندن موقعیت فعلی trackbar مشخص شده.

توضیح پارامترها :

  • trackbarname : نام trackbar مدنظر.
  • winname : نام پنجره ای که trackbar مدنظر داخلش قرار داره.
  • return : موقعیت فعلی trackbar مدنظر.

 

11) تابع setTrackbarPos : تغییر موقعیت فعلی trackbar مشخص شده.

توضیح پارامترها :

  • trackbarname : نام trackbar مدنظر.
  • winname : نام پنجره ای که trackbar مدنظر داخلش قرار داره.
  • pos : موقعیت جدید trackbar مدنظر.

 

کد نمونه 3 : تو این کد، برا خواندن و تغییر موقعیت فعلی trackbar، از توابع getTrackbarPos و setTrackbarPos استفاده کردم؛ کار این کد اینه که وقتی مقدار trackbar به 20 برسه، مقدارشو 0 میکنه.

 

12) تابع setTrackbarMin : تغییر مقدار حداقل trackbar مشخص شده.

 

13) تابع setTrackbarMax : تغییر مقدار حداکثر trackbar مشخص شده.

14 و 15) توابع setMouseCallback و getMouseWheelDelta

14) تابع setMouseCallback : یک وقفه برای مدیریت موس و کلید های کنترلی کیبورد ( shift - alt - ctrl ) در پنجره مدنظر، ایجاد میکنه.

توضیح پارامترها :

  • winname : نام پنجره ای که قراره این کنترل داخلش نمایش داده بشه.
  • onMouse : تابعی که وقتی مقدار کنترل تغییر کرد، فراخونی میشه.
  • userdata : داده های کاربر؛ داده هایی که میخواید به تابعی که در پارامتر onMouse تعریف کردید، ارسال بشه.

 

پارامتر onMouse : نحوه تعریف تابع وقفه، به صورت زیر هستش ( اسم تابع مهم نی، هرچی تنظیم کردید! )؛ میتونیم موقعیت موس، فشرده شدن کلیدهای موس و کلیدهای کنترلی کیبورد ( shift - alt - ctrl ) رو تشخیص بدیم؛ دابل-کلیک، چرخش کلید وسط موس رو میتونیم تشخیص بدیم و...

توضیح پارامترها :

  • event : مقدارش رو به کمک نوع شمارشی cv::MouseEventTypes میخونیم.
  • x و y : موقعیت x و y فعلی موس.
  • flags : مقادیرش رو به کمک نوع شمارشی cv::MouseEventFlags میخونیم.
  • userdata : پارامتر اختیاری داده ای کاربر برای این تابع فرستاده.

 

نوع شمارشی cv::MouseEventTypes :

 

نوع شمارشی cv::MouseEventFlags : 

 

15) تابع getMouseWheelDelta : یه تابع اضافی برای راحتی بیشتر!؛ کارش اینه که وقتی رخداد های EVENT_MOUSEWHEEL و EVENT_MOUSEHWHEEL رخ میدن، میتونیم مقدار دلتای کلید وسط موس، همون چرخشی هه! ( wheel ) رو بخونیم که خب برای موس های معمولی، دلتا مضربی از 120 هستش، که خب برای موس هایی با دقت بالا، مقدار فوق کوچکتر خواهد بود؛ البته من موس با 2 تا کلید چرخشی ندیدم ولی خب رخداد EVENT_MOUSEWHEEL برای کلید چرخشی عادی ( عمودی ) هستش و رخداد EVENT_MOUSEHWHEEL برای کلید چرخشی افقی هستش ( هر موسی، کلید چرخشی افقی رو نداره! )؛ برای EVENT_MOUSEWHEEL مقادیر مثبت و منفی به ترتیب به معنای اسکرول رو به جلو و عقب هستند. برای EVENT_MOUSEHWHEEL، در صورت وجود، مقادیر مثبت و منفی به ترتیب به معنای پیمایش راست و چپ هستند.

توضیح پارمترها :

  • flags : پارامتر flags از callback موس ( تابع وقفه موس! ) رو به این پارامتر میدید.
  • return : در بالا صحبت شد دربارش.

 

کد نمونه :

16 تا 18) توابع waitKey و waitKeyEx و pollKey

16) تابع waitKey : صبر کردن تا زمانی که یه کلیدی فشرده شود.

توضیح پارامترها :

  • delay : مدت زمانی که تابع باید صبر کنه تا یه کلیدی فشرده بشه، بر حسب ms؛ اگر \( delay ≤ 0 \) باشه، تابع به مدت بی نهایت صبر میکنه تا زمانی که یه کلیدی فشرده بشه.
  • return : چیزی که تابع برمیگردونه، کد اسکی ( ASCII Code ) کلید فشرده شده هستش؛ اگر با پایان زمان تعیین شده و اتمام کار تابع، کلیدی فشرده نشده باشه، تابع مقدار -1 رو برمیگردونه.

 

17) تابع waitKeyEx : شبیه تابع WaitKey هستش، اما کد کامل، کلید فشرده شده رو برمیگردونه؛ بسته به این که از چه backend یی استفاده میکنید، کد ارسالی ممکنه متفاوت باشه : QT، GTK، Win32 و...

 

18) تابع pollKey : شبیه تابع WaitKey هستش، با این تفاوت که دیگه صبر نمیکنه تا یه کلیدی فشرده بشه، دیگه خبری از تاخیر زمانی ( delay ) نیست.

 

توجه : توابع بالا فقط در صورتی کار میکنند که حداقل یک پنجره HighGUI ایجاد شده باشه ( درباره نحوه ایجاد پنجره / window تو این مطلب صحبت کردیم ) و فعال هم باشه. ( منظور از فعال بودن اینه که اولا minimise نشده باشه، و دوما focus موس روش باشه، مثلا الان فوکوس موس من رو مرورگر گوگل کروم هستش، اگه چیزی تایپ کنم، متن من در گوگل کروم نمایش داده میشه و نه جای دیگه ای! )

19 و 20) توابع selectROI و selectROIs

19) تابع selectROI : به کاربران اجازه میدهد تا یک ROI را در تصویر داده شده انتخاب کنند؛ به کمک کلیک چپ موس، میتونید منطقه مدنظر رو انتخاب کنید، بعد به کمک کلیدهای Space یا Enter پایان عملیات انتخاب ناحیه رو اعلام میکنید و تابع ناحیه انتخاب شده رو return میکنه، اگه هم کلید C رو فشار بدید، عملیات انتخاب ناحیه لغو میشه (  کنسل میشه! ) و یک ناحیه خالی return میشه. ( ناحیه خالی تمام مقادیرش 0 هستند، نقطه شروعش و طول و عرضش 0 هست )؛ اگه فقط 1 پنجره دارید، تعریف دوم تابع بنظر ساده تر میاد!، ولی اگه چندین پنجره دارید بهتره از تعریف اول تابع استفاده کنید.

توضیح پارامترها : پارامترهای showCrosshair و fromCenter رو اگه متوجه نشدید، در کد نمونه زیر، مقادیرشون رو تغییر بدید و ببینید در اجرا چه اتفاقی میوفته، اینطوری حتما متوجه میشید.

  • windowName : نام پنجره ای که فرآیند انتخاب ROI قراره اونجا عمل کنه.
  • img : تصویری که قراره ازش یه ROI انتخاب کنید.
  • showCrosshair : تصویر crosshair داخل مستطیل ناحیه ای که دارید انتخاب میکنید نمایش داده بشه یا نه. ( مثل نشانگر نشانه گیری تفنگ تو بازی کانتر! که به شکل + هستش!!! )
  • fromCenter : مرکز انتخاب ناحیه، از گوشه بالا-چپ باشد ( false ) یا از مرکز / وسط ناحیه ( true ).

کد نمونه :

نتیجه کد بالا :

آموزش تابع selectROI در OpenCV

 

20) تابع selectROIs : همانند تابع selectROI هستش، با این تفاوت که این تابع، برای انتخاب چندین ROI بکار میره و نه فقط یکی!؛ به کمک کلیک چپ موس، میتونید منطقه مدنظر رو انتخاب کنید، بعد از هر انتخاب ROI، برای تایید، کلید Space یا Enter رو بزنید تا برید سراغ انتخاب ROI بعدی، بعد از اتمام کارتون و انتخاب تمام ROI ها، کلید ESC رو بزنید تا عملیات انتخاب ROI ها، تموم بشه.

توضیح پارامترها :

  • boundingBoxes : یه متغییر خروجی از نوع std::vector<cv::Rect> که لیست تمام ROI های انتخاب شده، داخلش ذخیره میشه.

کد نمونه :

نتیجه کد بالا :

آموزش تابع selectROIs در OpenCV

21) تابع imshow

21) تابع imshow : نمایش یک تصویر در پنجره مشخص شده؛ اگر پنجره با پرچم cv::WINDOW_AUTOSIZE ایجاد شده باشه، تصویر با اندازه اصلی خود نشان داده میشه، اما همچنان به وضوح ( resolution ) صفحه محدود هستش، در غیر این صورت، تصویر به تناسب پنجره کوچک میشه؛

این تابع، تصویر را بسته به عمق آن تغییر میدهد! :

  • اگر تصویر CV_8U ( 8-بیتی بدون علامت / طبیعی! ) باشد، همانطور که هست نمایش داده میشود.
  • اگر تصویر CV_16U ( 16-بیتی بدون علامت / طبیعی! ) باشد، پیکسل ها بر 256 تقسیم میشوند؛ یعنی محدوده مقادیر [0, 255*256] به [0, 255] نگاشت میشنود.
  • اگر تصویر CV_32F و CV_64F ( 32-بیتی یا 64-بیتی اعشاری ) باشد، پیکسل ها در 255 ضرب میشوند؛ یعنی محدوده مقادیر [0, 1] به [0, 255] نگاشت میشنود.
  • تصاویر CV_32S ( 32-بیتی علامت-دار / صحیح! ) به دلیل ابهام تبدیل مورد نیاز دیگر پردازش نمیشوند؛ اگه میخواید این نوع تصاویر رو نمایش بدید، ابتدا باید نوعشون رو CV_8U تبدیل کنید و بعد تصویر رو بدید به تابع imshow !!!
  • میمونه تصاویر CV_8S و CV_16S ( 8 و 16 بیتی علامت دار / صحیح! ) که سایت OpenCV صحبتی دربارشون نکرده؛ تابع imshow مشکلی تو نمایش این نوع از تصاویر نداره؛ نمیدونم این تابع، این نوع تصاویر رو قبل از نمایش تغییر میده یا نه، اگه هم بخواد تغییر بده احتمالا مقادیر منفی رو اول مثبت میکنه و بعد اگه نیاز بود ( برا تصاویر 16-بیتی )، محدوده مقادیر [0, 255*256] رو به [0, 255] نگاشت میکنه.

 

تعریف تابع :

 

توجه 1 : اگر پنجره قبل از این تابع ایجاد نشده باشه ( تابع namedWindow )، بعد بیاید از تابع imshow استفاده کنید، ابتدا یه پنجره با نامی که در تابع imshow تنظیم کردید و با پرچم cv::WINDOW_AUTOSIZE، ایجاد میشه و سپس تصویر شما نمایش داده میشه؛ اگه میخواید پرچم پنجره شما WINDOW_AUTOSIZE نباشه، مجبورید که قبل از تابع imshow، از تابع namedWindow استفاده کنید!!!

توجه 2 ( مخصوص ویندوز ) : با CTRL+C میتویند تصویری که تو پنجره داره نمایش داده میشه رو کپی کنید ( تصویر در حافظه موقت Clipboard ذخیره میشه فک کنم )؛ با CTRL+S هم میتونید ذخیره اش کنید ( بعد از این کار پنچره SAVE AS نمایش داده میشه که میتونید تصویر رو هرجایی که خواستید ذخیره کنید ).

22) تابع startWindowThread

22) تابع startWindowThread : در مورد این تابع، سایت OpenCV توضیحی ارائه نداده، فعلا تا زمانی که به این تابع نیازی ندارم و تو پروژه هام ازش استفاده نکردم، همینجا ولش میکنم، تا بعد.

 

توجه : یه سری دیگه تابع داریم که مختص QT هستند و در اینجا ( Qt New Functions ) معرفی شدن که فعلا کاری بهشون ندارم. ( حوصله توضیحشون رو ندارم، هر موقع نیاز بهشون پیدا کردم، میام اینجا و توضیحشون میدم! )

 

منابع : 

 

تا مطلب بعد اگه زنده بودیم و قسمت شد، یا علی.

 

تعداد مطالب : 362 تا
جنگ ما فتح قدس را به همراه خواهد داشت. [ امام خمینی (ره) ]
بقیه جلسات : آموزش OpenCV
ارسال دیدگاه
0

1) نظرات غیر فارسی به صورت خودکار حذف میشوند ( حداقل 5 حرف فارسی وارد کنید ).

2) پسورد فایل های سایت : www.dmf313.ir

3) نظرات حاوی کد برنامه نویسی تایید نمیشوند ( قالب سایت بهم میریزه )