// Comment by Naoto Tani 2014/02/20

using UnityEngine;
using System.Collections;
using OpenCvSharp;
using OpenCvSharp.MachineLearning;
using System.Runtime.InteropServices;

public class FaceDetectScript : MonoBehaviour
{
    const int CAPTURE_WIDTH = 320;
	const int CAPTURE_HEIGHT = 240;
	
	const double Scale = 1.04;
	const double ScaleFactor = 1.139;
	const int MinNeighbors = 2;

    public Vector2 facepos;
	
	// 物体検出のための Haar 特徴に基づくカスケード分類器
    private CvHaarClassifierCascade cascade;

	// Webカメラからの画像取り込み準備
    private CvCapture capture;

    CvColor[] colors = new CvColor[]{
                new CvColor(0,0,255),
                new CvColor(0,128,255),
                new CvColor(0,255,255),
                new CvColor(0,255,0),
                new CvColor(255,128,0),
                new CvColor(255,255,0),
                new CvColor(255,0,0),
                new CvColor(255,0,255),
            };

    // Use this for initialization
    void Start()
    {
		// カスケードファイル読み込み
        cascade = CvHaarClassifierCascade.FromFile(@"./Assets/haarcascade_frontalface_alt.xml");
        // Webカメラを使うという宣言
		capture = Cv.CreateCameraCapture(0);
		// 横幅と縦幅の設定
        Cv.SetCaptureProperty(capture, CaptureProperty.FrameWidth, CAPTURE_WIDTH);
        Cv.SetCaptureProperty(capture, CaptureProperty.FrameHeight, CAPTURE_HEIGHT);
        // Webカメラからフレーム取得
		IplImage frame = Cv.QueryFrame(capture);
		// Unity上に縦幅と横幅のフレームサイズをコンソール出力
        Debug.Log("width:" + frame.Width + " height:" + frame.Height);
		// おなじみのウィンドウの名前設定
        Cv.NamedWindow("FaceDetect");

		// SVM = サポートベクタマシン 機械学習の１つ
		CvSVM svm = new CvSVM ();
		// 目標精度（第3引数）に向かって反復アルゴリズムを行う
	  	CvTermCriteria criteria = new CvTermCriteria (CriteriaType.Epsilon, 1000, double.Epsilon);
	  	// SVM用のパラメータ設定
		CvSVMParams param = new CvSVMParams (CvSVM.C_SVC, CvSVM.RBF, 10.0, 8.0, 1.0, 10.0, 0.5, 0.1, null, criteria);
    }

    // Update is called once per frame
    void Update()
    {
        IplImage frame = Cv.QueryFrame(capture);

        using (IplImage img = Cv.CloneImage(frame))
        using (IplImage smallImg = new IplImage(new CvSize(Cv.Round(img.Width / Scale), Cv.Round(img.Height / Scale)), BitDepth.U8, 1))
        {
            // イメージの準備
            using (IplImage gray = new IplImage(img.Size, BitDepth.U8, 1))
            {
                Cv.CvtColor(img, gray, ColorConversion.BgrToGray);
                Cv.Resize(gray, smallImg, Interpolation.Linear);
                Cv.EqualizeHist(smallImg, smallImg);
            }

            using (CvMemStorage storage = new CvMemStorage())
            {
                storage.Clear();

                // ここで顔検出！
                CvSeq<CvAvgComp> faces = Cv.HaarDetectObjects(smallImg, cascade, storage, ScaleFactor, MinNeighbors, 0, new CvSize(64, 64));

                // 顔が検出できたら円を描きます
                for (int i = 0; i < faces.Total; i++)
                {
                    CvRect r = faces[i].Value.Rect;
                    CvPoint center = new CvPoint
                    {
                        X = Cv.Round((r.X + r.Width * 0.5) * Scale),
                        Y = Cv.Round((r.Y + r.Height * 0.5) * Scale)
                    };
                    int radius = Cv.Round((r.Width + r.Height) * 0.25 * Scale);
                    img.Circle(center, radius, colors[i % 8], 3, LineType.AntiAlias, 0);
                }

                // 複数の顔が検出されたら, 最初に検出した顔を基準にする
                if (faces.Total > 0)
                {
                    CvRect r = faces[0].Value.Rect;
                    facepos = new Vector2((r.X + r.Width / 2.0f) / CAPTURE_WIDTH, (r.Y + r.Height / 2.0f) / CAPTURE_HEIGHT);
                }
            }

            // 画像をウィンドウに出力
            Cv.ShowImage("FaceDetect", img);
        }
    }

    // 終了処理
    void OnDestroy()
    {
        Cv.DestroyAllWindows();
        Cv.ReleaseCapture(capture);
        Cv.ReleaseHaarClassifierCascade(cascade);
    }
}
