Lesson_14_histogram

  |   Source

الهستوغرام : اوجد , ارسم , حلل

الهدف:

تعلم:

  • ايجاد الهستوغرامات باستخدام كل من توابع OpenCV و Numpy

  • رسم الهستوغرامات باستخدام توابع OpenCV و Matplotlib

  • وسترى هذه التوابع : cv2.calcHist , np.histogram الخ..

النظرية:

الهستوغرام يعطينا فكرة عامة عن الصورة , ويتمثل بقيم البكسلات على المحور X وعددها الموافق على المحور Y ..

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

ومن الصورة التالية , لصورة مع هستوغرامها نلاحظ مايلي:

In [1]:
from IPython.display import Image
Image('histex.jpg')
Out[1]:

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

ايجاد الهستوغرام :

الان وبعد ان امتلكنا فكرة عن الهستوغرام , يمكننا النظر لكيفية ايجاده عبر كل من OpenCV و Numpy , ولكن قبل فعل ذلك , نحتاج ان نفهم بعض المصطلحات المرتبطة بالهستوغرام

  • BINS : الهستوغرام اعلاه يظهر عدد البكسلات لكل قيمة شدة لبكسل , وبالتالي سنحتاج ل 256 قيمة للاظهار , ولكن اذا اعتبرنا اننا يجب ان نحسب الهستوغرام لمجموعة بكسلات قيمها تقع في مجال محدد جزئي ؟ مثلاً , نريد ايجاد عدد البكسلات ذات الشدات بين 0 الى 15 , ثم 16 الى 31 ...., 240 الى 255 . سنحتاج فقط 16 قيمة لتمثيل الهستوغرام وهذا يظهر في بعض امثلة OpenCV .

ولذلك , فان ما يجب فعله هو جمع القيم في الهستوغرام الكلي المشكلة لقيمة واحدة بالهستوغرام الاصغر , اي قسم الاول ل 16 قسم متساوي , , وعندها يدعى كل قسم منها BIN , وفي الحالة الاولى كان عددها 256 .. ويرمز لقيم ال BINs السابقة ب histSize في توثيق التوابع ل OpenCV..

  • DIMS : وهذا هو عدد البارامترات التي نجمع البيانات لاجلها , وبالسابق اخذنا فقط شدة البكسل بعين الاعتبار لذلك تكون قيمتها هنا 1.

  • RANGE : وهذا هو مجال الشدات التي نريد قياسها, عادة من 0 الى 256 اي المجال كامل.

1. حسابات الهستوغرام في OpenCV: لذلك سنستخدم الان التابع cv2.calcHist لايجاد الهستوغرام , لذلك سنتعرف عليه وعلى متغيراته:

$$ cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])$$

  1. images: وهي الصورة الاصل من نوع uint8 هو float32 . ويجب ان تعطى ضمن اقواس "[img]"

  2. channels : ويعطى ايضاً ضمن اقواس , وهو دليل القناة التي نريد اخذ هستوغرام لمستوي الصورة فيها , في حالة الصورة الرمادية , القناة مفردة وفي حالة الصورة الملونة بامكاننا تحديد اي مستوي لوني من [0], [1],[2] ..

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

  4. histSize : وهذا يمثل عدد ال BINs ويجب اعطاؤه ضمن اقواس [] وللمجال الكامل نمرر [256].

  5. ranges : وهذا مجالنا عادة [0,256].

لذلك سنبدأ بالمثال التالي :

In [1]:
%matplotlib inline
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('wt.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])

print hist.shape
(256, 1)

هنا مصفوفة hist بشكل 256X1 , كل قيمة منها تعود لعدد البكسلات ذات الشدة الموافقة

2. حسابات الهستوغرام في Numpy: ايضاً هنا لدينا التابع np.histogram للحساب كالتالي:

In [2]:
hist,bins = np.histogram(img.ravel(),256,[0,256])

print hist.shape
(256,)

وبالتالي الناتج نفس السابق .

ايضاً , لدى Numpy تابع اسرع ب 10X مرة من np.histogram . لذلك للهستوغرام ببعد واحد , من الافضل استخدام np.bincount ولكن يجب وضع minlength = 256 مثلاً :

In [3]:
hist = np.bincount(img.ravel(),minlength=256)

print hist.shape
(256,)

ملاحظة : توابع OpenCV اسرع بحوالي 40X مرة من np.histogram. لذلك ابق مع توابع OpenCV.

فيما يلي علينا رسم الهستوغرام:

رسم الهستوغرام:

هناك طريقتان لفعل ذلك :

  1. طريقة قصيرة: باستخدام توابع الرسم ل Matplotlib.

  2. طريقة طويلة : باستخدام توابع الرسم ل OpenCV.

1. باستخدام Matplotlib: تاتي هذه المكتبة مع تابع الرسم للهستوغرام matplotlib.pyplot.hist وهذا يجد مباشرة الهستوغرام ويرسمه . ولا تحتاج لاستخدام calcHist او np.histogram لايجاد الهستوغرام , انظر للكود التالي:

In [42]:
img = cv2.imread('opencv_ios.png',0)

plt.subplot(121,axisbg = 'y')
plt.hist(img.ravel(),256,[0,256])
plt.subplot(122)
plt.imshow(img,cmap = 'gray')

plt.show(d)

والسابق يوضح النتيجة , مع الصورة

او يمكنك استخدام الرسم العادي في matplotlib لرسم هستوغرامات الصور الملونة , على نفس المحور ولكن يجب اجراء الحسابات بالبداية لها , كما التالي:

In [46]:
img = cv2.imread('opencv_ios.png')
color = ('b','g','r')
for i,col in enumerate(color):
    histr = cv2.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.grid()
plt.show()

وبامكانك التحقق من الصورة اعلاه بملاحظة ان الالوان ذات قيم مرتفعة وذلك لعدم وجود الا اللون الابيض او الالوان الاساسية فحسب في الشعار الموجود بالصورة...

2. استخدام OpenCV: حسناً , هنا ستعدل قيم الهستوغرام مع ارقام ال bins خاصتها , بحيث تعطينا الاحداثيات القابلة للرسم عبر cv2.line و cv2.polyline وهذا ممكن كما في الامثلة الرسمية ل Opencv-python.

تطبيق القناع:

لقد استخدمنا فيما سبق التابع cv2.calcHist لايجاد الهستوغرام لكامل الصورة . اما اذا اردنا حسابه لمنطقة محددة منها فما علينا الا ان ننشأ صورة اخرى بنفس القياس ذات منطقة بيضاء في مكان المنطقة الهامة ROI والباقي اسود ومن ثم نمررها للتابع كالتالي:

In [50]:
img = cv2.imread('opencv_ios.png',0)

# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[50:150, 50:200] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)

# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])

plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])

plt.grid()
plt.show()

# again bigger
plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])
plt.grid()
plt.show()

النتيجة السابقة للهستوغرام , وفيها الخط الازرق للصورة الكاملة والاخضر للصورة الجزئية (المقنعة).

موارد اضافية :

تمارين:

Comments powered by Disqus