2016年12月15日 星期四

emgucv_Canny邊緣偵測介紹

首先我們準備好程式工作環境











這裡給自己準備一張  用  小畫家畫的一張圖

寬、高 設置 200*200










上下按看看可以查詢到 可放一個路徑字串資訊載圖


設置 寬高各為200


右鍵點開做資訊查看符合 剛才的 小畫家 獲得之資訊


Stage1. 一張原影像載入



Canny邊緣偵測 實驗

寫法1.  獲取 255的白色邊緣資訊(其餘黑為0)



查個字典


Find the edges on this image and marked them in the returned image.


參數說明  link

http://www.emgu.com/wiki/files/2.4.10/document/html/9547d8d7-dd5d-ef91-4cee-7d0544a1fe58.htm








一個邊緣(Edge)的定義

可以想像成是一個急速下滑的山谷

如果我們針對一張影像中間為較深黑色(接近0)區塊
旁邊兩側為地勢較高的(較白接近255的)區域
我們從左側至右側就會形成類似山谷的曲線~~~




或是一個水溝(中間凹下去一般)




寫法1.  Image<Gray, byte> Canny(double thresh, double threshLinking);


這裡用的參數是來自官方範例給定的

第一個參數  thresh
大(高)門檻值用來控制強邊緣的初始分割
The threshhold to find initial segments of strong edges

如果一個像素的梯度大於上限值,則被認為是邊緣像素

如果小於下限閾值,則被拋棄

如果該點的梯度在兩者之間則當這個點與高於上限值的像素點連接時我們才保留
否則刪除。




第二個參數  threshLinking
小(低)門檻值(thresh)用來控制邊緣連接
The threshold used for edge Linking



通常高低閾值比在2:1 到3:1之間


這裡我用的此張範例不是很好清楚得知參數條動差異


因此改用自己照片







關於 Canny 邊緣偵測器的解釋

Canny Edge Detection










寫法2. 關鍵函數


CvInvoke.cvCanny  設計等於下方架構

public static void cvCanny(
IntPtr  image,
IntPtr  edges,
double  threshold1,
double  threshold2,
int  apertureSize
)
第一個參數image,Input image
表示輸入圖像,必須為單通道灰度圖
第二個參數edges,Image to store the edges found by the function
表示輸出的邊緣圖像,為單通道黑白圖
第三個參數threshold1,The first threshold
第四個參數threshold2,The second threshold.
第三個參數和第四個參數表示閾值,這二個閾值中當中的小閾值用來控制邊緣連接,大的閾值用來控制強邊緣的初始分割,即如果一個像素的梯度大於上限值,則被認為是邊緣像素,如果小於下限閾值,則被拋棄。如果該點的梯度在兩者之間則當這個點與高於上限值的像素點連接時我們才保留,否則刪除。
第五個參數aperture,Aperture parameter for Sobel operator
表示Sobel算子大小,默認為3即表示一個3*3的矩陣。


Code練習

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.Util;


namespace emgucv_canny_basic
{
    public partial class Form1 : Form
    {
        #region variable
        //Image<Bgr, byte> img = new Image<Bgr, byte>(200, 200);
        string imgPath = @"C:\img_res\Myphoto.jpg";
        Image<Bgr, byte> img;
        #endregion
        public Form1()
        {
            InitializeComponent();
            img = new Image<Bgr, byte>(imgPath);
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            //寫法1.       
            //imageBox1.Image = img.Canny(400, 250);
            //寫法2.
            imageBox1.Image = img;
            Image<Gray, byte> grayImg = img.Convert<Gray, byte>(); //灰階
            imageBox2.Image = grayImg;
            Image<Gray, byte> binaryImg = grayImg.ThresholdToZero(new Gray(200));
            imageBox3.Image = binaryImg;
            Image<Gray, byte> edgeResult = new Image<Gray, byte>(img.Size);
            CvInvoke.cvCanny(binaryImg, edgeResult ,300,100,3);
            imageBox4.Image = edgeResult;
        }
    }
}












2016年12月3日 星期六

人臉貼圖動態呈現實作

這是來自班上




之前兩組所遇到的問題



如何做一個貼圖人臉功能



首先是先達到即時偵測人臉



之後針對左上角座標設定貼圖位置

用其 X  及 Y

Rectangle 結構

https://msdn.microsoft.com/zh-tw/library/system.drawing.rectangle(v=vs.110).aspx









using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.Util;
using Emgu.CV.CvEnum;

namespace 人臉上面貼圖效果
{
    public partial class Form1 : Form
    {        
        CascadeClassifier face = new CascadeClassifier("haarcascade_frontalface_default.xml");
        Image<Bgr, byte> frame; // 靜態圖用的原彩色影像結構體
        Image<Gray, byte> grayImg; // 靜態圖用的灰階影像結構體(for 暫存)
        List<Rectangle> faces = new List<Rectangle>();//存放影像中出現多個人臉的List


        Capture cap;//攝影機
        Image<Bgr, byte> img;//載入的影格(不斷更新、不只一張)
        Image<Gray, byte> grayframe;
        Image<Bgr, byte> texture;
        Image<Gray, byte> grayTexture;
        bool openWebcam = false;
        bool detectFace = false;
        bool pasteTexture = false;


        public Form1()
        {
            InitializeComponent();
        }

        private void btnLoadImg_Click(object sender, EventArgs e)
        {
            string strFileName = string.Empty;

            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                frame = new Image<Bgr, byte>(ofd.FileName);
                imageBox2.Image = frame;
            }
        }

        private void btnFaceDetect_OnImage_Click(object sender, EventArgs e)
        {
            grayImg = frame.Convert<Gray, byte>();
            //Face Detect 
            Rectangle[] faceDetected = face.DetectMultiScale(grayImg,
                                                                1.1,
                                                                10,
                                                                new Size(30, 30),
                                                                Size.Empty);
            faces.AddRange(faceDetected);

            foreach(Rectangle f in faces)
            {
                frame.Draw(f , new Bgr(Color.Blue) , 2);
            }
            imageBox2.Image = frame;
        }

        private void btnPasteImg_onImage_Click(object sender, EventArgs e)
        {
            string strFileName = string.Empty;
            OpenFileDialog ofd = new OpenFileDialog();
            Image<Bgr, byte> texture;
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                texture = new Image<Bgr, byte>(ofd.FileName);//取得貼圖
                foreach (Rectangle f in faces)
                {
                    //img.Draw(f, new Bgr(Color.Red), 2);

                    frame.ROI = new Rectangle(f.X, f.Y, texture.Width, texture.Height);
                    texture.CopyTo(frame);
                    frame.ROI = new Rectangle();
                    imageBox2.Image = frame;
                }
            }
        }


        private void btnOpenWebcam_Click(object sender, EventArgs e)
        {
            cap = new Capture(0);           
            Application.Idle += updateFrame;
            openWebcam = true;
            
            //imageBox1.Image = img;
        }

        private void updateFrame(object sender, EventArgs e)
        {
            if(openWebcam)
            {
                img = cap.QueryFrame();
                imageBox1.Image = img; //顯示
            }
            
            //imageBox1.Image = img; //顯示

            if(detectFace)
            {
                List<Rectangle> faces = new List<Rectangle>();                

                img = cap.QueryFrame();

                grayframe = img.Convert<Gray, byte>();

                Rectangle[] faceDetected = face.DetectMultiScale(grayframe,
                                                                     1.1,
                                                                     10,
                                                                     new Size(30, 30),
                                                                     Size.Empty);

                faces.AddRange(faceDetected);

                foreach (Rectangle f in faces)
                {
                    img.Draw(f, new Bgr(Color.Red), 2);
                }
                imageBox1.Image = img;
            }


            if(pasteTexture)
            {
                List<Rectangle> faces = new List<Rectangle>();

                img = cap.QueryFrame();

                grayframe = img.Convert<Gray, byte>();

                Rectangle[] faceDetected = face.DetectMultiScale(grayframe,
                                                                     1.1,
                                                                     10,
                                                                     new Size(30, 30),
                                                                     Size.Empty);
                faces.AddRange(faceDetected);

                foreach (Rectangle f in faces)
                {
                    //img.Draw(f, new Bgr(Color.Red), 2);
                    img.ROI = new Rectangle(f.X  , f.Y - 60, texture.Width, texture.Height);// 帽子要帶高一些
                    //method.1
                    //texture.CopyTo(img);


                    //method.2
                    grayTexture = texture.Convert<Gray,byte>();
                    //CvInvoke.cvNamedWindow("灰圖");
                    //CvInvoke.cvShowImage("灰圖", grayTexture);

                    CvInvoke.cvThreshold(grayTexture, grayTexture, 125, 255, THRESH.CV_THRESH_BINARY);
                    //CvInvoke.cvShowImage("二值圖", grayTexture);



                    CvInvoke.cvCopy(texture, img, grayTexture);

                    img.ROI = new Rectangle();
                }
                imageBox1.Image = img;
            }
        }

        private void btnFaceDetect_OnWebcam_Click(object sender, EventArgs e)
        {
            detectFace = true;
            pasteTexture = false;
        }

        private void btnPasteImg_onWebcam_Click(object sender, EventArgs e)
        {
            string strFileName = string.Empty;
            OpenFileDialog ofd = new OpenFileDialog();            
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                texture = new Image<Bgr, byte>(ofd.FileName);//取得貼圖
            }

            detectFace = false;
            pasteTexture = true;
        }
    }
}









2016年12月2日 星期五

背景相減_手部提取示範


這次目標做出一個背景相減




原理:

Step1. 先建立一個背景模型


Step2. 在針對後續出現的每一張一直不斷更新的影像去跟背景模型做相減

一個一個像素數值會具有亮度的差異


最後得到我們想要的移動前景



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;


namespace 背景相減提取手部
{
    public partial class Form1 : Form
    {
        #region Globle Var
        Capture _capture = null; // 攝影機
        Image<Gray, Byte> difI; //相減後顯示灰階亮度差異
        Image<Gray, byte> grayFrame; //在做背景模型前先準備灰階暫存用的影像存放結構體
        Image<Bgr, Byte> frame; //原彩影               
        Image<Gray, Byte> refI; //參考影像
        Image<Gray, Byte> grayBg; //接受參考影像用來做顯示背景模型用

        //切換流程用的Flag
        bool _newRef = false;
        bool cap = false;
        bool frameDiff = false;
        bool frameBinary = false;
        #endregion


        public Form1()
        {
            InitializeComponent();
            Application.Idle += updateFrame;
        }

        private void updateFrame(object sender, EventArgs e)
        {
            if (cap)
            {
                frame = _capture.QueryFrame();//這裡不斷接收新的下一frame
                imageBox1.Image = frame.Flip(Emgu.CV.CvEnum.FLIP.HORIZONTAL);//水平翻轉

                if (frameDiff)
                {
                    grayFrame = frame.Convert<Gray, Byte>();//轉灰階
                    grayFrame = grayFrame.Flip(Emgu.CV.CvEnum.FLIP.HORIZONTAL);//水平翻轉

                    if (_newRef!=true)
                    {
                        refI = grayFrame;
                        _newRef = true;
                        grayBg = new Image<Gray, Byte>(refI.Width, refI.Height);
                    }

                    difI = grayFrame.AbsDiff(refI);
                    imageBox2.Image = difI;

                    if (frameBinary)
                    {
                        difI = difI.ThresholdBinary(new Gray(60), new Gray(255));
                        imageBox3.Image = difI;
                    }
                }
            }
        }

        private void openWebcam_Click(object sender, EventArgs e)
        {
            cap = true;
            _capture = new Capture();
        }

        private void btnBG_Click(object sender, EventArgs e)
        {
            frameDiff = true;             
        }

        private void btnBinary_Click(object sender, EventArgs e)
        {
            frameBinary = true;
        }
    }
}





動態的
就好比一直有手這個前景出現在視訊畫面中
跟後面背景有明顯差異