Mohsen Khoshnazar Homepage

Mohsen Khoshnazar Homepage
سلام به همه.
سعی می کنم تجربیات خودم رو در این وبلاگ قرار بدم تا دیگر علاقه مندان نیز بتوانند از آنها استفاده کنند.
موفق باشید:)

Telegram Channel: mohsenteq
Instagram: mohsenteq
Site: www.mteq.ir
دنبال کنندگان ۱ نفر
این وبلاگ را دنبال کنید
آخرین نظرات
پنجشنبه, ۶ خرداد ۱۳۹۵، ۰۳:۲۳ ب.ظ

دنبال کردن اشیا با استفاده از دستور CamShift

سلام به همه دوستان.

   امروز قصد دارم با استفاده از OpenCV، دوربین رزبری پای و زبان برنامه نویسی Python، برنامه ای برای دنبال کردن اشیا در ویدئو بنویسم. اما قبل از شروع کار بد نیست تفاوت بین دنبال کردن اشیا و تشخیص حرکت بدم؛ چون خیلی از افراد این دو موضوع رو یکی می دونن. به هنگام دنبال کردن اشیا، شما یک شی را در ویدئو انتخاب می کنید و برنامه با رسم یک مستطیل به دور شی آن را مشخص می کند. سپس در صورت تغییر در وضع فعلی آن شی (حرکت کردن شی)، کادر مستطیلی نیز همراه با حرکت شی به حرکت در می آید. اما در فرایند تشخیص حرکت، فرض بر این است که تصویر پس زمینه در ویدئو در ابتدا ثابت بوده است. سپس در صورت به وجود آمدن تغییر در این پس زمینه، برنامه تشخیص وقوع حرکت را می دهد و ادامه ماجرا.

   خب زیاد به این موضوع نمی پردازم و اجازه بدین در مورد برنامه خودمون حرف بزنیم. توی این برنامه با فشرده شدن کلید i بر روی کیبورد، ویدئو بر روی آخرین فریم قفل می شود (یعنی فریم بعدی نمایش داده نمی شود). در این حالت کاربر باید توسط ماوس بر روی جهار نقطه از شی مورد نظر کلیک کند (در این حالت مکان کلیک توسط یک دایره سبز رنگ مشخص خواهد شد). سپس با فشردن هر کلیدی از کیبورد ادامه برنامه اجرا خواهد شد و جسم توسط یک مستطیل سبز رنگ مشخص خواهد شد. در صورت حرکت شی، مستطیل سبز رنگ نیز تغییر وضعیت خواهد داد. با من همراه باشید تا با هم به مرور این کد بپردازیم.

# import the necessary packages
import numpy as np
import cv2

   در ابتدا پکیج های ضروری را وارد می کنیم. numpy برای Numerical Processing و cv2 برای OpenCV هستند.

# initialize the current frame of the video, along with the list of
# ROI points along with whether or not this is input mode
frame = None
roiPts = []
inputMode = False

   سه متغیر کلی (Global) تعریف می کنیم. متغیر frame برای فریم هایی که از دوربین دریافت می کنیم. متغیر roiPts، لیست نقاط ROI را در خود ذخیره می کند و در آخر متغیر inputMode به منظور مشخص شدن زمانی که کاربر قصد دارد با استفاده از ماوس شی مورد نظر خود را مشخص نماید.

def selectROI(event, x, y, flags, param):
    # grab the reference to the current frame, list of ROI
    # points and whether or not it is ROI selection mode
    global frame, roiPts, inputMode

    # if we are in ROI selection mode, the mouse was clicked,
    # and we do not already have four points, then update the
    # list of ROI points with the (x, y) location of the click
    # and draw the circle
    if inputMode and event == cv2.EVENT_LBUTTONDOWN and len(roiPts) < 4:
        roiPts.append((x, y))
        cv2.circle(frame, (x, y), 4, (0, 255, 0), 2)
        cv2.imshow("frame", frame)

   تابع selectROI را به منظور تعیین ROI برای دنبال کردن تعریف می کنیم. متغیرهای سراسری را درون تابع فراخوانی می کنیم.

   پس از آن یک شرط با سه حالت داریم. اول آن که inputMode مقدار True داشته باشد. دوم فشرده شدن کلید چپ ماوس رخ دهد. و سوم آن که تعداد نقاط انتخاب شده توسط کاربر (roiPts) کمتر از چهار نقطه باشد. با برقرار بودن این سه شرط به طور همزمان، مختصات دکارتی (y,x) هر کلیک ماوس را در lمتغیر roiPts ذخیره می کنیم.

   در آخر برای مشخص کردن محل کلیک، یک دایره سبز رنگ در آن مختصات می کشیم.

def main():
    global frame, roiPts, inputMode
    camera = cv2.VideoCapture(-1)
    # setup the mouse callback
    cv2.namedWindow("frame")
    cv2.setMouseCallback("frame", selectROI)
    # initialize the termination criteria for camshift, indicating
    # a maximum of ten iterations or movement by a least one pixel
    # along with the bounding box of the ROI
    termination = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
    roiBox = None

   اکنون تابع اصلی را تعریف می کنیم. این تابع قرار است به طور پیوسته بارها اجرا شود. متغیرهای سراسری را مجدداً فراخوانی می کنیم.

   دوربین را آماده می کنیم و برای اینکه محدوده ماوس مشخص شود یک پنجره با نام "frame" می سازیم. این کار برای این است که برنامه تنها کلیک هایی که در این پنجره زده می شوند را در نظر بگیرد.

   الگوریتم CamShift یک الگوریتم تکرارشونده است؛ بدین معنی که مرتباً در حال اجرا شدن است تا بتواند به درستی حرکت را تشخیص دهد. چون می خواهیم از دستور CamShift استفاده کنیم لازم است تا شرایطی را برای پایان دادن به تکرار الگوریتم تعریف کنیم. راحت ترین روش چک کردن دو مورد است:

  1. آیا تغییری در پیکسل های ROI انتخاب شده رخ داده است یا خیر؟

  2. چند بار تکرار شود؟

   مسلماً هرچه تعداد تکرارها بیشتر باشد تشخیص دقیق تر خواهد بود ولی چون داریم در حالت Real-Time کار می کنیم، زیاد بودن تعداد تکرارهای می تواند بر سرعت پردازش تاثیر بگذارد. برای همین منظور تعداد 10 تکرار را در نظر می گیریم.

   متغیر roiBox نشان دهنده است که مستطیلی برای ROI مشخص شده است یا خیر.

    # keep looping over the frames
    while True:
        # grab the current frame
        suc, frame = camera.read()       
        # if the see if the ROI has been computed
        if roiBox is not None:
            # convert the current frame to the HSV color space
            # and perform mean shift
            hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
            backProj = cv2.calcBackProject([hsv], [0], roiHist, [0, 180], 1)           
            # apply cam shift to the back projection, convert the
            # points to a bounding box, and then draw them
            (r, roiBox) = cv2.CamShift(backProj, roiBox, termination)
            pts = np.int0(cv2.boxPoints(r))
            cv2.polylines(frame, [pts], True, (0, 255, 0), 2)

   حلقه ای برای اجرای دائمی دستورات می سازیم. و سپس فریم ها را از دوربین می خوانیم و آنها را در متغیر سراسری frame ذخیره می کنیم.

   در صورتی که متغیر roiBox خالی نباشد (یعنی مستطیلی برای محدوده ROI پیدا شده است)، ابتدا آن فریم را از حالت BGR به HSV تبدیل می نماییم.

   چون می خواهیم از دستور CamShift استفاده کنیم ضروری است تا histogram قسمت محصور در مستطیل را بدست می آوریم. برای این کار فقط کانال H را در نظر می گیریم (البته امکان استفاده از هیستوگرام دو بعدی برای کانال های بیشتر نیز وجود دارد).

   در مرحله بعدی از دستور CamShift استفاده می کنیم. این دستور دارای دو خروجی است. خروجی اول مکان، اندازه و جهت شی را مشخص می کند و خروجی دوم تخمینی از مکان جدید ROI را نشان می دهد.

   توسط دستور boxPoints، مختصات چهار نقطه از مستطیل دوران یافته را پیدا می کنیم و در آخر این مستطیل را رسم می کنیم.

   اگر توجه کرده باشیم هنگام استفاده از دستور calcBackProject، یکی از المان های ورودی آن متغیر roiHist است. در قسمت بعدی می خواهیم ورودی ROI مربوط به histogram را بدست بیاوریم.

        # show the frame
        cv2.imshow("frame", frame)
        key=cv2.waitKey(1)&0xFF                 
        # handle if the 'i' key is pressed, then go into ROI
        # selection mode
        if key == ord("i") and len(roiPts) < 4:
            # indicate that we are in input mode and clone the
            # frame
            inputMode = True
            orig = frame.copy()                         

            # keep looping until 4 reference ROI points have
            # been selected; press any key to exit ROI selction
            # mode once 4 points have been selected
            while len(roiPts) < 4:
                cv2.imshow("frame", frame)
                cv2.waitKey(0)                         

            # determine the top-left and bottom-right points
            roiPts = np.array(roiPts)
            s = roiPts.sum(axis = 1)
            tl = roiPts[np.argmin(s)]
            br = roiPts[np.argmax(s)]                         

            # grab the ROI for the bounding box and convert it
            # to the HSV color space
            roi = orig[tl[1]:br[1], tl[0]:br[0]]
            roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)                         

            # compute a HSV histogram for the ROI and store the
            # bounding box
            roiHist = cv2.calcHist([roi], [0], None, [16], [0, 180])
            roiHist = cv2.normalize(roiHist, roiHist, 0, 255, cv2.NORM_MINMAX)           
            roiBox = (tl[0], tl[1], br[0], br[1])

        # if the 'q' key is pressed, stop the loop
        elif key == ord("q"):
            break

   اگر کاربر کلید i را فشار دهد و تعداد نقاط پیدا شده کمتر از چهار عدد باشد، inputMode به حالت True می رود و مادامی که تعداد نقاط کمتر از چهار عدد است آخرین فریم مرتباً نشان داده می شود تا کاربر بتوان به راحتی نقاط را انتخاب کند. سپس با فشردن هر کلید دیگر از کیبورد از این حلقه خارج می شویم.

   سپس دو نقطه بالا-چپ و پایین-راست را باید پیدا کنیم. برای این کار از ریاضیات مربوط به پرسپکتیو استفاده می کنیم که در یکی از پست های قبلی (تبدیل Perspective با استفاده از مختصات نقاط اولیه و نهایی 4 نقطه از تصویر) نحوه این کار توضیح داده شد.

   در قسمت بعدی این محدوده را از حالت BGR به hSV تبدیل می کنیم تا بتوان هستوگرام محدوده ROI را بدست آورد. سپس آن را نرمالیزه خواهیم کرد و درون متغیر roiBox ذخیره می شود.

   برای خاتمه برنامه کاربر باید کلید q را فشار بدهد.

    # cleanup the camera and close any open windows
    camera.release()
    cv2.destroyAllWindows()   

if __name__ == "__main__":
    main()

   در مرحله پایانی توجه کنید که دوربین را خاموش کنید. در خط آخر به Python می فهمانیم که تایع main را اجرا کند. برای تست کد پس از اجرای آن و راه اندازی دوربین، تصویر به صورت پیوسته  در پنجره "frame" نشان داده خواهد شد.

   با فشردن کلید i، ویدئو متوقف می شود و آخرین فریم نمایش داده می شود. اکنون کاربر باید توسط ماوس بر روی 4 نقطه مورد نظر بر روی شی کلیک نماید.

   در آخر با فشردن یک کلید دیگر ویدئو ادامه پیدا می کند و این بار شی انتخاب شده توسط یک مستطیل مشخص خواهد شد. در صورت حرکت شی، مستطیل نیز تغییر وضعیت خواهد داد.

   از لینک زیر کدهای مربوطه را می توانید دانلود کنید. تمامی کدها توسط OpenCV 3.0 و Python 2.7 نوشته شده اند.

دریافت

حجم: 2.89 مگابایت

توضیحات: Using CamShift to Track Objects in Video


نظرات (۸)

با سلام ممنون بابت اموزش عالیتون من تحقیقی رو تست رفتاری ماهی انجام میدم بابت برنامه ای دارم مینویسم براش نیاز دارم هر ده ثانیه مقدار جابجایی ماهی جهت جابچایی ماهی و میزان چرخش سر ماهی بهم بده میشه راهنماییم کنید.
پاسخ:
سلام. ابتدا در چند فریم اولیه سعی کنید همه ماهی ها رو پیدا و شماره گذاری کنید. برای این کار از کانتور استفاده کنید. سپس به طور مداوم حرکت هر ماهی را دنبال و میزان جایجایی آن را نسبت به موقعیت قبلی محاسبه کنید. سپس این محاسبات را هر ده ثانیه به خروجی دهید.
موفق باشید.
۰۵ خرداد ۹۷ ، ۰۴:۲۲ سعید همایونی

سلام طاعات قبول 

یه سوال داشتم خدمتتون 

می خوام یه قایق درست کنم که دو تا موتور داره و با رزبری ( رسپری ) کار میکنه  و دنبال اشیا میره . روش کار هم این جوره که یه عکس نمونه بهش میدیم خودش هم توسط دوربین یه عکس میگیره بعد تصویر دوربین رو به سه قسمت عمودی تقسیم میکنه اگر تصویر نمونه در یک سوم سمت راست بود موتور سمت چپ دو ثانیه میچرخه و اگر تصویر نمونه در یک سوم سمت چپ بود موتور راست دو ثانیه میچرخه و اگر تصویر نمونه در یک سوم وسط بود هر دو تا موتور با هم میچرخه حالا میخوام با پایتون کد بزنم ولی نمیدونم چه طوری ؟ 



پاسخ:
باسلام
به چند روش می‌تونید پروژه رو انجام بدین.
اول به کمک رنگ چسم و تابع inRange
دوم به کمک تصویر جسم و تابع matchTemplate
سوم به کمک نمونه‌ای از بافت جسم و تابع camShift
برنامه رو اینجور بنویسید که مدام فاصله مرکز تصویر تا مرکز جسم رو پیدا کنه؛ یعنی تصویر را به چهار ناحیه مثل دستگاه مختصات دکارتی تقسیم کنید و برنامه رو جوری بنویسید که هدفش، کاهش فاصله مرکز تصویر تا مرکز جسم باشه.
موفق باشید.
۰۳ خرداد ۹۷ ، ۰۱:۰۹ سعید همایونی
سلام تشکر
برای جدا کردن x, y  چی کار باید بکنم ؟
میخوام این دو تا رو داخل دو تا تغیر دیگه ذخیره کنم ؟

تشکر و خیلی ممنون
پاسخ:
سلام.
خروجی تابع camShift رو به صورت زیر بنویسید. سپس از مقادیر x و y استفاده کنید یا توی دو تا متغیر دیگه کپی کنید.
camShift=(زاویه,(w,h),(x,y))
۰۲ خرداد ۹۷ ، ۱۰:۳۲ سعید همایونی
سلام
بابت مطلب خوبتون ممنون اما یه سوالی داشتم .
گفتید خروجی اولی یعنی r مختصات مکان جسم رو میده حالا اگر ما بخوایم مختصات گوشه بالا سمتر راست یا چپ تصویر هدف رو پیدا کنیم چی کار باید بکنیم . منظورم اینه که مثلا همین کتاب خودتون میخوایم بدونیم مستطیلی که دورش کشیده شده مختصات گوشه چپ بالا یا راست بالا چیه ؟
لطفا راهنمایی بفرمایید
تشکر
پاسخ:
باسلام
دوست عزیز در تابع camShift، خروجی r خودش سه قسمت داره
(زاویه,(w,h),(x,y))=r
که در x و y مختصات گوشه بالا- چپ مستطیل هستند.
موفق باشید.



مطلب خیلی مفیدی بود ممنونم. سوالی که داشتم این بود که چجوری میتونم پلاک خودرو رو تشخیص بدم؟
پاسخ:
باسلام
برای تشخیص پلاک خودرو روش های مختلفی وجود دارد. مثلا توابع از پیش آماده شده ای است که توسط یادگیری ماشین برنامه نویسی شده اند و مختص به این کار هستند. روش دیگه نوشتن این برنامه به صورت دستی هستش. مثلا اگر دوربین ثابت باشه می تونید با تشخیص اعداد و حروفی که مقابل دوربین ظاهر می شن پلاک رو پیدا و ضبط کنید. روش دیگر ابتدا تشخیص خودرو و بعدا با توجه به اینکه پلاک معمولا پایین خودرو هستش اون رو پیدا کنید. روش دیگری استفاده از رنگ پلاک برای محدود کردن محیط جستجو هستش. در کل روش های مختلفی برای نوشتن یک کد در پردازش تصویر وجود دارد. برای اطلاع پیدا کردن از بهترین روش توصیه می کنم اینترنت رو سرچ کنید.
موفق باشید.
سلام
مطمن هستید که " پردازش تصویر با پایتون شروع شده؟؟"
زبان سی++ رو اخه برای برنامه نویسی میکرو یه خورده اشنایی دارم. با پایتون که نمیشه واسه اونا برنامه نویسی کرد.
لینک دانلود نسخه های مختلف اپن سی وی در لینک زیر هست:
http://opencv.org/downloads.html
من ورژن 3.1 رو دارم
میشه ایمیلتون رو داشته باشم؟
ممنون
پاسخ:
سلام.
دوست عزیز اگر بخواهید پردازش تصویر را بر روی میکروکنترلر اجرا کنید به کلاک (فرکانس کاری میکرو) بالایی نیاز دارید. برای مثال تشخیص چهره را بر روی برد Raspberry pi 2 که با کلاک 900MHz کار می کرد پیاده کردم ولی حدود نیم ثانیه تاخیر توی اجرای کد داشت و برای برنامه های پیشرفته تر مانند تشخیص حرکت، اصلا جواب گو نبود. بنابراین اگر قصد انجام پردازش تصویر به صورت Real-time رو دارید، استفاده از میکروکنترلر اصلا توصیه نمی شه. بیشتر ربات هایی هم که در مسابقات حضور دارند (مانند ربات های پرنده در مسابقات ایران اپن امسال که در دانشگاه امیرکبیر برگذار شد) دارای بردهای Odroid و یا Raspberry pi هستند.
نکته دیگر اینکه من هیچ وقت نگفتم پردازش تصویر با پایتون شروع شده، بلکه گفتم بیشتر کدها و آموزش هایی که من پیدا کردم و با آنها پردازش تصویر را یاد گرفتم بر پایه پایتون بودند و صد البته بردهایی مثل Raspberry pi و Odroid از پایتون پشتیبانی می کنند. به همین دلیل هیچ وقت نیازی به زبان C نداشتم.
ایمیل شخصی اینجانب: mohsen_k.nazar@yahoo.com
موفق باشید.
سلام مجدد
اگر میشه یه نمونه کد تشخیص مردمک یا چشم با زبان  c++ اینجا بزارید. (اماتورم...)
خیلی ممنون
پاسخ:
باسلام.
دوست عزیز تشخیص چشم با تشخیص مردمک تفاوت دارد. تشخیص چشم توسط الگوریتم های از پیش تعیید شده انجام می شود ولی برای تشخیص مردمک تنها کافیست دایره های درون محدوده چشم را پیدا کنیم.
ولی در رابطه با کد، چون پکیج های OpenCV را برای پایتون نصب کرده ام، به غیر از زبان پایتون نمی تونم با زبان دیگری کد بزنم (پکیج های OpenCV را برای زبان C یا ++C پیدا نکردم). ولی با سرچ توی گوگل مطمئناً معادل کد ++C رو پیدا خواهید کرد.
و نکته دیگر آ«که اگر مصمم به یادگیری پردازش تصویر هستید حتما با زبان پایتون شروع کنید؛ چون بیشتر آموزش ها و کدهای نمونه به این زبان نوشته شده اند؛ اصلا پردازش تصویر با پایتون شناخته می شود. این طوری هم پایتون یاد می گیرید و هم پردازش تصویر.
موفق باشید.
سلام
برای اینکه چشمها رو تشخیص بدیم باید چکار کنیم؟
در کل میخوام حرکت مردمک چشم رو تعقیب کنم در این حالت باید چکار کنم؟
ممنون.
پاسخ:
سلام.
دوست عزیز OpenCV برای این منظور (تشخیص چهره و یا چشم ها) دارای فایل هایی با فرمت XML. می باشد (haarcascade_eye.xml). بعد از فراخوانی آنها در محیط برنامه نویسی، از دستور detectMultiScale به منظور شناسایی چشم ها استفاده کنید. این دستور به شما یک خروجی با چهار المان می دهد که به ترتیب طول، عرض مکان قرار گیری چشم ها است.
پست آینده را صرفاً به توضیح همین موضوع (تشخیص چهره و چشمان) تخصیص خواهم داد.
موفق باشید.

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی