diff --git a/NumberRecognizer/NumberRecognizer.App/Common/BooleanToVisibilityConverter.cs b/NumberRecognizer/NumberRecognizer.App/Common/BooleanToVisibilityConverter.cs new file mode 100644 index 0000000..8e2bff4 --- /dev/null +++ b/NumberRecognizer/NumberRecognizer.App/Common/BooleanToVisibilityConverter.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Graphics.Display; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Data; + +namespace NumberRecognizer.App.Common +{ + /// + /// Wertkonverter, der TRUE in und FALSE in + /// übersetzt. + /// + public class BooleanToVisibilityConverter : IValueConverter + { + /// + /// Ändert die Quelldaten vor der Übergabe an das Ziel zur Anzeige in der Benutzeroberfläche. + /// + /// Die Quelldaten, die ans Ziel übergeben werden. + /// Der Typ der Zieleigenschaft. Dadurch wird ein anderer Typ verwendet, abhängig davon, ob Sie mit Microsoft .NET oder Visual C++-Komponentenerweiterungen (C++/CX) programmieren. Siehe Hinweise. + /// Ein optionaler Parameter, der in der Konverterlogik verwendet wird. + /// Die Sprache der Konvertierung. + /// + /// Der Wert, der an die Zielabhängigkeitseigenschaft übergeben werden soll. + /// + public object Convert(object value, Type targetType, object parameter, string language) + { + if (parameter != null) + { + return (value is bool && (bool)value) ? Visibility.Collapsed : Visibility.Visible; + } + return (value is bool && (bool)value) ? Visibility.Visible : Visibility.Collapsed; + } + + /// + /// Ändert die Zieldaten vor der Übergabe an das Quellobjekt. Diese Methode wird nur in TwoWay-Bindungen aufgerufen. + /// + /// Die Zieldaten, die an die Quelle übergeben werden. + /// Der Typ der Zieleigenschaft, angegeben durch eine Hilfestruktur, die den Typnamen umschließt. + /// Ein optionaler Parameter, der in der Konverterlogik verwendet wird. + /// Die Sprache der Konvertierung. + /// + /// Der Wert, der an das Quellobjekt weitergeleitet wird. + /// + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return value is Visibility && (Visibility)value == Visibility.Visible; + } + } +} diff --git a/NumberRecognizer/NumberRecognizer.App/Control/InkCanvasRT.cs b/NumberRecognizer/NumberRecognizer.App/Control/InkCanvasRT.cs index 7e6b0c2..219bb96 100644 --- a/NumberRecognizer/NumberRecognizer.App/Control/InkCanvasRT.cs +++ b/NumberRecognizer/NumberRecognizer.App/Control/InkCanvasRT.cs @@ -138,6 +138,14 @@ private static double Distance(Point p1, Point p2) /// The source of the event. /// The instance containing the event data. private void InkCanvasRT_RightTapped(object sender, RightTappedRoutedEventArgs e) + { + ClearInk(); + } + + /// + /// Clears the ink canvas. + /// + public void ClearInk() { this.Children.Clear(); this.inkManager = new InkManager(); diff --git a/NumberRecognizer/NumberRecognizer.App/DataModel/LocalTrainingImage.cs b/NumberRecognizer/NumberRecognizer.App/DataModel/LocalTrainingImage.cs new file mode 100644 index 0000000..940cd3f --- /dev/null +++ b/NumberRecognizer/NumberRecognizer.App/DataModel/LocalTrainingImage.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Storage; +using NumberRecognizer.Cloud.Contract.Data; + +namespace NumberRecognizer.App.DataModel +{ + public class LocalTrainingImage + { + public TrainingImage ImageData { get; set; } + + public String LocalImagePath { get; set; } + } +} diff --git a/NumberRecognizer/NumberRecognizer.App/DataModel/LocalTrainingImageGroup.cs b/NumberRecognizer/NumberRecognizer.App/DataModel/LocalTrainingImageGroup.cs new file mode 100644 index 0000000..fa94084 --- /dev/null +++ b/NumberRecognizer/NumberRecognizer.App/DataModel/LocalTrainingImageGroup.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GalaSoft.MvvmLight; + +namespace NumberRecognizer.App.DataModel +{ + public class LocalTrainingImageGroup : ViewModelBase + { + /// + /// The titel + /// + private string title; + + /// + /// Initializes a new instance of the class. + /// + /// The unique identifier. + /// The title. + public LocalTrainingImageGroup(string uniqueId, string title) + { + this.UniqueId = uniqueId; + this.Title = title; + this.Items = new ObservableCollection(); + } + + /// + /// Gets the unique identifier. + /// + /// + /// The unique identifier. + /// + public string UniqueId { get; private set; } + + /// + /// Gets the title. + /// + /// + /// The title. + /// + public string Title + { + get + { + return title; + } + private set + { + this.title = value; + RaisePropertyChanged(() => Title); + } + } + + /// + /// Gets the items. + /// + /// + /// The items. + /// + public ObservableCollection Items { get; set; } + } +} diff --git a/NumberRecognizer/NumberRecognizer.App/Help/ImageHelperRT.cs b/NumberRecognizer/NumberRecognizer.App/Help/ImageHelperRT.cs index 6f64b6a..8bcea1e 100644 --- a/NumberRecognizer/NumberRecognizer.App/Help/ImageHelperRT.cs +++ b/NumberRecognizer/NumberRecognizer.App/Help/ImageHelperRT.cs @@ -150,7 +150,7 @@ public static byte[] GetByteArrayFrom2DPixelArrayAsync(double[,] pixelArray2D) /// /// The red, green, blue, alpha byte array as bitmap image asynchronous. /// - public static async Task SaveRGBAByteArrayAsBitmapImageAsync(byte[] rgbaByteArray, double width, double height, string name) + public static async Task SaveRGBAByteArrayAsBitmapImageAsync(byte[] rgbaByteArray, double width, double height, string name) { var storageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(name + ".png", CreationCollisionOption.ReplaceExisting); @@ -161,6 +161,8 @@ public static async Task SaveRGBAByteArrayAsBitmapImageAsync(byte[] rgbaByteArra bitmapEncoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.NearestNeighbor; await bitmapEncoder.FlushAsync(); } + + return storageFile.Path; } /// diff --git a/NumberRecognizer/NumberRecognizer.App/NumberRecognizer.App.csproj b/NumberRecognizer/NumberRecognizer.App/NumberRecognizer.App.csproj index dea49a4..4f03df2 100644 --- a/NumberRecognizer/NumberRecognizer.App/NumberRecognizer.App.csproj +++ b/NumberRecognizer/NumberRecognizer.App/NumberRecognizer.App.csproj @@ -111,7 +111,10 @@ App.xaml + + + @@ -122,6 +125,7 @@ + GroupedNetworksPage.xaml @@ -139,6 +143,9 @@ RecognizePage.xaml + + ValidateTrainingDataPage.xaml + @@ -208,6 +215,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + diff --git a/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/NumberRecognizerService4.xsd b/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/NumberRecognizerService4.xsd index 28dfc4d..3dd76bb 100644 --- a/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/NumberRecognizerService4.xsd +++ b/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/NumberRecognizerService4.xsd @@ -1,4 +1,4 @@ - + @@ -13,7 +13,7 @@ - + @@ -22,14 +22,14 @@ - + - + - + @@ -39,10 +39,10 @@ - + - + diff --git a/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/Reference.cs b/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/Reference.cs index aaf9960..d049792 100644 --- a/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/Reference.cs +++ b/NumberRecognizer/NumberRecognizer.App/Service References/NumberRecognizerService/Reference.cs @@ -21,7 +21,7 @@ public interface INumberRecognizerService { [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/INumberRecognizerService/GetNetworks", ReplyAction="http://tempuri.org/INumberRecognizerService/GetNetworksResponse")] System.Threading.Tasks.Task> GetNetworksAsync(); - [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/INumberRecognizerService/CreateNetwork", ReplyAction="http://tempuri.org/INumberRecognizerService/CreateNetworkResponse")] + [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/INumberRecognizerService/ValidateTrainingData", ReplyAction="http://tempuri.org/INumberRecognizerService/CreateNetworkResponse")] System.Threading.Tasks.Task CreateNetworkAsync(string networkName, string username, System.Collections.ObjectModel.ObservableCollection individualTrainingsData); [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/INumberRecognizerService/CreateNetworkWithTrainingDataCopy", ReplyAction="http://tempuri.org/INumberRecognizerService/CreateNetworkWithTrainingDataCopyResp" + diff --git a/NumberRecognizer/NumberRecognizer.App/Style/AppStyle.xaml b/NumberRecognizer/NumberRecognizer.App/Style/AppStyle.xaml index 4d07261..850b2d8 100644 --- a/NumberRecognizer/NumberRecognizer.App/Style/AppStyle.xaml +++ b/NumberRecognizer/NumberRecognizer.App/Style/AppStyle.xaml @@ -27,4 +27,15 @@ + + + diff --git a/NumberRecognizer/NumberRecognizer.App/View/CreateNetworkPage.xaml b/NumberRecognizer/NumberRecognizer.App/View/CreateNetworkPage.xaml index 1480f7b..8256dec 100644 --- a/NumberRecognizer/NumberRecognizer.App/View/CreateNetworkPage.xaml +++ b/NumberRecognizer/NumberRecognizer.App/View/CreateNetworkPage.xaml @@ -15,6 +15,8 @@ 4.0 40.0 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + private void LoadCommands() { - CreateNetwork = new RelayCommand(() => App.Frame.Navigate(typeof(CreateNetworkPage))); + ValidateTrainingData = new RelayCommand(ExecuteValidateTrainingData); + ClearInk = new RelayCommand((string inkCanvasIndex) => canvasList[Convert.ToInt32(inkCanvasIndex)].ClearInk()); } /// @@ -71,7 +81,7 @@ private void LoadCommands() /// private async void InitializeNetworkName() { - NetworkName = string.Format("{0}_{1}", await UserInformation.GetLastNameAsync(), DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss")); + NetworkName = string.Format("{0}_{1}", await UserInformation.GetLastNameAsync(), DateTime.Now.ToFileTime()); } #endregion @@ -115,6 +125,25 @@ public string NetworkName } } + /// + /// Gets or sets a value indicating whether [is validation error]. + /// + /// + /// true if [is validation error]; otherwise, false. + /// + public bool IsValidationError + { + get + { + return isValidationError; + } + set + { + isValidationError = value; + RaisePropertyChanged(() => IsValidationError); + } + } + #endregion #region Commands @@ -125,16 +154,16 @@ public string NetworkName /// /// The add network. /// - public ICommand CreateNetwork + public ICommand ValidateTrainingData { get { - return cmdCreateNetwork; + return cmdValidateTrainingData; } set { - cmdCreateNetwork = value; - RaisePropertyChanged(() => CreateNetwork); + cmdValidateTrainingData = value; + RaisePropertyChanged(() => ValidateTrainingData); } } @@ -144,16 +173,16 @@ public ICommand CreateNetwork /// /// The refresh networks. /// - public ICommand Cancel + public RelayCommand ClearInk { get { - return cmdCancel; + return cmdClearInk; } set { - cmdCancel= value; - RaisePropertyChanged(() => Cancel); + cmdClearInk = value; + RaisePropertyChanged(() => ClearInk); } } @@ -162,10 +191,12 @@ public ICommand Cancel /// /// Handles the Click event of the NextButton control. /// - /// The source of the event. - /// The instance containing the event data. - private async Task> GetTrainingData(object sender, Windows.UI.Xaml.RoutedEventArgs e) + /// + private async Task>> GetTrainingData() { + Dictionary> trainingData = new Dictionary>(); + int pattern = 0; + foreach (InkCanvasRT inkCanvas in canvasList) { var renderTargetBitmap = new RenderTargetBitmap(); @@ -178,6 +209,8 @@ private async Task> GetTrainingData(object sender double canvasHeight = inkCanvas.ActualHeight; double[,] canvasPixels = await ImageHelperRT.Get2DPixelArrayFromByteArrayAsync(canvasBytes, canvasWidth, canvasHeight); + trainingData.Add(pattern, new List()); + inkCanvas.ConnectedComponentLabeling = new ConnectedComponentLabeling(canvasPixels, 0.0); foreach (ConnectedComponent connectedComponent in inkCanvas.ConnectedComponentLabeling.ConnectedComponents) @@ -214,21 +247,55 @@ private async Task> GetTrainingData(object sender } } - byte[] orgRGBABytes = ImageHelperRT.GetRGBAByteArrayFromByteArrayAsync(orgBytes, Colors.Black); - await ImageHelperRT.SaveRGBAByteArrayAsBitmapImageAsync(orgRGBABytes, mbr.Width, mbr.Height, inkCanvas.Name + "_org"); - connectedComponent.Pixels = await ImageHelperRT.Get2DPixelArrayFromByteArrayAsync(orgBytes, mbr.Width, mbr.Height); + //byte[] orgRGBABytes = ImageHelperRT.GetRGBAByteArrayFromByteArrayAsync(orgBytes, Colors.Black); + //await ImageHelperRT.SaveRGBAByteArrayAsBitmapImageAsync(orgRGBABytes, mbr.Width, mbr.Height, inkCanvas.Name + "_org"); + //connectedComponent.Pixels = await ImageHelperRT.Get2DPixelArrayFromByteArrayAsync(orgBytes, mbr.Width, mbr.Height); byte[] scaRGBABytes = ImageHelperRT.GetRGBAByteArrayFromByteArrayAsync(scaBytes, Colors.Black); scaRGBABytes = await ImageHelperRT.ScaleRGBAByteArrayAsync(scaRGBABytes, mbr.Size, mbr.Size, ImageHelperRT.ImageWidth, ImageHelperRT.ImageHeight); scaBytes = await ImageHelperRT.GetByteArrayFromRGBAByteArrayAsync(scaRGBABytes, inkCanvas.ForegroundColor); - await ImageHelperRT.SaveRGBAByteArrayAsBitmapImageAsync(scaRGBABytes, ImageHelperRT.ImageWidth, ImageHelperRT.ImageHeight, inkCanvas.Name + "_sca"); - connectedComponent.ScaledPixels = await ImageHelperRT.Get2DPixelArrayFromByteArrayAsync(scaBytes, ImageHelperRT.ImageWidth, ImageHelperRT.ImageHeight); + double[,] pixels = await ImageHelperRT.Get2DPixelArrayFromByteArrayAsync(scaBytes, ImageHelperRT.ImageWidth, ImageHelperRT.ImageHeight); + connectedComponent.ScaledPixels = pixels; + + //save image to storage + string imagename = string.Format("{0}_{1}", networkName, pattern); + string path = await ImageHelperRT.SaveRGBAByteArrayAsBitmapImageAsync(scaRGBABytes, ImageHelperRT.ImageWidth, ImageHelperRT.ImageHeight, imagename); + + var imageData = new TrainingImage(){ + Height = Convert.ToInt32(ImageHelperRT.ImageHeight), + Width = Convert.ToInt32(ImageHelperRT.ImageWidth), + Pattern = pattern.ToString() + }; + + imageData.TransformFrom2DArrayToImageData(pixels); + + trainingData[pattern].Add(new LocalTrainingImage() + { + LocalImagePath = path, + ImageData = imageData + }); } + + pattern++; } - //TODO - return null; + return trainingData; + } + /// + /// Executes the validate training data. + /// + private async void ExecuteValidateTrainingData() + { + var trainData = await GetTrainingData(); + + IsValidationError = !trainData.All(keyvalpair => keyvalpair.Value.Count > 0); + + if(!IsValidationError) + { + App.Frame.Navigate(typeof(ValidateTrainingDataPage), trainData); + } + } } } diff --git a/NumberRecognizer/NumberRecognizer.App/ViewModel/ValidateTrainingDataViewModel.cs b/NumberRecognizer/NumberRecognizer.App/ViewModel/ValidateTrainingDataViewModel.cs new file mode 100644 index 0000000..947f9b8 --- /dev/null +++ b/NumberRecognizer/NumberRecognizer.App/ViewModel/ValidateTrainingDataViewModel.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GalaSoft.MvvmLight; +using NumberRecognizer.App.DataModel; + +namespace NumberRecognizer.App.ViewModel +{ + public class ValidateTrainingDataViewModel : ViewModelBase + { + private ObservableCollection groups; + + public ValidateTrainingDataViewModel(Dictionary> trainData) + { + LoadGroups(trainData); + } + + private void LoadGroups(Dictionary> trainData) + { + //Property Changed Raise + Groups = new ObservableCollection(); + + foreach (int i in trainData.Keys) + { + var group = new LocalTrainingImageGroup(i.ToString(), i.ToString()); + + foreach (LocalTrainingImage image in trainData[i]) + { + group.Items.Add(image); + } + + groups.Add(group); + } + } + + public ObservableCollection Groups + { + get + { + return groups; + } + set + { + groups = value; + RaisePropertyChanged(() => Groups); + } + } + + + } +}