TakePictureForm.cs 13.2 KB
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using ImageFilters;
using MoyaSignup.Dataclasses;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MoyaSignup
{
    public partial class TakePictureForm : Form
    {
        Pen RetanglePen = new Pen(Color.Blue);
        int RetangleHeight = 424;
        int RetangleWidth = 310;

        int DefaultCropWidth = 263;
        int DefaultCropHeight = 360;

        int camHeight = 720;

        //bool freeze;
        //        bool pictureTaken = false;
        //bool cameraOpen = false;
        //private CameraFrameSource _frameSource;
        private static Bitmap latestFrame;
        public event EventHandler Changed;

        public bool PictureTaken = false;


        Image<Bgr, Byte> currentFrame;
        Image<Bgr, Byte> currentFullFrame;
        HaarCascade face;
        Image<Gray, byte> gray = null;

        Rectangle cropRectangle = new Rectangle();


        List<Capture> webCams;

        int pictureCountDown = 3;

        
        Dictionary<decimal, Capture> camScores = new Dictionary<decimal, Capture>();

        public TakePictureForm()
        {
            InitializeComponent();
        }

        Capture selectedCam = null;

        private void TakePictureForm_Load(object sender, EventArgs e)
        {
            startCapturing();
            cropRectangle.X = 189;
            cropRectangle.Y = 0;
            cropRectangle.Width = DefaultCropWidth;
            cropRectangle.Height = DefaultCropHeight;
        }

        private void startCapturing()
        {
            if (!DesignMode)
            {
                Debug.WriteLine("[takepictureform] Starting capture for cameras");
                webCams = new List<Emgu.CV.Capture>();
                try
                {
                    
                    face = new HaarCascade("haarcascade_frontalface_default.xml");
                }

                catch (Exception ex)
                {
                    pictureBox.Text = "Select A Camera";
                    MessageBox.Show(ex.Message);
                    Debug.WriteLine("[takepictureform] Failed to initialize camera settings");
                    return;
                }
                if (Program.CaptureDevices != null)
                {
                    foreach (DsDeviceContainer device in Program.CaptureDevices)
                    {
                        try
                        {
                            Capture capture = new Capture(device.Idx);
                            capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, 720);
                            capture.SetCaptureProperty(CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, 1280);
                            capture.QueryFrame();

                            Debug.WriteLine("[takepictureform] adding webcam " + device.Device.Name + "' to webcams");

                            webCams.Add(capture);
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine("[takepictureform] Could not start capturing for device '" + device.Device.Name + "'. Error: " + ex.Message);
                            continue;
                        }
                    }

                    Program.WebCams = webCams;

                    captureTimer.Enabled = true;
                    captureTimer.Start();
                    selectCamTimer.Enabled = true;
                    selectCamTimer.Start();
                }
                else
                {
                    Debug.WriteLine("[takepictureform] Failed to start capturing because videoCaptureDevices was null");
                }

            }
        }

        private void takePictureTimer_Tick(object sender, EventArgs e)
        {
            if (pictureCountDown == 0)
            {
                takePictureTimer.Enabled = false;
                takePictureTimer.Stop();

                captureTimer.Enabled = false;
                captureTimer.Stop();
                Debug.WriteLine("[takepictureform] Kuva otettu");
                pictureCountDown = 3;

                secondsLabel.Visible = false;
                //pictureTakenLabel.Text = "Kuva otettu";

                btnTakePic.Enabled = false;
                //cameraOpen = false;
                btnTakePic.Text = "Ota kuva";
                pictureTakenLabel.Visible = false;
                pictureTakenLabel.Text = "Kuva otetaan automaattisesti";
                //thrashOldCamera();
                //pbIncoming.Image = latestFrame;

                if (Program.Form1 != null)
                    Program.Form1.SetUsersPicture(latestFrame);

                btnTakePic.Enabled = true;
                PictureTaken = true;

                foreach (Capture cam in webCams)
                {
                    cam.Dispose();
                }

                if (Changed != null)
                    Changed(null, null);

                this.Visible = false;
                this.SendToBack();
            }
            else
            {
                Debug.WriteLine("[takepictureform] Kuvanottolaskuri käynnissä, " + pictureCountDown.ToString() + "s jäljellä.");
                pictureCountDown -= 1;
                secondsLabel.Text = pictureCountDown.ToString() + "s päästä";
            }
        }

        private void captureTimer_Tick(object sender, EventArgs e)
        {
            FrameGrabber(captureTimer, null);
        }

        private void FrameGrabber(object sender, EventArgs e)
        {
            //Debug.WriteLine("[takepictureform] Trying to find a face in the frames from the webcams");
            Dictionary<Image<Bgr, byte>, decimal> croppedImages = new Dictionary<Image<Bgr, byte>, decimal>();
            Dictionary<Image<Bgr, byte>, Rectangle> cropRectangles = new Dictionary<Image<Bgr, byte>, Rectangle>();
            List<Image<Bgr, byte>> fullFrames = new List<Image<Bgr, byte>>();

            lock(camScores)
            {
                camScores = new Dictionary<decimal, Capture>();
            }

            for (int i = 0; i < webCams.Count; i++)
            {
                decimal score = 0;
                Capture grabber = webCams[i];

                try
                {
                    currentFullFrame = grabber.QueryFrame();
                    currentFrame = currentFullFrame.Resize(640, 360, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC); //640x360
                }catch(Exception ex)
                {
                    Debug.WriteLine("[takepictureform] Could not get frame from camera. Ex: " + ex.Message);
                    return;
                }
                gray = currentFrame.Convert<Gray, Byte>();

                MCvAvgComp[][] facesDetected = null;

                if (gray != null)
                {
                    //Face Detector
                    facesDetected = gray.DetectHaarCascade(
                  face,
                  1.1,
                  10,
                  Emgu.CV.CvEnum.HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
                  new Size(50, 50));
                }
                //Action for each element detected
                if (facesDetected != null && facesDetected[0].Length > 0)
                {
                    MCvAvgComp f = facesDetected[0][0];

                    //decimal width = ((decimal)f.rect.Width) * 3.1M;
                    decimal width = ((decimal)f.rect.Width) * 2.8M;
                    decimal height = width * 1.36774193548M;

                    int x = (f.rect.X + f.rect.Width / 2) * 2;
                    x = (int)(x - width / 2);

                    int y = (f.rect.Y + f.rect.Height / 2) * 2;
                    y = (int)(y - height / 2);

                    int x1 = (int)(x + width / 2);
                    if (x1 >= 1280)
                        x1 = 1279;
                    int y1 = (int)(y + height / 2);
                    if (y1 >= 720)
                        y1 = 719;

                    decimal distance = (decimal)Math.Sqrt(Math.Pow((double)(x1 - 640), 2) + Math.Pow((double)(y1 - 360), 2));

                    score += distance;

                    // allow offset
                    if (y < 0 && y > -50)
                        y = 0;

                    if (y + height > camHeight && y + height < camHeight + 150)
                        y = camHeight - (int)height - 1;

                    if (x + width <= 1280 && y + height <= camHeight && x >= 0 && y >= 0)
                    {
                        Rectangle cropRect = new Rectangle(x, y, (int)width, (int)height);
                        
                        Image<Bgr, byte> croppedImage = currentFullFrame.Copy(cropRect);
                        
                        croppedImage = croppedImage.Resize(310, 424, INTER.CV_INTER_CUBIC);

                        //Debug.WriteLine("[takepictureform] Adding score '" + score + "' to croppedImageDictionary.");

                        croppedImages.Add(croppedImage, score);
                        cropRectangles.Add(croppedImage, cropRect);
                        lock (camScores)
                        {
                            camScores.Add(score, webCams[i]);
                        }

                        currentFullFrame.Draw(cropRect, new Bgr(Color.Green), 2);
                        fullFrames.Add(currentFullFrame.Resize(640, 360, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC));
                    }
                }
            }

            decimal bestScore = 300;
            Image<Bgr, byte> bestImage = null;
            int bestImageIdx = 0;
            int j = 0;

            foreach (Image<Bgr, byte> image in croppedImages.Keys)
            {
                if (croppedImages[image] < bestScore)
                {
                    if (selectedCam != null)
                    {
                        lock (camScores)
                        {
                            if (camScores[croppedImages[image]] == selectedCam)
                            {
                                bestImage = image;
                                bestImageIdx = j;
                                bestScore = croppedImages[image];
                                break;
                            }
                        }
                    }
                    else
                    {
                        bestImage = image;
                        bestImageIdx = j;
                        bestScore = croppedImages[image];
                    }
                }
                j++;
            }
            

            if (bestImage != null)
            {
                //Debug.WriteLine("[takepictureform] Found best image from web cam captures.");

                if(selectedCam == null)
                {
                    decimal score = croppedImages[bestImage];
                    lock(camScores)
                    {
                        selectedCam = camScores[score];
                    }
                }
                Rectangle rect = cropRectangles[bestImage];
                int x = (int) rect.X / 2;
                int y = (int)rect.Y / 2;
                int width = (int)rect.Width / 2;
                int height = (int)rect.Height / 2;
                cropRectangle = new Rectangle( x, y, width, height);
                latestFrame = BitmapFilter.Crop(bestImage.Bitmap, RetangleWidth, RetangleHeight, ImageFilters.BitmapFilter.AnchorPosition.Free);
                if (fullFrames.Count > bestImageIdx)
                    pictureBox.Image = fullFrames[bestImageIdx].Bitmap;
                //pbIncoming.Image = latestFrame;
            }
            else
            {
                //Debug.WriteLine("[takepictureform] Could not find best picture from web cam feeds");
                Image<Bgr, byte> fullFrame = null;
                if (selectedCam != null)
                    fullFrame = selectedCam.QueryFrame().Resize(640, 360, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);
                else
                    fullFrame = webCams[0].QueryFrame().Resize(640, 360, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);
                latestFrame = BitmapFilter.Crop(fullFrame.Bitmap, cropRectangle.Width, cropRectangle.Height, ImageFilters.BitmapFilter.AnchorPosition.Free);
                //int x = (640 - DefaultCropWidth) / 2;
                //int y = (360 - DefaultCropHeight) / 2;

                //cropRectangle = new Rectangle(x, y, DefaultCropWidth, DefaultCropHeight);
                
                fullFrame.Draw(cropRectangle, new Bgr(Color.Red), 2);
                //pbIncoming.Image = latestFrame;
                pictureBox.Image = fullFrame.Bitmap;
            }

        }

        private void btnTakePic_Click(object sender, EventArgs e)
        {
            takePictureTimer.Enabled = true;
            takePictureTimer.Start();

            pictureTakenLabel.Visible = true;
            secondsLabel.Visible = true;
        }

        private void selectCamTimer_Tick(object sender, EventArgs e)
        {
            lock (camScores)
            {
                if (camScores != null && camScores.Keys.Count > 0)
                {
                    decimal score = camScores.Keys.Min();
                    selectedCam = camScores[score];
                }
            }
        }
    }
}