السبت، 27 أبريل 2013

vb.net and emgu Face Detection اكتشاف الوجوه

مقدمة

توجد كثير من التقنيات يمكن استخدامها بلغة Vb.net بكل سهولة  لكشف الوجوه Face Detection  منها   تقنية Code التي توفرها شركة Google التي موجودة على الرابط التالي
والثانية تقنية OpenCV's توفرها شركة emgu  التي تعتمد على نظرية Viola-Jones method  في تحديد واستكشاف الوجوه في الصورة  موجودة على الرابط التالي  نقوم بتحميل المكتبة من الموقع



Face Detection Example



 كيف تستكشف الوجوه بنظرية Viola-Jones
سيكون شرحنا على طريقة عمل  تقنية OpenCV's  التي توفرها شركة emgu  لأننا سنبرمج باستخدام هذه التقنية.     تقنية OpenCV's تعتمد على نظرية Viola-Jones method في استكشاف الوجوه  .   تم اكتشاف هذا النظرية عام 2001 وهذا النظرية تستكشف  كل الأجزاء objects الموجودة في الصورة  ومنها أجزاء الوجه ونحن نجمع أجزاء الوجه لنكون وجه شخص وتلخص في أربع نقاط :
1.    مستطيل بسيط يسمى Haar features
2.    عمل تكامل الصورة لسرعة اكتشاف الأشكال أو الوجوه
3.    استخدام نظرية AdaBoost machine-learning
4.    عمل تجميع لأجزاء الوجه (انف عيون فم) لتكون صورة باستخدام cascaded classifier

نظرية Viola-Jones method تعتمد على  تحويل Haar wavelets في استكشاف الصور                
Haar wavelets هو موجة واحدة مربعة (جزء واحد عالي وجزء واحد واطئ ). في ثنائية الإبعاد هو موجة مربعة مكونة من أزواج من المستطيلات المتجاورة  كل واحدة  لونين اسود وابيض كما في Figure 1
          


Figure 1


معدل Haar   في كل مستطيل  يحسب بطرح مجموع قيم pixel في مناطق سوداء من مجموع قيم pixel في مناطق بيضاء وتقسيمها على مجموع pixel في المنطقتين إذا كان نتيجة الاختلاف اعلي من a threshold  الذي يحدد خلال  نظرية machine-learning يعتبر هذا الجزء هو جزء من الوجه ويتجه ليبحث عن بقية الأجزاء الخاصة بالوجه ضمن نفس المربع بتطبيق
مستطيلات Haar   على بقية pixel  شاهد Figure 4. عن طريق استخدام تكامل integrating يعني جمع أجزاء الصغيرة مع بعض في هذه الحالة الأجزاء الصغيرة هي قيم  pixel .قيم integral لكل pixel هي مجموع كل pixel التي تقع فوقه . أي في Figure 2. قيمة pixel في موقع 4 هي مجموع (A+B+C+D) قيمة pixel في موقع3 هي (A+C) قيمة pixel في موقع 2 هي (ِ A+B) قيمة pixel في الموقع 1 في (A)
وتلخص العملية في هذه المعادلة
integer operations: (x4, y4) - (x2, y2) - (x3, y3) + (x1, y1). 

Figure 2
 


يبدأ بتحريك المربع البحث (الذي يحتوي في داخله على مستطيلات Haar)  من الجزء الأعلى الأيسر  في الصورة وينفذ مستطيلات Haar على كل Pixel الموجودة في المربع باحثا عن وجه الشخص  وبعدها يتحرك المربع إلى اليمين ويستمر إلى أن يصل إلى نهاية الجزء الأيمن  وبعدها يتحرك  المربع إلى الأسفل ويستمر سطر سطر وبعد أن ينتهي من دورة واحدة على كل الصورة بمربع ذو حجم معين يكبر حجم المربع ويعيد البحث في كل الصورة من جديد ويستمر يكبر المربع وينفذ مستطيلات Haar على كل Pixel الموجودة في المربع  حتى أخر مرة يكون المربع مساوي لحجم الصورة حتى يساعده في اكتشاف جميع الوجوه في الصورة مهما اختلف كبر رأس الشخص



Figure 4



شرح استخدام مكاتب  emgu  بلغة VB.Net  في اكتشاف الوجوه Face Detection

بالبداية ندخل على الربط التالي  نحمل النسخة التي تلاءم نظامنا حسب نوع نظام التشغيل الخاص بنا



 

إنا هنا اختارت الإصدار  2.4.0.1717 الخاصة بالمعالج ذو 32 bit لان نظامي هكذا.؟؟وسأشرح على  النسخة 2.4.0.1717 نقوم بتحميل النسخة من الموقع..؟
بعد التحميل نقوم بتنصيبها في الحاسوب على المسار الذي نريده؟ هنا نصبناها  على قرص \:F 

 





سيكون مسار للمجلد  الخاص بالمكتبة  Emgucv  بالشكل التالي
F:\emgucv-windows-x86 2.4.0.1717\



 

أكثر مجلدين سنستخدمهم هما مجلد bin ومجلد opencv



ألان نكون مشروع VB.NET جديد فيه نافذتين كما في الشكل  كل وحدة مصممة وفيها أدوات حسب الشكل؟


 

يوجد نوعين من الأنظمة  نظام bit=X86 ) 32(  ونظام bit=X64)  64   ( نستطيع معرفة نوع نظامنا بالضغط بزر Mouse الأيمن على My Computer واختيار خصائص  سيعرض لنا نوع نظام التشغيل الخاص بنا؟؟ لتحديد النظام الذي سيعمل عليه البرنامج  نصممه نذهب إلى التبويب التالي
(My Project--Compile-- Advanced Compile Options--Target CPU)
  


نضغط على Add reference  ونضيف المكاتب التالية Emgu.CV  , Emgu.Util   او كل مكاتب  Emgucv  
حتى لا نواجه مشكلة الموجودة في مجلد ( F:\emgucv-windows-x86 2.4.0.1717\bin )










نذهب داخل مجلد emgucv-windows-x86 2.4.0.1717
اذا كان نظام تشغيلنا 32 bit   نذهب الى المسار التالي
F:\emgucv-windows-x86 2.4.0.1717\bin\x86
اذا كان نظام تشغيلنا 64 bit    نذهب الى المسار التالي
F:\emgucv-windows-x86 2.4.0.1717\bin\x64




 

وننسخ جميع ملفات DLL التي تبدأ بأسم    opencv أو جميعها حتى لا نواجه مشكلة أما إلى المسار الذي يعمل منه البرنامج أي مجلد bin\Debug آو  إلى المسار C:\Windows\System32







كود البرنامج كامل الذي سيكتب في Form1
VB.net Code
Imports Emgu.CV
Imports Emgu.CV.Structure
Imports Emgu.Util
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports System.Drawing
Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
OpenFileDialog1.ShowDialog()


'Load the image from file
Dim img As New Image(Of Bgr, Byte)(OpenFileDialog1.FileName)

'Load the object detector
Dim faceDetector As New HaarCascade("F:\emgucv-windows-x86 2.4.0.1717\opencv\data\haarcascades\haarcascade_frontalface_default.xml")

'Convert the image to Grayscale
Dim imgGray As Image(Of Gray, Byte) = img.Convert(Of Gray, Byte)()

For Each face As MCvAvgComp In faceDetector.Detect( _
imgGray, _
1.1, _
10, _
CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING, _
New Size(20, 20), _
Size.Empty)
img.Draw(face.rect, New Bgr(Color.White), 1)
Next

Form2.Show()
Form2.PictureBox1.Image = img.Bitmap


End Sub
End Class





شرح كود اكتشاف الوجود خطوة بخطوة

1.     نستدعي المكاتب التالية من مكتبة   Emgu بعد أن نضيفها إلى المشروع عن طريق Add Reference
VB.net Code
Imports Emgu.CV
Imports Emgu.CV.Structure
Imports Emgu.Util

وقد نحتاج إلى المكاتب التالية يفضل أضافتها حتى لا نواجه مشاكل في الرسم
VB.net Code
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports System.Drawing

2.     نقرئ الصورة التي نريد تحديد الوجوه فيها  من المسار المخزنة به ونخزنها داخل متغير img  من نوع صورة؟هنا قرئنا صورة مخزنة داخل  المسار
  C:\test.jpg

VB.net Code
Dim img As New Image(Of Bgr, Byte)("C:\test.jpg")

3.    نقرئ الملف haarcascade_frontalface_default.xml الموجود بالمسار
F:\emgucv-windows-x86 2.4.0.1717\opencv\data\haarcascades
وهذا الملف متخصص في تجميع ملامح الوجه  HaarCascade وتكوين وجه للشخص لتحديده شاهد Figure 3

VB.net Code
Dim faceDetector As New HaarCascade("F:\emgucv-windows-x86 2.4.0.1717\opencv\data\haarcascades\haarcascade_frontalface_default.xml")

4.     نحول الصورة التي قرئناها إلى صورة ابيض واسود Gray لان إجراء البحث Detect الموجود في HaarCascade يأخذ الصورة بصيغة Gray
VB.net Code
Dim imgGray As Image(Of Gray, Byte) = img.Convert(Of Gray, Byte)()

5.     نبحث عن كل وجه ضمن الصورة ونضع مستطيل حول الوجه
VB.net Code
For Each face As MCvAvgComp In faceDetector.Detect(imgGray, 1.1, 10, CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING, New Size(20, 20),Size.Empty)
img.Draw(face.rect, New Bgr(Color.White), 1)
Next

يعيد إجراء  Detect بيانات وجه بشكل MCvAvgComp توضيح برامترات إجراء Detect
Construct
Detec ( _
        haarObj As HaarCascade, _
        scaleFactor As Double, _
        minNeighbors As Integer, _
        flag As HAAR_DETECTION_TYPE, _
        minSize As Size _
) As MCvAvgComp()()


الوظيفة
البرامتر
هي الصورة التي نريد تحديد الوجوه فيها  بعد تحويلها  الى Gray
haarObj
تمثل Factor الذي نستخدمه Windows في تحديد فترة البحث بين بحثين متتاليين مثلا 1.1 يمثل زيادة بحث Windows  بمقدار  %10
scaleFactor
اصغر رقم للمستطيل المجاور الذي يمثل كل وجه  إذا كان يساوي صفر فان الدالة لا تجمع الإشكال وتعيد كل جزء object بشكل مستطيل منفصل
minNeighbors
تستخدم في إهمال أطراف بعض الصور التي تحتوي على قليل أو كثير من الأطراف التي لا تحتوي على object للبحث
flag
    اصغر حجم  Windows بشكل تلقائي يجعل حجم Samples هو 20*20
minSize





يأخذ إجراء Draw ثلاث برامترات
Construct
img.Draw(face.rect, New Bgr(Color.White), 1)
        §            البرامتر الأول الوجه الذي نريد تخطيطه
        §            البرامتر  الثاني  لون الخط الذي سنرسم به مستطيل حول الوجه
        §            البرامتر الثالث هو سمك الخط أو سمك أطراف المستطيل الذي سيغطي كل وجه

6.     الآن تم تحديد كل الوجوه في الصورة بقي فقط نستعرض الصورة ويفضل استعراضها على نافذة ثانية ندرج فيها أداة Picturebox وتستعرض بالشكل التالي
VB.net Code
Form2.Show()
Form2.PictureBox1.Image = img.Bitmap

وإذا أردنا أيضا تحديد عيون الأشخاص في الصور فقط نستدعي الملف haarcascade_eye.xml الخاص بمعالج عيون الأشخاص في الصور الصور الموجود في المسار  التالي
F:\emgucv-windows-x86 2.4.0.1717\opencv\data\haarcascades\
ونضيف هذا الكود

VB.net Code
'Load the object detector
Dim faceDetector1 As New HaarCascade("F:\emgucv-windows-x86 2.4.0.1717\opencv\data\haarcascades\haarcascade_eye.xml")

For Each face As MCvAvgComp In faceDetector1.Detect(imgGray, 1.1, 10, CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING, New Size(20, 20), Size.Empty)
img.Draw(face.rect, New Bgr(Color.Red), 2)
Next
 














حمل المثال من هنا





السبت، 6 أبريل 2013

كتاب التحكم عبر الحاسوب بالأجهزة الخارجية بلغة VB.NET

كتاب التحكم عبر الحاسوب بالاجهزة الخارجية
استخدام Serial Port بلغة VB.NET 
نبذة:
يتحدث هذا الكتاب عن كيفية التحكم بجهاز خارجي عبر الحاسوب وارسال بيانات له واستلام بيانات منه



  



الجمعة، 5 أبريل 2013

التحكم عبر الحاسوب بالاجهزة الخارجية بلغة VB.NET



مقدمة
كثيرا ما نحتاج الى استخدام الحاسوب لعرض نتائج معينة ترسل من جهاز خارجي او التحكم بجهاز خارجي عبر الحاسوب .او تحسس بحالة جهاز معين وتمرير حالته الى الحاسوب الذي بدوره يحلل الحالة ويأمر الجهاز الخارجي بتنفيذ عمل معين من خلال إرسال أوامر للجهاز الخارجي.مثلا أجهزة التكييف تمرر نسبة هواء الغرفة الى الحاسوب وهو بدوره يرى اذا كانت درجة البرودة اعلى من المطلوب يرسل امر الى المكيف بتقليل نسبة البرودة . او  مثلا التحكم بمروحة نشغلها ونوقفها  عبر الحاسوب  .
اي هنا الحاسوب فقط يرسل أوامر تم تعريف معناها لدى الجهاز الخارجي لتنفذ . مثلا في برمجة PIC (الذي يكتب كوده بلغة Micro C )الخاص بجهاز إنارة خارجي مربوط بحاسوب  تم تحديد الرقم 8H لتشغيل الضوء الأخضر على الجهاز الخارجي فعند ارسال 8H بلغة VB.NET الى الجهاز الخارجي عبر المنفذ المربوط عليه الجهاز سوف يعمل الضوء الأخضر في داخله .
اي هنا دور لغة VB.NET هي تمرير أوامر  من الحاسوب الى الجهاز الخارجي  وتكون هذه الأوامر معرفة لدى الجهاز الخارجي   او يستلم الحاسوب  بيانات من الجهاز الخارجي  ليحللها ويعالجها
سنحتاج الى Cable  يربط بين الحاسوب والجهاز الخارجي ليمرر البيانات بين الطرفين كما في الشكل(1) بالأسفل   طبعا الأجهزة الخارجية ممكن ان تتصل بالحاسوب عبر Serial Port او عبر USB ففي كلا الحالتين يفتح منفذ بين الجهاز والحاسوب لكن في حالة  USB تنتقل البيانات بشكل تسلسلي وسريع و Serial Port  أيضا ينقل بشكل تسلسلي .واغلب الحواسيب الحديثة لا تحتوي على Serial Port وتستخدم USB في إرسال واستلام البيانات
 
شكل(1) Cable يربط بين حاسوب وجهاز خارجي


المنافذ COM & LPT
وبما إننا نريد أن نتعامل مع أجهزة خارجية فلا بد من وجود منفذ معين يتصل به هذا الجهاز الخارجي وكل جهاز خارجي يتصل مع الحاسوب يكون له  الحاسوب منفذ معين من خلال هذا المنفذ نستطيع إرسال بيانات للجهاز واستلام البيانات القادمة من الجهاز من خلال هذا المنفذ وتكون أرقام المنافذ بشكل التالي COM وبعده رقم المنفذ مثلا COM3 معناه المنفذ الثالث .
ولمعرفة المنفذ الذي سيشغله أي جهاز نفتح  Device Manager من خلال الذهاب إلى

 ("Start"? "Run"? "devmgmt.msc")
صورة من جهاز LapTop نرى انه يستخدم كثير من المنافذ من اجل تقنية Bluetooth 
صورة من حاسبة مكتبية

في تبويب Ports (COM & LPT) سوف تظهر لنا المنافذ المستغلة في الحاسوب مثلا هنا حدد COM1  مستخدم من قبل منفذ الاتصال. فعند ربط أي جهاز خارجي بالحاسوب سيظهر هنا اسم الجهاز ورقم المنفذ الذي سيستغله ومن خلال معرفة الرقم الذي يستغله الجهاز الخارجي نستخدم هذا المنفذ في إرسال بيانات له ونستقبل بيانات منه

التعامل مع الأجهزة الخارجية في بيئة VB.NET
هناك مكتبة خاصة بالتعامل مع الأجهزة الخارجية تحتوي على جميع الأدوات التي سنحتاجها في التعامل مع أي جهاز خارجي  سوف نستدعيها في أي برنامج نتعامل من خلاله مع المنافذ وهي المكتبة التالية

VB.NET Code
Imports System.IO.Ports
بما إننا نتعامل مع المنافذ  هناك أداة اسمها SerialPort  تمكنا من الاتصال والتعامل مع الأجهزة الخارجية فيها  فيه كل ما نحتاج إليه



أو نكونها بالكود نعرف  كائن جديد من نوع  SerialPort في التعريفات العامة بشكل التالي 
VB.NET Code
Dim SerialPort1 As New SerialPort

تطبيق: لدينا جهاز خارجي فيه شاشة عرض يعرض البيانات القادمة من الحاسوب على شاشته .عند ربطه بالحاسوب يأخذ   منفذ COM1 .وهذا الجهاز مبرمج انه يستقبل البيانات بشكل سلسلة نصية لكلي يعرضها على شاشته؟
 


المطلوب انه نكون برنامج فيه Textbox1 وزر إرسال Button1 ونكتب في Textbox1 بيانات ونضغط إرسال تعرض هذه البيانات على الجهاز الخارجي. وأداة ComboBox1 لعرض جميع منافذ. نكون مشروع جديد كما في الشكل
 


في البداية نستدعي المكتبة المتخصصة بالتعامل مع المنافذ ونكون كائن جديد من نوع SerialPort  للتعامل من خلاله مع المنفذ وإرسال بيانات إلى الجهاز الخارجي

VB.NET Code
 Imports System.IO.Ports
Public Class Form1
Dim SerialPort1 As New SerialPort
End Class
لكي نحمل ComboBox1 بجميع منافذ الحاسبة الفعالة في حدث تحميل النافذة Form1_Load نكتب الكود التالي. حيث يعمل أجراء SerialPort.GetPortNames بجلب جميع منافذ الحاسبة الفعالة لكي نعرضها منفذ منفذ داخل اداة ComboBox1  وحتى يسهل لنا اختيار المنفذ المطلوب بكل برنامج
VB.NET Code
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
ComboBox1.Items.Clear()
For Each Nport As String In SerialPort.GetPortNames
ComboBox1.Items.Add(Nport)
Next
End Sub
في زر الإرسال نكتب الكود التالي  .هنا استخدمنا أسلوب يفتح منفذ و بعد كل إرسال يغلق المنفذ نستطيع أيضا فتح المنفذ مرة واحدة واستمرار إرسال البيانات عليه.وسيقوم هذا الكود بإرسال النصوص التي سنكتبها داخل  Textbox1 الى الجهاز الخارجي المربوط على المنفذ الذي سنختاره من ComboBox1
VB.NET Code
Try
With SerialPort1
.PortName = ComboBox1.Text
.BaudRate = 34800
.DataBits = 8
.Parity = IO.Ports.Parity.None
.StopBits = IO.Ports.StopBits.One
.Handshake = IO.Ports.Handshake.None
End With

If Not (SerialPort1.IsOpen = True) Then
SerialPort1.Open()
End If

SerialPort1.DiscardOutBuffer()
SerialPort1.Write(TextBox1.Text)

SerialPort1.Close()
MsgBox("تم ارسال البيانات الى الجهاز الخارجي")
Catch ex As Exception
MsgBox(ex.Message)
End Try
كود البرنامج كامل
VB.NET Code
Imports System.IO.Ports
Public Class Form1
Dim SerialPort1 As New SerialPort
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
With SerialPort1
.PortName = ComboBox1.Text
.BaudRate = 34800
.DataBits = 8
.Parity = IO.Ports.Parity.None
.StopBits = IO.Ports.StopBits.One
.Handshake = IO.Ports.Handshake.None
End With
If Not (SerialPort1.IsOpen = True) Then
SerialPort1.Open()
End If
SerialPort1.DiscardOutBuffer()
SerialPort1.Write(TextBox1.Text)
SerialPort1.Close()
MsgBox("تم ارسال البيانات الى الجهاز الخارجي")
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
ComboBox1.Items.Clear()
For Each Nport As String In SerialPort.GetPortNames
ComboBox1.Items.Add(Nport)
Next
End Sub
End Class



تطبيق : برنامج استلام بصمة شخص من جهاز خارجي: ونفس الوقت نستطيع ارسال صورة من نفس الحاسبة له للتأكد من صحة توصيله للبيانات





VB.NET Code
Imports System.IO.Ports
Imports System.Text
Imports System.IO
Public Class Form1
Dim sizeimage As Integer = 0
Dim SerialPort1 As New SerialPort
Dim sendimage As Image

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Try
sizeimage = SerialPort1.BytesToRead
Dim comBuffer As Byte() = New Byte(sizeimage - 1) {}
SerialPort1.Read(comBuffer, 0, sizeimage)
Dim MS As MemoryStream = New MemoryStream(comBuffer)
PictureBox1.Image = Image.FromStream(MS)
MS.Close()
SerialPort1.DiscardOutBuffer()
Catch ex As Exception
End Try
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
With SerialPort1
.PortName = ComboBox1.Text
.BaudRate = 34800
.DataBits = 8
.Parity = IO.Ports.Parity.None
.StopBits = IO.Ports.StopBits.One
.Handshake = IO.Ports.Handshake.None
.WriteBufferSize = 120000000
.ReadBufferSize = 120000000
End With
If Not (SerialPort1.IsOpen = True) Then
SerialPort1.Open()
End If
Timer1.Enabled = True
MsgBox("تم تشغيل بدء التنصت  ")
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Timer1.Enabled = False
End Sub

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Try
Dim pfd As New OpenFileDialog
pfd.ShowDialog()
Dim a12 As Image = Image.FromFile(pfd.FileName)
Dim MY_MemoryStream As MemoryStream = New MemoryStream
a12.Save(MY_MemoryStream, Imaging.ImageFormat.Jpeg)
Dim ArrImage As Byte() = MY_MemoryStream.GetBuffer ' here change it to Bytes
MY_MemoryStream.Close()
SerialPort1.Write(ArrImage, 0, ArrImage.Length)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
ComboBox1.Items.Clear()
For Each Nport As String In SerialPort.GetPortNames
ComboBox1.Items.Add(Nport)
Next
If ComboBox1.Items.Count > 0 Then
ComboBox1.Text = ComboBox1.Items(0)
End If
End Sub
End Class
حجم الصورة التي تتم معالجتها هنا يجب ان يكون صغير لصغر حجم Buffer