/// <summary> /// add two images /// </summary> /// <param name="Img">ref Image</param> /// <param name="Summand">image to add</param> /// <example> /// <code> /// <![CDATA[ /// ImageData Original = new ImageData(...); /// ImageData Summand = new ImageData(...); /// // apply filter to original /// Filter.Add(ref Original,Summand); /// ]]> /// </code> /// </example> /// <returns></returns> public static void Add(ref ImageData Img, ImageData Summand) { if ((Img.Width == Summand.Width) && (Img.Height == Summand.Height)) { for (Int32 column = 0; column < Summand.Width; column++) { for (Int32 row = 0; row < Summand.Height; row++) { Color a = Summand[column, row]; Color b = Img[column, row]; int cr = a.R + b.R; int cg = a.G + b.G; int cb = a.B + b.B; if (cr > 255) cr -= 255; if (cg > 255) cg -= 255; if (cb > 255) cb -= 255; Img[column, row] = Color.FromArgb(cr, cg, cb); } } } }
/// <summary> /// Get the average rgb-values from an image in a given rectangle /// By default the function calculate the average rgb-values from the whole image /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// double[] AverageColor = CommonFunctions.AverageRGBValues(img); /// int left = 0; /// int top = 5; /// double[] AverageColorShifted = CommonFunctions.AverageRGBValues(img,left,top); /// int width = 50; /// int height = 100; /// double[] AverageColorRectangle = CommonFunctions.AverageRGBValues(img,left,top,width,height); /// ]]> /// </code> /// </example> /// <param name="Img">Image</param> /// <param name="Left">left of rectangle (default=0)</param> /// <param name="Top">top of rectangle (default=0)</param> /// <param name="Width">width of rectangle (default=full width)</param> /// <param name="Height">height of rectangle (default=full height)</param> /// <returns>averag rgb-values</returns> public static double[] AverageRGBValues(ImageData Img, int Left = 0, int Top = 0, int Width = -1, int Height = -1) { Color CurrentColor; long[] totals = new long[] { 0, 0, 0 }; if (Width == -1) Width = Img.Width; if(Height==-1) Height = Img.Height; for (int x = Left; x < Left + Width; x++) { for (int y = Top; y < Top + Height; y++) { CurrentColor = Img.GetPixel(x, y); totals[0] += CurrentColor.R; totals[1] += CurrentColor.G; totals[2] += CurrentColor.B; } } int count = Width * Height; double[] retvar = new double[] { (totals[0] / (double)count), (totals[1] / (double)count), (totals[2] / (double)count) }; return retvar; }
/// <summary> /// Searches for an image in another image /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// ImageData Search= new ImageData(...); /// // search with tolerance 25 /// List<Rectangle> Positions = Template.AllImages(Img,Search,25); /// ]]> /// </code> /// </example> /// <param name="img">Image to look in</param> /// <param name="Ref">Image to look for</param> /// <param name="tolerance">Tolerance of similarity (0,...,255)</param> /// <returns>A Rectangle list of matching positions</returns> public static List<Rectangle> AllImages(ImageData img, ImageData Ref, uint tolerance = 0) { //Double bestScore = (Math.Abs(Byte.MaxValue - Byte.MinValue)*3); var retVal = new List<Rectangle>(); //Int32 currentScore = 0; //Point location = Point.Empty; //Boolean Found = false; for (int originalX = 0; originalX < img.Width - Ref.Width; originalX++) { for (int originalY = 0; originalY < img.Height - Ref.Height; originalY++) { Color currentInnerPictureColor = Ref[0, 0]; Color currentOuterPictureColor = img[originalX, originalY]; if (CommonFunctions.ColorsSimilar(currentInnerPictureColor, currentOuterPictureColor, tolerance)) { //currentScore = 0; Boolean allSimilar = true; for (int referenceX = 0; referenceX < Ref.Width; referenceX++) { if (!allSimilar) break; for (Int32 referenceY = 0; referenceY < Ref.Height; referenceY++) { if (!allSimilar) break; currentInnerPictureColor = Ref[referenceX, referenceY]; currentOuterPictureColor = img[originalX + referenceX, originalY + referenceY]; if ( !CommonFunctions.ColorsSimilar(currentInnerPictureColor, currentOuterPictureColor, tolerance)) allSimilar = false; /*currentScore += (Math.Abs(CurrentInnerPictureColor.R - CurrentOuterPictureColor.R) + Math.Abs(CurrentInnerPictureColor.G - CurrentOuterPictureColor.G) + Math.Abs(CurrentInnerPictureColor.B - CurrentOuterPictureColor.B));*/ } } if (allSimilar) { retVal.Add(new Rectangle(originalX, originalY, Ref.Width, Ref.Height)); } } } } return retVal; }
/// <summary> /// Searches for an image in another image /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// ImageData Search= new ImageData(...); /// // search with tolerance 25 /// List<Rectangle> Positions = Template.AllImages(Img,Search,25); /// ]]> /// </code> /// </example> /// <param name="img">Image to look in</param> /// <param name="Ref">Image to look for</param> /// <param name="tolerance">Tolerance of similarity (0,...,255)</param> /// <returns>A Rectangle list of matching positions</returns> public static List<Rectangle> AllImages(ImageData img, ImageData Ref, uint tolerance = 0) { var retVal = new List<Rectangle>(); for (int originalX = 0; originalX < img.Width - Ref.Width; originalX++) { for (int originalY = 0; originalY < img.Height - Ref.Height; originalY++) { Color currentInnerPictureColor = Ref[0, 0]; Color currentOuterPictureColor = img[originalX, originalY]; if (CommonFunctions.ColorsSimilar(currentInnerPictureColor, currentOuterPictureColor, tolerance)) { Boolean allSimilar = true; for (int referenceX = 0; referenceX < Ref.Width; referenceX++) { if (!allSimilar) break; for (Int32 referenceY = 0; referenceY < Ref.Height; referenceY++) { if (!allSimilar) break; currentInnerPictureColor = Ref[referenceX, referenceY]; currentOuterPictureColor = img[originalX + referenceX, originalY + referenceY]; if ( !CommonFunctions.ColorsSimilar(currentInnerPictureColor, currentOuterPictureColor, tolerance)) allSimilar = false; } } if (allSimilar) { retVal.Add(new Rectangle(originalX, originalY, Ref.Width, Ref.Height)); } } } } return retVal; }
/// <summary> /// Gets the average RGB values from a given rectangle in an image /// By default the average RGB values from the whole image are calculated /// </summary> /// <remarks> /// to detect the color of bubbles or coins this function can be helpful in connection with IdentifyColor() /// </remarks> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// // average color in whole image /// double[] AverageColor = CommonFunctions.AverageRGBValues(img); /// int left = 0; /// int top = 5; /// // average color in clipped image /// double[] AverageColorShifted = CommonFunctions.AverageRGBValues(img,left,top); /// int width = 50; /// int height = 100; /// // average color in predefined rectangle /// double[] AverageColorRectangle = CommonFunctions.AverageRGBValues(img,left,top,width,height); /// ]]> /// </code> /// </example> /// <param name="img">The image to process</param> /// <param name="left">The left of the rectangle (default=0)</param> /// <param name="top">The top of the rectangle (default=0)</param> /// <param name="width">The width of the rectangle (default=full width)</param> /// <param name="height">The height of the rectangle (default=full height)</param> /// <returns>The average RGB values</returns> public static double[] AverageRGBValues(ImageData img, int left = 0, int top = 0, int width = -1, int height = -1) { long[] totals = {0, 0, 0}; if (width == -1) width = img.Width; if (height == -1) height = img.Height; for (int x = left; x < left + width; x++) { for (int y = top; y < top + height; y++) { Color currentColor = img.GetPixel(x, y); totals[0] += currentColor.R; totals[1] += currentColor.G; totals[2] += currentColor.B; } } int count = width*height; double[] retvar = {(totals[0]/(double) count), (totals[1]/(double) count), (totals[2]/(double) count)}; return retvar; }
/// <summary> /// Finds all pixels matching a specified color /// </summary> /// <example> /// <code> /// <![CDATA[ /// List<Point> WaterPixel = CommonFunctions.FindColors(ScreenShot.create(),Color.Blue,20); /// ]]> /// </code> /// </example> /// <param name="img">The image to look in</param> /// <param name="searchColor">The color to look for</param> /// <param name="tolerance">The tolerance to use</param> /// <returns></returns> public static List<Point> FindColors(ImageData img, Color searchColor, uint tolerance) { var collection = new List<Point>(); for (int column = 0; column < img.Width; column++) { for (int row = 0; row < img.Height; row++) { if (ColorsSimilar(img.GetPixel(column, row), searchColor, tolerance)) { collection.Add(new Point(column, row)); } } } return collection; }
/// <summary> /// Finds all pixels matching a specified color /// /accelerated/ /// </summary> /// <remarks> /// this function skips in both dimension (x and y) a predefined amount of pixels in each iteration. /// You can use this function to test every n-th pixel /// </remarks> /// <param name="img">The image to look in</param> /// <param name="searchColor">The color to look for</param> /// <param name="skipX">The X pixels to skip each time</param> /// <param name="skipY">The Y pixels to skip each time</param> /// <param name="tolerance">The tolerance to use</param> /// <returns></returns> public static List<Point> FindColors(ImageData img, Color searchColor, uint tolerance, int skipX = 1, int skipY = 1) { if (skipX < 1 && skipY < 1) return null; // Cannot be non-positive numbers var collection = new List<Point>(); for (int column = 0; column < img.Width; column = column + skipX) { for (int row = 0; row < img.Height; row = row + skipY) { if (ColorsSimilar(img.GetPixel(column, row), searchColor, tolerance)) { collection.Add(new Point(column, row)); } } } return collection; }
/// <summary> /// sharpen an image /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// // apply filter to image /// Filter.Sharpen(ref Img, 4.0); /// ]]> /// </code> /// </example> /// <param name="Img">image to manipulate</param> /// <param name="weight">weight</param> /// <see cref="sharpen"/> /// <returns></returns> public static void Sharpen(ref ImageData Img, double weight) { ConvolutionMatrix CMatrix = new ConvolutionMatrix(3); CMatrix.SetAll(1); CMatrix.Matrix[0, 0] = 0; CMatrix.Matrix[1, 0] = -2; CMatrix.Matrix[2, 0] = 0; CMatrix.Matrix[0, 1] = -2; CMatrix.Matrix[1, 1] = weight; CMatrix.Matrix[2, 1] = -2; CMatrix.Matrix[0, 2] = 0; CMatrix.Matrix[1, 2] = -2; CMatrix.Matrix[2, 2] = 0; CMatrix.Factor = weight - 8; ApplyConvolution3x3(ref Img, CMatrix); }
/// <summary> /// Retrieves the red channel as an array /// </summary> /// <param name="img">image</param> /// <returns>The red channel as an array</returns> public static uint[,] ExtractRedChannel(ImageData img) { var red = new uint[img.Width, img.Height]; for (Int32 column = 0; column < img.Width; column++) { for (Int32 row = 0; row < img.Height; row++) { Color c = img.GetPixel(column, row); red[column, row] = c.R; } } return red; }
/// <summary> /// Calculates the similarity of image "img" in a given rectangle and a reference image "reference" /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Screenshot = new ImageData(...); /// ImageData Coin = new ImageData(...); /// // do magic or using a clipped region of screenshot (500px from left and 100px from top) /// int left_region = 500; /// int top_region = 100; /// double measure = Similarity(Screenshot,Coin,left_region,top_region); /// ]]> /// </code> /// </example> /// <param name="img">Image A</param> /// <param name="reference">Image B</param> /// <param name="left">The offset from left of image A</param> /// <param name="top">The offset from top of image A</param> /// <param name="width">The width of the rectangle (default: full width)</param> /// <param name="height">The height of the rectangle (default: full height)</param> /// <param name="offsetLeft">The left offset</param> /// <param name="offsetTop">The top offset</param> /// <returns>The similarity result (1=exact, 0=none)</returns> public static double Similarity(ImageData img, ImageData reference, int left = 0, int top = 0, int width = -1, int height = -1, int offsetLeft = 0, int offsetTop = 0) { double sim = 0.0; width = (width == -1) ? img.Width - left : width; height = (height == -1) ? img.Height - top : height; if ((img.Width == reference.Width) && (img.Height == reference.Height)) { for (Int32 column = left; column < left + width; column++) { for (Int32 row = top; row < top + height; row++) { Color a = img.GetPixel(offsetLeft + column, offsetTop + row); Color b = reference.GetPixel(column, row); int cr = Math.Abs(a.R - b.R); int cg = Math.Abs(a.G - b.G); int cb = Math.Abs(a.B - b.B); sim += (cr + cg + cb)/3; //TODO: Fix possible loss of fraction } } sim /= 255.0; sim /= (img.Height*img.Width); } return 1 - sim; }
/// <summary> /// blur the image /// </summary> /// <param name="Img">image to manipulate</param> /// <param name="weight">weight of effect</param> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// // apply filter to image /// Filter.Blur(ref Img, 30.0); /// ]]> /// </code> /// </example> /// <see cref="blur"/> /// <returns></returns> public static void Blur(ref ImageData Img, double weight) { ConvolutionMatrix CMatrix = new ConvolutionMatrix(3); CMatrix.SetAll(1); CMatrix.Matrix[1, 1] = weight; CMatrix.Factor = weight + 8; ApplyConvolution3x3(ref Img, CMatrix); }
/// <summary> /// identify the color of a part(rectangle) from an image by a given list of reference colors (root means square error from average) /// </summary> /// <param name="Img">image to look in</param> /// <param name="statReference">list of possible colors</param> /// <param name="Left">left of rectangle (default: 0)</param> /// <param name="Top">top of rectangle (default: 0)</param> /// <param name="Width">width of rectangle (default: full width)</param> /// <param name="Height">height of rectangle (default: full height)</param> /// <returns>Color</returns> public static Color IdentifyColor(ImageData Img, Dictionary<Color, List<double>> statReference, int Left = 0, int Top = 0, int Width = -1, int Height = -1) { double[] av = CommonFunctions.AverageRGBValues(Img, Left, Top, Width, Height); double bestScore = 255; double currentScore = 0; Color Foo = Color.White; foreach (KeyValuePair<Color, List<double>> item in statReference) { currentScore = Math.Pow((item.Value[0] / 255.0) - (av[0] / 255.0), 2) + Math.Pow((item.Value[1] / 255.0) - (av[1] / 255.0), 2) + Math.Pow((item.Value[2] / 255.0) - (av[2] / 255.0), 2); if (currentScore < bestScore) { Foo = item.Key; bestScore = currentScore; } } return Foo; }
/// <summary> /// Identifies the colors of the pixels in a rectangle and compares them with those from an image by a given list of /// reference colors (majority vote) /// </summary> /// <remarks> /// This tests every pixel in the given rectangle and majority votes the color from the given dictionary of possible /// colors. Each pixel votes for a color. /// /// if the image patch is /// /// " red red " /// " red blue " /// /// then 4 pixels vote for similary colors to red and one pixel votes for a similar color to blue /// /// </remarks> /// <example> /// <code> /// <![CDATA[ /// ImageData Screenshot = new ImageData(...); /// Dictionary<Color, List<double>> Choose = new Dictionary<Color, List<double>> (); /// Choose.Add(Color.Red, new List<double> { 255, 144.23, 140.89 }); /// Choose.Add(Color.White, new List<double> { 218, 219, 222 }); /// Choose.Add(Color.Blue, new List<double> { 21, 108, 182 }); /// Choose.Add(Color.Green, new List<double> { 86, 191, 50 }); /// Choose.Add(Color.Yellow, new List<double> { 233, 203, 118 }); /// Choose.Add(Color.Orange, new List<double> { 246, 122, 11 }); /// Choose.Add(Color.Black, new List<double> { 94, 98, 98 }); /// Choose.Add(Color.Violet, new List<double> { 223, 80, 195 }); /// Choose.Add(Color.MediumSeaGreen, new List<double> { 106, 227, 216 }); /// // ... /// Color PieceColor = CommonFunctions.IdentifyColorByVoting(Screenshot,Choose,leftoffset,topoffset,width,height); /// ]]> /// </code> /// </example> /// <param name="img">The image to look in</param> /// <param name="statReference">The list of possible colors</param> /// <param name="left">The left of the rectangle (default: 0)</param> /// <param name="top">The top of the rectangle (default: 0)</param> /// <param name="width">The width of the rectangle (default: full width)</param> /// <param name="height">The height of the rectangle (default: full height)</param> /// <returns>The best matching color</returns> public static Color IdentifyColorByVoting(ImageData img, Dictionary<Color, List<double>> statReference, int left = 0, int top = 0, int width = -1, int height = -1) { int[] votes = Enumerable.Repeat(0, statReference.Count).ToArray(); if (width == -1) width = img.Width; if (height == -1) height = img.Height; for (int x = left; x < left + width; x++) { for (int y = top; y < top + height; y++) { // color from image Color currentColor = img.GetPixel(x, y); int bestDist = 255*50; int bestIndex = 0; for (int i = 0; i < statReference.Count; i++) { List<double> RGB = statReference.ElementAt(i).Value; // from from dictionary Color cCol = Color.FromArgb(Convert.ToInt32(RGB.ElementAt(0)), Convert.ToInt32(RGB.ElementAt(1)), Convert.ToInt32(RGB.ElementAt(2))); // distance int currentDist = Math.Abs(cCol.R - currentColor.R) + Math.Abs(cCol.G - currentColor.G) + Math.Abs(cCol.B - currentColor.B); if (currentDist < bestDist) { bestDist = currentDist; bestIndex = i; } } votes[bestIndex]++; } } // this is faster than LINQ int m = -1; int ans = 0; for (int i = 0; i < votes.Length; i++) { if (votes[i] > m) { m = votes[i]; ans = i; } } return statReference.ElementAt(ans).Key; }
/// <summary> /// Searches for a binary pattern /// </summary> /// <param name="img">The image to look in</param> /// <param name="pattern">The pattern to look for</param> /// <param name="tolerance">Tolerance (0,...,255)</param> /// <example> /// <code> /// <![CDATA[ /// ImageData img = new ImageData(...); /// int[,] pattern = new int[,] { /// {1,1,0,0,0,0,0,0}, /// {0,0,1,0,0,0,0,0}, /// {0,0,1,0,0,0,0,0}, /// {0,1,0,0,0,0,0,0}, /// {1,0,0,0,0,0,0,0}, /// {1,1,1,0,0,0,0,0}, /// {0,0,0,0,0,0,0,0}, /// {0,0,0,0,0,0,0,0} /// }; /// Rectangle location = Template.BinaryPattern(img, pattern, 2); /// ]]> /// </code> /// </example> /// <returns></returns> public static Rectangle BinaryPattern(ImageData img, int[,] pattern, uint tolerance = 0) { Point location = Point.Empty; Color referenceColor = Color.Empty; for (int imageColumn = 0; imageColumn < img.Width - pattern.GetLength(1); imageColumn++) { for (int imageRow = 0; imageRow < img.Height - pattern.GetLength(0); imageRow++) { bool patternMatch = true; for (int patternColumn = 0; patternColumn < pattern.GetLength(1); patternColumn++) { if (!patternMatch) break; for (int patternRow = 0; patternRow < pattern.GetLength(0); patternRow++) { if (!patternMatch) break; if (pattern[patternRow, patternColumn] == 1) { if (referenceColor == Color.Empty) { referenceColor = img[imageColumn, imageRow]; } else { if (!CommonFunctions.ColorsSimilar(referenceColor, img[imageColumn + patternColumn, imageRow + patternRow], tolerance)) { patternMatch = false; referenceColor = Color.Empty; break; } } } else { if (referenceColor != Color.Empty) { if (CommonFunctions.ColorsSimilar(referenceColor, img[imageColumn + patternColumn, imageRow + patternRow], tolerance)) { patternMatch = false; referenceColor = Color.Empty; } } } } } if (patternMatch) { location.X = imageColumn; location.Y = imageRow; return new Rectangle(location.X, location.Y, pattern.GetLength(1), pattern.GetLength(0)); } } } return Rectangle.Empty; }
/// <summary> /// find first occurence of color with tolerance /// </summary> /// <param name="Img">image to look in</param> /// <param name="SearchColor">color to look for</param> /// <param name="Tolerance">tolerance</param> /// <returns></returns> public static bool[,] FindColors(ImageData Img, Color SearchColor, uint Tolerance) { bool[,] grid = new bool[Img.Width, Img.Height]; for (Int32 column = 0; column < Img.Width; column++) { for (Int32 row = 0; row < Img.Height; row++) { if (CommonFunctions.ColorsSimilar(Img.GetPixel(column, row), SearchColor, Tolerance)) { grid[column, row] = true; } else { grid[column, row] = false; } } } return grid; }
private static void ApplyConvolution3x3(ref ImageData Img, ConvolutionMatrix m) { ImageData newImg = Img.Clone(); Color[,] pixelColor = new Color[3, 3]; int A, RedChannel, GreenChannel, BlueChannel; for (int y = 0; y < Img.Height - 2; y++) { for (int x = 0; x < Img.Width - 2; x++) { pixelColor[0, 0] = Img[x, y]; pixelColor[0, 1] = Img[x, y + 1]; pixelColor[0, 2] = Img[x, y + 2]; pixelColor[1, 0] = Img[x + 1, y]; pixelColor[1, 1] = Img[x + 1, y + 1]; pixelColor[1, 2] = Img[x + 1, y + 2]; pixelColor[2, 0] = Img[x + 2, y]; pixelColor[2, 1] = Img[x + 2, y + 1]; pixelColor[2, 2] = Img[x + 2, y + 2]; A = pixelColor[1, 1].A; RedChannel = (int)((((pixelColor[0, 0].R * m.Matrix[0, 0]) + (pixelColor[1, 0].R * m.Matrix[1, 0]) + (pixelColor[2, 0].R * m.Matrix[2, 0]) + (pixelColor[0, 1].R * m.Matrix[0, 1]) + (pixelColor[1, 1].R * m.Matrix[1, 1]) + (pixelColor[2, 1].R * m.Matrix[2, 1]) + (pixelColor[0, 2].R * m.Matrix[0, 2]) + (pixelColor[1, 2].R * m.Matrix[1, 2]) + (pixelColor[2, 2].R * m.Matrix[2, 2])) / m.Factor) + m.Offset); GreenChannel = (int)((((pixelColor[0, 0].G * m.Matrix[0, 0]) + (pixelColor[1, 0].G * m.Matrix[1, 0]) + (pixelColor[2, 0].G * m.Matrix[2, 0]) + (pixelColor[0, 1].G * m.Matrix[0, 1]) + (pixelColor[1, 1].G * m.Matrix[1, 1]) + (pixelColor[2, 1].G * m.Matrix[2, 1]) + (pixelColor[0, 2].G * m.Matrix[0, 2]) + (pixelColor[1, 2].G * m.Matrix[1, 2]) + (pixelColor[2, 2].G * m.Matrix[2, 2])) / m.Factor) + m.Offset); BlueChannel = (int)((((pixelColor[0, 0].B * m.Matrix[0, 0]) + (pixelColor[1, 0].B * m.Matrix[1, 0]) + (pixelColor[2, 0].B * m.Matrix[2, 0]) + (pixelColor[0, 1].B * m.Matrix[0, 1]) + (pixelColor[1, 1].B * m.Matrix[1, 1]) + (pixelColor[2, 1].B * m.Matrix[2, 1]) + (pixelColor[0, 2].B * m.Matrix[0, 2]) + (pixelColor[1, 2].B * m.Matrix[1, 2]) + (pixelColor[2, 2].B * m.Matrix[2, 2])) / m.Factor) + m.Offset); RedChannel = (RedChannel > 255) ? 255 : RedChannel; RedChannel = (RedChannel < 0) ? 0 : RedChannel; GreenChannel = (GreenChannel > 255) ? 255 : GreenChannel; GreenChannel = (GreenChannel < 0) ? 0 : GreenChannel; BlueChannel = (BlueChannel > 255) ? 255 : BlueChannel; BlueChannel = (BlueChannel < 0) ? 0 : BlueChannel; newImg.SetPixel(x + 1, y + 1, Color.FromArgb(A, RedChannel, GreenChannel, BlueChannel)); } } Img = newImg.Clone(); }
/// <summary> /// Searches for an image in another image /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// ImageData Search= new ImageData(...); /// // search with tolerance 25 /// Rectangle Position = Template.Image(Img,Search,25); /// ]]> /// </code> /// </example> /// <param name="img">Image to look in</param> /// <param name="Ref">Image to look for</param> /// <param name="tolerance">Tolerance of similarity (0,...,255)</param> /// <returns>The best matching position as a rectangle</returns> public static Rectangle Image(ImageData img, ImageData Ref, uint tolerance = 0) { Double bestScore = (Math.Abs(Byte.MaxValue - Byte.MinValue)*3); Point location = Point.Empty; Boolean found = false; for (int originalX = 0; originalX < img.Width - Ref.Width; originalX++) { for (int originalY = 0; originalY < img.Height - Ref.Height; originalY++) { Color currentInnerPictureColor = Ref[0, 0]; Color currentOuterPictureColor = img[originalX, originalY]; if (CommonFunctions.ColorsSimilar(currentInnerPictureColor, currentOuterPictureColor, tolerance)) { Int32 currentScore = 0; Boolean allSimilar = true; for (int referenceX = 0; referenceX < Ref.Width; referenceX++) { if (!allSimilar) break; for (Int32 referenceY = 0; referenceY < Ref.Height; referenceY++) { if (!allSimilar) break; currentInnerPictureColor = Ref[referenceX, referenceY]; currentOuterPictureColor = img[originalX + referenceX, originalY + referenceY]; if ( !CommonFunctions.ColorsSimilar(currentInnerPictureColor, currentOuterPictureColor, tolerance)) allSimilar = false; currentScore += (Math.Abs(currentInnerPictureColor.R - currentOuterPictureColor.R) + Math.Abs(currentInnerPictureColor.G - currentOuterPictureColor.G) + Math.Abs(currentInnerPictureColor.B - currentOuterPictureColor.B)); } } if (allSimilar) { if ((currentScore/(Double) (Ref.Width*Ref.Height)) < bestScore) { location.X = originalX; location.Y = originalY; bestScore = (currentScore/(Double) (Ref.Width*Ref.Height)); found = true; } } } } } if (found) return new Rectangle(location.X, location.Y, Ref.Width, Ref.Height); return Rectangle.Empty; }
/// <summary> /// Get the pattern of the magics match sticks /// </summary> /// <param name="Image">image of character</param> /// <returns>pattern</returns> public float[] GetMagicMatchSticksState(ImageData Image) { int Width = Image.Width; // width of image int Height = Image.Height; // height of image int n = Num(); // num of magicsticks float[] Pattern = new float[n]; // state int lastx = -1, lasty = -1; int tmpx = -1, tmpy = -1; for (int i = 0; i < n; i++) { Pattern[i] = 0.0f; } for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { // is the pixel black? if (Image.GetPixel(x, y).R == 0) { tmpx = Convert.ToInt16(x * 100 / Width); tmpy = Convert.ToInt16(y * 100 / Height); if ((tmpx != lastx) || (tmpy != lasty)) { lastx = tmpx; lasty = tmpy; for (int i = 0; i < n; i++) { // skip already activated receptors if (Pattern[i] == 1.0f) continue; if (MagicStickList[i].GetMagicMatchStickState(tmpx, tmpy)) Pattern[i] = 1.0f; } } } } } return Pattern; }
/// <summary> /// Identifies the colors of the pixels in a rectangle from an image by a given list of /// reference colors (root means square error /// from average) /// </summary> /// <remarks> /// if the color differs sometimes you can use this function to find the best matching colors in a dictionary list. /// In most you do not need to know the exact rgb values. You only wants to know whether the rectangle is more likely to the color /// red, blue, rgb(?,?,?), .... /// /// this functions maps the result to the correct color. If the image contains a pale red and you want to identify this as #ff0000 /// you have to add the pair (#FF8C8C "pale red", #FF0000) /// /// This uses the RMSE root mean square error to find the best color. /// Attention! If there are some pixel that have to complete other colors, they can destroy the accurately /// see: IdentifyColorByVoting /// </remarks> /// <example> /// <code> /// <![CDATA[ /// ImageData Screenshot = new ImageData(...); /// Dictionary<Color, List<double>> Choose = new Dictionary<Color, List<double>> (); /// Choose.Add(Color.Red, new List<double> { 255, 144.23, 140.89 }); /// Choose.Add(Color.White, new List<double> { 218, 219, 222 }); /// Choose.Add(Color.Blue, new List<double> { 21, 108, 182 }); /// Choose.Add(Color.Green, new List<double> { 86, 191, 50 }); /// Choose.Add(Color.Yellow, new List<double> { 233, 203, 118 }); /// Choose.Add(Color.Orange, new List<double> { 246, 122, 11 }); /// Choose.Add(Color.Black, new List<double> { 94, 98, 98 }); /// Choose.Add(Color.Violet, new List<double> { 223, 80, 195 }); /// Choose.Add(Color.MediumSeaGreen, new List<double> { 106, 227, 216 }); /// // ... /// Color PieceColor = CommonFunctions.IdentifyColor(Screenshot,Choose,leftoffset,topoffset,width,height); /// ]]> /// </code> /// </example> /// <param name="img">The image that should be analyzed</param> /// <param name="statReference">The list of possible colors</param> /// <param name="left">The left of the rectangle (default: 0)</param> /// <param name="top">The top of the rectangle (default: 0)</param> /// <param name="width">The width of the rectangle (default: full width)</param> /// <param name="height">The height of the rectangle (default: full height)</param> /// <returns>The best matching color</returns> public static Color IdentifyColor(ImageData img, Dictionary<Color, List<double>> statReference, int left = 0, int top = 0, int width = -1, int height = -1) { double[] av = AverageRGBValues(img, left, top, width, height); double bestScore = 255; Color foo = Color.White; foreach (var item in statReference) { double currentScore = Math.Pow((item.Value[0]/255.0) - (av[0]/255.0), 2) + Math.Pow((item.Value[1]/255.0) - (av[1]/255.0), 2) + Math.Pow((item.Value[2]/255.0) - (av[2]/255.0), 2); if (currentScore < bestScore) { foo = item.Key; bestScore = currentScore; } } return foo; }
/// <summary> /// recognize a character in image /// </summary> /// <param name="Img">image of character</param> /// <returns>detected character (this is possible wrong, if the training wasn't successfull)</returns> public Char Recognize(ImageData Img) { float[] Output = WorkingNeuralNetwork.Output(ImageSense.GetMagicMatchSticksState(Img)); var indexAtMax = Output.ToList().IndexOf(Output.Max()); return CharactersToRecognize[indexAtMax]; }
/// <summary> /// Gets the average color from an image in a given rectangle /// By default the the average color from the whole image is calculated /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// // average color in whole image /// double[] AverageColor = CommonFunctions.AverageRGBValues(img); /// // average color in predefined rectangle /// Color AverageColorInRectangle = CommonFunctions.AverageRGBValues(img,left,top,width,height); /// ]]> /// </code> /// </example> /// <param name="img">The image to process</param> /// <param name="left">The left of the rectangle (default=0)</param> /// <param name="top">The top of the rectangle (default=0)</param> /// <param name="width">The width of the rectangle (default=full width)</param> /// <param name="height">The height of the rectangle (default=full height)</param> /// <returns>average color</returns> public static Color AverageColor(ImageData img, int left = 0, int top = 0, int width = -1, int height = -1) { double[] retvar = AverageRGBValues(img, left, top, width, height); return Color.FromArgb(Convert.ToInt32(retvar[0]), Convert.ToInt32(retvar[1]), Convert.ToInt32(retvar[2])); }
/// <summary> /// resize an image /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// // downscale to image of size 100px x 120px /// ImageData ResizedImg = Img.Resize(100,120); /// /// // or use another Interpolationmode /// ImageData ResizedImg2 = Img.Resize(100,120,InterpolationMode.Bilinear); /// ]]> /// </code> /// </example> /// <param name="NewWidth">new Width of image</param> /// <param name="NewHeight">new Height of image</param> /// <param name="Mode">Mode of Interpolation (Default: HighQualityBicubic, Possible parameter: HighQualityBicubic,HighQualityBilinear,Bilinear,...)</param> /// <returns></returns> public ImageData Resize(int NewWidth, int NewHeight, InterpolationMode Mode = InterpolationMode.HighQualityBicubic) { //create a new Bitmap the size of the new image Bitmap bmp = new Bitmap(NewWidth, NewHeight, BmpPixelFormat); //create a new graphic from the Bitmap Graphics graphic = Graphics.FromImage((Image)bmp); graphic.InterpolationMode = InterpolationMode.HighQualityBicubic; //draw the newly resized image graphic.DrawImage(this.Bitmap, 0, 0, NewWidth, NewHeight); //dispose and free up the resources graphic.Dispose(); //return the image ImageData ANS = new ImageData(bmp); return ANS; }
/// <summary> /// Identifies the best matching image from a list of images /// </summary> /// <example> /// <code> /// <![CDATA[ /// Dictionary<string, ImageData> Choose = new Dictionary<string, ImageData> (); /// Choose.Add("coin", new ImageData("coin.bmp")); /// Choose.Add("enemy", new ImageData("warrior.bmp")); /// Choose.Add("water", new ImageData("blue_piece.bmp")); /// // ... /// string PieceColor = CommonFunctions.IdentifyImage(new ImageData("unkown.bmp"),Choose); /// ]]> /// </code> /// </example> /// <remarks> /// If you want to compare an image patch to a collection of interesting images, you can use this /// function to find the best matching from the given list /// </remarks> /// <param name="img">The image to look for</param> /// <param name="statReference">The list of images</param> /// <returns>The name of the best matching image in the list</returns> public static string IdentifyImage(ImageData img, Dictionary<string, ImageData> statReference) { double similar = 0.0; string keyword = ""; foreach (var item in statReference) { // exact size match cause easier way of compare the image if ((img.Width == item.Value.Width) && (img.Height == item.Value.Height)) { double s = Similarity(img, item.Value); if (s > similar) { keyword = item.Key; similar = s; } } else { if ((img.Width > item.Value.Width) && (img.Height > item.Value.Height)) { // search within the greater image for (int column = 0; column < img.Width - item.Value.Width; column++) { for (int row = 0; row < img.Height - item.Value.Height; row++) { double s = Similarity(img, item.Value, 0, 0, item.Value.Width, item.Value.Height, column, row); if (s > similar) { keyword = item.Key; similar = s; } } } } } } return keyword; }
/// <summary> /// apply threshold /// </summary> /// <param name="Img">ref Image</param> /// <param name="threshold">threshold (0,...,255)</param> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// // it's better to grayscale first /// Filter.Grayscale(ref Img); /// // apply filter to image /// Filter.Threshold(ref Img, 20); /// ]]> /// </code> /// </example> /// <see cref="threshold"/> /// <returns></returns> public static void Threshold(ref ImageData Img,uint threshold) { for (Int32 column = 0; column < Img.Width; column++) { for (Int32 row = 0; row < Img.Height; row++) { Color c = Img[column, row]; if (c.R > threshold) { Img[column, row] = Color.FromArgb(255, 255, 255); } else { Img[column, row] = Color.FromArgb(0, 0, 0); } } } }
/// <summary> /// Get the average color from an image in a given rectangle /// By default the function calculate the average color from the whole image /// </summary> /// <param name="Img">Image</param> /// <param name="Left">left of rectangle (default=0)</param> /// <param name="Top">top of rectangle (default=0)</param> /// <param name="Width">width of rectangle (default=full width)</param> /// <param name="Height">height of rectangle (default=full height)</param> /// <returns>average color</returns> public static Color AverageColor(ImageData Img, int Left = 0, int Top = 0, int Width = -1, int Height = -1) { double[] retvar = CommonFunctions.AverageRGBValues(Img, Left, Top, Width, Height); return Color.FromArgb(Convert.ToInt32(retvar[0]), Convert.ToInt32(retvar[1]), Convert.ToInt32(retvar[2])); }
/// <summary> /// black and white image by replace /// all similar color to black by blackand /// all similiar colors to white by white /// </summary> /// <param name="Img">ref image</param> /// <param name="Tolerance">tolerance (0,...,255)</param> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// Filter.Grayscale(ref Img); /// // apply filter to original /// Filter.BlackAndWhite(ref Img,90); /// ]]> /// </code> /// </example> /// <see cref="blackandwhite"/> /// <returns></returns> public static void BlackAndWhite(ref ImageData Img, uint Tolerance) { Filter.ReplaceSimilarColor(ref Img, Color.Black, Color.Black, Tolerance); Filter.ReplaceDifferentColor(ref Img, Color.Black, Color.White, Tolerance); }
/// <summary> /// get the pattern of the magic sticks, which the network learns /// </summary> /// <param name="Img">image of character</param> /// <returns>pattern of magic sticks as array of floats (0.0f, 1.0f)</returns> public float[] GetMagicSticksPattern(ImageData Img) { return ImageSense.GetMagicMatchSticksState(Img); }
/// <summary> /// identify the color of a part(rectangle) from an image by a given list of reference colors (majority vote) /// </summary> /// <remarks> /// this tests every pixel in the given rectangle and majority votes the color from the given dictionary of possible colors /// </remarks> /// <param name="Img">image to look in</param> /// <param name="statReference">list of possible colors</param> /// <param name="Left">left of rectangle (default: 0)</param> /// <param name="Top">top of rectangle (default: 0)</param> /// <param name="Width">width of rectangle (default: full width)</param> /// <param name="Height">height of rectangle (default: full height)</param> /// <returns>Color</returns> public static Color IdentifyColorByVoting(ImageData Img, Dictionary<Color, List<double>> statReference, int Left = 0, int Top = 0, int Width = -1, int Height = -1) { double[] av = CommonFunctions.AverageRGBValues(Img, Left, Top, Width, Height); Color Foo = Color.White; int[] votes = Enumerable.Repeat(0, statReference.Count).ToArray(); if (Width == -1) Width = Img.Width; if (Height == -1) Height = Img.Height; for (int x = Left; x < Left + Width; x++) { for (int y = Top; y < Top + Height; y++) { // color from image Color CurrentColor = Img.GetPixel(x, y); int best_dist = 255*50; int best_idx = 0; for (int i = 0; i < statReference.Count;i++ ) { List<double> RGB = statReference.ElementAt(i).Value; // from from dictionary Color CCol = Color.FromArgb(Convert.ToInt32(RGB.ElementAt(0)), Convert.ToInt32(RGB.ElementAt(1)), Convert.ToInt32(RGB.ElementAt(2))); // distance int current_dist = Math.Abs(CCol.R - CurrentColor.R) + Math.Abs(CCol.G - CurrentColor.G) + Math.Abs(CCol.B - CurrentColor.B); if (current_dist < best_dist) { best_dist = current_dist; best_idx = i; } } votes[best_idx]++; } } // this is faster than LINQ int m=-1; int ans = 0; for (int i=0;i<votes.Length;i++) { if(votes[i]>m){ m=votes[i]; ans=i; } } return statReference.ElementAt(ans).Key; }
/// <summary> /// creates a new imagedata object from another imagedata object /// </summary> /// <example> /// <code> /// <![CDATA[ /// ImageData Img = new ImageData(...); /// ImageData Img2 = new ImageData(Img); /// ]]> /// </code> /// </example> /// <param name="Path">path to bitmap</param> public ImageData(ImageData Img) { LoadBitmap(Img.Bitmap); }
/// <summary> /// calculate the similarity of image A in a given rectangle and a reference image B /// </summary> /// <param name="Img">image A</param> /// <param name="Reference">image B</param> /// <param name="Left">offset from left of image A</param> /// <param name="Top">offset from top of image A</param> /// <param name="Width">width of rectangle (default: full width)</param> /// <param name="Height">height of rectangle (default: full height)</param> /// <returns>similarity (1=exact,0=none)</returns> public static double Similarity(ImageData Img, ImageData Reference, int Left = 0, int Top = 0, int Width = -1, int Height = -1, int OffsetLeft = 0, int OffsetTop = 0) { double sim = 0.0; Width = (Width == -1) ? Img.Width - Left : Width; Height = (Height == -1) ? Img.Height - Top : Height; if ((Img.Width == Reference.Width) && (Img.Height == Reference.Height)) { for (Int32 column = Left; column < Left + Width; column++) { for (Int32 row = Top; row < Top + Height; row++) { Color a = Img.GetPixel(OffsetLeft + column, OffsetTop + row); Color b = Reference.GetPixel(column, row); int cr = Math.Abs(a.R - b.R); int cg = Math.Abs(a.G - b.G); int cb = Math.Abs(a.B - b.B); sim += (cr + cg + cb) / 3; } } sim /= 255.0; sim /= (Img.Height * Img.Width); } return 1 - sim; }