به نام خدا : در این مطلب به معرفی ماژول High-level GUI و توابعش میپردازیم، ایجاد پنجره و نمایش تصاویر و انتخاب ROI و کنترل موس و کیبورد و… از کارهایی هستش که این ماژول انجام میده.
1) ROI چیست : در OpenCV، کلمه ROI مخفف عبارت "region of interest" به معنی "منطقه مورد نظر/علاقه" هستش؛ قسمت از یه تصویر که بُرِش خورده میشه و بهش میگن ROI.
2) backend چیست و انواع آن ( QT ،GTK ،Win32 و... )
خلاصه بگم که یه Backend داریم و یه Frontend؛ حالا Backend یعنی قسمت از پروژه که کاربر باهاش کاری نداره، نمیبینه و... یعنی کدهای پروژه، Frontend هم یعنی قسمت GUI، قسمت گرافیکی پروژه که کاربر میبینه و ازشون استفاده میکنه و... مثلا تو کدنویسی وب، HTML و CSS میشن Frontend و PHP میشه Backend.
کتابخانه مورد نیاز این مطلب :
1 |
#include <opencv2/highgui.hpp> |
توضیح توابع ماژول High-level GUI
1) تابع namedWindow : به کمک تابع namedWindow میتونید یه پنجره ایجاد کنید تا کنترل ها و تصاویر و... رو داخلش نمایش بدید.
1 |
CV_EXPORTS_W void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE); |
توضیح پارامترها :
- winname : نام پنجره؛ اگه پنجره با این نام قبلا ایجاد شده باشه، این تابع کاری نمیکنه؛ برای عنوان ( title ) پنجره، از همین پارامتر استفاده میشه؛ اگه میخواید عنوان پنجره رو یه مقدار دیگه تنظیم کنید، به تابع setWindowTitle مراجعه کنید.
- flags : پرچم های پنجره؛ به کمک نوع شمارشی cv::WindowFlags مقدار دهی میشه.
نوع شمارشی cv::WindowFlags :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//! Flags for cv::namedWindow enum WindowFlags { WINDOW_NORMAL = 0x00000000, //!< the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size. WINDOW_AUTOSIZE = 0x00000001, //!< the user cannot resize the window, the size is constrainted by the image displayed. WINDOW_OPENGL = 0x00001000, //!< window with opengl support. WINDOW_FULLSCREEN = 1, //!< change the window to fullscreen. WINDOW_FREERATIO = 0x00000100, //!< the image expends as much as it can (no ratio constraint). WINDOW_KEEPRATIO = 0x00000000, //!< the ratio of the image is respected. WINDOW_GUI_EXPANDED=0x00000000, //!< status bar and tool bar WINDOW_GUI_NORMAL = 0x00000010, //!< old fashious way }; |
گزینه WINDOW_FREERATIO، مختص Qt backend هستش.
2) تابع setWindowTitle : به کمک این تابع میتونید عنوان پنجره ( ای که به کمک تابع namedWindow ایجاد کردید ) رو تغییر بدید؛ در پارامتر winname، نام پنجره مدنظر، و در پارامتر title، عنوان جدید پنجره فوق رو قرار بدید.
1 |
CV_EXPORTS_W void setWindowTitle(const String& winname, const String& title); |
3) تابع moveWindow : جابجا کردن پنجره مشخص شده؛ پارامتر winname، نام پنجره مدنظر که میخواید جابجاش کنید، پارامتر های x و y هم مختصات جدید پنجره هستند ( مختصات بالا-چپ پنجره )
1 |
CV_EXPORTS_W void moveWindow(const String& winname, int x, int y); |
4) تابع resizeWindow : تغییر اندازه پنجره مشخص شده؛ اگر هنگام ایجاد پنجره ( به کمک تابع namedWindow )، مقدار پارامتر flags رو، روی WINDOW_AUTOSIZE تنظیم کرده باشید ( که مقدار پیشفرضش هم همینه )، تابع resizeWindow، نمیتونه اندازه پنجره رو تغییر بده.
1 2 |
CV_EXPORTS_W void resizeWindow(const String& winname, int width, int height); CV_EXPORTS_W void resizeWindow(const String& winname, const cv::Size& size); |
5 و 6) توابع getWindowProperty و setWindowProperty : -
1 2 |
CV_EXPORTS_W double getWindowProperty(const String& winname, int prop_id); CV_EXPORTS_W void setWindowProperty(const String& winname, int prop_id, double prop_value); |
7 و 8) توابع destroyWindow و destroyAllWindows : نابود کردن پنجره مشخص شده یا تمام پنجره ها.
1 2 |
CV_EXPORTS_W void destroyWindow(const String& winname); CV_EXPORTS_W void destroyAllWindows(); |
9) تابع createTrackbar : برای ایجاد یه Trackbar افقی از این تابع استفاده میکنیم ( تو QT کنترلی شبیه این با نام Horizontal Slider هستش )؛ قبل از استفاده از این تابع، باید به کمک تابع cv::namedWindow، یک پنجره ایجاد کنید ( این Trackbar، داخل پنجره ای که ایجاد کردید قرار میگیره )؛ شروع این کنترل همیشه از 0 هستش و نمیشه شروعشو تغیر داد! اما میشه پایان ( مقدار حداکثر ) شو تعیین کرد!؛ اگه میخواید مقدار شروع / حداقل رو تعیین کنید، باید از تابع setTrackbarMin استفاده کنید.
1 2 3 4 5 6 7 |
CV_EXPORTS int createTrackbar( const String& trackbarname, const String& winname, int* value, int count, TrackbarCallback onChange = 0, void* userdata = 0 ); |
توضیح پارامترها :
- trackbarname : نام کنترل.
- winname : نام پنجره ای که قراره این کنترل داخلش نمایش داده بشه.
- value : مقدار فعلی کنترل ( value ≤ count )؛ مقدار های آینده هم داخل این پارامتر قرار میگیره!
- count : مقدار حداکثر کنترل.
- onChange : تابعی که وقتی مقدار کنترل تغییر کرد، فراخونی میشه.
- userdata : داده های کاربر؛ داده هایی که میخواید به تابعی که در پارامتر onChange تعریف کردید، ارسال بشه.
خب برا کار با این کنترل چند تا مثال در ادامه قرار میدم :
کد نمونه 1 : تو این کد مقدار کنترل بین مقدار 0 تا 50 میتونه تغییر کنه؛ زمانی که مقدار کنترل تغیر کنه، تابع trackbar1_changed اجرا میشه؛ اگه کد رو اجرا کنید میبینید که trackbar1_pos و pos همیشه برابر هستند؛ فلذا وقتی مقدار کنترل تغییر بکنه، متغییر trackbar1_pos هم بروزرسانی میشه.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> static int trackbar1_pos = 13; static void trackbar1_changed(int pos, void* userdata) { std::cout << "trackbar1_changed, " << trackbar1_pos << ", " << pos << std::endl; } static void trackbar_test1() { std::string win_name = "Output!"; int maximum = 50; cv::namedWindow(win_name); cv::createTrackbar("trackbar1", win_name, &trackbar1_pos, maximum, trackbar1_changed); } |
کد نمونه 2 : خب حالا میخوایم از پارامتر userdata استفاده کنیم و یه داده دلخواهی رو به تابع trackbar2_changed ارسال کنیم. ( البته نمیدونم اسم تابع درست باشه یا نه؛ چون معمولا به این مدل توابع میگن وقفه/interrupt، رخداد/event و در اینجا میگن Callback فک کنم؛ در هر صورت کاری به اسمش نداریم ).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> static void trackbar2_changed(int pos, void* userdata) { std::string *data = ((std::string *)userdata); *data = "DMF" + std::to_string(pos); std::cout << "user_data in event = " << *data << std::endl; } static void trackbar_test2() { std::string win_name = "Output!"; int trackbar2_pos = 13; int maximum = 50; std::string user_data = "DMF313.IR"; cv::namedWindow(win_name); cv::createTrackbar("trackbar2", win_name, &trackbar2_pos, maximum, trackbar2_changed, (void*)&user_data); while(true) { std::cout << "user_data in while = " << user_data << std::endl; cv::waitKey(100); } } |
توجه : داخل تابع اصلیتون ( اونجایی که تابع createTrackbar رو فراخونی میکنید و یه وقفه برا trackbar ایجاد میکنید ) بهتره که حلقه while قرار بدید که شامل تابع cv::waitKey هم باشه ( مقدارش بزرگتر از 1 باشه ) یا اگه حلقه while ندارید همین تابع رو بدون پارامتر ( یا با مقدار 0 ) انتهای تابع قرار بدید ( تا به مدت بی نهات، صبر کنه! )؛ این طوری تابع فوق، هیچ وقت بسته نمیشه و متغییرهاش نابود نمیشن، فلذا در این حالت وقتی وقفه فوق اجرا میشه، برنامه کرش نمیکنه!؛ حالا بنظرتون چرا برنامه کرش میکنه؟ بله درسته، متغیر user_data داخل تابع trackbar_test2 تعریف شده و وقتی این تابع تموم بشه، متغییر فوق هم نابود میشه، و وقتی وقفه trackbar2_changed رخ میده، میبینه که همچین متغییری ( user_data ) وجود خارجی نداره و این باعث کرش کردن برنامه میشه؛ برای حل این مشکل همونطور که گفتم یا باید از تابع cv::waitKey استفاده کنید، یا این که متغییر user_data رو سراسری ( خارج از تابع ) تعریف کنید.
10) تابع getTrackbarPos : خواندن موقعیت فعلی trackbar مشخص شده.
1 |
CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname); |
توضیح پارامترها :
- trackbarname : نام trackbar مدنظر.
- winname : نام پنجره ای که trackbar مدنظر داخلش قرار داره.
- return : موقعیت فعلی trackbar مدنظر.
11) تابع setTrackbarPos : تغییر موقعیت فعلی trackbar مشخص شده.
1 |
CV_EXPORTS_W void setTrackbarPos(const String& trackbarname, const String& winname, int pos); |
توضیح پارامترها :
- trackbarname : نام trackbar مدنظر.
- winname : نام پنجره ای که trackbar مدنظر داخلش قرار داره.
- pos : موقعیت جدید trackbar مدنظر.
کد نمونه 3 : تو این کد، برا خواندن و تغییر موقعیت فعلی trackbar، از توابع getTrackbarPos و setTrackbarPos استفاده کردم؛ کار این کد اینه که وقتی مقدار trackbar به 20 برسه، مقدارشو 0 میکنه.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> std::string win_name = "code3"; std::string tb_name = "trackbar3"; static void trackbar3_changed(int pos, void* userdata) { std::cout << "trackbar3_changed = " << pos << std::endl; } static void trackbar_test3() { int pos = 13; int maximum = 50; cv::namedWindow(win_name); cv::createTrackbar(tb_name, win_name, &pos, maximum, trackbar3_changed); } int main() { trackbar_test3(); while(true) { if(cv::getTrackbarPos(tb_name, win_name) >= 20) { cv::setTrackbarPos(tb_name, win_name, 0); } cv::waitKey(100); } return 0; } |
12) تابع setTrackbarMin : تغییر مقدار حداقل trackbar مشخص شده.
1 |
CV_EXPORTS_W void setTrackbarMin(const String& trackbarname, const String& winname, int minval); |
13) تابع setTrackbarMax : تغییر مقدار حداکثر trackbar مشخص شده.
1 |
CV_EXPORTS_W void setTrackbarMax(const String& trackbarname, const String& winname, int maxval); |
14) تابع setMouseCallback : یک وقفه برای مدیریت موس و کلید های کنترلی کیبورد ( shift - alt - ctrl ) در پنجره مدنظر، ایجاد میکنه.
1 |
CV_EXPORTS void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0); |
توضیح پارامترها :
- winname : نام پنجره ای که قراره این کنترل داخلش نمایش داده بشه.
- onMouse : تابعی که وقتی مقدار کنترل تغییر کرد، فراخونی میشه.
- userdata : داده های کاربر؛ داده هایی که میخواید به تابعی که در پارامتر onMouse تعریف کردید، ارسال بشه.
پارامتر onMouse : نحوه تعریف تابع وقفه، به صورت زیر هستش ( اسم تابع مهم نی، هرچی تنظیم کردید! )؛ میتونیم موقعیت موس، فشرده شدن کلیدهای موس و کلیدهای کنترلی کیبورد ( shift - alt - ctrl ) رو تشخیص بدیم؛ دابل-کلیک، چرخش کلید وسط موس رو میتونیم تشخیص بدیم و...
1 |
static void onMouse_changed(int event, int x, int y, int flags, void* userdata) |
توضیح پارامترها :
- event : مقدارش رو به کمک نوع شمارشی cv::MouseEventTypes میخونیم.
- x و y : موقعیت x و y فعلی موس.
- flags : مقادیرش رو به کمک نوع شمارشی cv::MouseEventFlags میخونیم.
- userdata : پارامتر اختیاری داده ای کاربر برای این تابع فرستاده.
نوع شمارشی cv::MouseEventTypes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
enum MouseEventTypes { EVENT_MOUSEMOVE = 0, // indicates that the mouse pointer has moved over the window. EVENT_LBUTTONDOWN = 1, // indicates that the left mouse button is pressed. EVENT_RBUTTONDOWN = 2, // indicates that the right mouse button is pressed. EVENT_MBUTTONDOWN = 3, // indicates that the middle mouse button is pressed. EVENT_LBUTTONUP = 4, // indicates that left mouse button is released. EVENT_RBUTTONUP = 5, // indicates that right mouse button is released. EVENT_MBUTTONUP = 6, // indicates that middle mouse button is released. EVENT_LBUTTONDBLCLK = 7, // indicates that left mouse button is double clicked. EVENT_RBUTTONDBLCLK = 8, // indicates that right mouse button is double clicked. EVENT_MBUTTONDBLCLK = 9, // indicates that middle mouse button is double clicked. EVENT_MOUSEWHEEL = 10,// positive and negative values mean forward and backward scrolling, respectively. EVENT_MOUSEHWHEEL = 11 // positive and negative values mean right and left scrolling, respectively. }; |
نوع شمارشی cv::MouseEventFlags :
1 2 3 4 5 6 7 8 9 |
enum MouseEventFlags { EVENT_FLAG_LBUTTON = 1, // indicates that the left mouse button is down. EVENT_FLAG_RBUTTON = 2, // indicates that the right mouse button is down. EVENT_FLAG_MBUTTON = 4, // indicates that the middle mouse button is down. EVENT_FLAG_CTRLKEY = 8, // indicates that CTRL Key is pressed. EVENT_FLAG_SHIFTKEY = 16,// indicates that SHIFT Key is pressed. EVENT_FLAG_ALTKEY = 32 // indicates that ALT Key is pressed. }; |
15) تابع getMouseWheelDelta : یه تابع اضافی برای راحتی بیشتر!؛ کارش اینه که وقتی رخداد های EVENT_MOUSEWHEEL و EVENT_MOUSEHWHEEL رخ میدن، میتونیم مقدار دلتای کلید وسط موس، همون چرخشی هه! ( wheel ) رو بخونیم که خب برای موس های معمولی، دلتا مضربی از 120 هستش، که خب برای موس هایی با دقت بالا، مقدار فوق کوچکتر خواهد بود؛ البته من موس با 2 تا کلید چرخشی ندیدم ولی خب رخداد EVENT_MOUSEWHEEL برای کلید چرخشی عادی ( عمودی ) هستش و رخداد EVENT_MOUSEHWHEEL برای کلید چرخشی افقی هستش ( هر موسی، کلید چرخشی افقی رو نداره! )؛ برای EVENT_MOUSEWHEEL مقادیر مثبت و منفی به ترتیب به معنای اسکرول رو به جلو و عقب هستند. برای EVENT_MOUSEHWHEEL، در صورت وجود، مقادیر مثبت و منفی به ترتیب به معنای پیمایش راست و چپ هستند.
1 |
CV_EXPORTS int getMouseWheelDelta(int flags); |
توضیح پارمترها :
- flags : پارامتر flags از callback موس ( تابع وقفه موس! ) رو به این پارامتر میدید.
- return : در بالا صحبت شد دربارش.
کد نمونه :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
#include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> static void onMouse_changed(int event, int x, int y, int flags, void* userdata) { int mouseWheelDelta = cv::getMouseWheelDelta(flags); // event parameter std::string event_status = std::to_string(event); //--- switch (event) { case cv::MouseEventTypes::EVENT_MOUSEMOVE : // mouse pointer has moved over the window. event_status = "MOUSEMOVE"; break; case cv::MouseEventTypes::EVENT_LBUTTONDOWN : // left mouse button is pressed. event_status = "LBUTTONDOWN"; break; case cv::MouseEventTypes::EVENT_RBUTTONDOWN : // right mouse button is pressed. event_status = "RBUTTONDOWN"; break; case cv::MouseEventTypes::EVENT_MBUTTONDOWN : // middle mouse button is pressed. event_status = "MBUTTONDOWN"; break; case cv::MouseEventTypes::EVENT_LBUTTONUP : // left mouse button is released. event_status = "LBUTTONUP"; break; case cv::MouseEventTypes::EVENT_RBUTTONUP : // right mouse button is released. event_status = "RBUTTONUP"; break; case cv::MouseEventTypes::EVENT_MBUTTONUP : // middle mouse button is released. event_status = "MBUTTONUP"; break; case cv::MouseEventTypes::EVENT_LBUTTONDBLCLK : // left mouse button is double clicked. event_status = "LBUTTONDBLCLK"; break; case cv::MouseEventTypes::EVENT_RBUTTONDBLCLK : // right mouse button is double clicked. event_status = "RBUTTONDBLCLK"; break; case cv::MouseEventTypes::EVENT_MBUTTONDBLCLK : // middle mouse button is double clicked. event_status = "MBUTTONDBLCLK"; break; case cv::MouseEventTypes::EVENT_MOUSEWHEEL : // positive and negative values mean forward and backward scrolling, respectively. event_status = "MOUSEWHEEL"; break; case cv::MouseEventTypes::EVENT_MOUSEHWHEEL : // positive and negative values mean right and left scrolling, respectively. event_status = "MOUSEHWHEEL"; break; } // flags parameter std::string flags_status = ""; //--- if( flags & cv::MouseEventFlags::EVENT_FLAG_LBUTTON ) // indicates that the left mouse button is down. { flags_status += "LBUTTON "; } if( flags & cv::MouseEventFlags::EVENT_FLAG_RBUTTON ) // right mouse button is down. { flags_status += "RBUTTON "; } if( flags & cv::MouseEventFlags::EVENT_FLAG_MBUTTON ) // middle mouse button is down. { flags_status += "MBUTTON "; } if( flags & cv::MouseEventFlags::EVENT_FLAG_CTRLKEY ) // CTRL Key is pressed. { flags_status += "CTRL "; } if( flags & cv::MouseEventFlags::EVENT_FLAG_SHIFTKEY ) // SHIFT Key is pressed. { flags_status += "SHIFT "; } if( flags & cv::MouseEventFlags::EVENT_FLAG_ALTKEY ) // ALT Key is pressed. { flags_status += "ALT "; } //--- if(flags_status == "") flags_status = "0"; std::cout << "Mouse Pos = ( " << x << "," << y << " ), " << "Mouse Wheel = " << mouseWheelDelta << ", " << "event = " << event_status << ", " << "flags = " << flags_status << std::endl; } int main() { cv::Mat img(400, 800, CV_8UC1, cv::Scalar::all(255)); std::string win_name = "winName!"; cv::imshow(win_name, img); cv::setMouseCallback(win_name, onMouse_changed); while(true) cv::waitKey(); return 0; } |
16) تابع waitKey : صبر کردن تا زمانی که یه کلیدی فشرده شود.
1 |
CV_EXPORTS_W int waitKey(int delay = 0); |
توضیح پارامترها :
- delay : مدت زمانی که تابع باید صبر کنه تا یه کلیدی فشرده بشه، بر حسب ms؛ اگر \( delay ≤ 0 \) باشه، تابع به مدت بی نهایت صبر میکنه تا زمانی که یه کلیدی فشرده بشه.
- return : چیزی که تابع برمیگردونه، کد اسکی ( ASCII Code ) کلید فشرده شده هستش؛ اگر با پایان زمان تعیین شده و اتمام کار تابع، کلیدی فشرده نشده باشه، تابع مقدار -1 رو برمیگردونه.
17) تابع waitKeyEx : شبیه تابع WaitKey هستش، اما کد کامل، کلید فشرده شده رو برمیگردونه؛ بسته به این که از چه backend یی استفاده میکنید، کد ارسالی ممکنه متفاوت باشه : QT، GTK، Win32 و...
1 |
CV_EXPORTS_W int waitKeyEx(int delay = 0); |
18) تابع pollKey : شبیه تابع WaitKey هستش، با این تفاوت که دیگه صبر نمیکنه تا یه کلیدی فشرده بشه، دیگه خبری از تاخیر زمانی ( delay ) نیست.
1 |
CV_EXPORTS_W int pollKey(); |
توجه : توابع بالا فقط در صورتی کار میکنند که حداقل یک پنجره HighGUI ایجاد شده باشه ( درباره نحوه ایجاد پنجره / window تو این مطلب صحبت کردیم ) و فعال هم باشه. ( منظور از فعال بودن اینه که اولا minimise نشده باشه، و دوما focus موس روش باشه، مثلا الان فوکوس موس من رو مرورگر گوگل کروم هستش، اگه چیزی تایپ کنم، متن من در گوگل کروم نمایش داده میشه و نه جای دیگه ای! )
19) تابع selectROI : به کاربران اجازه میدهد تا یک ROI را در تصویر داده شده انتخاب کنند؛ به کمک کلیک چپ موس، میتونید منطقه مدنظر رو انتخاب کنید، بعد به کمک کلیدهای Space یا Enter پایان عملیات انتخاب ناحیه رو اعلام میکنید و تابع ناحیه انتخاب شده رو return میکنه، اگه هم کلید C رو فشار بدید، عملیات انتخاب ناحیه لغو میشه ( کنسل میشه! ) و یک ناحیه خالی return میشه. ( ناحیه خالی تمام مقادیرش 0 هستند، نقطه شروعش و طول و عرضش 0 هست )؛ اگه فقط 1 پنجره دارید، تعریف دوم تابع بنظر ساده تر میاد!، ولی اگه چندین پنجره دارید بهتره از تعریف اول تابع استفاده کنید.
1 2 |
CV_EXPORTS_W Rect selectROI(const String& windowName, InputArray img, bool showCrosshair = true, bool fromCenter = false); CV_EXPORTS_W Rect selectROI(InputArray img, bool showCrosshair = true, bool fromCenter = false); |
توضیح پارامترها : پارامترهای showCrosshair و fromCenter رو اگه متوجه نشدید، در کد نمونه زیر، مقادیرشون رو تغییر بدید و ببینید در اجرا چه اتفاقی میوفته، اینطوری حتما متوجه میشید.
- windowName : نام پنجره ای که فرآیند انتخاب ROI قراره اونجا عمل کنه.
- img : تصویری که قراره ازش یه ROI انتخاب کنید.
- showCrosshair : تصویر crosshair داخل مستطیل ناحیه ای که دارید انتخاب میکنید نمایش داده بشه یا نه. ( مثل نشانگر نشانه گیری تفنگ تو بازی کانتر! که به شکل + هستش!!! )
- fromCenter : مرکز انتخاب ناحیه، از گوشه بالا-چپ باشد ( false ) یا از مرکز / وسط ناحیه ( true ).
کد نمونه :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> int main() { cv::Mat img = cv::imread("C:/Users/Mahdi/Desktop/1.jpg"); cv::imshow("image_window", img); cv::Rect rect = cv::selectROI("image_window", img, true, false); //cv::Rect rect = cv::selectROI(img, true, false); if( !rect.empty() ) { cv::Mat roi = img(rect); cv::imshow("roi_window", roi); } std::cout << rect << std::endl; cv::waitKey(); return 0; } |
نتیجه کد بالا :
20) تابع selectROIs : همانند تابع selectROI هستش، با این تفاوت که این تابع، برای انتخاب چندین ROI بکار میره و نه فقط یکی!؛ به کمک کلیک چپ موس، میتونید منطقه مدنظر رو انتخاب کنید، بعد از هر انتخاب ROI، برای تایید، کلید Space یا Enter رو بزنید تا برید سراغ انتخاب ROI بعدی، بعد از اتمام کارتون و انتخاب تمام ROI ها، کلید ESC رو بزنید تا عملیات انتخاب ROI ها، تموم بشه.
1 2 3 4 5 6 |
CV_EXPORTS_W void selectROIs( const String& windowName, InputArray img, CV_OUT std::vector<Rect>& boundingBoxes, bool showCrosshair = true, bool fromCenter = false ); |
توضیح پارامترها :
- boundingBoxes : یه متغییر خروجی از نوع std::vector<cv::Rect> که لیست تمام ROI های انتخاب شده، داخلش ذخیره میشه.
کد نمونه :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> int main() { cv::Mat img = cv::imread("C:/Users/Mahdi/Desktop/1.jpg"); cv::imshow("image_window", img); std::vector<cv::Rect> rects; cv::selectROIs("image_window", img, rects, true, false); if( rects.size() == 0 ) { std::cout << "No region selected!" << std::endl; } else { for(int i = 0; i < rects.size(); ++i) { cv::Mat roi = img(rects[i]); std::string roi_win_name = "roi" + std::to_string(i) + "_window"; cv::imshow(roi_win_name, roi); std::cout << "rects[" << i << "] = " << rects[i] << std::endl; } } cv::waitKey(); return 0; } |
نتیجه کد بالا :
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 |
CV_EXPORTS_W void imshow(const String& winname, InputArray mat); |
توجه 1 : اگر پنجره قبل از این تابع ایجاد نشده باشه ( تابع namedWindow )، بعد بیاید از تابع imshow استفاده کنید، ابتدا یه پنجره با نامی که در تابع imshow تنظیم کردید و با پرچم cv::WINDOW_AUTOSIZE، ایجاد میشه و سپس تصویر شما نمایش داده میشه؛ اگه میخواید پرچم پنجره شما WINDOW_AUTOSIZE نباشه، مجبورید که قبل از تابع imshow، از تابع namedWindow استفاده کنید!!!
توجه 2 ( مخصوص ویندوز ) : با CTRL+C میتویند تصویری که تو پنجره داره نمایش داده میشه رو کپی کنید ( تصویر در حافظه موقت Clipboard ذخیره میشه فک کنم )؛ با CTRL+S هم میتونید ذخیره اش کنید ( بعد از این کار پنچره SAVE AS نمایش داده میشه که میتونید تصویر رو هرجایی که خواستید ذخیره کنید ).
22) تابع startWindowThread : در مورد این تابع، سایت OpenCV توضیحی ارائه نداده، فعلا تا زمانی که به این تابع نیازی ندارم و تو پروژه هام ازش استفاده نکردم، همینجا ولش میکنم، تا بعد.
توجه : یه سری دیگه تابع داریم که مختص QT هستند و در اینجا ( Qt New Functions ) معرفی شدن که فعلا کاری بهشون ندارم. ( حوصله توضیحشون رو ندارم، هر موقع نیاز بهشون پیدا کردم، میام اینجا و توضیحشون میدم! )
منابع :
تا مطلب بعد اگه زنده بودیم و قسمت شد، یا علی.