/// <summary> /// This creates solid colored blobs with areas proportional to the number of items contained. When the user /// mouses over a blob, the caller can show examples of the items as tooltips /// </summary> public static void ShowResults2D_Blobs(Border border, SOMResult result, Func<SOMNode, Color> getNodeColor, BlobEvents events = null) { #region validate #if DEBUG if (!result.Nodes.All(o => o.Position.Length == 2)) { throw new ArgumentException("Node positions need to be 2D"); } #endif #endregion Point[] points = result.Nodes. Select(o => new Point(o.Position[0], o.Position[1])). ToArray(); VoronoiResult2D voronoi = Math2D.GetVoronoi(points, true); voronoi = Math2D.CapVoronoiCircle(voronoi); Color[] colors = result.Nodes. Select(o => getNodeColor(o)). ToArray(); //ISOMInput[][] inputsByNode = UtilityCore.ConvertJaggedArray<ISOMInput>(result.InputsByNode); Vector size = new Vector(border.ActualWidth - border.Padding.Left - border.Padding.Right, border.ActualHeight - border.Padding.Top - border.Padding.Bottom); Canvas canvas = DrawVoronoi_Blobs(voronoi, colors, result.Nodes, result.InputsByNode, size.X.ToInt_Floor(), size.Y.ToInt_Floor(), events); border.Child = canvas; }
public DBRowsDetail(SelfOrganizingMapsDBWindow.QueryResults queryResults, SelfOrganizingMapsDBWindow.ColumnSetStats columns, SOMResult somResult, int nodeIndex) { InitializeComponent(); _queryResults = queryResults; _columns = columns; _somResult = somResult; _nodeIndex = nodeIndex; }
public void Add(VectorND item) { //TODO: Probably want something that ignores images if this is too soon from a previous call SOMItem somItem = GetSOMItem(item, _instructions); SOMResult result = _result; if (result != null) { if (_discardDupes) { // Run this through the SOM var closest = SelfOrganizingMaps.GetClosest(result.Nodes, somItem); // If it's too similar to another, then just ignore it if (IsTooClose(somItem, result.InputsByNode[closest.Item2], _dupeDistSquared)) { return; } } } #region process batch // Store this in a need-to-work list // When that list gets to a certain size, build a new SOM SOMItem[] newItemBatch = null; lock (_lock) { _newItems.Add(somItem); if (!_isProcessingBatch && _newItems.Count > _nextBatchSize) { _nextBatchSize = StaticRandom.Next(BATCHMIN, BATCHMAX); newItemBatch = _newItems.ToArray(); _newItems.Clear(); _isProcessingBatch = true; } } if (newItemBatch != null) { Task.Run(() => { _result = ProcessNewItemBatch(newItemBatch, _discardDupes, _dupeDistSquared, _result); }). ContinueWith(t => { lock (_lock) _isProcessingBatch = false; }); } #endregion }
private static SOMResult ProcessNewItemBatch(SOMItem[] newItemBatch, bool discardDupes, double dupeDistSquared, SOMResult existing) { const int TOTALMAX = BATCHMAX * 10; // Items only make it here when they aren't too similar to the som nodes, but items within this list may be dupes if (discardDupes) { newItemBatch = DedupeItems(newItemBatch, dupeDistSquared); } if (newItemBatch.Length > BATCHMAX) { // There are too many, just take a sample newItemBatch = UtilityCore.RandomRange(0, newItemBatch.Length, BATCHMAX). Select(o => newItemBatch[o]). ToArray(); } SOMItem[] existingItems = null; if (existing != null) { existingItems = existing.InputsByNode. SelectMany(o => o). Select(o => ((SOMInput <SOMItem>)o).Source). ToArray(); } SOMItem[] allItems = UtilityCore.ArrayAdd(existingItems, newItemBatch); //TODO: This is too simplistic. See if existingItems + newItemBatch > total. If so, try to draw down the existing nodes evenly. Try //to preserve previous images better. Maybe even throw in a timestamp to get a good spread of times // //or get a SOM of the new, independent of the old. Then merge the two pulling representatives to keep the most diversity. Finally, //take a SOM of the combined if (allItems.Length > TOTALMAX) { allItems = UtilityCore.RandomRange(0, allItems.Length, TOTALMAX). Select(o => allItems[o]). ToArray(); } SOMInput <SOMItem>[] inputs = allItems. Select(o => new SOMInput <SOMItem>() { Source = o, Weights = o.Weights, }). ToArray(); //TODO: May want rules to persist from run to run SOMRules rules = GetSOMRules_Rand(); return(SelfOrganizingMaps.TrainSOM(inputs, rules, true)); }
/// <summary> /// This divides the border up into a voronoi, then each node is tiled with examples /// </summary> public static void ShowResults2D_Tiled(Border border, SOMResult result, int tileWidth, int tileHeight, Action<DrawTileArgs> drawTile, BlobEvents events = null) { //TODO: Take a func that will render the input onto a writable bitmap, or something dynamic but efficient? // or take these in? //int tileWidth, int tileHeight Point[] points = result.Nodes. Select(o => new Point(o.Position[0], o.Position[1])). ToArray(); Vector size = new Vector(border.ActualWidth - border.Padding.Left - border.Padding.Right, border.ActualHeight - border.Padding.Top - border.Padding.Bottom); VoronoiResult2D voronoi = Math2D.GetVoronoi(points, true); voronoi = Math2D.CapVoronoiCircle(voronoi); //voronoi = Math2D.CapVoronoiRectangle(voronoi, aspectRatio: 1d); //TODO: Implement this Canvas canvas = DrawVoronoi_Tiled(voronoi, result.Nodes, result.InputsByNode, size.X.ToInt_Floor(), size.Y.ToInt_Floor(), tileWidth, tileHeight, drawTile, events); border.Child = canvas; }
private void Exception_SOM(SOMRequest req, Exception ex) { _result = new SOMResult(new SOMNode[0], new RowInput[0][], false); DoSOM(); }
private static SOMResult ProcessNewItemBatch(SOMItem[] newItemBatch, bool discardDupes, double dupeDistSquared, SOMResult existing) { const int TOTALMAX = BATCHMAX * 10; // Items only make it here when they aren't too similar to the som nodes, but items within this list may be dupes if (discardDupes) { newItemBatch = DedupeItems(newItemBatch, dupeDistSquared); } if (newItemBatch.Length > BATCHMAX) { // There are too many, just take a sample newItemBatch = UtilityCore.RandomRange(0, newItemBatch.Length, BATCHMAX). Select(o => newItemBatch[o]). ToArray(); } SOMItem[] existingItems = null; if (existing != null) { existingItems = existing.InputsByNode. SelectMany(o => o). Select(o => ((SOMInput<SOMItem>)o).Source). ToArray(); } SOMItem[] allItems = UtilityCore.ArrayAdd(existingItems, newItemBatch); //TODO: This is too simplistic. See if existingItems + newItemBatch > total. If so, try to draw down the existing nodes evenly. Try //to preserve previous images better. Maybe even throw in a timestamp to get a good spread of times // //or get a SOM of the new, independent of the old. Then merge the two pulling representatives to keep the most diversity. Finally, //take a SOM of the combined if (allItems.Length > TOTALMAX) { allItems = UtilityCore.RandomRange(0, allItems.Length, TOTALMAX). Select(o => allItems[o]). ToArray(); } SOMInput<SOMItem>[] inputs = allItems. Select(o => new SOMInput<SOMItem>() { Source = o, Weights = o.Weights, }). ToArray(); //TODO: May want rules to persist from run to run SOMRules rules = GetSOMRules_Rand(); return SelfOrganizingMaps.TrainSOM(inputs, rules, true); }
private void Timer_Tick(object sender, EventArgs e) { try { #region prep lblError.Text = ""; lblError.Visibility = Visibility.Collapsed; if (_recognizer == null) { _timer.Stop(); ClearVisuals("no recognizer set"); } if (!_recognizer.IsOn) { ClearVisuals("Powered Off"); return; } Tuple <int, int> cameraWidthHeight = _recognizer.CameraWidthHeight; if (cameraWidthHeight == null) { ClearVisuals("recognizer's camera not set"); return; } bool isColor = _recognizer.IsColor; #endregion #region latest image double[] image = _recognizer.LatestImage; if (image == null) { canvasPixels.Source = null; } else { if (isColor) { canvasPixels.Source = UtilityWPF.GetBitmap_RGB(image, cameraWidthHeight.Item1, cameraWidthHeight.Item2); } else { canvasPixels.Source = UtilityWPF.GetBitmap(image, cameraWidthHeight.Item1, cameraWidthHeight.Item2); } } #endregion #region nn outputs Tuple <LifeEventType, double>[] nnOutputs = _recognizer.CurrentOutput; panelOutputs.Children.Clear(); if (nnOutputs != null) { DrawNNOutputs(panelOutputs, nnOutputs); } #endregion #region som SOMResult som = _recognizer.SOM; bool shouldRenderSOM = false; if (som == null) { panelSOM.Visibility = Visibility.Collapsed; panelSOM.Child = null; _currentSom = null; } else if (som != null) { shouldRenderSOM = true; if (_currentSom != null && som.Nodes.Length == _currentSom.Nodes.Length && som.Nodes.Length > 0 && som.Nodes[0].Token == _currentSom.Nodes[0].Token) { shouldRenderSOM = false; } } if (shouldRenderSOM) { SelfOrganizingMapsWPF.ShowResults2D_Tiled(panelSOM, som, cameraWidthHeight.Item1, cameraWidthHeight.Item2, DrawSOMTile); _currentSom = som; panelSOM.Visibility = Visibility.Visible; } #endregion #region training data var trainingData = _recognizer.TrainingData; if (trainingData == null || trainingData.Item1 == null || trainingData.Item1.ImportantEvents == null || trainingData.Item1.ImportantEvents.Length == 0) { _currentTrainingData = null; panelTrainingData.Child = null; panelTrainingData.Visibility = Visibility.Collapsed; } else { if (!IsSame(_currentTrainingData, trainingData)) { _currentTrainingData = trainingData; DrawTrainingData(panelTrainingData, trainingData); panelTrainingData.Visibility = Visibility.Visible; } } #endregion } catch (Exception ex) { lblError.Text = ex.Message; lblError.Visibility = Visibility.Visible; } }
private void ColumnSlider_ValueChanged(object sender, EventArgs e) { try { SliderShowValues senderCast = sender as SliderShowValues; if (senderCast == null) { MessageBox.Show("Couldn't cast sender as a SliderShowValues", TITLE, MessageBoxButton.OK, MessageBoxImage.Warning); return; } ColumnStats column = senderCast.Tag as ColumnStats; if (column == null) { MessageBox.Show("Couldn't cast sender.Tag as ColumnStats", TITLE, MessageBoxButton.OK, MessageBoxImage.Warning); return; } column.Width = senderCast.Value.ToInt_Round(); _result = null; DoSOM(); } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
private void ColumnTextCheckbox_Checked(object sender, RoutedEventArgs e) { try { CheckBox senderCast = sender as CheckBox; if (senderCast == null) { MessageBox.Show("Couldn't cast sender as a checkbox", TITLE, MessageBoxButton.OK, MessageBoxImage.Warning); return; } ColumnStats column = senderCast.Tag as ColumnStats; if (column == null) { MessageBox.Show("Couldn't cast sender.Tag as ColumnStats", TITLE, MessageBoxButton.OK, MessageBoxImage.Warning); return; } column.ForceText = senderCast.IsChecked.Value; _result = null; DoSOM(); } catch (Exception ex) { MessageBox.Show(ex.ToString(), TITLE, MessageBoxButton.OK, MessageBoxImage.Error); } }
private void ShowResults_Disk(Border border, SOMResult result, Func<SOMNode, Color> getNodeColor) { Point[] points = result.Nodes. Select(o => new Point(o.Position[0], o.Position[1])). ToArray(); VoronoiResult2D voronoi = Math2D.GetVoronoi(points, true); voronoi = Math2D.CapVoronoiCircle(voronoi); Color[] colors = result.Nodes. Select(o => getNodeColor(o)). ToArray(); ImageInput[][] imagesByNode = UtilityCore.ConvertJaggedArray<ImageInput>(result.InputsByNode); Vector size = new Vector(border.ActualWidth - border.Padding.Left - border.Padding.Right, border.ActualHeight - border.Padding.Top - border.Padding.Bottom); Canvas canvas = DrawVoronoi(voronoi, colors, result.Nodes, imagesByNode, size.X.ToInt_Floor(), size.Y.ToInt_Floor()); border.Child = canvas; // This is for the manual manipulate buttons _nodes = result.Nodes; _imagesByNode = imagesByNode; _wasEllipseTransferred = false; }
private void Timer_Tick(object sender, EventArgs e) { try { #region prep lblError.Text = ""; lblError.Visibility = Visibility.Collapsed; if (_recognizer == null) { _timer.Stop(); ClearVisuals("no recognizer set"); } if (!_recognizer.IsOn) { ClearVisuals("Powered Off"); return; } Tuple<int, int> cameraWidthHeight = _recognizer.CameraWidthHeight; if (cameraWidthHeight == null) { ClearVisuals("recognizer's camera not set"); return; } bool isColor = _recognizer.IsColor; #endregion #region latest image double[] image = _recognizer.LatestImage; if (image == null) { canvasPixels.Source = null; } else { if (isColor) { canvasPixels.Source = UtilityWPF.GetBitmap_RGB(image, cameraWidthHeight.Item1, cameraWidthHeight.Item2); } else { canvasPixels.Source = UtilityWPF.GetBitmap(image, cameraWidthHeight.Item1, cameraWidthHeight.Item2); } } #endregion #region nn outputs Tuple<LifeEventType, double>[] nnOutputs = _recognizer.CurrentOutput; panelOutputs.Children.Clear(); if (nnOutputs != null) { DrawNNOutputs(panelOutputs, nnOutputs); } #endregion #region som SOMResult som = _recognizer.SOM; bool shouldRenderSOM = false; if (som == null) { panelSOM.Visibility = Visibility.Collapsed; panelSOM.Child = null; _currentSom = null; } else if (som != null) { shouldRenderSOM = true; if (_currentSom != null && som.Nodes.Length == _currentSom.Nodes.Length && som.Nodes.Length > 0 && som.Nodes[0].Token == _currentSom.Nodes[0].Token) { shouldRenderSOM = false; } } if (shouldRenderSOM) { SelfOrganizingMapsWPF.ShowResults2D_Tiled(panelSOM, som, cameraWidthHeight.Item1, cameraWidthHeight.Item2, DrawSOMTile); _currentSom = som; panelSOM.Visibility = Visibility.Visible; } #endregion #region training data var trainingData = _recognizer.TrainingData; if (trainingData == null || trainingData.Item1 == null || trainingData.Item1.ImportantEvents == null || trainingData.Item1.ImportantEvents.Length == 0) { _currentTrainingData = null; panelTrainingData.Child = null; panelTrainingData.Visibility = Visibility.Collapsed; } else { if (!IsSame(_currentTrainingData, trainingData)) { _currentTrainingData = trainingData; DrawTrainingData(panelTrainingData, trainingData); panelTrainingData.Visibility = Visibility.Visible; } } #endregion } catch (Exception ex) { lblError.Text = ex.Message; lblError.Visibility = Visibility.Visible; } }
private static SelfOrganizingMapsDBWindow.RowInput[] GetMatchingRows(SelfOrganizingMapsDBWindow.ColumnStats[] columns, SelfOrganizingMapsDBWindow.QueryResults queryResults, SOMResult som, int nodeIndex) { return(SelfOrganizingMapsDBWindow.GetSOMInputs(columns, queryResults, false). Where(o => SelfOrganizingMaps.GetClosest(som.Nodes, o).Item2 == nodeIndex). ToArray()); }
private static SelfOrganizingMapsDBWindow.RowInput[] GetMatchingRows(SelfOrganizingMapsDBWindow.ColumnStats[] columns, SelfOrganizingMapsDBWindow.QueryResults queryResults, SOMResult som, int nodeIndex) { return SelfOrganizingMapsDBWindow.GetSOMInputs(columns, queryResults, false). Where(o => SelfOrganizingMaps.GetClosest(som.Nodes, o).Item2 == nodeIndex). ToArray(); }
/// <summary> /// This looks at the current inputs, and starts a new SOM async /// </summary> private void DoSOM() { #region Init // Parse all the inputs QueryRequest queryReq = GetQueryRequest(); if (queryReq.Columns.Length == 0) { lblErrorMessage.Text = "No columns specified"; lblQueryStatus.Text = ""; pnlColumns.Children.Clear(); _queryResults = null; _columns = null; _result = null; return; } #endregion #region Query // Run query if changed if (_queryResults == null || _queryResults.ConnectionString != queryReq.ConnectionString || _queryResults.SQLStatement != queryReq.SQLStatement) { lblQueryStatus.Text = "Running query..."; lblErrorMessage.Text = ""; pnlColumns.Children.Clear(); _columns = null; _result = null; _workerQuery.Start(queryReq); return; //_workerQuery.finish will call this method again, and execution will flow past this if statement } lblQueryStatus.Text = ""; if (!string.IsNullOrEmpty(_queryResults.Exception)) { lblErrorMessage.Text = _queryResults.Exception; _result = null; return; } lblQueryStatus.Text = string.Format("{0} row{1}", _queryResults.Results.Length.ToString("N0"), _queryResults.Results.Length == 1 ? "" : "s"); #endregion #region Columns if (_columns != null) { _columns = _columns.CloneIfSameNames(queryReq.Columns, _queryResults.ColumnNames); //NOTE: This will still clone if it just holds an exception } if (_columns == null || !IsSame(queryReq.Columns, _columns.Names)) { lblErrorMessage.Text = ""; _result = null; _workerColumns.Start(new ColumnStatsRequest(queryReq.Columns, _queryResults)); return; } if (_columns != null && !string.IsNullOrEmpty(_columns.Exception)) { lblErrorMessage.Text = _columns.Exception; _result = null; return; } #endregion #region Do SOM if (_result == null) { RowInput[] inputs = GetSOMInputs(_columns.Columns, _queryResults, true); //TODO: Get these from the gui. Add an option to randomize against their settings SOMRules rules = GetSOMRules_Rand(); //TODO: Make an option for display1D. Then do a SOM for each column and put the results in the column details dump _workerSOM.Start(new SOMRequest(inputs, rules)); return; } #endregion #region Show Results var events = new SelfOrganizingMapsWPF.BlobEvents(Polygon_MouseMove, Polygon_MouseLeave, Polygon_Click); SelfOrganizingMapsWPF.ShowResults2D_Blobs(panelDisplay, _result, SelfOrganizingMapsWPF.GetNodeColor, events); #endregion }
private void Finished_SOM(SOMRequest req, SOMResult result) { _result = result; DoSOM(); }
private static void SaveImages(string parentFolder, string childFolder, int width, int height, SOMResult som, bool isColor) { if (som == null || som.InputsByNode == null || som.InputsByNode.Length == 0) { return; } // Pull out the images double[][] images = som.InputsByNode. SelectMany(o => o). Select(o => { var cast = o as SOMInput<SOMList.SOMItem>; if (cast == null) return null; return cast.Source.Original; }). Where(o => o != null). ToArray(); SaveImages(parentFolder, childFolder, width, height, images, isColor); }
public void Add(double[] item) { //TODO: Probably want something that ignores images if this is too soon from a previous call SOMItem somItem = GetSOMItem(item, _instructions); SOMResult result = _result; if (result != null) { if (_discardDupes) { // Run this through the SOM var closest = SelfOrganizingMaps.GetClosest(result.Nodes, somItem); // If it's too similar to another, then just ignore it if (IsTooClose(somItem, result.InputsByNode[closest.Item2], _dupeDistSquared)) { return; } } } #region process batch // Store this in a need-to-work list // When that list gets to a certain size, build a new SOM SOMItem[] newItemBatch = null; lock (_lock) { _newItems.Add(somItem); if (!_isProcessingBatch && _newItems.Count > _nextBatchSize) { _nextBatchSize = StaticRandom.Next(BATCHMIN, BATCHMAX); newItemBatch = _newItems.ToArray(); _newItems.Clear(); _isProcessingBatch = true; } } if (newItemBatch != null) { Task.Run(() => { _result = ProcessNewItemBatch(newItemBatch, _discardDupes, _dupeDistSquared, _result); }). ContinueWith(t => { lock (_lock) _isProcessingBatch = false; }); } #endregion }