using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;

using System.Drawing;
using System.Timers;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using Emgu.CV.UI;


using System.Drawing.Imaging;
using System.IO;



namespace PC_Gui
{

    /// <summary>
    /// Třída pro uchovávání parametrů DisplayReader
    /// </summary>
    public class DisplayReaderParams
    {
        /// <summary>
        /// Velikost displeje klienta
        /// </summary>
        private Size deviceScreen = new Size(478, 250);


        /// <summary>
        /// Velikost displeje klienta
        /// </summary>
        public Size DeviceScreen
        {
            get { return deviceScreen; }
            set { deviceScreen = value; }
        }


        /// <summary>
        /// Maximální doba mezi snímky
        /// </summary>
        private int maxSampling = 100;


        /// <summary>
        /// Maximální doba mezi snímky
        /// </summary>
        public int MaxSampling
        {
            get { return maxSampling; }
            set { maxSampling = value; }
        }

        /// <summary>
        /// Minimální doba mezi snímky
        /// </summary>
        private int minSampling = 20;


        /// <summary>
        /// Minimální čas mezi snímky
        /// </summary>
        public int MinSampling
        {
            get { return minSampling; }
            set { minSampling = value; }
        }

        /// <summary>
        /// Čas mezi nucenými voláními Garbage Collectoru
        /// </summary>
        private int collectorTimeOut = 5000;


        /// <summary>
        /// Čas mezi nucenými voláními Garbage Collectoru
        /// </summary>
        public int CollectorTimeOut
        {
            get { return collectorTimeOut; }
            set { collectorTimeOut = value; }
        }


        /// <summary>
        /// Počet měření pro výpočet výkonu
        /// </summary>
        private int performanceCount = 20;


        /// <summary>
        /// Počet měření pro výpočet výkonu
        /// </summary>
        public int PerformanceCount 
        {
            get { return performanceCount; }
            set { performanceCount = value; }
        }

        /// <summary>
        /// Zpoždění vzorkování
        /// </summary>
        private int samplingDelay = 5;


        /// <summary>
        /// Zpoždění vzorkování
        /// </summary>
        public int SamplingDelay
        {
            get { return samplingDelay; }
            set { samplingDelay = value; }
        }


        
        public DisplayReaderParams()
        {

        }
    }

    /// <summary>
    /// Delegát změny stavu zásobníku s daty
    /// </summary>
    /// <param name="max">délka zásobníku</param>
    /// <param name="current">aktuální množství dat</param>
    public delegate void BufferStateChangeDelegate(int max, int current);


    /// <summary>
    /// Delegát změny stavu odesílání
    /// </summary>
    public delegate void SendActionDelegate();


    /// <summary>
    /// Třída pro snímání dat obrazovky
    /// </summary>
    public class DisplayReader
    {

        /// <summary>
        /// Časovač třídy pro pravidelné spouštění snímání
        /// </summary>
        System.Timers.Timer Scheduler = new System.Timers.Timer();

        /// <summary>
        /// Poslední pozice kurzoru
        /// </summary>
        private Point lastCursorPosition;

        /// <summary>
        /// Pole měření délky zpracování dat
        /// </summary>
        private List<int> performanceArray;

        /// <summary>
        /// Poslední sejmutá obrazovka
        /// </summary>
        private Image<Rgb, Byte> lastFrame;

        /// <summary>
        /// Velikost obrazovky
        /// </summary>
        Size PC_SCREEN = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

        /// <summary>
        /// Parametry snímání
        /// </summary>
        private DisplayReaderParams displayParams;

        /// <summary>
        /// Aktuální stav třídy
        /// </summary>
        private bool isStarted = false;

        /// <summary>
        /// Stream pro odesílání dat
        /// </summary>
        private Stream clientStream;

        /// <summary>
        /// Výstupní zásobník
        /// </summary>
        private List<byte[]> outputBuffer = new List<byte[]>();

        /// <summary>
        /// Odeslání dat povoleno
        /// </summary>
        private bool nextSendAllowed;

        /// <summary>
        /// true pokud byla odeslána informace o offsetu
        /// </summary>
        private bool paramsSent = false;

        /// <summary>
        /// Maximální délka zásobníku
        /// </summary>
        private int maxBufferLength = 5;

        /// <summary>
        /// Kodér dat
        /// </summary>
        private System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();

        /// <summary>
        /// Metoda je volána při změně stavu obrazového bufferu
        /// </summary>
        public BufferStateChangeDelegate OnOutputBufferChange;

        /// <summary>
        /// Metoda je volána při zahájení vysílání
        /// </summary>
        public SendActionDelegate OnSendBegin;

        /// <summary>
        /// Metoda je volána po ukončení vysílání
        /// </summary>
        public SendActionDelegate OnSendFinish;

        #region Veřejné vlastnosti k výše popsaným proměnným
        public Stream ClientStream
        {
            get { return clientStream; }
            set { clientStream = value; }
        }
        

        public bool IsStarted
        {
            get { return isStarted; }
        }
        

        public DisplayReaderParams DisplayParams
        {
            get { return displayParams; }
            set
            {
                displayParams = value;
                
                if (isStarted)
                {
                    Stop();
                    Start();
                }

                
            }
        }

        #endregion


        /// <summary>
        /// Časovač Garbage Collectoru
        /// </summary>
        private System.Timers.Timer collector = new System.Timers.Timer();

        /// <summary>
        /// Konstruktor třídy
        /// </summary>
        /// <param name="stream">stream pro odeslání dat</param>
        public DisplayReader(Stream stream)
        {
            displayParams = new DisplayReaderParams();
            clientStream = stream;
        }

        /// <summary>
        /// Konstruktor třídy
        /// </summary>
        /// <param name="stream">objekt pro odeslání dat</param>
        /// <param name="P">konfigurační parametry</param>
 
        public DisplayReader(Stream stream, DisplayReaderParams P)
        {
            displayParams = P;
            clientStream = stream;
        }

        /// <summary>
        /// Spuštění čtení
        /// </summary>
        public void Start()
        {
            try
            {
                
                //Nastavení intervalu
                Scheduler.Interval = displayParams.MinSampling;   
                //Inicializace pole měření             
                performanceArray = new List<int>(displayParams.PerformanceCount);
                //Přidání metody pro časovač
                Scheduler.Elapsed += new ElapsedEventHandler(FrameAcquisitionTimer_Elapsed);
                //Spuštění časovače
                Scheduler.Start();
                //Získání prázdného snímku
                lastFrame = GetNewFrame(true);
                //Vynukování flagu
                paramsSent = false;
                //Inicializace časovače collectoru
                collector.Interval = displayParams.CollectorTimeOut;
                //Přidání metody časovače
                collector.Elapsed += new ElapsedEventHandler(collector_Elapsed);
                //Spuštění časovače
                collector.Start();
                //Povolení odesílání
                nextSendAllowed = true;
                
                //outputThread = new Thread(new ParameterizedThreadStart(outputThreadCallback));
                
                
            }
            catch (Exception)
            {
                //Pokud vznikla chyba, ukonči odesílání
                Stop();
                isStarted = false;
                Console.WriteLine("Starting Display reader failed.");
                return;
            }
            isStarted = true;
        }

        /// <summary>
        /// Odeslání sady dat ze zásobníku
        /// </summary>
        public void sendNextFrame()
        {
            
            //Pokud neexistuji data, konci
            if (outputBuffer.Count == 0)
            {
                nextSendAllowed = true;
                return;
            }
            byte[] data = null;

            //Pokud existuje odberatel udalosti OnBegin, volej patricne metody
            if (OnSendBegin != null)
                OnSendBegin();

            //Uzamknout objekt bufferu
            lock (outputBuffer)
	        {
                //Odebrat data na vrcholu
                data = outputBuffer[0];
                outputBuffer.RemoveAt(0);
                
                //Odeslat udalost o zmene dat v bufferu
                if (OnOutputBufferChange!=null)
                    OnOutputBufferChange(maxBufferLength, outputBuffer.Count);
	        }

            try
            {
                //Odeslani dat do streamu
                clientStream.Write(data, 0, data.Length);
            }
            catch (Exception)
            {

                Console.WriteLine("Data could not be sent...");
            }

            //Odeslani udalosti ukonceni odesilani
            if (OnSendFinish != null)
                OnSendFinish();
        }


        /// <summary>
        /// Zaktualizuje pole poslednich casu zpracovani obrazu pro mereni vykonu
        /// </summary>
        /// <param name="value"></param>
        private void updatePerformanceArray(int value)
        {
            //Pokud je pole plne
            if (performanceArray.Count >= displayParams.PerformanceCount)
            {
                //Odstran nejstarsi hodnotu
                while (performanceArray.Count >= displayParams.PerformanceCount)
                    performanceArray.RemoveAt(0);
            }
            //Pridej nejnovejsi hodnotu
            performanceArray.Add(value);
        }

        /// <summary>
        /// Vraci FPS pro ucely testovani vykonnosti snimani a zpracovani obrazu
        /// </summary>
        /// <returns>FPS</returns>
        public double GetFPS()
        {
            double fps = 0;
            //Vypocet prumeru z poslednich n hodnot
            for (int i = 0; i < performanceArray.Count; i++)
            {
                fps += performanceArray[i];
            }
            return 1000/(fps / performanceArray.Count);
        }

        /// <summary>
        /// Destruktor tridy
        /// </summary>
        ~DisplayReader()
        {
            //Zastaveni procesu ve tride
            Stop();
        }


        /// <summary>
        /// Provede ukonceni vsech akci v instanci tridy
        /// </summary>
        public void Stop()
        {
            try
            {
                //Ukonceni casovace odesilani
                Scheduler.Stop();
                //Ukonceni casovace garbage collectoru
                collector.Stop();
                //Odregistrovani prislusnych akci
                Scheduler.Elapsed -= FrameAcquisitionTimer_Elapsed;
                collector.Elapsed -= collector_Elapsed;

            }
            catch (Exception)
            {
                Console.WriteLine("Stopping reader failed.");
            }
            //Vystavit flag o zastaveni
            isStarted = false;
            //zrusit info o odeslanych offsetech
            paramsSent = false;

            //Uzamknuti bufferu
            lock (outputBuffer)
            {
                //Vymazani a odeslani udalosti o zmene.
                outputBuffer.Clear();
                if (OnOutputBufferChange != null)
                    OnOutputBufferChange(maxBufferLength, outputBuffer.Count);
            }

        }


        /// <summary>
        /// Akce prirazena casovaci GC
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void collector_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                //Vynucene uklizeni bince
                GC.Collect();
            }
            catch (Exception)
            {

                Console.WriteLine("GC: Garbage collection failed.");
            }
            
        }

        /// <summary>
        /// Vymazani vystupniho bufferu
        /// </summary>
        public void ClearOutputBuffer()
        {
            //Nejprve je nutne uzamknout
            lock (outputBuffer)
            {
                //Pak az mazat
                outputBuffer.Clear();
            }
        }

        /// <summary>
        /// Vyprsel casovac pro vytvoreni noveho snimku obrazovky
        /// </summary>
        /// <param name="sender">volajici objekt</param>
        /// <param name="e">parametry</param>
        private void FrameAcquisitionTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            //Stopky pro mereni delky zpracovani
            Stopwatch sw = new Stopwatch();
            sw.Start();

            //Pokud je zaplnen vystupni buffer, nic se nedela
            if (outputBuffer.Count > maxBufferLength)
            {

                Console.WriteLine("Output buffer full: " + outputBuffer.Count.ToString() + "/" + maxBufferLength.ToString());
                //Odeslani informace o zmene stavu bufferu
                if (OnOutputBufferChange != null)
                    OnOutputBufferChange(maxBufferLength, outputBuffer.Count);
                return;

            }
            //Zastavi se casovac pro zpracovani obrazu
            Scheduler.Stop();

            try
            {

            //Ziskani obrazovky ze systemu
            Image<Rgb, Byte> img = GetNewFrame(false);
            
            //Vypocteni rozdiloveho snimku
            Image<Rgb, Byte> sub = img.AbsDiff(lastFrame);

            //Prevedeni rozdilaku na sedotonovy
            Image<Gray, Byte> subGray = (sub.Convert<Gray,Byte>())/*new Image<Gray,Byte>(img.Width, img.Height,new Gray(0.5))*/;

            //Secteni pixelu
            Gray val = subGray.GetSum();

            //Pokud je hodnota > 2
            if (val.Intensity > 2)
            {
                //resetuji se limity pro spusteni casovace
                SchedulerReset();
                Console.WriteLine("New frame created: " + val.ToString());

                //Ulozeni snimku
                lastFrame = img;

                //Provedeni dilatace nad sedotonovym rozdilovym snimkem
                Image<Gray, Byte> dil = subGray.Dilate(20);            
                
                List<Image<Gray, Byte>> segments = SegmentScreen(dil);

                //Vyhledani obdelniku s rozdily
                List<Rectangle> rectangles = FindObjects(segments);

                //Pokud jeste nebyly odeslany parametry offsetu
                if (!paramsSent)
                {
                    paramsSent = true;
                    //Sestaveni offsetoveho paketu
                    int x_offset = (this.DisplayParams.DeviceScreen.Width - lastFrame.Width) / 2;
                    int y_offset = (this.DisplayParams.DeviceScreen.Height - lastFrame.Height) / 2;
                    string dataToSend = "O" + x_offset.ToString("000") + ";" + y_offset.ToString("000") + "\r\n";
                    //Odeslani paketu
                    AddDataToSend(dataToSend);
                }
                //Zpracovani rozdilu obrazovky pro odeslani
                PrepareFramesToSend(clientStream, rectangles, lastFrame);
   
            }
            
            //Pokud se zmenila pozice kurzoru
            if (!lastCursorPosition.Equals(Cursor.Position))
            {
                
                //Vyresetuj limity casovace
                SchedulerReset();
                Console.WriteLine("Mouse moved to position: " + Cursor.Position.ToString());
                //Uloz pozici kurzoru
                lastCursorPosition = Cursor.Position;
                //Sestav paket s novou pozici kurzoru
                int x_pos =(int) (lastCursorPosition.X * (float)img.Width/this.PC_SCREEN.Width);
                int y_pos = (int)(lastCursorPosition.Y * (float)img.Height / this.PC_SCREEN.Height);
                string mousePacket = "M" + x_pos.ToString("000") + ";" + y_pos.ToString("000") + "\r\n";
                //Odesli data
                AddDataToSend(mousePacket,true);
            }

            Console.WriteLine("Time proceeded: " + sw.ElapsedMilliseconds.ToString() + "ms.");
            //Znovu spusteni casovace pro snimani obrazu
            Scheduler.Start();
            //Aktualizace pole pro statistiku delky zpracovani
            updatePerformanceArray((int)sw.ElapsedMilliseconds);
            }
            catch (Exception)
            {
                //Pokud doslo k chybe, resetuj casovac.
                Scheduler.Start();
            }
        }


        /// <summary>
        /// Pridani dat do seznamu k odeslani
        /// </summary>
        /// <param name="dataToSend">prislusna data</param>
        private void AddDataToSend(string dataToSend)
        {
            
            AddDataToSend(dataToSend,false);
        }

        /// <summary>
        /// Pridani dat do seznamu k odeslani
        /// </summary>
        /// <param name="dataToSend">prislusna data</param>
        /// <param name="priority">flag priority</param>
        private void AddDataToSend(string dataToSend, bool priority)
        {
            //Ziskani bajtu ze stringu
            byte[] dataBytes = encoding.GetBytes(dataToSend);
            AddDataToSend(dataBytes, priority);
        }

        /// <summary>
        /// Pridani dat do seznamu k odeslani
        /// </summary>
        /// <param name="dataToSend">bajtovy retezec k odeslani</param>
        private void AddDataToSend(byte[] dataToSend)
        {
            AddDataToSend(dataToSend,false);
        }

        /// <summary>
        /// Pridani dat do seznamu k odeslani
        /// </summary>
        /// <param name="dataToSend">bajtovy retezec k odeslni</param>
        /// <param name="priority">flag priority</param>
        private void AddDataToSend(byte[] dataToSend, bool priority)
        {
            //Uzamknuti vystupniho bufferu
            lock (outputBuffer)
            {
                //POkud jde o prioritni data
                if (priority)
                {   
                    //Vymaz ostatni data tohoto typu ze seznamu
                    foreach (byte[] item in outputBuffer)
                    {   
                        //Rozliseni pomoci prvniho bajtu
                        if (item[0] == dataToSend[0])
                        {
                            outputBuffer.Remove(item);
                        }
                    }
                    //Vlozeni prioritnich dat na prvni misto
                    outputBuffer.Insert(0, dataToSend);
                }
                else
                {
                    //Jinak se zaradi na konec
                    outputBuffer.Add(dataToSend);
                }
                    
                //Pokud je povoleno odeslni dat
                if (nextSendAllowed == true)
                {   
                    //Odesli dalsi paket
                    sendNextFrame();
                    //Zakaz nove odesilani
                    nextSendAllowed = false;
                    //Odesli info o zmene bufferu
                    if (OnOutputBufferChange != null)
                        OnOutputBufferChange(maxBufferLength, outputBuffer.Count);
                }
            }
        }

        private void PrepareFramesToSend(Stream clientStream, List<Rectangle> rectangles, Image<Rgb, byte> sub)
        {
            byte[, , ] data = sub.Data;
            
            //Pro kazdy obdelnik obsahujici zmenu
            for (int i = 0; i < rectangles.Count; i++)
            {
                int xmin = rectangles[i].X;
                int xmax = rectangles[i].Width+rectangles[i].X;
                int ymin = rectangles[i].Y;
                int ymax = rectangles[i].Height + rectangles[i].Y;
                int d1 = 0;
                int cntr = 0;
                //Pokud se zmena tyka cele obrazovky
                if (xmin == 1 && ymin == 1 && xmax == sub.Width && ymax == sub.Height)
                {
                    //Vymaz ostatni data
                    ClearOutputBuffer();
                    lastCursorPosition.X = -1;
                    Console.WriteLine("Buffer was cleared.");
                }
                //Sestaveni hlavicky obrazoveho paketu
                string header = "F" + rectangles[i].X.ToString("000") + "," + rectangles[i].Y.ToString("000") + "," + rectangles[i].Width.ToString("000") + "," + rectangles[i].Height.ToString("000") + "\n\0";
                System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
                byte[] headerByte = encoding.GetBytes(header);
                //Vytvoreni bufferu pro data i s hlavickou
                byte[] writerData = new byte[rectangles[i].Width * rectangles[i].Height*2+headerByte.Length+10];
                //Kopirovani hlavicky do vystupniho bufferu
                for (int k = 0; k < headerByte.Length; k++)
                {
                    writerData[cntr++] = headerByte[k];
                }

                //Zpracovani kazdeho pixelu a zkopirovani dat do vystupniho bufferu
                for (int y = ymin; y < ymax; y++)
                {
                    for (int x = xmin; x < xmax; x++)
                    {
                        //d1 = (0xF00&(data[y,x,0] << 4))|(0xF0&data[y,x,1])|(0xF&(data[y,x,2]>>4))&0xFFF;  
                        //12-ti bitový formát
                        d1 = ((0xF00 & ((data[y, x, 2]) << 4)) | (0xF0 & ((data[y, x, 1])<<0)) | (0x0F & ((data[y, x, 0]) >>4))) & 0x0FFF;
                        //d1 = ((0x7C00 & ((data[y, x, 0]) << 7)) | (0x3E0 & ((data[y, x, 1]) << 2)) | (0x1F & ((data[y, x, 2]) >> 3))) & 0x7FFF;
                        writerData[cntr++] = (byte)(d1 & 0xFF);
                        //writerData[cntr++] = (byte)((d1 >> 8)&0x7F) ;   
                        writerData[cntr++] = (byte)((d1 >> 8) & 0x0F); 
                    }
                    
                }
                
                //Priprava dat k odeslani
                AddDataToSend(writerData);
                //Pokud je mozne odeslat data, provede se odeslani
                if (nextSendAllowed == true)
                {
                    sendNextFrame();
                    nextSendAllowed = false;

                }
                
                Console.WriteLine("Data sent: " + (writerData.Length / 1024).ToString() + "kB.");
            }
        }

        /// <summary>
        /// Vyhledani objektu ve vysegmentovanem obraze
        /// </summary>
        /// <param name="segments">seznam segmentu</param>
        /// <returns>Seznam obdelniku s temito segmenty</returns>
        private List<Rectangle> FindObjects(List<Image<Gray, byte>> segments)
        {
            List<Rectangle> rectangles = new List<Rectangle>(20);
            //Pro kazdy ze segmentu nalezni obdelnik
            for (int i = 0; i < segments.Count; i++)
			{
                rectangles.Add(FindObject(segments[i]));
			}
            return rectangles;
        }


        /// <summary>
        /// Vyhleda obdelnik prislusejici segmentu v obraze
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        private Rectangle FindObject(Image<Gray, byte> image)
        {
            
            //Vyhledani hranic jednotlivych segmentu -> jsou zjisteny souradnice obdelniku, kteremu nalezi
            int xmax = image.Width;
            int ymax = image.Height;
            int left = xmax-1, right = 0, top = ymax-1, bottom = 0;
            image.ROI = new Rectangle(new Point(1, 1), new Size(image.Width - 1, image.Height - 1));

            byte[,,] data = image.Data;
            for (int x = 1; x < xmax-1; x++)
            {
                for (int y = 1; y < ymax-1; y++)
                {
                    if (data[y,x,0] != 0)
                    {
                        if (x < left)
                            left = x;
                        if (x > right)
                            right = x;
                        if (y < top)
                            top = y;
                        if (y > bottom)
                            bottom = y;
                    }
                }
            }
            Rectangle rect = new Rectangle(new Point(left, top), new Size(right - left, bottom - top));
                return rect;
        }

        /// <summary>
        /// Metoda provede segmentaci dilatovaneho obrazu za ucelem vyhledani rozdilnych objektu
        /// </summary>
        /// <param name="dil">dilatovany obraz</param>
        /// <returns>seznam segmentu</returns>
        private List<Image<Gray, byte>> SegmentScreen(Image<Gray, Byte> dil)
        {
            List<Image<Gray, byte>> segments = new List<Image<Gray, byte>>(20);
            //Vyprahovani obrazu - inverzne
            dil = dil.ThresholdBinaryInv(new Gray(1), new Gray(255));
            MCvScalar s = new MCvScalar(0);
            MCvScalar si = new MCvScalar(100);
            Point p = new Point();

            int xmax = dil.Width;
            int ymax = dil.Height;
            byte[,,] data = dil.Data;
            //Prochazeni obrazu po bodech, na kazdy nenulovy bod je volan flood fill
            for (int x = 0; x < xmax; x++)
            {
                for (int y = 0; y < ymax; y++)
                {

                    if (data[y, x, 0] == 0)
                    {

                        p = new Point(x, y);
                        MCvConnectedComp comp = new MCvConnectedComp();

                        Image<Gray, Byte> tmp = new Image<Gray, byte>(new Size(xmax + 2, ymax + 2));
                        //Je ziskana maska nalezejici vyplavenemu objektu
                        CvInvoke.cvFloodFill(dil.Ptr, p, si, s, s, out comp, (int)/*(Emgu.CV.CvEnum.FLOODFILL_FLAG.MASK_ONLY|Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT)*/0, tmp.Ptr);
                        //Segment je pridan do seznamu
                        segments.Add(tmp*255);

                        //continue;
                    }

                }

            }
            return segments;
        }

        /// <summary>
        /// Nastavi interval obrazoveho casovace na minimum
        /// </summary>
        private void SchedulerReset()
        {
            Scheduler.Interval = displayParams.MinSampling;
        }


        /// <summary>
        /// Ziskani obrazu ze systemu
        /// </summary>
        /// <param name="empty">pokud ma metoda vracet prazdny obraz, true</param>
        /// <returns>Aktualni obrazovka</returns>
        private Image<Rgb, Byte> GetNewFrame(bool empty)
        {
            //Vytvoreni prazdne bitmapy
            Bitmap b = new Bitmap(PC_SCREEN.Width, PC_SCREEN.Height);
            //Vytvoreni objektu Graphics (platno) z bitmapy
            Graphics gr = Graphics.FromImage(b);
            if (!empty)
            {
                //Pokud neni pozadavek na prazdny screen, zkopiruje se z obrazovky
                gr.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);    
            }
            
            //Prevedeni platna na objekt obrazku
            Image<Rgb, Byte> img = new Image<Rgb, byte>(b);

            //Uprava rozliseni obrazu
            double x_scale = (double)displayParams.DeviceScreen.Width / PC_SCREEN.Width;
            double y_scale = (double)displayParams.DeviceScreen.Height / PC_SCREEN.Height;
            img = img.Resize(x_scale < y_scale ? x_scale : y_scale, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR);
            //vraci obrazek
            return img;

        }

    }
}
