الدرس الثاني عشر - الاطارات

Lesson_12 slides

: الاطارات

:الهدف

فهم ما هي الاطارات

تعلم ايجاد ورسم الاطارات الخ

سنرى هذه التوابع

cv2.findContours() , cv2.drawContours()

:ما هي الاطارات؟

يمكن اعتبار الاطارات كمنحنيات تجمع كل النقاط المتصلة , التي لديها نفس الشدة او اللون , وهذه الاطارات هي ادوات مفيدة لتحليل الاشكال واكتشاف او التعرف على الاجسام

اولا - من اجل نتائج افضل استخدم الصور الثنائية و لهذا استخدم التعتيب , او مكتشف الحواف كاني

ثانيا - تابع ايجاد الحواف يحور الصورة الاصل . لذلك اذا اردت الحفاظ على الصور بامكانك استخدام نسخة احتياطية لها

ثالثا - ايجاد الاطارات في

OpenCV

يماثل ايجاد الاجسام البيضاء من الخلفية السوداء لذلك على الخلفية ان تكون سوداء و الجسم ان يكون ابيض

والتالي مثال

In [22]:
import numpy as np
import cv2

im = cv2.imread('box.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,120,255,0)
contours, hierarchy = cv2.findContours(
    thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

لذلك هناك 3 متغيرات للتابع السابق , اولها صورة الدخل , والثاني حالة استرداد الاطار والثالث طريقة تقريب الاطار . والخرج هو الصورة الثنائية وسلسلة الاطارات المرتبة المكتشفة كقائمة بايثون . وكل اطار مفرد هو مصفوفة

Numpy

من الاحداثيات لنقاط الحدود للجسم

ملاحظة

سنناقش المتغيرات الثالثة والرابعة لاحقاً

:كيف سترسم الاطارات

لنرسم الاطارات علينا استخدام التابع

cv2.drawContours()

ويمكن نظريا استخدامه لرسم اي شكل لدينا نقاطه المحيطية ,دخله الاول هو صورة الاصل الدخل الثاني هو الاطارات المطلوب رسمها والتي تمرر كقائمة بايثون والدخل الثالث ترتيب الاطار المرغوب رسمه وفقي حالة اردنا رسمها كلها نمرر -1 وبقية المداخل هي السماكة واللون . الخ

ولرسم كل الاطارات بالصورة نكتب

In [ ]:
cv2.drawContours(img, contours, -1, (0,255,0), 3)

ولرسم اطار مفرد نكتب

In [ ]:
cv2.drawContours(img, contours, 3, (0,255,0), 3)

ولكن معظم الوقت الطريقة ادناه مفيدة

, وحيث قد تلاحظ ان لها نفس الاثر , سنرى قريبا انها اكثر فائدة

In [ ]:
cnt = contours[4]
img = cv2.drawContours(img, [cnt], 0, (0,255,0), 3)

:طرق تقريب الاطارات

وهذا هو المتغير الثالث في التابع , ماذا تعطي عملياً؟

cv2.findControus()

واعلاه قلنا ان الاطارات هي حدود الشكل مع نفس الشدة , ولقد خزنّا احداثيات نقاطها , ولكن ما الدقة التي ستخزن خلالها , هذا ما يتم تحديده وفق طريقة التقريب

فاذا مررنا

cv2.CHAIN_APPROX_NONE

سيتم تخزين كل النقاط للحدود , ولكن هل نحتاجها؟ , مثلاً لخط مستقيم هل نحتاج كل نقاطه , بل تكفي نقطتا البداية والتهاية ,

وهذا ما تفعله

cv2.CHAIN_APPROX_SIMPLE

فهي تزيل كل النقاط الفائضة , وتوفر بذلك من حجم الذاكرة

: والتالي يعطي الاطار بالحالة الاولى

In [72]:
%matplotlib inline
from matplotlib import pyplot as plt

img = cv2.imread('box.png')
im = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(im,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(
    thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (255,0,0), 3)

cv2.imshow('dd' , img)
plt.imshow(img, cmap = 'gray')
plt.show()
cv2.waitKey(100000)
cv2.destroyAllWindows()

وهنا الحالة الثانية , فقط الزوايا

In [73]:
img = cv2.imread('box.png')
im = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(im,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(
    thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, -1, (255,0,0), 3)
print contours
cv2.imshow('dd' , img)
plt.imshow(img, cmap = 'gray')
plt.show()
cv2.waitKey(-1)
cv2.destroyAllWindows()
[array([[[143,  78]],

       [[143,  79]],

       [[143,  80]],

       ..., 
       [[146,  78]],

       [[145,  78]],

       [[144,  78]]])]

وعلى كل الاحوال نتيجة الرسم واحدة , لأن التابع يرسم بكلا الحالتين

: خصائص الاطارات

وهنا سنتعلم : ايجاد الخصائص المختلفة للاطارات مثل المساحة , الابعاد والصندوق المحيط

وسنرى العديد من التوابع المرتبطة بالاطارات

العزوم

عزوم الصورة تساعدك على حساب بعض الخصائص مثل مركز الكتلة للجسم مساحة الجسم ,

راجع الموسوعة الحرة (ويكيبيديا) حول تلك النقطة

cv2.moments()

يعطي قاموساً لكل قيم العزوم المحسوبة

شاهد المثال التالي

In [76]:
import cv2
import numpy as np

img = cv2.imread('box.png',0)
ret,thresh = cv2.threshold(img,127,255,0)
contours,hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)

print len(M)
print '=================================='
print M
24
==================================
{'mu02': 52290430.833333254, 'mu03': 4.57763671875e-05, 'm11': 742586000.0, 'nu02': 0.10833333333333317, 'm12': 131546634613.33333, 'mu21': 2.86102294921875e-06, 'mu20': 30941083.333333254, 'nu20': 0.06410256410256394, 'm30': 217013332640.0, 'nu21': 3.9989520724987846e-17, 'mu11': 0.0, 'mu12': 1.1444091796875e-05, 'nu11': 0.0, 'nu12': 1.5995808289995138e-16, 'm02': 632435743.3333333, 'm03': 119765198312.5, 'm00': 21970.0, 'm01': 3570125.0, 'mu30': 6.103515625e-05, 'nu30': 8.531097754664074e-16, 'nu03': 6.398323315998055e-16, 'm10': 4569760.0, 'm20': 981451163.3333333, 'm21': 159485814041.66666}

ومن هذه القيم يمكن اشتقاق قيم مفيدة

مثل المركز او المساحة , الاول تحديداً , يحسب كالاتي

In [77]:
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

print cx,cy
208 162

: مساحة الاطار

تعطى مساحة الاطار بالتابع ,

cv2.contourArea() OR M['m00']

In [79]:
area = cv2.contourArea(cnt)
print area
21970.0

: محيط الاطار

ويدعى ايضا طول القطر , ويتم ايجاده عبر التابع

cv2.arcLength()

ومتغيره الثاني يحدد ما اذا كان الشكل هو اطارا مغلقاً ,1, ام فقط منحني , كالتالي

In [80]:
perimeter = cv2.arcLength(cnt,True)
print perimeter
598.0

:تقريب الاطار

سيقرب شكل الاطار لشكل اخر مع عدد اقل من الرؤوس بالاعتماد على الدقة التي نحددها , وهذا يطبق وفق خوارزمية محددة

مثلا افترض انك تبحث عن مربع في صورة , ولكن بسبب مشكلة ما حصلت على مربع مشوه ,

لذلك يمكننا استخدام هذا التابع للحصول على المربع المطلوب ومتغيره الثاني هو قيمة الخطأ الاعظمي المسموح والذي يجب اختياره بعناية للحصول على النتيجة المرغوبة

In [81]:
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)

والسابق لاجل قيمة العتبة مساوية ل 1% من طول المنحني ,

المتغير الثالث يحدد ما اذا كان المنحني مغلق ام لا

Convex Hull

وهذا قد يبدو كتقريب الاطار ولكنه ليس كذلك , فهو يعني , مقاطع المنحنيات البارزة (المحدبة) وباقي المقاطع (المقعرة)تدعى عيوب للمنحني , ولذلك فكل الفروق عن الشكل المحدب الكامل للمنحني يمكن ايجادها وفق التابع

In [ ]:
hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]])

متغيرات الدخل , هي:

النقاط: وهي اطار الدخل

الخرج , نتجنبه غالباً

الاتجاه , اذا صح , مع عقارب الساعة والا العكس

النقاط المستعادة: افتراضياً , صح , ليعطي , النقاط للخرج , والا يعطي فقط , ادلة النقاط الناتج من مصفوفة الدخل

وبذلك قد نكتفي بالتالي

hull = cv2.convexHull(cnt)

: التحقق من التحدب

التابع هذا يتحقق فقط ما اذا كان المحيط محدبا

True

ام لا ,كالتالي

k = cv2.isContourConvex(cnt)

: المستطيل المحدد

وله نوعان

اولا - المستطيل المحدد المستقيم

وهذا لا يعطي اي اعتبار للتدوير ولذلك لا يكون حجمه اصغريا , ويعطي الزاوية العليا اليسرى والطول والعرض للمستطيل

كالتالي

In [95]:
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.imread('box.png')
print x,y,w,h
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),3)

plt.imshow(img ,cmap = 'gray')
plt.show()
143 78 131 170

:ثانيا- المستطيل المحدد المدور

وهنا يرسم المستطيل المحدد بمساحة اصغرية , وياخذ الدوران بالاعتبار

والتابع هو

cv2.minAreaRect()

والذي يعطي زاوية الدوران بالاضافة للعرض والطول

وللرسم نحتاج الزوايا الاربع والتي يمكن اكتسابها وفق التابع ,

cv2.boxPoints()

: الدائرة الاصغرية المحيطة

cv2.minEnclosingCircle()

In [96]:
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(img,center,radius,(0,255,0),2)

plt.imshow(img ,cmap = 'gray')
plt.show()

: القطع الناقص المحيط

In [104]:
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img,ellipse,(0,255,0),2)

plt.imshow(img ,cmap = 'gray')
plt.show()
 

ملائمة خط

ايضا يمكننا رسم خط في الجسم المرغوب كالتالي

In [110]:
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_LABEL_PIXEL,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

plt.imshow(img ,cmap = 'gray')
plt.show()
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-110-bde0bcf65ff5> in <module>()
      4 righty = int(((cols-x)*vy/vx)+y)
      5 
----> 6 cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
      7 
      8 plt.imshow(img ,cmap = 'gray')

OverflowError: Python int too large to convert to C long

ا الخطأ بالكود اعلاه يبين ان ميل المستقيم لانهائي لانه تماماً شاقولي وهذا دقيق جداً بجالتنا

مراجع وتمارين..

In [ ]:
 

هناك تعليق واحد: