//NOTE: This ignores gain and iterations public static Convolution2D Subtract(Convolution2D orig, Convolution2D filtered, string description = "") { int width = Math.Min(orig.Width, filtered.Width); int height = Math.Min(orig.Height, filtered.Height); #region Calculate offsets int diffX = orig.Width - filtered.Width; int diffY = orig.Height - filtered.Height; int offsetOrigX = 0; int offsetOrigY = 0; int offsetFiltX = 0; int offsetFiltY = 0; if (diffX > 0) { offsetOrigX = Subtract_GetOffset(diffX); } else if (diffX < 0) { offsetFiltX = Subtract_GetOffset(-diffX); } if (diffY > 0) { offsetOrigY = Subtract_GetOffset(diffY); } else if (diffY < 0) { offsetFiltY = Subtract_GetOffset(-diffY); } #endregion double[] values = new double[width * height]; for (int y = 0; y < height; y++) { int filterY = (offsetFiltY + y) * filtered.Width; int origY = (offsetOrigY + y) * orig.Width; int valuesY = y * width; for (int x = 0; x < width; x++) { int filterIndex = filterY + offsetFiltX + x; int origIndex = origY + offsetOrigX + x; values[valuesY + x] = orig.Values[origIndex] - filtered.Values[filterIndex]; } } return new Convolution2D(values, width, height, true, description: description); }
public static Convolution2D Convolute(Convolution2D image, ConvolutionBase2D kernel, string description = "") { if (kernel is Convolution2D) { return Convolute_Single(image, (Convolution2D)kernel, description); } else if (kernel is ConvolutionSet2D) { return Convolute_Set(image, (ConvolutionSet2D)kernel, description); } else { throw new ApplicationException("Unexpected type of kernel: " + kernel.GetType().ToString()); } }
private void Browse_Click(object sender, RoutedEventArgs e) { const double MAXMULT = 2; try { var dialog = new Microsoft.Win32.OpenFileDialog(); dialog.Multiselect = false; dialog.Title = "Please select an image"; bool? result = dialog.ShowDialog(); if (result == null || !result.Value) { return; } _origConv = UtilityWPF.ConvertToConvolution(new BitmapImage(new Uri(dialog.FileName))); lblOrigWidth.Text = _origConv.Width.ToString("N0"); lblOrigHeight.Text = _origConv.Height.ToString("N0"); double max = Math.Max(_origConv.Width, _origConv.Height) * MAXMULT; trkWidth.Minimum = _origConv.Width; trkWidth.Maximum = max; trkWidth.Value = _origConv.Width; trkHeight.Minimum = _origConv.Height; trkHeight.Maximum = max; trkHeight.Value = _origConv.Height; _timer.IsEnabled = false; // touching the sliders causes the timer to be enabled grdSize.Visibility = Visibility.Visible; ShowConvolution(_origConv); } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
/// <summary> /// This is used for calling GetKernelBitmap /// </summary> /// <returns> /// Item1 = The dimensions of the image control (thumbSize, but maintains aspect ratio) /// Item2 = How big each pixel should be /// </returns> public static Tuple<Size, int> GetThumbSizeAndPixelMultiplier(Convolution2D kernel, int thumbSize) { // Figure out thumb size double width, height; if (kernel.Width == kernel.Height) { width = height = thumbSize; } else if (kernel.Width > kernel.Height) { width = thumbSize; height = Convert.ToDouble(kernel.Height) / Convert.ToDouble(kernel.Width) * thumbSize; } else { height = thumbSize; width = Convert.ToDouble(kernel.Width) / Convert.ToDouble(kernel.Height) * thumbSize; } // Figure out pixel size multiplier int pixelWidth = Convert.ToInt32(Math.Ceiling(width / kernel.Width)); int pixelHeight = Convert.ToInt32(Math.Ceiling(height / kernel.Height)); int pixelMult = Math.Max(pixelWidth, pixelHeight); if (pixelMult < 1) { pixelMult = 1; } return Tuple.Create(new Size(width, height), pixelMult); }
private static double[][] GetRotations_BW(double[] input, int width, int height) { Convolution2D conv = new Convolution2D(input, width, height, false); Convolution2D conv90 = Convolutions.Rotate_90(conv, true); return new[] { input, conv90.Values, Convolutions.Rotate_90(conv90, true).Values, Convolutions.Rotate_90(conv, false).Values, }; }
/// <summary> /// This just shows the kernel, no options to change it /// </summary> public void ViewKernel(Convolution2D kernel) { this.Title = string.Format("{0} - {1}x{2}", TITLE, kernel.Width, kernel.Height); panelEdit.Visibility = Visibility.Collapsed; StoreKernel(kernel); }
public void UpdateColors(bool isZeroToOne, bool isNegativeRedBlue) { // Coloring ConvolutionResultNegPosColoring coloring; if (isZeroToOne) { coloring = ConvolutionResultNegPosColoring.BlackWhite; } else if (isNegativeRedBlue) { coloring = ConvolutionResultNegPosColoring.RedBlue; } else { coloring = ConvolutionResultNegPosColoring.Gray; } // Maximize color range double min = this.Bars.Min(o => o.Height); double max = this.Bars.Max(o => o.Height); double absMax = Math.Max(Math.Abs(min), Math.Abs(max)); double scale; if (Math1D.IsInvalid(absMax) || Math1D.IsNearZero(absMax)) { scale = 1d; } else { scale = 255d / absMax; } // Get colors double[] values = this.Bars.Select(o => o.Height).ToArray(); Convolution2D conv = new Convolution2D(values, values.Length, 1, !isZeroToOne); byte[][] colors = Convolutions.GetColors(conv, coloring, scale); // Apply colors for (int cntr = 0; cntr < this.Bars.Length; cntr++) { Color color = Color.FromRgb(colors[cntr][1], colors[cntr][2], colors[cntr][3]); Material material = UtilityWPF.GetUnlitMaterial(color); this.Bars[cntr].Model.BackMaterial = material; this.Bars[cntr].Model.Material = material; } }
private void StoreKernel(Convolution2D kernel) { _isProgramaticallyChangingSettings = true; _width = kernel.Width; _height = kernel.Height; // The kernel passed in is probably unit. Maximize the height so that it looks nice and 3D _values = Convolutions.ToMaximized(kernel.Values).ToArray(); // taking ToArray to ensure it's cloned if (kernel.IsNegPos) { radRangeNegPos.IsChecked = true; } else { radRangeZeroPos.IsChecked = true; } txtWidth.Text = _width.ToString(); txtHeight.Text = _height.ToString(); _isProgramaticallyChangingSettings = false; RedrawAxis(); RebuildBars(); PixelValueChanged(); double dist = Math1D.Avg(_axis.HalfX, _axis.HalfY) * 3; Vector3D newPos = _camera.Position.ToVector().ToUnit() * dist; _camera.Position = newPos.ToPoint(); }
/// <summary> /// This takes the absolute value of each pixel in a convolution /// </summary> public static Convolution2D Abs(Convolution2D conv, bool toUnit = false) { double[] values = conv.Values. Select(o => Math.Abs(o)). ToArray(); if (toUnit) { values = ToUnit(values); } string description = ""; if (!string.IsNullOrEmpty(conv.Description)) { description = string.Format("Abs({0})", description); } return new Convolution2D(values, conv.Width, conv.Height, false, conv.Gain, conv.Iterations, conv.ExpandBorder, description); }
public static Convolution2D ExtendBorders(Convolution2D conv, int width, int height) { if (width < conv.Width || height < conv.Height) { throw new ArgumentException(string.Format("The new size can't be smaller than old. Old={0},{1} -- New={2},{3}", conv.Width, conv.Height, width, height)); } VectorInt offset = new VectorInt() { X = (width - conv.Width) / 2, Y = (height - conv.Height) / 2, }; double[] values = new double[width * height]; #region Copy the image for (int y = 0; y < conv.Height; y++) { int offsetOrigY = y * conv.Width; int offsetNewY = (y + offset.Y) * width; for (int x = 0; x < conv.Width; x++) { values[offsetNewY + offset.X + x] = conv.Values[offsetOrigY + x]; } } #endregion #region Edges bool hasNegX = offset.X > 0; AxisFor forNegX = new AxisFor(Axis.X, offset.X - 1, 0); if (hasNegX) { ExtendEdge(values, width, conv.Width, forNegX, new AxisFor(Axis.Y, offset.Y, offset.Y + conv.Height - 1)); } bool hasNegY = offset.Y > 0; AxisFor forNegY = new AxisFor(Axis.Y, offset.Y - 1, 0); if (hasNegY) { ExtendEdge(values, width, conv.Height, forNegY, new AxisFor(Axis.X, offset.X, offset.X + conv.Width - 1)); } bool hasPosX = width > offset.X + conv.Width; AxisFor forPosX = new AxisFor(Axis.X, offset.X + conv.Width, width - 1); if (hasPosX) { ExtendEdge(values, width, conv.Width, forPosX, new AxisFor(Axis.Y, offset.Y, offset.Y + conv.Height - 1)); } bool hasPosY = height > offset.Y + conv.Height; AxisFor forPosY = new AxisFor(Axis.Y, offset.Y + conv.Height, height - 1); if (hasPosY) { ExtendEdge(values, width, conv.Height, forPosY, new AxisFor(Axis.X, offset.X, offset.X + conv.Width - 1)); } #endregion #region Corners if (hasNegX && hasNegY) { ExtendCorner(values, width, forNegX, forNegY); } if (hasPosX && hasNegY) { ExtendCorner(values, width, forPosX, forNegY); } if (hasPosX && hasPosY) { ExtendCorner(values, width, forPosX, forPosY); } if (hasNegX && hasPosY) { ExtendCorner(values, width, forNegX, forPosY); } #endregion return new Convolution2D(values, width, height, conv.IsNegPos); }
private static Convolution2D Convolute_ExpandedBorder(Convolution2D image, Convolution2D kernel, string description) { throw new ApplicationException("finish this"); }
/// <summary> /// This applies the convolution kernel to the image /// </summary> /// <remarks> /// http://homepages.inf.ed.ac.uk/rbf/HIPR2/convolve.htm /// http://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm /// http://homepages.inf.ed.ac.uk/rbf/HIPR2/sobel.htm /// http://homepages.inf.ed.ac.uk/rbf/HIPR2/canny.htm /// /// http://www.tannerhelland.com/952/edge-detection-vb6/ /// </remarks> /// <param name="image">This is the original image</param> /// <param name="kernel">This is the kernel image (a small patch that will be slid over image)</param> /// <param name="expandBorder"> /// True: The output image will be the same size as the input (but border cells need to be made up for this to happen) /// False: The output image will be smaller (output.W = image.W - kernel.W + 1, same with height) /// </param> /// <returns></returns> private static Convolution2D Convolute_Single(Convolution2D image, Convolution2D kernel, string description) { Convolution2D retVal = image; for (int cntr = 0; cntr < kernel.Iterations; cntr++) { if (kernel.ExpandBorder) { retVal = Convolute_ExpandedBorder(retVal, kernel, description); } else { retVal = Convolute_Standard(retVal, kernel, description); } } return retVal; }
private static Convolution2D Convolute_Set_MaxOf(Convolution2D image, ConvolutionSet2D kernel, string description) { #region validate #if DEBUG if (kernel.OperationType != SetOperationType.MaxOf) { throw new ArgumentException("kernel must be MaxOf: " + kernel.OperationType.ToString()); } else if (kernel.Convolutions.Length < 2) { throw new ArgumentException("MaxOf kernel set needs at least two children"); } VectorInt firstReduce = kernel.Convolutions[0].GetReduction(); for (int cntr = 1; cntr < kernel.Convolutions.Length; cntr++) { VectorInt nextReduce = kernel.Convolutions[cntr].GetReduction(); if (firstReduce.X != nextReduce.X || firstReduce.Y != nextReduce.Y) { throw new ArgumentException("When the operation is MaxOf, then all kernels must reduce the same amount"); } } #endif #endregion Convolution2D[] children = kernel.Convolutions. Select(o => Convolute(image, o, description)). ToArray(); double[] retVal = new double[children[0].Values.Length]; for (int index = 0; index < retVal.Length; index++) { double maxValue = 0; double maxAbsValue = 0; for (int cntr = 0; cntr < children.Length; cntr++) { double abs = Math.Abs(children[cntr].Values[index]); if (abs > maxAbsValue) { maxValue = children[cntr].Values[index]; maxAbsValue = abs; } } retVal[index] = maxValue; } return new Convolution2D(retVal, children[0].Width, children[0].Height, image.IsNegPos || children.Any(o => o.IsNegPos), description: description); }
private static Convolution2D Convolute_Set(Convolution2D image, ConvolutionSet2D kernel, string description) { if (kernel.OperationType == SetOperationType.MaxOf) { // This is special, because it needs to convolute each child, then pick the values it wants to keep. Also, each child will need to be the same size return Convolute_Set_MaxOf(image, kernel, description); } Convolution2D retVal = image; foreach (ConvolutionBase2D child in kernel.Convolutions) { retVal = Convolute(retVal, child, description); } if (kernel.OperationType == SetOperationType.Subtract) { retVal = Convolutions.Subtract(image, retVal, description); } return retVal; }
/// <summary> /// This looks at a patch that is extracted from the result of an image and a convolution. This patch should be a relatively /// small rectangle centered around a bright spot (convolution.GetMax()). This returns a score from 0 to 1. This is looking /// for a peak that drops off (ideally a very large difference between the max pixel and min pixel) /// </summary> /// <remarks> /// TODO: May want another overload that takes a sample ideal patch to compare to: /// Subtract patches, compare differences /// line up the brightest point before subtracting /// allow larger differences farther away from center /// </remarks> public static double GetExtractScore(Convolution2D patch, VectorInt brightestPoint, double? idealBrightness = null) { const double PERCENT_POS_MIN = .25; const double PERCENT_POS_MAX = .75; const double PERCENT_NEG_MIN = -.25; const double PERCENT_NEG_MAX = .5; if (patch.Width < 3 || patch.Height < 3) { return 0; } double max = patch[brightestPoint]; // Get the min. If not enough difference from max, then return zero var min = patch.GetMin(); double percent = min.Item2 / max; double retVal = 0; if (patch.IsNegPos) { #region Neg/Pos if (percent < PERCENT_NEG_MIN) { retVal = 1; } else if (percent < PERCENT_NEG_MAX) { retVal = UtilityCore.GetScaledValue(1, 0, PERCENT_NEG_MIN, PERCENT_NEG_MAX, percent); } else { return 0; } #endregion } else { #region Positive if (percent < PERCENT_POS_MIN) { retVal = 1; } else if (percent < PERCENT_POS_MAX) { retVal = UtilityCore.GetScaledValue(1, 0, PERCENT_POS_MIN, PERCENT_POS_MAX, percent); } else { return 0; } #endregion } if (idealBrightness == null || max >= idealBrightness.Value) { return retVal; } return retVal * (max / idealBrightness.Value); }
public static string GetToolTip(Convolution2D conv, ConvolutionToolTipType type) { StringBuilder retVal = new StringBuilder(100); if (!string.IsNullOrEmpty(conv.Description)) { retVal.Append(conv.Description); } if (type == ConvolutionToolTipType.Size || type == ConvolutionToolTipType.Size_MinMax) { if (retVal.Length > 0) { retVal.AppendLine(); } retVal.AppendFormat("{0}x{1}", conv.Width, conv.Height); } if (type == ConvolutionToolTipType.Size_MinMax) { retVal.AppendLine(); double min = conv.Values.Min(); double max = conv.Values.Max(); retVal.AppendFormat("min: {0}\r\nmax: {1}", min.ToStringSignificantDigits(2), max.ToStringSignificantDigits(2)); } if (retVal.Length == 0) { return null; // otherwise there will be a tiny empty tooltip } else { return retVal.ToString(); } }
//TODO: Take an enum or something that lets the user pick what type of removals to choose from public static Convolution2D RemoveSection(Convolution2D conv, int? count = null) { int actualCount = count ?? StaticRandom.Next(1, 4); //Convolution2D mask = GetMask_ScatterShot(conv.Width, conv.Height); Convolution2D mask = GetMask_Ellipse(conv.Width, conv.Height, actualCount); double[] values = new double[conv.Values.Length]; for (int cntr = 0; cntr < values.Length; cntr++) { values[cntr] = conv.Values[cntr] * mask.Values[cntr]; } values = Convolutions.ToUnit(values); return new Convolution2D(values, conv.Width, conv.Height, conv.IsNegPos, conv.Gain, conv.Iterations, conv.ExpandBorder, conv.Description); }
private static Convolution2D Convolute_Standard(Convolution2D image, Convolution2D kernel, string description) { int returnWidth = image.Width - kernel.Width + 1; int returnHeight = image.Height - kernel.Height + 1; if (returnWidth <= 0 || returnHeight <= 0) { throw new ArgumentException(string.Format("The kernel is too large for the image. Image={0}x{1}, Kernel={2}x{3}", image.Width, image.Height, kernel.Width, kernel.Height)); } double[] values = new double[returnWidth * returnHeight]; for (int retY = 0; retY < returnHeight; retY++) { int returnOffset = retY * returnWidth; for (int retX = 0; retX < returnWidth; retX++) { double returnValue = 0; // Calculate the convoluted value at this return pixel for (int kerY = 0; kerY < kernel.Height; kerY++) { int imageOffset = (retY + kerY) * image.Width; int kernelOffet = kerY * kernel.Width; for (int kerX = 0; kerX < kernel.Width; kerX++) { returnValue += image.Values[imageOffset + retX + kerX] * kernel.Values[kernelOffet + kerX]; } } // Store it values[returnOffset + retX] = returnValue * kernel.Gain; } } return new Convolution2D(values, returnWidth, returnHeight, image.IsNegPos || kernel.IsNegPos, description: description); }
/// <summary> /// This reduces the convolution, keeping only the largest values in each source cell /// </summary> /// <remarks> /// NOTE: It's ok to enlarge if you need to, there will just be a hole of zeros in the center /// /// MaxPool isn't really meant to be visually appealing, it's just a way to reduce in order to feed a neural net /// </remarks> public static Convolution2D MaxPool(Convolution2D conv, int width, int height) { if (conv.Width == width && conv.Height == height) { return conv; } #region Cell Sizes // X int[] xRanges; if (conv.Width == width) { xRanges = Enumerable.Range(0, width).Select(o => 1).ToArray(); } else { xRanges = GetPoolCells(conv.Width, width); } Tuple<int, int>[] xStops = GetStops(xRanges); // Y int[] yRanges; if (conv.Height == height) { yRanges = Enumerable.Range(0, height).Select(o => 1).ToArray(); } else { yRanges = GetPoolCells(conv.Height, height); } Tuple<int, int>[] yStops = GetStops(yRanges); #endregion double[] values = new double[width * height]; for (int y = 0; y < height; y++) { int yOffset = y * width; for (int x = 0; x < width; x++) { // Get the largest offset from zero from this cell values[yOffset + x] = GetMax(conv.Values, conv.Width, xStops[x], yStops[y]); } } return new Convolution2D(values, width, height, conv.IsNegPos); }
private static Border GetThumbnail_Single(Convolution2D kernel, int thumbSize, ContextMenu contextMenu, ConvolutionToolTipType tooltipType) { // Figure out thumb size var sizes = GetThumbSizeAndPixelMultiplier(kernel, thumbSize); // Display it as a border and image Image image = new Image() { Source = GetBitmap_Aliased(kernel, sizes.Item2), Width = sizes.Item1.Width, Height = sizes.Item1.Height, ToolTip = GetToolTip(kernel, tooltipType), }; Border border = new Border() { Child = image, BorderBrush = new SolidColorBrush(UtilityWPF.ColorFromHex("C0C0C0")), BorderThickness = new Thickness(1), Margin = new Thickness(6), HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, ContextMenu = contextMenu, Tag = kernel, }; return border; }
public static Convolution2D Normalize(Convolution2D conv, double scale = 1d) { double[] values = MathND.Normalize(conv.Values); if(!scale.IsNearValue(1d)) { values = values. Select(o => o * scale). ToArray(); } string description = ""; if (!string.IsNullOrEmpty(conv.Description)) { description = string.Format("Normalize({0})", description); } return new Convolution2D(values, conv.Width, conv.Height, conv.IsNegPos, conv.Gain, conv.Iterations, conv.ExpandBorder, description); }
private static double GetColorScale(Convolution2D conv, double? absMaxValue) { if (absMaxValue == null) { double min = conv.Values.Min(o => o); double max = conv.Values.Max(o => o); double absMax = Math.Max(Math.Abs(min), Math.Abs(max)); if (Math1D.IsInvalid(absMax) || Math1D.IsNearZero(absMax)) { return 1d; } else { return 255d / absMax; } } else { if (Math1D.IsInvalid(absMaxValue.Value) || Math1D.IsNearZero(absMaxValue.Value)) { return 1d; } else { return 255d / absMaxValue.Value; } } }
private void PixelValueChanged() { Convolution2D kernel = new Convolution2D(_values, _width, _height, radRangeNegPos.IsChecked.Value); ConvolutionResultNegPosColoring coloring = chkIsRedBlue.IsChecked.Value ? ConvolutionResultNegPosColoring.RedBlue : ConvolutionResultNegPosColoring.BlackWhite; // Show Kernel BitmapSource kernelBitmap = Convolutions.GetBitmap_Aliased(kernel, negPosColoring: coloring); imagePreview.Source = kernelBitmap; imagePreview.Width = kernelBitmap.PixelWidth; imagePreview.Height = kernelBitmap.PixelHeight; }
public static Convolution2D Rotate_90(Convolution2D convolution, bool isClockwise) { double[] newValues = Rotate_90(convolution.Values, convolution.Width, convolution.Height, isClockwise); //NOTE: width becomes height return new Convolution2D(newValues, convolution.Height, convolution.Width, convolution.IsNegPos, convolution.Gain, convolution.Iterations, convolution.ExpandBorder, convolution.Description); }
/// <summary> /// This shows the kernel, and allows the user to edit it /// </summary> public void EditKernel(Convolution2D kernel) { this.Title = TITLE; panelEdit.Visibility = Visibility.Visible; StoreKernel(kernel); }
/// <summary> /// Runs a gaussian over the current row and the two prior. Then copies those blurred values onto this row /// </summary> /// <param name="orthIndex">The row being copied to</param> private static void OverlayBlurredPixels(double[] values, int width, int orthHeight, int orthIndex, int orthInc, int edgeMidStart, int edgeMidStop, AxisFor orth, AxisFor edge, Convolution2D gauss, /*int opacityDistance,*/ Random rand) { if (orthHeight < 2 || edgeMidStop < edgeMidStart) { // There's not enough to do a full 3x3 blur. A smaller sized blur could be done, but that's a lot of extra logic, and not really worth the trouble return; } //double opacity = UtilityCore.GetScaledValue_Capped(0d, 1d, 0, opacityDistance, Math.Abs(orthIndex - orth.Start)); // Get a random midpoint int edgeMid = rand.Next(edgeMidStart, edgeMidStop + 1); AxisFor orth3 = new AxisFor(orth.Axis, orthIndex + (orthInc * 2), orthIndex); AxisFor leftEdge = new AxisFor(edge.Axis, edge.Start, edgeMid + (edge.Increment * 2)); AxisFor rightEdge = new AxisFor(edge.Axis, edgeMid - (edge.Increment * 2), edge.Stop); // Copy the values (these are 3 tall, and edgeMid-edgeStart+2 wide) Convolution2D leftRect = CopyRect(values, width, leftEdge, orth3, false); Convolution2D rightRect = CopyRect(values, width, rightEdge, orth3, true); // this one is rotated 180 so that when the gaussian is applied, it will be from the right border to the mid // Apply a gaussian (these are 1 tall, and edgeMid-edgeStart wide) Convolution2D leftBlurred = Convolutions.Convolute(leftRect, gauss); Convolution2D rightBlurred = Convolutions.Convolute(rightRect, gauss); // Overlay onto this newest row //TODO: use an opacity LERP from original edge OverlayRow(values, width, orthIndex, leftBlurred.Values, orth, edge, /*opacity,*/ true); OverlayRow(values, width, orthIndex, rightBlurred.Values, orth, edge, /*opacity,*/ false); }
private void Save_Click(object sender, RoutedEventArgs e) { try { if (this.SaveRequested == null) { MessageBox.Show("There is no listener to the save event", this.Title, MessageBoxButton.OK, MessageBoxImage.Warning); return; } double[] normalized = Convolutions.ToUnit(_values); Convolution2D kernel = new Convolution2D(normalized, _width, _height, radRangeNegPos.IsChecked.Value); this.SaveRequested(this, kernel); } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
public static Convolution2D Rotate_45(Convolution2D convolution, bool isClockwise, bool shouldAdvanceEvensExtra = false) { if (convolution.Width != convolution.Height) { throw new ArgumentException("Width and height must be the same for 45 degree rotations"); } double[] newValues = Rotate_45(convolution.Values, convolution.Width, isClockwise, shouldAdvanceEvensExtra); return new Convolution2D(newValues, convolution.Width, convolution.Height, convolution.IsNegPos, convolution.Gain, convolution.Iterations, convolution.ExpandBorder, convolution.Description); }
// These merge the 3 back into one big array private static double[] MergeColor(Convolution2D r, Convolution2D g, Convolution2D b) { return MergeColor(r.Values, g.Values, b.Values); }
public static Convolution2D Invert(Convolution2D convolution, double? maxValue = null) { double[] newValues = Invert(convolution.Values, convolution.IsNegPos, maxValue); return new Convolution2D(newValues, convolution.Width, convolution.Height, convolution.IsNegPos, convolution.Gain, convolution.Iterations, convolution.ExpandBorder, convolution.Description); }