Created
June 21, 2019 08:31
-
-
Save akosnikhazy/b7c75e3d86d826b5ecf35f7d1386ab40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* For this to work you need a form named "Form1" | |
* with a PictureBox named "camera". | |
* | |
* Next to the exe you need a "images" folder | |
* | |
* You need to install MjpegProcessor. It should work with | |
* other streams too, when I wrote this, happened that I | |
* had a mjpeg stream on my hand. | |
*/ | |
using MjpegProcessor; | |
using System; | |
using System.Collections.Generic; | |
using System.Drawing; | |
using System.Drawing.Imaging; | |
using System.Windows.Forms; | |
namespace motion_detector | |
{ | |
public partial class MainWindow : Form | |
{ | |
//shouldn't touch these | |
MjpegDecoder m_mjpeg; | |
Image lastImage; //image just seen | |
Image beforeLastImage; //image before that | |
Image lastImageChanged; //we modify both images smallSize * smallSize pixels, and greyscale | |
Image beforeLastImageChanged; | |
int steps = 0; //images saved for comparing | |
int hammeringDistance; //the difference between two images colors | |
int hammeringDistanceInTime = 0; //we add up the hammering distances "steps" times | |
List<int> lastImageMeanBits; //list of color mean values | |
List<int> beforeLastImageMeanBits; | |
//might touch these with care | |
int smallSize = 64; //the size of the modified image, darker cameras might do better with bigger value | |
int maxSteps = 10; //max images saved | |
Bitmap[] imageSteps = new Bitmap[10];//We save maxSteps number of images. If there is a movement we save them to file. Should be te size of max steps. | |
//settings | |
int itIsConsideredMovement = 15; //the value we consider movement on the images. The noisier the camera the higer this should be | |
string feedURI = "http://your-camera-ip/mjpg/video.mjpg"; | |
public MainWindow() | |
{ | |
InitializeComponent(); | |
} | |
private void Form1_Load(object sender, EventArgs e) | |
{ | |
m_mjpeg = new MjpegDecoder(); | |
m_mjpeg.ParseStream(new Uri(feedURI)); | |
m_mjpeg.FrameReady += mjpeg_FrameReady; | |
} | |
private void mjpeg_FrameReady(object sender, FrameReadyEventArgs e) | |
{ | |
hammeringDistance = 0; | |
//this needed if you have GUI with PictureBox. Good for testing. | |
camera.Image = e.Bitmap; | |
//camera.Image = Crop(e.Bitmap); | |
camera.Width = e.Bitmap.Width; | |
camera.Height = e.Bitmap.Height; | |
if (lastImage == null) | |
{//we don't care about the first image bceause there is no way to compare anything to it but save it | |
lastImage = e.Bitmap; | |
} | |
else | |
{//now we are talking! We have two images so we start to capture and compare them | |
beforeLastImage = lastImage; | |
lastImage = e.Bitmap; | |
//make last and one before that small, greyscale and cropped (lot of camera feeds has a clock on it that can be seen as movement, so we crop it) | |
//modify if cropping is not needed. Also cropping could be useful if you know movement will be seen only part of the image. In that case modify | |
//the Crop method to that extent. | |
lastImageChanged = MakeGrayscale3(new Bitmap(Crop(lastImage), new Size(smallSize, smallSize))); | |
beforeLastImageChanged = MakeGrayscale3(new Bitmap(Crop(beforeLastImage), new Size(smallSize, smallSize))); | |
//make bits from the two images | |
lastImageMeanBits = bits(colorMeanValue(lastImageChanged,smallSize)); | |
beforeLastImageMeanBits = bits(colorMeanValue(beforeLastImageChanged,smallSize)); | |
//calculationg hammering distance between the two images | |
for (int a = 0; a < smallSize* smallSize; a++) | |
{ | |
if (lastImageMeanBits[a] != beforeLastImageMeanBits[a]) | |
{ | |
hammeringDistance++; | |
} | |
} | |
//add up hammering distances maxSteps times. If hammering distance in maxSteps times is bigger or equal with | |
//itIsConsideredMovement then it is a movement and we save the images from those steps. | |
if (steps < maxSteps) | |
{ | |
hammeringDistanceInTime += hammeringDistance; | |
imageSteps[steps] = e.Bitmap; //we collect the step images so if there is movement we can save images | |
steps++; | |
} | |
else | |
{ | |
if (hammeringDistanceInTime >= itIsConsideredMovement) | |
{ | |
var count = 0; | |
foreach(Bitmap img in imageSteps) | |
{ | |
try | |
{//here I don't really care anymore about this script so if it fails it fails. It saves enough images anyway. | |
//that is why it is not a repositoriy just a gist | |
img.Save("images/" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + "-" + count + ".jpg", ImageFormat.Jpeg); | |
} | |
catch { }; | |
count++; | |
} | |
} | |
steps = hammeringDistanceInTime= 0; | |
} | |
} | |
} | |
public static Bitmap Crop(Image myImage) | |
{ | |
Bitmap croppedBitmap = new Bitmap(myImage); | |
croppedBitmap = croppedBitmap.Clone( | |
new Rectangle(0, 0, myImage.Width, myImage.Height - 20), | |
System.Drawing.Imaging.PixelFormat.DontCare); | |
return croppedBitmap; | |
} | |
private static Bitmap MakeGrayscale3(Image original) | |
{//from here https://stackoverflow.com/questions/2265910/convert-an-image-to-grayscale | |
//the whole thing could make different resoult with different grayscale method. If you | |
//find a better: use that. | |
//create a blank bitmap the same size as original | |
Bitmap newBitmap = new Bitmap(original.Width, original.Height); | |
//get a graphics object from the new image | |
Graphics g = Graphics.FromImage(newBitmap); | |
//create the grayscale ColorMatrix | |
ColorMatrix colorMatrix = new ColorMatrix( | |
new float[][] | |
{ | |
new float[] {.3f, .3f, .3f, 0, 0}, | |
new float[] {.59f, .59f, .59f, 0, 0}, | |
new float[] {.11f, .11f, .11f, 0, 0}, | |
new float[] {0, 0, 0, 1, 0}, | |
new float[] {0, 0, 0, 0, 1} | |
}); | |
//create some image attributes | |
ImageAttributes attributes = new ImageAttributes(); | |
//set the color matrix attribute | |
attributes.SetColorMatrix(colorMatrix); | |
//draw the original image on the new image | |
//using the grayscale color matrix | |
g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), | |
0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes); | |
//dispose the Graphics object | |
g.Dispose(); | |
return newBitmap; | |
} | |
private static MeanValues colorMeanValue(Image i,int smallSize) | |
{ | |
//returns the mean value of the colors and the list of all pixel's colors | |
List<int> colorList = new List<int>(); | |
int colorSum = 0; | |
MeanValues returnObject = new MeanValues(); | |
int a, b; | |
Bitmap c = new Bitmap(i); | |
for (a = 0; a < smallSize; a++) | |
{ | |
for (b = 0; b < smallSize; b++) | |
{ | |
Color pixelColor = c.GetPixel(a, b); | |
colorList.Add(pixelColor.ToArgb()); | |
colorSum += pixelColor.ToArgb(); | |
} | |
} | |
returnObject.colorList = colorList; | |
returnObject.colorMean = colorSum / (smallSize* smallSize); | |
return returnObject; | |
} | |
private List<int> bits(MeanValues colorMean) | |
{ | |
//returns an array of 1s and 0s. If a color is bigger than the mean value of colors it is a 1 | |
List<int> bits = new List<int>(); | |
List<int> colorList = colorMean.colorList; | |
foreach (int color in colorList) | |
{ | |
if (color >= colorMean.colorMean) | |
{ | |
bits.Add(1); | |
} | |
else | |
{ | |
bits.Add(0); | |
} | |
} | |
return bits; | |
} | |
} | |
public class MeanValues{ | |
//use this object to collect these values. Easier down the line | |
public List<int> colorList = new List<int>(); | |
public int colorMean = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment