استفاده از ماوس در OpenCv

مدیریت رویدارهای ماوس در OpenCV:

در این قسمت از آموزش OpenCV با هم یاد می‌گیریم که چگونه رویداهای ماوس رو در OpenCV مدیریت کنیم تا بتوانیم از ماوس در برنامه‌هامون استفاده کنیم.

OpenCV به ما کمک می‌کنه بتونیم راست-کلیک، چپ-کلیک، موقعیت ماوس روی پنجره و … رو تشخیص بدهیم.

شکل کلی و پارامترهای setMouseCallback رو با هم ببینم و  در ادامه با چند مثال یاد خواهیم گرفت که چطور باهاش کار کنیم.

cv2.setMouseCallback(windowName, onMouse[, param])

Parameters:

  • windowName – Name of the OpenCV window. All mouse events related to this window will be registered
  • onMouse – Name of the callback function. Whenever mouse events related to the above window occur, this callback function will be called. This function should have the signature like the following
    • void FunctionName(int event, int x, int y, int flags, void* userdata)
      • event – Type of the mouse event. These are the entire list of mouse events
        • EVENT_MOUSEMOVE
        • EVENT_LBUTTONDOWN
        • EVENT_RBUTTONDOWN
        • EVENT_MBUTTONDOWN
        • EVENT_LBUTTONUP
        • EVENT_RBUTTONUP
        • EVENT_MBUTTONUP
        • EVENT_LBUTTONDBLCLK
        • EVENT_RBUTTONDBLCLK
        • EVENT_MBUTTONDBLCLK
      • x – x coordinate of the mouse event
      • y – y coordinate of the mouse event
      • flags – Specific condition whenever a mouse event occurs. Here is the entire list of enum values which will be possesed by “flags”
        • EVENT_FLAG_LBUTTON
        • EVENT_FLAG_RBUTTON
        • EVENT_FLAG_MBUTTON
        • EVENT_FLAG_CTRLKEY
        • EVENT_FLAG_SHIFTKEY
        • EVENT_FLAG_ALTKEY
      • userdata – Any pointer passes to the “setMouseCallback” function as the 3rd parameter (see below)
    • param – This pointer will be passed to the callback function

 

به لیست event‌ها دقت کنید، از حرکت کردن ماوس، کلیک و رها شدن اون گرفته تا دوبار فشرده شدن اون دکمه‌ی وسط ماوس رو داریم.

مثال اول:

در این برنامه وقتی روی صفحه دوبار کلیک کنیم (کلیک راست رو دوبار پشت سر هم فشار بدهیم) یک دایره به شعاع ۵۰ به مرکز اشاره‌گر ماوس ایجاد خواهد شد، برای خروج از برنامه باید کلید Esc فشرده شود.

# کتابخانه‌های OpenCV و NumPy رو فرا می‌خونیم
import cv2
import numpy as np

center = []
# تابعی که روتین فراخوانی ماوس صدا می‌زند رو ایجاد کینم
# این تابع در صورتی که دبل کیک کنیم یک دایره به مرکز اشاره‌گر ماوس به شعاع ۱ رسم می‌کند و
# دایره‌ای دیگر به هماهن مرکز و شعاع ۵۰
def drawCircle(event, x, y, flags, param):
 global center
 if event == cv2.EVENT_LBUTTONDBLCLK:
 center = [(x,y)]
 print (center)
 cv2.circle(image, center[0], 1, (0, 255, 255), 2)
 cv2.circle(image, center[0], 50, (255,255,255), 1)

# یک تصویر سیاه ایجاد می‌کنیم
image = np.zeros((400,400,3), np.uint8)
# پنجره‌ای که می‌خواهیم وقایع ماوس در آن را دنبال کنیم ایجاد می‌کنیم
cv2.namedWindow("Window")

# روتین فراخوانی وقایع ماوس را صدا می‌زنیم
# پارامتر اول نام پنجره‌ای که می‌خواهیم وقایع ماوس آن دنبال شود
# پارامتر دوم تابعی که بعد از هر واقعه‌ی ماوس فراخوانده می‌شود
cv2.setMouseCallback("Window",drawCircle)


# تا زمانی که کاربر کلید Esc را نزده است پنجره نمایش داده شود
key = 0
while key != 27:
 cv2.imshow("Window",image)
 key = cv2.waitKey(1) & 0xFF

# پنجره‌ی Window بسته شود
cv2.destroyWindow("Window")

در خط‌های ۱ و ۲ کتابخانه‌های مورد نیاز رو فرا می‌خونیم. در خط ۵اوم آمدیم و یک متغیر خالی برای ذخیره‌ی مرکز دایره ایجاد کردیم که در خط ۱۲ اون رو مقدار دهی خواهیم کرد.

خطوط ۹ تا ۱۵ تابعی هست که هنگام وقایع ماوس فرا خونده می‌شه. در خط ۱۱مشخص کرده‌ایم که اگر رخداد ماوس دابل‌کلیک بود مختتصات x و y  در متغیر center ذخیره بشود. الان مختصات مرکز رو داریم، دو دایره یکی به شعاع ۱ و ضخامت خط ۲ (که عملا یک نقطه خواهد شد) و دایره‌ی دیگه‌ای به شعاع ۵۰ رسم می‌کنیم (خطوط ۱۴ و ۱۵).

خط ۱۸ رو در برنامه‌های قبلی هم داشتیم، کاری که انجام داده‌ایم اینه که یک تصویر سیاه با ابعاد ۴۰۰×۴۰۰ تولید می‌کنیم. در خط ۲۰ یک پنجره برای نمایش تصویر ایجاد شده می‌سازیم .

در خط ۲۵ ما تایع Callback ماوس رو مقدار دهی کردیم، متغیر اول اسم پنجره‌ای که می‌خواهیم وقایع ماوس اون رو دنبال کنیم و متغیر دوم تابعی که هنگام رخدادن وقایع ماوس فراخوانی می‌شود.

خط ۲۹ تا ۳۲ یک حلقه ایجاد کرده‌ایم و تا زمانی که کاربر کلید Esc رو فشار ندهد اون پنجره‌ای که ایجاد کردیم نمایش داده خواهد شد.

مثال دوم:

برنامه‌ی قبل رو کمی پیچیده‌تر کنیم، مرکز دایره‌ای که می‌خواهیم رسم کنیم با کلیک چپ موس مشخص بشه و زمانی که کلیک ماوس رو رها می‌کنیم دایره‌ای به مرکز اشاره‌گر موس و شعاع نقطه‌ی فعلی اشاره‌گر ماوس روی صفحه ایجاد بشه، برای پاک کردن صفحه از کلید C و برای خروج از برنامه Esc رو باید بزنیم.

# کتابخانه‌ه‌ی OpenCV و Math رو به برنامه‌مون اضاف می‌کنیم
import cv2
import math

# برای ذخیره‌ی موقعیت ماوس دو لیست خالی ایجاد می‌کنیم
center = []
circumference = []


# تابعی که هنگام روتین فراخوانی وقایع ماوس صدا زده می‌شود را ایجاد می‌کنیم
def drowCircle(action, x, y, flags, userdata):
    global center, circumference
    #  در صورت چپ-کلیک ماوس موقعیت فعلی به عنوان مرکز دایره در نظر گرفته می‌شود
    if action == cv2.EVENT_LBUTTONDOWN:
        center = [(x, y)]
        print(center)
        # جهت مشخص کردن مرکز دایره، دایره‌ای به شعاع ۱ رسم می‌کنیم
        cv2.circle(image, center[0], 1, (0, 255, 255), 2)
    # در صورت رها کردن ماوس موقعیت اشاره گر به عنوان محیط دایره در نظر گرفته می‌شود
    elif action == cv2.EVENT_LBUTTONUP:
        circumference = [(x, y)]
        print(circumference)
        # شعاع دایره را محاسبه می‌کنیم
        radius = math.sqrt(math.pow(center[0][0] - circumference[0][0], 2)
                           + math.pow(center[0][1] - circumference[0][1], 2))

        # دایره‌ را رسم می‌کنیم
        cv2.circle(image, center[0], int(radius), (0, 255, 0), 2)

# یک عکس را باز می‌کنیم
image = cv2.imread("stallman.jpg")
# برای این که بتوانیم ضفحه را پاک کنیم کپی‌ای از عکس اصلی ایجاد می‌کنیم
imageCopy = image.copy()
# پنجره‌ای را جهت نمایش تصویرایجاد می‌کنیم
cv2.namedWindow("Window")
# روتن فراخوانی وقایع ماوس را ایجاد می‌کنیم
cv2.setMouseCallback("Window", drowCircle)

key = 0
# تا زمانی که کابر کلید خروج را فشار نداده‌است تصویر را نمایش می‌دهیم
while key != 27:
    cv2.imshow("Window", image)
    cv2.putText(image, "Chose Center and drag, press Esc to exit and c to clear", (20, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
    key = cv2.waitKey(1) & 0xFF
# اگر کاربر کلید c را فشار داد صفحه را پاک می‌کنیم
    if key == ord('c'):
        image = imageCopy.copy()

# تمام پنجره‌های باز را می‌بنیدم
cv2.destroyAllWindows()

تنها چیزی که فکر می‌کنم نیاز به توضیح داشته باشه پاک کردن صفحه‌س، ما در ابتدای برنامه یک کپی از عکس اصلی ایجاد می‌کنیم و هرکجا که خواستیم دایره‌های رسم شده روی صفحه رو پاک کنیم کپی‌ای که اول کار ایجاد کردیم رو با عکس فعلی که روی اون دایره رسم شده جایگزین می‌کنیم.

مثال سوم:

حالا نوبت یک برنامه‌ی کابردی‌تره، در آینده در مورد الگوریتم‌های طبقه‌بندی عکس صحبت خواهیم کرد، ممکنه برای فاز train یا سایر کابردها نیاز پیدا کنیم تا به صورت دستی بخشی از عکس رو انتخاب کنیم (Region of Interest (ROI)) و اون قسمت رو برش بزنم،‌ در مثال زیر ما بخشی از عکس رو به کمک ماوس انتخاب می‌کنیم، اون رو برش می‌زنیم. در صورتی که کابر کلید r را فشار بدهد کادر ریست شده و مربع‌های رسم شده بر روی تصویر پاک می شوند، برای خروج از برنامه هم کلید Esc رو رو قرار داده‌ایم.

# کتابخانه‌ی openCV رو فرا می‌خونیم
import cv2

# یک لیست خالی جهت ذخیره‌ی نقاط مورد نیاز برای رسم مربع ایجاد می‌کنیم
refPt = []

# تابع روتین وقایع ماوس
def click_and_crop(event, x, y, flags, param):

    global refPt

# وقتی رخداد ماوس راست کلیک است مقدار آن را ذخیره کرده و یک نقطه جهت نمایش آن بر روی صفحه رسم می‌کنیم
    if event == cv2.EVENT_LBUTTONDOWN:
        refPt = [(x, y)]
        cv2.circle(image, refPt[0], 1, (0,0,255), 2)
# زمانی که رخداد ماوس رها شدن کلیک است مقدار آن را به انتهای refPt افزورده و یک مربع با استفاده
# از نقاط بدست آمده رسم می‌کنیم
    elif event == cv2.EVENT_LBUTTONUP:
        refPt.append((x, y))
        cv2.circle(image, refPt[1], 1, (0, 0, 255), 2)
        cv2.rectangle(image, refPt[0], refPt[1], (0, 255, 0), 2)

# عکس موزد نظر خود را بارگذای می‌کنیم
image = cv2.imread("stallman.jpg")
# از عکس اصلی یک کپی تهیه می‌کنیم
clone = image.copy()
# یک پنجره برای نمایش عکس ایجاد می‌کنیم
cv2.namedWindow("image")
# تابع کال‌بک ماوس را مقدار دهی می‌کنیم
cv2.setMouseCallback("image", click_and_crop)



key = 0
# تا زمانی که کابر کلید Esc را وارد نکرده است این حلقه تکار می‌شود
while key != 27:
    key = cv2.waitKey(1) & 0xFF
    cv2.imshow("image", image)

# اگر کاربر کلید r را فشار دهد مربع‌های رسم شده بر روی تصویر پاک می‌شود.
    if key == ord("r"):
        image = clone.copy()

# اگر هر دو مختصات شروع و پایان مربع وارد شده بود و مثدار آن غیر از صفر بود عکس را برش زده و نمایش می‌دهیم
    if len(refPt) == 2:
        if abs(refPt[0][1]-refPt[1][1]) > 0  and abs(refPt[0][0] - refPt[1][0]) > 0:
            X = [refPt[0][1] , refPt[1][1]]
            Y = [refPt[0][0] , refPt[1][0]]
            X.sort()
            Y.sort()

            roi = clone[X[0]:X[1], Y[0]:Y[1]]
            cv2.imshow("ROI", roi)

# تمام پنجره‌های باز را می‌بندد
cv2.destroyAllWindows()

فکر می‌کنم تنها خطوط ۴۵ تا ۵۳ نیاز به توضیح داشته باشه. هروقت متغیر refPt دوتا مقدار گرفت یعنی ما برای دفعه‌ی اول بخشی از تصویر رو انتخاب کرده‌ایم و می‌تونیم اون رو نمایش بدهیم. باید چک کنیم نقاطی که برای رسم مربع انتخاب شده‌اند یکی نباشند، چون در این صورت مربعی با ندازه‌ی ۰×۰ خواهیم داشت و OpenCV خطا خواهد داد. همچین الزامی نیست که کاربر اول بالا سمت چپ کلیک کنه و بعد بیاد پاین سمت راست:-) مختصاتی که برای برش تولید شده‌اند رو از بزرگ به کوچک مرتب می‌کنیم تا به مشکل نخوریم.

جهت دانلودفایل‌های این آموزش می تونید به گیت‌هاب من (به این آدرس) مراجعه کنید.


اگر نمی‌دونید چطور دایره و سایر شکل‌های ساده رو در  OpenCV رسم کنید به این آموزش برای شماست.

در این آموزش هم در مورد خواندن و نمایش عکس، تغییر ابعاد، برش و … در OpenCV صحبت کردم.

اینجا هم مراحل نصب OpenCV در لینوکس رو گفتم.

نویسنده: محمد حسین سالاری.

موارد مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *