تبدیل Perspective با استفاده از مختصات اولیه و نهایی ۴ نقطه از تصویر
خیلی از مواقع پیش آمده است که با استفاده از دوربین گوشی همراه می خواهیم از یک برگه عکس بگیریم ولی سایه دست و گوشی باعث خراب شدن تصویر می شوند. در این پست قصد دارم یک کد ساده برای رفع این مشکل بنویسم؛ بدین صورت که حتماً لازم نیست دوربین را دقیقاً مقابل برگه بگیریم، بلکه کاری می کنیم تا تصویر گرفته شده به این حالت در بیاید. این کار را با دستورات cv2.warpPerspective و cv2.getPerspectiveTransform انجام خواهیم داد.
اگر مختصات دکارتی چند نقطه از یک تصویر و مختصات نهایی آن نقاط را در اختیار داشته باشیم، با استفاده از تابع cv2.getPerspectiveTransform، می توان یک ماتریس انتقالی برای تمامی نقاط آن تصویر بدست آورد. سپس با استفاده از تابع cv2.warpPerspective، این انتقال را به تمامی نقاط اعمال می کنیم. تصویر سمت چپ،عکس ورودی و عکس سمت راست خروجی نهایی کد خواهد بود.
مجهول اصلی در این تبدیل، مختصات اولیه و نهایی نقاط است. فرض می کنیم که برگه ها مستطیل شکل هستند و مختصات اولیه نقاط ( بالا-چپ، بالا-راست، پایین-راست و پایین-چپ) در دسترس هستند. بنابراین کاری که باید انجام دهیم بدست آوردن مختصال نهایی این ۴ نقطه خواهد بود. تمامی کدها توسط Python 2.7 و OpenCV 3.0.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 |
# import the necessary packages import numpy as np import cv2 def order_points(pts): # initialzie a list of coordinates that will be ordered # such that the first entry in the list is the top-left, # the second entry is the top-right, the third is the # bottom-right, and the fourth is the bottom-left rect = np.zeros((4, 2), dtype = "float32") # the top-left point will have the smallest sum, whereas # the bottom-right point will have the largest sum s = pts.sum(axis = 1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # now, compute the difference between the points, the # top-right point will have the smallest difference, # whereas the bottom-left will have the largest difference diff = np.diff(pts, axis = 1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] # return the ordered coordinates return rect |
ابتدا پکیج های مورد نیاز را وارد می کنیم. numpy برای numerical processing و cv2 برای opencv است.
تابع order_points، لیست ۴ نقطه مورد نظر را در اختیار ما می گذارد. از یک آرایه خالی با ابعاد ۲*۴ به عنوان حافظه این تابع استفاده می کنیم.
طبق ریاضیات مربوط به پرسپکتیو، نقطه بالا-چپ، دارای کوچکترین مجموع (x+y) و نقطه پایین-راست، دارای بیشترین مجموع (x+y) هستند.
برای دو نقطه دیگر برعکس این ماجرا صدق می کند. نقطه بالا-راست، دارای کمترین مقدار تفاضل (x-y) و نقطه پایین-چپ دارای بیشترین تفاضل (x-y) هستند. در آخر تابع مقدار rect را بر می گردانیم.
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 |
def four_point_transform(image, pts): # obtain a consistent order of the points and unpack them # individually rect = order_points(pts) (tl, tr, br, bl) = rect # compute the width of the new image, which will be the # maximum distance between bottom-right and bottom-left # x-coordiates or the top-right and top-left x-coordinates widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2)) maxWidth = max(int(widthA), int(widthB)) # compute the height of the new image, which will be the # maximum distance between the top-right and bottom-right # y-coordinates or the top-left and bottom-left y-coordinates heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2)) heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2)) maxHeight = max(int(heightA), int(heightB)) # now that we have the dimensions of the new image, construct # the set of destination points to obtain a "birds eye view", # (i.e. top-down view) of the image, again specifying points # in the top-left, top-right, bottom-right, and bottom-left # order dst = np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype = "float32") # compute the perspective transform matrix and then apply it M = cv2.getPerspectiveTransform(rect, dst) warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight)) # return the warped image return warped |
تابع four_point_transform، دارای دو پارامتر image و pts است. image، عکسی است که تبدیل پرسپکتیو را به آن اعمال می کنیم و pts، لیست ۴ نقطه از تصویر است. با استفاده از تابع order_points، نقاط را فراخوانی و به متغیرهایی اختصاص می دهیم.
عرض عکس جدید، بیشترین فاصله بین نقاط پالا-چپ و راست-راست و یا بیشترین فاصله نقاط پایین-چپ و پایین-راست است.
به صورت مشابه ارتفاع عکس جدید نیز بیشترین فاصله بین نقاط بالا-راست و پایین-راست است و یا بیشترین فاصله بین نقاط بالا-چپ و پایین-چپ خواهد بود.
آرایه dst شامل مختصات نهایی ۴ نقطه هستند. اکنون نوبت به محاسبه ماتریس انتقال است. و پس از آن اعمال این ماتریس انتقال به تصویر اصلی است.
1 2 3 4 5 6 7 8 9 10 11 12 |
image = cv2.imread('example_01.png') pts = np.array([[73, 239], [356, 117], [475, 265], [187, 443]], dtype = "float32") # apply the four point tranform to obtain a "birds eye view" of # the image warped = four_point_transform(image, pts) # show the original and warped images cv2.imshow("Original", image) cv2.imshow("Warped", warped) cv2.waitKey(0) cv2.destroyAllWindows() |
برای تست کد از یک تصویر نمونه و مختصات اولیه نقاط آن استفاده خواهیم کرد.
از لینک زیر می توانید کد کامل به همراه عکس نمونه را دریافت کنید. موفق باشید
منبع: آقای خوش نظر