/// <summary> /// Evaluates a list of kernels against a byte[,] image. /// </summary> /// <param name="operand">The image to parse.</param> /// <param name="kernels">The kernels to apply.</param> /// <param name="evaluateKernel">The "map" function to use. For further explaination, /// refer to the documentation of Kernel2DBatch.</param> /// <param name="reduce">The "reduce" function to use. For further explaination, /// refer to the documentation of Kernel2DBatch.</param> /// <returns>The transformed byte[,].</returns> public static byte[,] Evaluate( byte[,] operand, Kernel2D[] kernels, EvaluateKernelDelegate evaluateKernel, ReduceDelegate reduce) { int width = operand.GetLength(0); int height = operand.GetLength(1); byte[,] ret = new byte[width, height]; List<double> evaluations = new List<double>(); for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { evaluations.Clear(); // -> Map for (int i = 0; i < kernels.Length; ++i) { // Evaluate evaluations.Add(evaluateKernel(kernels[i], operand, x, y)); } // -> Reduce ret[x, y] = reduce(evaluations); } } return ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="imageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] imageSrc, SortedDictionary<string, object> iConfigs) { // Generate window ... int window_size = int.Parse(iConfigs["window size"].ToString()); if (window_size % 2 == 0) throw new Exception("Window size must be odd!"); double[,] window = new double[window_size, window_size]; for (int x = 0; x < window_size; ++x) { for (int y = 0; y < window_size; ++y) { window[x, y] = 1; } } Kernel2D kernel = new Kernel2D(0, 1.0, window); int i; double exponent = 1.0 / (1.0 * window_size * window_size); // Apply convolution with window byte[,] tmp_ret = Kernel2D.ApplyFunction( kernel, imageSrc, delegate(Tuple<Point, Point>[] mappings, byte[,] operand) { double ret = 1.0; try { for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { ret *= operand[mappings[i].Item2.X, mappings[i].Item2.Y]; } } } catch (OverflowException) { ret = double.MaxValue; } catch(Exception ex) { throw new Exception("Error while applying Geometric filter.", ex); } ret = Math.Pow(ret, exponent); // trim ret = Math.Min(byte.MaxValue, Math.Max(byte.MinValue, ret)); return (Byte)ret; }); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="imageSrc"></param> /// <param name="configs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] imageSrc, SortedDictionary<string, object> configs) { // Generate window ... int window_size = int.Parse(configs["window size"].ToString()); if (window_size % 2 == 0) throw new Exception("Window size must be odd!"); double[,] window = new double[window_size, window_size]; for (int x = 0; x < window_size; ++x) { for (int y = 0; y < window_size; ++y) { window[x, y] = 1; } } // Run window throgh image Kernel2D kernel = new Kernel2D(0, 1, window); List<byte> tmp = new List<byte>(); int i; byte[,] tmp_ret = Kernel2D.ApplyFunction( kernel, imageSrc, delegate(Tuple<Point, Point>[] mappings, byte[,] op) { tmp.Clear(); // Get all pixels for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { tmp.Add(op[mappings[i].Item2.X, mappings[i].Item2.Y]); } } if (tmp.Count > 0) { // Sort tmp.Sort(); // Get pixel in middle return tmp[tmp.Count / 2]; } return 0; }); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="imageSrc"></param> /// <param name="configs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] imageSrc, SortedDictionary<string, object> configs) { // Generate window ... int window_size = int.Parse(configs["window size"].ToString()); if (window_size % 2 == 0) throw new Exception("Window size must be odd!"); double[,] window = new double[window_size, window_size]; for (int x = 0; x < window_size; ++x) { for (int y = 0; y < window_size; ++y) { window[x, y] = 1; } } // Run window throgh image Kernel2D kernel = new Kernel2D(0, 1, window); int i; byte _max, _min; byte[,] tmp_ret = Kernel2D.ApplyFunction( kernel, imageSrc, delegate(Tuple<Point, Point>[] mappings, byte[,] op) { _max = byte.MinValue; _min = byte.MaxValue; // Get all pixels for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { _max = Math.Max(_max, op[mappings[i].Item2.X, mappings[i].Item2.Y]); _min = Math.Min(_min, op[mappings[i].Item2.X, mappings[i].Item2.Y]); } } return (Byte)Math.Min( byte.MaxValue, Math.Max( byte.MinValue, Math.Ceiling( 0.5 * (_max + _min)))); }); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="imageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] imageSrc, SortedDictionary<string, object> iConfigs) { // Generate window ... int window_size = int.Parse(iConfigs["window size"].ToString()); if (window_size % 2 == 0) throw new Exception("Window size must be odd!"); double[,] window = new double[window_size, window_size]; for (int x = 0; x < window_size; ++x) { for (int y = 0; y < window_size; ++y) { window[x, y] = 1; } } Kernel2D kernel = new Kernel2D(0, 1.0, window); int i; double numerator = (window_size * window_size); // Apply convolution with window byte[,] tmp_ret = Kernel2D.ApplyFunction( kernel, imageSrc, delegate(Tuple<Point, Point>[] mappings, byte[,] operand) { double ret = 0.0; for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { ret += 1.0 / (double)operand[mappings[i].Item2.X, mappings[i].Item2.Y]; } } if (ret != 0) { ret = numerator / ret; } // trim ret = Math.Min(byte.MaxValue, Math.Max(byte.MinValue, ret)); return (Byte)ret; }); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="iImageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] iImageSrc, SortedDictionary<string, object> iConfigs) { // Kernels in Gonzalez and Woods: Figure 3.39, 3.41 //3x3: // 0 1 0 // 1 -4 1 // 0 1 0 // // 0 -1 0 // -1 4 -1 // 0 -1 0 // // 1 1 1 // 1 -8 1 // 1 1 1 // // -1 2 -1 // 2 -4 2 // -1 2 -1 double alpha = double.Parse(iConfigs["Alpha"].ToString()); Kernel2D kernel = new Kernel2D(0, 1.0, GenerateFilter(alpha)); byte[,] tmp_ret = Kernel2D.ApplyConvolution(kernel, iImageSrc); double t = double.Parse(iConfigs["Threshold"].ToString()); if (t > 0) { // Apply High and Low Thresholding ... HighThreshold h = new HighThreshold(); SortedDictionary<string, object> hConf = h.GetDefaultConfigs(); hConf["High-Threshold"] = t + 1; LowThreshold l = new LowThreshold(); SortedDictionary<string, object> lConf = l.GetDefaultConfigs(); lConf["Low-Thresold"] = t; tmp_ret = h.ApplyFilter(tmp_ret, hConf); tmp_ret = l.ApplyFilter(tmp_ret, lConf); } return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="imageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] imageSrc, SortedDictionary<string, object> iConfigs) { // Generate window ... int window_size = int.Parse(iConfigs["window size"].ToString()); if (window_size % 2 == 0) throw new Exception("Window size must be odd!"); double[,] window = new double[window_size, window_size]; for (int x = 0; x < window_size; ++x) { for (int y = 0; y < window_size; ++y) { window[x, y] = 1; } } Kernel2D kernel = new Kernel2D(0, 1.0 / (window_size * window_size), window); // Apply convolution with window byte[,] tmp_ret = Kernel2D.ApplyConvolution(kernel, imageSrc); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="iImageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] iImageSrc, SortedDictionary<string, object> iConfigs) { double sigma = double.Parse(iConfigs["Sigma"].ToString()); Kernel2D kernel = new Kernel2D(0, 1.0, GenerateFilter(sigma)); byte[,] tmp_ret = Kernel2D.ApplyConvolution(kernel, iImageSrc); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="img"></param> /// <param name="configs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] img, SortedDictionary<string, object> configs) { // (1) Take gradient of image - Sobel operator. // (Compute and take separately the x and y componentes into Gx and Gy) Kernel2D kernel_x = new Kernel2D(0, (1 / 4.0), new double[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } }); Kernel2D kernel_y = new Kernel2D(0, (1 / 4.0), new double[,] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } }); int width = img.GetLength(0), height = img.GetLength(1); double[,] edges_dir = new double[width, height]; byte[,] step2 = new byte[width, height]; const double degree_factor = 180.0 / Math.PI; Tuple<Point, Point>[] mappings; double g_x, g_y, dummy; int i; for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { // Get neighbors to parse mappings = kernel_x.ResolvePositions(x, y, width, height); g_x = 0; g_y = 0; // Calculate convolution for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { g_x += img[mappings[i].Item2.X, mappings[i].Item2.Y] * kernel_x.Matrix[mappings[i].Item1.X, mappings[i].Item1.Y]; g_y += img[mappings[i].Item2.X, mappings[i].Item2.Y] * kernel_y.Matrix[mappings[i].Item1.X, mappings[i].Item1.Y]; } } g_x = g_x * kernel_x.Multiplier + kernel_x.Threeshold; g_y = g_y * kernel_y.Multiplier + kernel_y.Threeshold; step2[x, y] = (byte)Math.Sqrt(g_x * g_x + g_y * g_y); // (2) Calculate edge direction, using Gx and Gy if (0 == g_x) { edges_dir[x, y] = (0 == g_y ? 0 : 90); } else { // need to convert in degree edges_dir[x, y] = Math.Atan(g_y / g_x) * degree_factor; } // (3) Relate edge direction to a direction that can be traced in the image (0º, 45º, 90º, 135º) dummy = edges_dir[x, y]; // [0;22.5] e [157.5;180] = 0 // [22.5;67.5] = 45 // [67.5;112.5] = 90 // [112.5;157.5] = 135 //if (dummy > 180) // throw new Exception(string.Format("Theta = {0}", dummy)); if (dummy >= 157.5 || dummy < 22.5) { edges_dir[x, y] = 0; } else if (dummy >= 112.5) { edges_dir[x, y] = 135; } else if (dummy >= 67.5) { edges_dir[x, y] = 90; } else if (dummy >= 22.5) { edges_dir[x, y] = 45; } } } mappings = null; // (4) Apply non maximum suppression. Set to 0 any pixel that is not a local maximum; // compare with neighbourhood using edge direction ... // // for point (x,y) test: // dir = 0, (x, y-1) and (x, y+1) // dir = 45, (x+1, y+1) and (x-1, y-1) // dir = 90, (x-1, y) and (x+1, y) // dir = 135, (x+1, y-1) and (x-1, y+1) Point[] neighborhood; for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { dummy = edges_dir[x, y]; // Ignore, PLEASE, out of bound values ... neighborhood = GetNeighborhood(x, y, width, height, dummy); i = 0; while (step2[x, y] != 0 && i < neighborhood.Length) { if (step2[x, y] < step2[neighborhood[i].X, neighborhood[i].Y]) { step2[x, y] = 0; } ++i; } } } return step2; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="imageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] imageSrc, SortedDictionary<string, object> iConfigs) { // Generate window ... int window_size = int.Parse(iConfigs["window size"].ToString()); if (window_size % 2 == 0) throw new Exception("Window size must be odd!"); double[,] window = new double[window_size, window_size]; for (int x = 0; x < window_size; ++x) { for (int y = 0; y < window_size; ++y) { window[x, y] = 1; } } // Run window through image Kernel2D kernel = new Kernel2D(0, 1, window); int p = int.Parse(iConfigs["p"].ToString()); List<byte> tmp = new List<byte>(); int i; double ret; byte[,] tmp_ret = Kernel2D.ApplyFunction( kernel, imageSrc, delegate(Tuple<Point, Point>[] mappings, byte[,] op) { tmp.Clear(); // Get all pixels for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { tmp.Add(op[mappings[i].Item2.X, mappings[i].Item2.Y]); } } // Do paddings with 0s and 255s. // Put more 255 on end if difference is odd { int diff = window_size * window_size - tmp.Count; if (diff > 0) { byte[] zeros = new byte[diff / 2]; byte[] maxes = new byte[(int)Math.Ceiling(diff / 2.0)]; // zeros automatically initialized with zeros // ... // init maxes to byte.MaxValue for (i = 0; i < maxes.Length; ++i) maxes[i] = byte.MaxValue; tmp.InsertRange(0, zeros); tmp.AddRange(maxes); } } if (tmp.Count > 0) { // Sort tmp.Sort(); ret = 0.0; for (i = p; i < tmp.Count - p; ++i) { ret += tmp[i]; } ret = 1.0 / ((double)tmp.Count - 2.0 * (double)p) * ret; // trim ret = Math.Min(byte.MaxValue, Math.Max(byte.MinValue, ret)); return (Byte)ret; } return 0; }); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="iImageSrc"></param> /// <param name="iConfigs"></param> /// <remarks>Final implementation based on edge.m</remarks> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] iImageSrc, SortedDictionary<string, object> iConfigs) { // Generating _masksAnti: Simplified Approach to Image Processing.pdf (pdf p.99) // See also de Difference of Gaussians: Simplified Approach to Image Processing.pdf (pdf p.100) //int dim = int.Parse(iConfigs["dim"].ToString()); //double std = double.Parse(iConfigs["std dev"].ToString()); //if (dim % 2 == 0) // throw new Exception("Dim should be odd!"); //double[,] lap_gauss = (double[,])Array.CreateInstance(typeof(double), dim, dim); //double funny_const = -1.0 / (Math.PI * std * std * std * std); //double funny_const2; //for (int x = 0; x < dim; ++x) //{ // for (int y = 0; y < dim; ++y) // { // funny_const2 = // -1.0 * (x*x + y*y) / // (2.0 * std * std); // lap_gauss[x, y] = (int) ( // funny_const // * (1.0 + funny_const2) // * Math.Pow(Math.E, funny_const2)); // } //} //Kernel2D kernel = new Kernel2D( // 0, // 1 / (9.0 * 9.0), // //lap_gauss // new double[,] { // { 0, 1, 1, 2, 2, 2, 1, 1, 0 }, // { 1, 2, 4, 5, 5, 5 , 4, 2, 1 }, // { 1, 4, 5, 3, 0, 3 , 5, 4, 1 }, // { 2, 5, 3, -12, -24, -12 , 3, 5, 2 }, // { 2, 5, 0, -24, -40, -24 , 0, 5, 2 }, // { 2, 5, 3, -12, -24, -12 , 3, 5, 2 }, // { 1, 4, 5, 3, 0, 3 , 5, 4, 1 }, // { 1, 2, 4, 5, 5, 5 , 4, 2, 1 }, // { 0, 1, 1, 2, 2, 2 , 1, 1, 0 } // } // ); double sigma = double.Parse(iConfigs["Sigma"].ToString()); Kernel2D kernel = new Kernel2D(0, 1.0, GenerateFilter(sigma)); double[,] tmp = Facilities.ToDouble(iImageSrc); tmp = Kernel2D.ApplyConvolution(kernel, tmp); int x, y, width = tmp.GetLength(0), height = tmp.GetLength(1); double t = double.Parse(iConfigs["Threshold"].ToString()) / (double)byte.MaxValue; // Find zero crossings bool[,] edges = new bool[width, height]; for (x = 1; x < width - 1; ++x) { for (y = 1; y < height - 1; ++y) { if (tmp[x, y] < 0 && tmp[x, y + 1] > 0 && Math.Abs(tmp[x, y] - tmp[x, y + 1]) > t) { edges[x, y] = true; } if (tmp[x, y] < 0 && tmp[x, y - 1] > 0 && Math.Abs(tmp[x, y] - tmp[x, y - 1]) > t) { edges[x, y] = true; } if (tmp[x, y] < 0 && tmp[x + 1, y] > 0 && Math.Abs(tmp[x, y] - tmp[x + 1, y]) > t) { edges[x, y] = true; } if (tmp[x, y] < 0 && tmp[x - 1, y] > 0 && Math.Abs(tmp[x, y] - tmp[x - 1, y]) > t) { edges[x, y] = true; } if (tmp[x, y] == 0.0) { if (tmp[x - 1, y - 1] < 0 && tmp[x + 1, y + 1] > 0 && Math.Abs(tmp[x - 1, y - 1] - tmp[x + 1, y + 1]) > t) { edges[x, y] = true; } if (tmp[x - 1, y - 1] > 0 && tmp[x + 1, y + 1] < 0 && Math.Abs(tmp[x - 1, y - 1] - tmp[x + 1, y + 1]) > t) { edges[x, y] = true; } if (tmp[x + 1, y - 1] < 0 && tmp[x - 1, y + 1] > 0 && Math.Abs(tmp[x + 1, y - 1] - tmp[x - 1, y + 1]) > t) { edges[x, y] = true; } if (tmp[x + 1, y - 1] > 0 && tmp[x - 1, y + 1] < 0 && Math.Abs(tmp[x + 1, y - 1] - tmp[x - 1, y + 1]) > t) { edges[x, y] = true; } } } } byte[,] tmp_ret = Facilities.To8bppGreyScale(edges); return tmp_ret; }
/// <summary> /// Apply filter to a byte[,]. /// </summary> /// <param name="iImageSrc"></param> /// <param name="iConfigs"></param> /// <returns></returns> public override byte[,] ApplyFilter(byte[,] iImageSrc, SortedDictionary<string, object> iConfigs) { int width = iImageSrc.GetLength(0), height = iImageSrc.GetLength(1); byte threshold = byte.Parse(iConfigs["Threshold"].ToString()); Kernel2D[] kernels = new Kernel2D[] { new Kernel2D(0, (1/15.0), new double[,] { { 5,5,5 }, { -3,0,-3 }, { -3,-3,-3 } }), new Kernel2D(0, (1/15.0), new double[,] { { 5,5,-3 }, { 5,0,-3 }, { -3,-3,-3 } }), new Kernel2D(0, (1/15.0), new double[,] { { 5,-3,-3 }, { 5,0,-3 }, { 5,-3,-3 } }), new Kernel2D(0, (1/15.0), new double[,] { { -3,-3,-3 }, { 5,0,-3 }, { 5,5,-3 } }), new Kernel2D(0, (1/15.0), new double[,] { { -3,-3,-3 }, { -3,0,-3 }, { 5,5,5 } }), new Kernel2D(0, (1/15.0), new double[,] { { -3,-3,-3 }, { -3,0,5 }, { -3,5,5 } }), new Kernel2D(0, (1/15.0), new double[,] { { -3,-3,5 }, { -3,0,5 }, { -3,-3,5 } }), new Kernel2D(0, (1/15.0), new double[,] { { -3,5,5 }, { -3,0,5 }, { -3,-3,-3 } }) }; byte[,] tmp_ret = Kernel2DBatch.Evaluate( iImageSrc, kernels, delegate(Kernel2D k, byte[,] op, int x, int y) { double total = 0; Tuple<Point, Point>[] mappings = k.ResolvePositions(x, y, width, height); for (int i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { total += ( op[mappings[i].Item2.X, mappings[i].Item2.Y] * k.Matrix[mappings[i].Item1.X, mappings[i].Item1.Y] ); } } total = total * k.Multiplier + k.Threeshold; return total; // subtle bug discovered at 02-06-2011 //return Math.Min(byte.MaxValue, Math.Max(byte.MinValue, total)); }, delegate(List<double> values) { double tot = values[0]; //for (int i = 0; i < values.Count; ++i) //{ // tot += Math.Pow(values[i], 2); //} //tot = Math.Sqrt(tot); for (int i = 0; i < values.Count; ++i) { tot += Math.Abs(values[i]); } // Values smaller than the threshold are forced to zero if (tot < threshold) { tot = 0; } return (byte)Math.Min(byte.MaxValue, Math.Max(byte.MinValue, tot)); }); return tmp_ret; }
/// <summary> /// Apply a function to a byte[,] image, using the Kernel2D kernel. /// </summary> /// <param name="kernel">The kernel to use in the parsing.</param> /// <param name="operand">The image to apply the function.</param> /// <param name="callback">The function to apply.</param> /// <returns>The transformed byte[,].</returns> public static byte[,] ApplyFunction(Kernel2D kernel, byte[,] operand, ByteKernel2DCallBack callback) { int width = operand.GetLength(0); int height = operand.GetLength(1); byte[,] ret = new byte[width, height]; Tuple<Point, Point>[] mappings = null; int x, y; for (x = 0; x < width; ++x) { for (y = 0; y < height; ++y) { try { mappings = kernel.ResolvePositions(x, y, width, height); ret[x, y] = callback(mappings, operand); } catch (Exception e) { throw new Exception("Exception at Kernel2D.ApplyFunction", e); } } } return ret; }
/// <summary> /// Apply a convolution to the byte[,] image using the Kernel2D kernel. /// </summary> /// <remarks> /// As the output is a byte[,], the temporary result is stored in a double, but it will /// be clamped to be stored in a byte variable [0 .. 255]. /// </remarks> /// <param name="kernel">The kernel to use in the convolution.</param> /// <param name="operand">The image to apply the convolution.</param> /// <returns>The transformed byte[,].</returns> public static byte[,] ApplyConvolution(Kernel2D kernel, byte[,] operand) { double tot; int i; byte normalized; return ApplyFunction( kernel, operand, delegate(Tuple<Point, Point>[] mappings, byte[,] op) { tot = 0; normalized = byte.MinValue; for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { tot += ( kernel._2d_matrix[mappings[i].Item1.X, mappings[i].Item1.Y] * op[mappings[i].Item2.X, mappings[i].Item2.Y]); } } tot = tot * kernel._multiplier + kernel._threeshold; normalized = (byte)Math.Max(byte.MinValue, Math.Min(byte.MaxValue, tot)); return normalized; }); }
/// <summary> /// Apply a convolution to the double[,] image using the Kernel2D kernel. /// </summary> /// <remarks> /// The output will not be clamped to be stored as byte [0 .. 255]. /// </remarks> /// <param name="kernel">The kernel to use in the convolution.</param> /// <param name="operand">The image to apply the convolution.</param> /// <returns>The transformed double[,].</returns> public static double[,] ApplyConvolution(Kernel2D kernel, double[,] operand) { double tot; int i; double normalized; DoubleKernel2DCallBack function = delegate(Tuple<Point, Point>[] mappings, double[,] op) { tot = 0; normalized = 0; for (i = 0; i < mappings.Length; ++i) { if (mappings[i].Item2.X != -1 && mappings[i].Item2.Y != -1) { tot += ( kernel._2d_matrix[mappings[i].Item1.X, mappings[i].Item1.Y] * op[mappings[i].Item2.X, mappings[i].Item2.Y]); } } tot = tot * kernel._multiplier + kernel._threeshold; normalized = tot; return normalized; }; return ApplyFunction(kernel, operand, function); }