public static UBitmap FromImage(Image image) { var bmp = new Bitmap(image); var ubmp = new UBitmap(image.Width, image.Height); for (var y = 0; y < image.Height - 1; y++) { for (var x = 0; x < image.Width - 1; x++) { ubmp.SetPixel(x, y, bmp.GetPixel(x, y)); } } return(ubmp); }
/// <summary> /// Detect objects in the image /// </summary> /// <param name="image"></param> /// <returns></returns> public static Detection[] DetectObjects(this Image image, float accuracy = 20.0f) { //Lower the size of the image so that it takes less resources and less time //The factor by which to downscale the size, determined by the accuracy given var resizeScale = image.Width / ((image.Width / 100) * accuracy); resizeScale = (image.Width / resizeScale) % 2 == 0 ? resizeScale : (int)(resizeScale + 1); //The width we want on the resized image var targetWidth = image.Width / resizeScale; //resize the image image = image.Resize((int)targetWidth); //Detect edges first so that it becomes easier to distinguish one object from another var ubmp = UBitmap.FromImage(image.DetectEdges()); //We'll use this list to keep our 'Points' of interest (no pun intended ;-) ) var detections = new List <Point>(); //Iterate through each pixel in the image row by row //y is row for (var y = 0; y < ubmp.Height - 1; y++) { //x is column for (var x = 0; x < ubmp.Width - 1; x++) { //Get the pixel represented at the (x,y) coordinates var pixelColor = ubmp.GetPixel(x, y); //get the argb color code of the pixel var pixel = pixelColor.ToArgb(); //check if pixel is white if (pixel != -1) { //if not white, add the coordinates to the list detections.Add(new Point(x, y)); } } } //If there are no points of interest, we can assume that we have no objects in the image, so no need to continue ¯\_(ツ)_/¯ if (!detections?.Any() ?? true) { return(null); } //if multiple points are 'clumped' together we can assume that its from the same object var clumps = new List <List <Point> >(); //add the first point as the first point of the first clump clumps.Add(new List <Point> { detections.First() }); //Always keeping a record of what was the last point we processed var lastPoint = clumps.Last().Last(); //iterate over each point except the first one since we added it already foreach (var point in detections.Skip(1)) { //The x coordinates of the last point and this point var xs = new int[] { lastPoint.X, point.X }; //The y coordinates of the last point and this point var ys = new int[] { lastPoint.Y, point.Y }; //the difference in the lastpoint's x coordinates var xDiff = xs.Max() - xs.Min(); //the difference in the lastpoint's y coordinates var yDiff = ys.Max() - ys.Min(); //Check to see if the two points are 'clumped together' (there is no blank-white-space between them) //This tells us that the points are of the same object if (xDiff < 2 || yDiff < 2) { //see if we have a previous record of the lastPoint (if lastpoint was interesting and of the same object) var clump = clumps.FirstOrDefault(c => c.Any(p => p.Equals(lastPoint))); //if no record exists, add a new record because why not? if (clump == null) { clump = new List <Point>(); //add the new record to the list clumps.Add(clump); } //add the point to the record clump.Add(point); //update the lastpoint lastPoint = point; } //this block would be processed if there is atleast one blank-white-space between this point and the one before else { //create a new record, since this is the first time we are dealing with this object var clump = new List <Point>(); //add the record to the list clumps.Add(clump); //add the point to the record clump.Add(point); //update the lastpoint lastPoint = point; } } //Make Detection objects out of our clumps (convert them into something meaninful and interpretable) //In here we multiply every X and Y values with resizeScale because the current points are relative to our resized image, //But we need to know where these points were on the original full-size image //We do Math.Ceiling because we cannot expect the value which comes after multiplying by resizeScale is gonna be a whole number, //in which case we may lose a few pixels var objects = clumps.Select((clump, i) => { //Create a new Detection Object var d = new Detection { //Use index as Id Id = i, //find lowest X coordinate in the clump LX = (int)Math.Ceiling(clump.Min(x => x.X) * resizeScale), //find lowest y coordinate in the clump LY = (int)Math.Ceiling(clump.Min(y => y.Y) * resizeScale), //find highest X coordinate in the clump HX = (int)Math.Ceiling(clump.Max(x => x.X) * resizeScale), //find highest X coordinate in the clump HY = (int)Math.Ceiling(clump.Max(y => y.Y) * resizeScale) }; return(d); }).ToArray(); //return the objects; return(objects); //var target = new Bitmap(image); //using (var pen = new Pen(Color.Aqua)) //using(var g = Graphics.FromImage(target)) //foreach (var obj in objects) //{ // g.DrawRectangle(pen, obj.Rectangle); //} //return target; }
/// <summary> /// Detect the edges in the image /// </summary> /// <param name="image"></param> /// <returns></returns> public static Image DetectEdges(this Image image) { //Downscale or Upscale //image = image.Resize(500); //image = image.MakeGrayscale3(); var bmp = UBitmap.FromImage(image); var target = new UBitmap(bmp.Width, bmp.Height); //var pixels = new List<Point>(); //Iterate through each pixel in the image row by row //y is row for (var y = 0; y < bmp.Height - 1; y++) { //x is column for (var x = 0; x < bmp.Width - 1; x++) { //Get the pixel represented at the (x,y) coordinates var pixelColor = bmp.GetPixel(x, y); //get the argb color code of the pixel var pixel = pixelColor.ToArgb(); //make sure the pixel is not the first one on either of X and Y planes, because we need to process neighbouring pixels if (x != 0 && y != 0) { //Pixel directly above this one, the names should be self explanatory, so not going into details of those. var top = bmp.GetPixel(x, y - 1).ToArgb(); var left = bmp.GetPixel(x - 1, y).ToArgb(); var bottom = bmp.GetPixel(x, y + 1).ToArgb(); var right = bmp.GetPixel(x + 1, y).ToArgb(); var topLeft = bmp.GetPixel(x - 1, y - 1).ToArgb(); var topRight = bmp.GetPixel(x + 1, y - 1).ToArgb(); var bottomLeft = bmp.GetPixel(x - 1, y + 1).ToArgb(); var bottomRight = bmp.GetPixel(x + 1, y + 1).ToArgb(); //Take all of the neighbouring pixels into an array for easier processing var colors = new int[] { top, left, bottom, right, topLeft, topRight, bottomLeft, bottomRight }; //check whether this pixel is worthy of being added to the 'edge' detection var shouldAdd = colors.Select(color => { //the pixels to compare //pixel is this pixel, color is the neighbouring one var compare = new int[] { pixel, color }; //Find the difference between the two var difference = compare.Max() - compare.Min(); //Find the percentage difference between the two //Also we need a positive number, whether they turn negative or positive, //so we convert any negatives into positives and leave positives untouched //If that didn't make any sense, you should read about 'Math.Abs' (Absolute) function //There should be tonnes of articles,answers and explanations on the internet var percentageDifference = Math.Abs(((double)difference / -1.0d) * 100d); //Check if the difference is atleast 50%, if so, we assume it as an edge return(percentageDifference >= 50.0d); }).Any(b => b); if (shouldAdd) { //Add the pixel as an edge target.SetPixel(x, y, pixelColor); } } } } //We cannot have any messy values in the image's pixels so we convert all of them into white color (-1 is white) for (var i = 0; i < target.Bits.Length; i++) { if (target.Bits[i] == 0) { target.Bits[i] = -1; } } //return the edge detected image return(target.Bitmap); }