ایجاد پانوراما با چندین عکس قسمت 2

مرحله 1: استخراج ویژگی:

ما می بایست از توصیف کننده opencv_contrib’s SIFT استفاده کنیم. SIFT، هم چنین در مقیاس ثابت تغییر ویژگی، یک الگوریتم بسیار قدرتمند CV است. برای خواندن بیشتر در مورد ویژگی ها، لطفا بسته کلمات ویژوال من برای پست طبقه بندی عکس را بخوانید. هم چنین OpenCV’s docs on SIFT را بررسی کنید. آنها یک منبع بسیار خوب هستند!

    sift_obj = cv2.xfeatures2d.SIFT_create()
    descriptors, keypoints = sift_obj.detectAndCompute(image_gray, None)

اگر شما ویژگی ها را طرح ریزی کنید، این است که چگونه آن را خواهید دید. (تصویر سمت چپ تصویر واقعی را نشان می دهد. تصویر سمت راست با ویژگی های شناسایی شده توسط SIFT یادداشت نویسی شده است.)

مثال 1: استفاده از تصویر Lunchroom

sift keypoints

گام دوم: تطبیق دادن مطابقت های بین تصاویر:

هنگامی که شما توصیف گرها و نقاط کلیدی دو تصویر را دارید، به این معنی که یک جفت تصویر، ما مطابقت های بین آنها را پیدا خواهیم کرد. چرا این کار را می کنیم؟ خوب، برای پیوستن هر دو تصویر به یک تصویر بزرگتر، ما باید درباره نقاط دارای اشتراک که چه هستند به دست بیاوریم. این نقاط دارای اشتراک به ما یک ایده از جهت گیری تصویر دوم w.r.t به دیگری می دهد. و بر اساس این نقاط رایج، ما یک ایده می گیریم که آیا تصویر دوم به تصویر بزرگتر اسلاید شده است یا چرخش داده شده و سپس همپوشانی شده است یا شاید مقیاس پایین/بالا شده و سپس نصب شده است. همه این اطلاعات از طریق ایجاد مطابقت ها به دست آمده است. این فرآیند ثبت(registration) نامیده می شود.

برای تطبیق، می توان از FLANN یا BFMatcher استفاده کرد که توسط opencv ارائه شده است.

  # FLANN parameters
    FLANN_INDEX_KDTREE = 0
    index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
    search_params = dict(checks=50)   # or pass empty dictionary

    flann = cv2.FlannBasedMatcher(index_params,search_params)

    matches = flann.knnMatch(des1,des2,k=2)

    img3 = cv2.drawMatchesKnn(img1c,kp1,img2c,kp2,matches,None,**draw_params)

    cv2.imshow("correspondences", img3)
    cv2.waitKey()

پس از محاسبه تطابق ها، شما یک خروجی مشابه دریافت می کنید:

با مجموعه داده Lunchroom

matches

تطبیق ویژگی در تصاویر Lunchroom

با مثال تپه:

matches

تطبیق ویژگی در تصویر نمونه تپه

مرحله 3: محاسبه هموگرافی

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

فرمول

از این رو، این برای ماتریس H حل می شود.

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

 H, __ = cv2.findHomography(srcPoints, dstPoints, cv2.RANSAC, 4)

هورا! ماتریس هموگرافی ما  چیزی شبیه به این به نظر می رسد …

ماتریس هموگرافی

به هر حال، گرافیک بسیار زیبا را کنار بگذارید،  آنچه که ماتریس هموگرافی است را بفهمید. هموگرافی خطوط مستقیم را در یک تصویر حفظ می کند. از این رو تنها تغییرات احتمالی ممکن است برگردان ها، وابسته انتقال های خطی و غیره باشند.

به عنوان مثال، برای انتقال خطی

تبدیل خطی

همچنین میتوانید با h13 و h23 برای برگردان بازی کنید.

گام چهارم: خم کردن و دوختن:

برای درک دوختن، من می خواهم پیشنهاد بدم  Adrian Rosebrock’s blog post on OpenCV Panorama stitching. وبلاگ او توضیح فوق العاده ای در مورد چگونگی اقدام به دوخت تصویر و ساخت پانوراما با استفاده از 2 تصویر ارائه می دهد.

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

انواع خم کردن:

*سطحی: در جایی که  هر تصویر یک عنصر از یک سطح مسطح است، با توجه به برگردان و چرخش …

* استوانه ای: در جایی که هر تصویر به گونه ای نشان شده است که سیستم مختصات استوانه ای باشد. و تصویر بر روی سطح منحنی استوانه ترسیم شد.

* کروی: در بالا به جای استوانه، مدل مرجع یک کره است.

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

warped_image = cv2.warpPerspective(image, homography_matrix, dimension_of_warped_image)

در اینجا کمی تجسم … است. در زیر تصاویر خم شده به راست و خم شده به چپ هستند.به جهت گیری و تصویربرداری هر تصویر توجه داشته باشید.

تصاویر خم شده Lunchroom

 

دوختن   :

سابقاً، ما یک تصویر خم شده را به دست آورده ایم، ما به سادگی تصویر خم شده را همراه با تصویر دوم اضافه می کنیم. این را زیاد از طریق دوختن در سمت راست و دوختن در سمت چپ تکرار کنید، و هورا! ما خروجی را داریم.

من می خواهم کمی عمیق تر به بخش نحوه انجام تصویر پیوستن بپردازم. می گوییم ما یک ماتریس هموگرافی H داریم. اگر مختصات شروع هر تصویر (0،0) و نقطه انتهایی فرمول باشد،ما می توانیم ابعاد تصویر خم شده جدید را با شروع تا پایان دریافت کنیم. توجه: اگر start_pt که بیرون می آید منفی باشد، برای یک تغییر برگردانی حساب می شود. یعنی “اقدام برای تغییر برگردان به تصویر توسط  استارت پی“. همچنین اطمینان حاصل کنید که ماتریس هموگرافی عادی شده است به طوری که آخرین ردیف به یک بردار واحد مربوط می شود.

شما می توانید توضیحات ذکر شده بالا را در زیر بررسی کنید این قطعه پیاده سازی از کد واقعی است. لطفا کد کامل را ببینید که بفهمید مطابق با پست باشد.

    def leftstitch(self):
        # self.left_list = reversed(self.left_list)
        a = self.left_list[0]
        for b in self.left_list[1:]:
            H = self.matcher_obj.match(a, b, 'left')
            print "Homography is : ", H
            xh = np.linalg.inv(H)
            print "Inverse Homography :", xh
            # start_p is denoted by f1
            f1 = np.dot(xh, np.array([0,0,1]))
            f1 = f1/f1[-1]
            # transforming the matrix 
            xh[0][-1] += abs(f1[0])
            xh[1][-1] += abs(f1[1])
            ds = np.dot(xh, np.array([a.shape[1], a.shape[0], 1]))
            offsety = abs(int(f1[1]))
            offsetx = abs(int(f1[0]))
            # dimension of warped image
            dsize = (int(ds[0])+offsetx, int(ds[1]) + offsety)
            print "image dsize   =  >  ", dsize
            tmp = cv2.warpPerspective(a, xh, dsize)
            # cv2.imshow("warped", tmp)
            # cv2.waitKey()
            tmp[offsety:b.shape[0]+offsety, offsetx:b.shape[1]+offsetx] = b
            a = tmp

روش دیگر: یک روش دیگر وجود دارد، یعنی استفاده از ساختارهای حلقه پایه for و دو تصویر را روی هم قرار دهیم. منطق ساده است. ورودی به متد تصویر ثابت و تصویر خم شده خواهد بود. تکرار از طریق هر دو تصویر، و اگر پیکسل ها برابر هستند، پیکسل را به عنوان آن مقدار قرار می دهد. جز این به یک پیکسل غیر سیاه ترجیح می دهم.

 def mix_match(self, leftImage, warpedImage)
        i1y, i1x = leftImage.shape[:2]
        i2y, i2x = warpedImage.shape[:2]

        for i in range(0, i1x):
            for j in range(0, i1y):
                try:
                    if(np.array_equal(leftImage[j,i],np.array([0,0,0])) and  \
                        np.array_equal(warpedImage[j,i],np.array([0,0,0]))):
                        # print "BLACK"
                        # instead of just putting it with black, 
                        # take average of all nearby values and avg it.
                        warpedImage[j,i] = [0, 0, 0]
                    else:
                        if(np.array_equal(warpedImage[j,i],[0,0,0])):
                            # print "PIXEL"
                            warpedImage[j,i] = leftImage[j,i]
                        else:
                            if not np.array_equal(leftImage[j,i], [0,0,0]):
                                bl,gl,rl = leftImage[j,i]                               
                                warpedImage[j, i] = [bl,gl,rl]
                except:
                    pass
        # cv2.imshow("waRPED mix", warpedImage)
        # cv2.waitKey()
        return warpedImage def mix_match(self, leftImage, warpedImage)
        i1y, i1x = leftImage.shape[:2]
        i2y, i2x = warpedImage.shape[:2]

        for i in range(0, i1x):
            for j in range(0, i1y):
                try:
                    if(np.array_equal(leftImage[j,i],np.array([0,0,0])) and  \
                        np.array_equal(warpedImage[j,i],np.array([0,0,0]))):
                        # print "BLACK"
                        # instead of just putting it with black, 
                        # take average of all nearby values and avg it.
                        warpedImage[j,i] = [0, 0, 0]
                    else:
                        if(np.array_equal(warpedImage[j,i],[0,0,0])):
                            # print "PIXEL"
                            warpedImage[j,i] = leftImage[j,i]
                        else:
                            if not np.array_equal(leftImage[j,i], [0,0,0]):
                                bl,gl,rl = leftImage[j,i]                               
                                warpedImage[j, i] = [bl,gl,rl]
                except:
                    pass
        # cv2.imshow("waRPED mix", warpedImage)
        # cv2.waitKey()
        return warpedImage

اما این روش بیش از حد پیکسل تکرار خواهد کرد. این خیلی آهسته است، به دو دلیل. اولاً، این با تکرار سنگین پیچیده می شود. و، خوب، من شخصاً چنین حلقه های سنگینی را در ++C اجرا می کنم و نه در پایتون.

بنابراین، اساساً، این همان نحوه ی عمل اصلی من به نظر می رسد… قلب و هسته تمام پیاده سازی ها

 if __name__ == '__main__':
        try:
            args = sys.argv[1]
        except:
            args = "txtlists/files1.txt"
        finally:
            print "Parameters : ", args
        s = Stitch(args)
        s.leftshift()
        # s.showImage('left')
        s.rightshift()
        print "done"
        cv2.imwrite("test.jpg", s.leftImage)
        print "image written"
        cv2.destroyAllWindows()

جزئیات پیاده سازی: بررسی کد

نتایج!!!

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

panorama

دوختن با استفاده از مثال اتاق

 

example

example

دوخت با میز ناهار خوری خونه من 🙂

example

دوخت با مجموعه داده های ترکیبی

 

در زیر تصاویر دیگر گرفته شده از github و منابع مختلف آنلاین هستند. مراجع را برای بیشتر ببینید.

 

example

example

دوختن با مثال ساختمان

 

example

example

دوختن با استفاده از مثال تپه

 

example

example

دوختن با استفاده از مثال اتاق

 

برای بررسی کد در Github اینجا کلیک کنید

من خم شدن استوانه ای و چگونه opencv واقعاً دوختن را اجرا می کند را در یک پست متفاوت پوشش خواهم داد.

منابع:

مقاله پایه برای پانوراما با استفاده از ویژگی های ثابت مقیاس:

[1] ” دوخت تصویر پانورامیک خودکار با استفاده از ویژگی های ثابت”, Download.springer.com , 2016. [Online]. Available: matthewalunbrown.com/papers/ijcv2007.pdf

تصاویر تست گرفته شده از

[2]”PASSTA Datasets”, Cvl.isy.liu.se, 2016. [Online]. Available: http://www.cvl.isy.liu.se/en/research/datasets/passta/.

[3] “OpenCV Stitching example (Stitcher class, Panorama)”, Study.marearts.com, 2013. [Online]. Available: http://study.marearts.com/2013/11/opencv-stitching-example-stitcher-class.html.

[4] “Github daeyun Image-Stitching Test Images”, 2016. [Online]. Available: https://github.com/daeyun/Image-Stitching/tree/master/img/hill.

[5] “Github tsherlock Test Images”, 2016. [Online]. Available: . https://github.com/tsherlock/panorama/

ترجمه شده از پیج https://kushalvyas.github.io/stitching.html

ایجاد پانوراما با چندین عکس قسمت 1
ایجاد پانوراما با چندین عکس قسمت 2

0 پاسخ

دیدگاه خود را ثبت کنید

تمایل دارید در گفتگوها شرکت کنید؟
در گفتگو ها شرکت کنید.

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

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