/// <summary>
 /// Runs the motion detection algorithm
 /// </summary>
 /// <param name="NewImage">The "new" frame</param>
 /// <param name="OldImage">The "old" frame</param>
 /// <param name="Threshold">The threshold used to detect changes in the image</param>
 /// <param name="DetectionColor">Color to display changes in the images as</param>
 /// <returns>A bitmap indicating where changes between frames have occurred overlayed on top of the new image.</returns>
 public static Bitmap Process(Bitmap NewImage, Bitmap OldImage, int Threshold, Color DetectionColor)
 {
     NewImage.ThrowIfNull("NewImage");
     OldImage.ThrowIfNull("OldImage");
     DetectionColor.ThrowIfNull("DetectionColor");
     using (Bitmap NewImage1 = NewImage.BlackAndWhite())
     {
         using (Bitmap OldImage1 = OldImage.BlackAndWhite())
         {
             using (Bitmap NewImage2 = NewImage1.SNNBlur(5))
             {
                 using (Bitmap OldImage2 = OldImage1.SNNBlur(5))
                 {
                     using (Bitmap OutputImage = new Bitmap(NewImage2, NewImage2.Width, NewImage2.Height))
                     {
                         using (Bitmap Overlay = new Bitmap(NewImage, NewImage.Width, NewImage.Height))
                         {
                             BitmapData NewImage2Data      = NewImage2.LockImage();
                             int        NewImage2PixelSize = NewImage2Data.GetPixelSize();
                             BitmapData OldImage2Data      = OldImage2.LockImage();
                             int        OldImage2PixelSize = OldImage2Data.GetPixelSize();
                             BitmapData OverlayData        = Overlay.LockImage();
                             int        OverlayPixelSize   = OverlayData.GetPixelSize();
                             int        Width  = OutputImage.Width;
                             int        Height = OutputImage.Height;
                             Parallel.For(0, Width, x =>
                             {
                                 for (int y = 0; y < Height; ++y)
                                 {
                                     Color NewPixel = NewImage2Data.GetPixel(x, y, NewImage2PixelSize);
                                     Color OldPixel = OldImage2Data.GetPixel(x, y, OldImage2PixelSize);
                                     if (System.Math.Pow((double)(NewPixel.R - OldPixel.R), 2.0) > Threshold)
                                     {
                                         OverlayData.SetPixel(x, y, Color.FromArgb(100, 0, 100), OverlayPixelSize);
                                     }
                                     else
                                     {
                                         OverlayData.SetPixel(x, y, Color.FromArgb(200, 0, 200), OverlayPixelSize);
                                     }
                                 }
                             });
                             Overlay.UnlockImage(OverlayData);
                             NewImage2.UnlockImage(NewImage2Data);
                             OldImage2.UnlockImage(OldImage2Data);
                             using (Bitmap Overlay2 = Overlay.EdgeDetection(25, DetectionColor))
                             {
                                 BitmapData Overlay2Data      = Overlay2.LockImage();
                                 int        Overlay2PixelSize = Overlay2Data.GetPixelSize();
                                 Width  = OutputImage.Width;
                                 Height = OutputImage.Height;
                                 Parallel.For(0, Width, x =>
                                 {
                                     for (int y = 0; y < Height; ++y)
                                     {
                                         Color Pixel1 = Overlay2Data.GetPixel(x, y, Overlay2PixelSize);
                                         if (Pixel1.R != DetectionColor.R || Pixel1.G != DetectionColor.G || Pixel1.B != DetectionColor.B)
                                         {
                                             Overlay2Data.SetPixel(x, y, Color.FromArgb(200, 0, 200), Overlay2PixelSize);
                                         }
                                     }
                                 });
                                 Overlay2.UnlockImage(Overlay2Data);
                                 return(OutputImage.Watermark(Overlay2, 1.0f, 0, 0, Color.FromArgb(200, 0, 200)));
                             }
                         }
                     }
                 }
             }
         }
     }
 }
 public static Bitmap EdgeDetection(this Bitmap OriginalImage, float Threshold, Color EdgeColor, string FileName = "")
 {
     OriginalImage.ThrowIfNull("OriginalImage");
     EdgeColor.ThrowIfNull("EdgeColor");
     ImageFormat FormatUsing = FileName.GetImageFormat();
     Bitmap NewBitmap = new Bitmap(OriginalImage, OriginalImage.Width, OriginalImage.Height);
     BitmapData NewData = NewBitmap.LockImage();
     BitmapData OldData = OriginalImage.LockImage();
     int NewPixelSize = NewData.GetPixelSize();
     int OldPixelSize = OldData.GetPixelSize();
     int Width = NewBitmap.Width;
     int Height = NewBitmap.Height;
     Parallel.For(0, Width, x =>
     {
         for (int y = 0; y < Height; ++y)
         {
             Color CurrentColor = OldData.GetPixel(x, y, OldPixelSize);
             if (y < Height - 1 && x < Width - 1)
             {
                 Color TempColor = OldData.GetPixel(x + 1, y + 1, OldPixelSize);
                 if (Distance(CurrentColor.R, TempColor.R, CurrentColor.G, TempColor.G, CurrentColor.B, TempColor.B) > Threshold)
                     NewData.SetPixel(x, y, EdgeColor, NewPixelSize);
             }
             else if (y < Height - 1)
             {
                 Color TempColor = OldData.GetPixel(x, y + 1, OldPixelSize);
                 if (Distance(CurrentColor.R, TempColor.R, CurrentColor.G, TempColor.G, CurrentColor.B, TempColor.B) > Threshold)
                     NewData.SetPixel(x, y, EdgeColor, NewPixelSize);
             }
             else if (x < Width - 1)
             {
                 Color TempColor = OldData.GetPixel(x + 1, y, OldPixelSize);
                 if (Distance(CurrentColor.R, TempColor.R, CurrentColor.G, TempColor.G, CurrentColor.B, TempColor.B) > Threshold)
                     NewData.SetPixel(x, y, EdgeColor, NewPixelSize);
             }
         }
     });
     NewBitmap.UnlockImage(NewData);
     OriginalImage.UnlockImage(OldData);
     if (!string.IsNullOrEmpty(FileName))
         NewBitmap.Save(FileName, FormatUsing);
     return NewBitmap;
 }
 public static Bitmap DrawRoundedRectangle(this Bitmap Image, Color BoxColor, int XPosition, int YPosition,
     int Height, int Width, int CornerRadius, string FileName = "")
 {
     Image.ThrowIfNull("Image");
     BoxColor.ThrowIfNull("BoxColor");
     ImageFormat FormatUsing = FileName.GetImageFormat();
     Bitmap NewBitmap = new Bitmap(Image, Image.Width, Image.Height);
     using (Graphics NewGraphics = Graphics.FromImage(NewBitmap))
     {
         using (Pen BoxPen = new Pen(BoxColor))
         {
             using (GraphicsPath Path = new GraphicsPath())
             {
                 Path.AddLine(XPosition + CornerRadius, YPosition, XPosition + Width - (CornerRadius * 2), YPosition);
                 Path.AddArc(XPosition + Width - (CornerRadius * 2), YPosition, CornerRadius * 2, CornerRadius * 2, 270, 90);
                 Path.AddLine(XPosition + Width, YPosition + CornerRadius, XPosition + Width, YPosition + Height - (CornerRadius * 2));
                 Path.AddArc(XPosition + Width - (CornerRadius * 2), YPosition + Height - (CornerRadius * 2), CornerRadius * 2, CornerRadius * 2, 0, 90);
                 Path.AddLine(XPosition + Width - (CornerRadius * 2), YPosition + Height, XPosition + CornerRadius, YPosition + Height);
                 Path.AddArc(XPosition, YPosition + Height - (CornerRadius * 2), CornerRadius * 2, CornerRadius * 2, 90, 90);
                 Path.AddLine(XPosition, YPosition + Height - (CornerRadius * 2), XPosition, YPosition + CornerRadius);
                 Path.AddArc(XPosition, YPosition, CornerRadius * 2, CornerRadius * 2, 180, 90);
                 Path.CloseFigure();
                 NewGraphics.DrawPath(BoxPen, Path);
             }
         }
     }
     if (!string.IsNullOrEmpty(FileName))
         NewBitmap.Save(FileName, FormatUsing);
     return NewBitmap;
 }
 private static float GetHeight(Color Color)
 {
     Color.ThrowIfNull("Color");
     return (float)Color.R / 255.0f;
 }
 /// <summary>
 /// Sets a pixel at the x,y coords
 /// </summary>
 /// <param name="Data">Bitmap data</param>
 /// <param name="x">X coord</param>
 /// <param name="y">Y coord</param>
 /// <param name="PixelColor">Pixel color information</param>
 /// <param name="PixelSizeInBytes">Pixel size in bytes</param>
 public static unsafe void SetPixel(this BitmapData Data, int x, int y, Color PixelColor, int PixelSizeInBytes)
 {
     Data.ThrowIfNull("Data");
     PixelColor.ThrowIfNull("PixelColor");
     byte* DataPointer = (byte*)Data.Scan0;
     DataPointer = DataPointer + (y * Data.Stride) + (x * PixelSizeInBytes);
     if (PixelSizeInBytes == 3)
     {
         DataPointer[2] = PixelColor.R;
         DataPointer[1] = PixelColor.G;
         DataPointer[0] = PixelColor.B;
         return;
     }
     DataPointer[3] = PixelColor.A;
     DataPointer[2] = PixelColor.R;
     DataPointer[1] = PixelColor.G;
     DataPointer[0] = PixelColor.B;
 }
 /// <summary>
 /// Runs a simplistic motion detection algorithm
 /// </summary>
 /// <param name="NewImage">The "new" frame</param>
 /// <param name="OldImage">The "old" frame</param>
 /// <param name="Threshold">The threshold used to detect changes in the image</param>
 /// <param name="DetectionColor">Color to display changes in the images as</param>
 /// <returns>A bitmap indicating where changes between frames have occurred overlayed on top of the new image.</returns>
 public static Bitmap MotionDetection(this Bitmap NewImage, Bitmap OldImage, int Threshold, Color DetectionColor)
 {
     NewImage.ThrowIfNull("NewImage");
     OldImage.ThrowIfNull("OldImage");
     DetectionColor.ThrowIfNull("DetectionColor");
     using (Bitmap NewImage1 = NewImage.BlackAndWhite())
     {
         using (Bitmap OldImage1 = OldImage.BlackAndWhite())
         {
             using (Bitmap NewImage2 = NewImage1.SNNBlur(5))
             {
                 using (Bitmap OldImage2 = OldImage1.SNNBlur(5))
                 {
                     using (Bitmap OutputImage = new Bitmap(NewImage2, NewImage2.Width, NewImage2.Height))
                     {
                         using (Bitmap Overlay = new Bitmap(NewImage, NewImage.Width, NewImage.Height))
                         {
                             BitmapData NewImage2Data = NewImage2.LockImage();
                             int NewImage2PixelSize = NewImage2Data.GetPixelSize();
                             BitmapData OldImage2Data = OldImage2.LockImage();
                             int OldImage2PixelSize = OldImage2Data.GetPixelSize();
                             BitmapData OverlayData = Overlay.LockImage();
                             int OverlayPixelSize = OverlayData.GetPixelSize();
                             int Width = OutputImage.Width;
                             int Height = OutputImage.Height;
                             Parallel.For(0, Width, x =>
                             {
                                 for (int y = 0; y < Height; ++y)
                                 {
                                     Color NewPixel = NewImage2Data.GetPixel(x, y, NewImage2PixelSize);
                                     Color OldPixel = OldImage2Data.GetPixel(x, y, OldImage2PixelSize);
                                     if (System.Math.Pow((double)(NewPixel.R - OldPixel.R), 2.0) > Threshold)
                                     {
                                         OverlayData.SetPixel(x, y, Color.FromArgb(100, 0, 100), OverlayPixelSize);
                                     }
                                     else
                                     {
                                         OverlayData.SetPixel(x, y, Color.FromArgb(200, 0, 200), OverlayPixelSize);
                                     }
                                 }
                             });
                             Overlay.UnlockImage(OverlayData);
                             NewImage2.UnlockImage(NewImage2Data);
                             OldImage2.UnlockImage(OldImage2Data);
                             using (Bitmap Overlay2 = Overlay.EdgeDetection(25, DetectionColor))
                             {
                                 BitmapData Overlay2Data = Overlay2.LockImage();
                                 int Overlay2PixelSize = Overlay2Data.GetPixelSize();
                                 Width = OutputImage.Width;
                                 Height = OutputImage.Height;
                                 Parallel.For(0, Width, x =>
                                 {
                                     for (int y = 0; y < Height; ++y)
                                     {
                                         Color Pixel1 = Overlay2Data.GetPixel(x, y, Overlay2PixelSize);
                                         if (Pixel1.R != DetectionColor.R || Pixel1.G != DetectionColor.G || Pixel1.B != DetectionColor.B)
                                         {
                                             Overlay2Data.SetPixel(x, y, Color.FromArgb(200, 0, 200), Overlay2PixelSize);
                                         }
                                     }
                                 });
                                 Overlay2.UnlockImage(Overlay2Data);
                                 return OutputImage.Watermark(Overlay2, 1.0f, 0, 0, Color.FromArgb(200, 0, 200));
                             }
                         }
                     }
                 }
             }
         }
     }
 }