پردازش تصویر در OpenCV

کانتورها: توابع بیشتر (Contours: More Functions)

۴ دقیقه مطالعهOpenCV 5.xبه‌روزرسانی:

هدف#

در این فصل، موارد زیر را یاد خواهیم گرفت:

  • نقص‌های تحدب (Convexity Defects) و نحوه یافتن آن‌ها.
  • یافتن کوتاه‌ترین فاصله از یک نقطه تا یک چندضلعی
  • تطبیق اشکال مختلف (Matching Different Shapes)

تئوری و کد#

۱. نقص‌های تحدب (Convexity Defects)#

در فصل دوم مربوط به کانتورها دیدیم که پوش محدب (Convex Hull) چیست. هر گونه انحراف شی از این پوش را می‌توان به عنوان نقص تحدب در نظر گرفت.

OpenCV تابعی آماده برای یافتن این موارد ارائه می‌دهد: cv.convexityDefects(). یک فراخوانی پایه از این تابع به شکل زیر است:

hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)

این تابع آرایه‌ای برمی‌گرداند که در آن هر سطر شامل مقادیر زیر است: [ نقطه شروع، نقطه پایان، دورترین نقطه، فاصله تقریبی تا دورترین نقطه ]. می‌توانیم این موارد را با یک تصویر تجسم کنیم. ما خطی که نقطه شروع و نقطه پایان را به هم متصل می‌کند رسم می‌کنیم، سپس یک دایره در دورترین نقطه می‌کشیم. به یاد داشته باشید که سه مقدار اول برگشتی، اندیس‌هایی از cnt هستند؛ بنابراین باید آن مقادیر را از cnt استخراج کنیم.

import cv2 as cv
import numpy as np

img = cv.imread('star.jpg')
assert img is not None, "file could not be read, check with os.path.exists()"
img_gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,thresh = cv.threshold(img_gray, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt = contours[0]

hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)

for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv.line(img,start,end,[0,255,0],2)
    cv.circle(img,far,5,[0,0,255],-1)

cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

و نتیجه را ببینید:

کانتورها: توابع بیشتر (Contours: More Functions) — تصویر ۱

۲. آزمون نقطه-چندضلعی (Point Polygon Test)#

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

برای مثال، می‌توانیم نقطه (50,50) را به شکل زیر بررسی کنیم:

dist = cv.pointPolygonTest(cnt,(50,50),True)

در این تابع، آرگومان سوم measureDist است. اگر True باشد، فاصله علامت‌دار (signed distance) را پیدا می‌کند. اگر False باشد، فقط مشخص می‌کند که نقطه داخل، خارج یا روی کانتور است (به ترتیب ‎+1، ‎-1 یا 0 برمی‌گرداند).

۳. تطبیق اشکال (Match Shapes)#

OpenCV تابعی به نام cv.matchShapes() دارد که امکان مقایسه دو شکل یا دو کانتور را فراهم می‌کند و یک معیار (metric) نشان‌دهنده شباهت برمی‌گرداند. هر چه مقدار برگشتی کمتر باشد، تطبیق بهتر است. این مقدار بر اساس مقادیر ممان هو (Hu-Moment) محاسبه می‌شود. روش‌های مختلف اندازه‌گیری در مستندات توضیح داده شده‌اند.

import cv2 as cv
import numpy as np

img1 = cv.imread('star.jpg', cv.IMREAD_GRAYSCALE)
img2 = cv.imread('star2.jpg', cv.IMREAD_GRAYSCALE)
assert img1 is not None, "file could not be read, check with os.path.exists()"
assert img2 is not None, "file could not be read, check with os.path.exists()"

ret, thresh = cv.threshold(img1, 127, 255,0)
ret, thresh2 = cv.threshold(img2, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv.findContours(thresh2,2,1)
cnt2 = contours[0]

ret = cv.matchShapes(cnt1,cnt2,1,0.0)
print( ret )

من سعی کردم اشکال مختلف داده شده در زیر را با یکدیگر تطبیق دهم:

کانتورها: توابع بیشتر (Contours: More Functions) — تصویر ۲

نتایج زیر را به دست آوردم:

  • تطبیق تصویر A با خودش = 0.0
  • تطبیق تصویر A با تصویر B = 0.001946
  • تطبیق تصویر A با تصویر C = 0.326911

همانطور که مشاهده می‌شود، حتی چرخش تصویر تأثیر چندانی بر این مقایسه ندارد.

تمرین‌ها#

  1. مستندات cv.pointPolygonTest() را بررسی کنید؛ می‌توانید تصویر زیبایی به رنگ‌های قرمز و آبی در آن بیابید. این تصویر فاصله تمام پیکسل‌ها تا منحنی سفید روی آن را نشان می‌دهد. تمام پیکسل‌های داخل منحنی بسته به فاصله آبی رنگ هستند. به همین ترتیب نقاط خارجی قرمز رنگند. لبه‌های کانتور با رنگ سفید مشخص شده‌اند. بنابراین مسئله ساده است: کدی بنویسید که چنین نمایشی از فاصله ایجاد کند.
  2. تصاویر ارقام یا حروف را با استفاده از cv.matchShapes() مقایسه کنید. (این می‌تواند گامی ساده به سوی تشخیص نوری کاراکتر (OCR) باشد.)