Free open source UI toolkits you should check out for your Windows Phone / Windows Store apps

<This post will be updated in time. Kindly check back for newer entries>

When windows phone hit it’s 8.1 upgrade from Windows Phone 8 version, it shifted itself up from silverlight to WinRT kernel. Some of the hardcore Windows Phone fanatics like me was pretty sad because we lost access to our favourite UI toolkits by then. Although a lot of the expected UI controls came through in the sdk itself, still something was missing. This is a log of a set of UI toolkits you should discover when you are making your Windows Phone/Universal Apps.

1. SlideView: A Facebook like panel for Windows Phone: Ever wonder how a android like SlideView would look like in your native Windows Phone? Check this toolkit out Today!

2. WinRT XAML Toolkit:  The perfect set of helpers, extensions and mostly all of the controls from Windows Phone Toolkit has been ported here. It has Calendar, Gauge and other data visualization tool too.

3. MyToolkit for .NET:  Are you a big fan Windows App Studio? Do you know what has been used inside to facilitate all that? Meet MyToolkit. From MVVM toolkit to DataGrid everything is here, possibly everything. It even supports Xamarin projects too. 🙂

4.ExpanderRT: ExpanderRT is the ExpanderView port from Windows Phone 8 to WinRT. Missing the great control, don’t! 😉

I will continuously keep updating this blog post as soon as I find a new toolkit to add. 😉

Saving a SpeechSynthesis Audio in a windows store/phone app

Usually after doing a little speech synthesizing in our phone we usually stumble on one question, can I save this stream?
Luckily you can! All you gotta do is pull this out of your sleeve. 🙂

 


var synth = Windows.Media.SpeechSynthesis.SpeechSynthesizer();

SpeechSynthesisStream speechstream = await synth.SynthesizeTextToStreamAsync("Hello People");

using(var reader = new DataReader(speechstream))
{

await reader.LoadAsync((uint)stream.Size);

IBuffer buffer = reader.ReadBuffer((uint)stream.Size);

await FileIO.WriteBufferAsync(outputFile, buffer);

}

And, you’re done! Your file would be a .wav file of course.

Making a game controller out of your Windows Phone using VJoy – Part 1.

I know the title sounds really intriguing. Truth to be told it is indeed intriguing. And as it goes the outcome is pretty intriguing too.

The whole tutorial would take a bit time to get inside your head, so Im going to take it step by step.

Step 1: The Bluetooth Server

To have this whole thing going we will need a bluetooth server going. To facilitate a very very simple bluetooth server we are going to use a Windows Store app. So I jotted down a very very simple GUi for the server first.


<Page
    x:Class="BluetoothServer.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BluetoothServer"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel VerticalAlignment="Bottom" Margin="0,0,0,150" >
            <TextBlock Name="StatusText" FontSize="25" Margin="35,0,35,57" TextAlignment="Center"></TextBlock>
            <Button Content="Initiate Server"  Name="ServerToggleButton" HorizontalAlignment="Center" Click="Button_Click" />
            
        </StackPanel>
    </Grid>
</Page>

The only input mechanism I have in here is a textbox and a button. You pick the button to initiate a server and that’s it. All the code that has been used here are actually very very raw and coded in a hurry. So try to get the gist of the idea first so you can get yourself started.

The first method we are going to put in the back is an Initialization method for the server. This would actually Initialize the Bluetooth server we are going to use.


private StreamSocket socket;

private DataWriter writer;
private RfcommServiceProvider rfcommProvider;
private StreamSocketListener socketListener;

public async void InitializeRfCommServer()
{
            try
            {
                rfcommProvider = await RfcommServiceProvider.CreateAsync(RfcommServiceId.FromUuid(RfcommServiceUuid));

                // Create a listener for this service and start listening
                socketListener = new StreamSocketListener();
                socketListener.ConnectionReceived += OnConnectionReceived;

                await socketListener.BindServiceNameAsync(rfcommProvider.ServiceId.AsString(),
                   SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);

                 // Set the SDP attributes and start Bluetooth advertising
                InitializeServiceSdpAttributes(rfcommProvider);
                rfcommProvider.StartAdvertising(socketListener);

                StatusText.Text="Listening for incoming connections";
                ServerInitiated = true;

            }
            catch(Exception e)
            {
                StatusText.Text = e.Message;
                
                ServerInitiated = false;
            }
}

Here, Windows.Devices.Bluetooth.Rfcomm comes in extremely handy as RfcommServiceProvider is the prefect class to start a RFComm session. But before all that you are going to need some basic stuff. First you will need a service GUID. You can make a GUID from any place but I suggest to try make an unique one. Here I used something I generated from a site online but you can definitely try your way. Then you are going to need a SdpServiceNameAttributeId and SdpServiceNameAttributeType. SdpServiceNameAttributeType is actually a 8 bit scenario where the least significant 3 bits contains attribute size and the most significant 5 bits contains the attribute type value. And last but not the least you will need a service name too.


        private static readonly Guid RfcommServiceUuid = Guid.Parse("482af5ed-3faf-4a51-9dd0-718c85aa64d0");
        private const UInt16 SdpServiceNameAttributeId = 0x100;
        private const byte SdpServiceNameAttributeType = (4 << 3) | 5;
        private const string SdpServiceName = "Bluetooth Rfcomm Listening Service for VJoy";

Please keep these in mind because we are going to need these in the client too. And you also have to define these in your Package.appxmanifest too to make the server authorized to use the bluetooth in your pc.


<m2:DeviceCapability Name="bluetooth.rfcomm">
      <m2:Device Id="any">
        <m2:Function Type="serviceId:34B1CF4D-1069-4AD6-89B6-E161D79BE4D8"/>
      </m2:Device>
    </m2:DeviceCapability>

After all these are done and dusted, let’s move on with initializing the bluetooth server. The next thing that comes is a StreamSocketListener. And it’s job is kind of explained in it’s name. It will serve as our stream listener that would be recieved from the clients. Thus it has a ConnectionReceived event that has to be populated too.

The next thing to do would be bind the service name. Our StreamSocketListener object has a BindServiceNameAsync method to do so with serviceID and an option to take as a parameter. I defined SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication because I didnt want much security over the communication. You can explore other options if you want to impose some security measures.

Next comes InitializeServiceSdpAttributes(rfcommProvider) and he looks like the following:


        private void InitializeServiceSdpAttributes(RfcommServiceProvider rfcommProvider)
        {
            var sdpWriter = new DataWriter();

            // Write the Service Name Attribute.

            sdpWriter.WriteByte(SdpServiceNameAttributeType);

            // The length of the UTF-8 encoded Service Name SDP Attribute.
            sdpWriter.WriteByte((byte)SdpServiceName.Length);

            // The UTF-8 encoded Service Name value.
            sdpWriter.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
            sdpWriter.WriteString(SdpServiceName);

            // Set the SDP Attribute on the RFCOMM Service Provider.
            rfcommProvider.SdpRawAttributes.Add(SdpServiceNameAttributeId, sdpWriter.DetachBuffer());   
        }

The purpose of this method is definitely putting the SdpRawAttributes in place by using a DataWriter. Here we put in possibly everything we defined before for our bluetooth device.

The only thing we need to do just before calling StartAdvertising, we need to define our ConnectionReceived event.


private async void OnConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
        {
            try
            {
                socketListener.Dispose();
                socketListener = null;

                socket = args.Socket;

                writer = new DataWriter(socket.OutputStream);

                var reader = new DataReader(socket.InputStream);
                bool remoteDisconnection = false;

                bool res;
                while (true)
                {
                    uint readLength = await reader.LoadAsync(sizeof(uint));
                    if (readLength < sizeof(uint))
                    {
                        remoteDisconnection = true;
                        break;
                    }
                    uint currentLength = reader.ReadUInt32();

                    readLength = await reader.LoadAsync(currentLength);
                    if (readLength < currentLength)
                    {
                        remoteDisconnection = true;
                        break;
                    }
                    string message = reader.ReadString(currentLength);
                }

                reader.DetachStream();
                if (remoteDisconnection)
                {
                    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        Disconnect();
                    });
                }

            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

This is where the server keeps looping for message and Im not kidding. You will find a while(true) inside and what it does is it keeps reading data from the socket until someone pulls the trigger or somehow the connection gets jeopardized. You will see even a basic check on buffer length triggers the server to stop. And in any case of an exception an async Disconnect() is due.

And Disconect() is nothing but as simple as the following:


        private void Disconnect()
        {
            if (rfcommProvider != null)
            {
                rfcommProvider.StopAdvertising();
                rfcommProvider = null;
            }

            if (socketListener != null)
            {
                socketListener.Dispose();
                socketListener = null;
            }

            if (writer != null)
            {
                writer.DetachStream();
                writer = null;
            }

            if (socket != null)
            {
                socket.Dispose();
                socket = null;
            }

            ServerInitiated = false;
        }

Now , we can trigger this one from a toggle button like the following:

private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (ServerInitiated)
            {
                Disconnect();
                ServerToggleButton.Content = "Initiate Server";
            }
            else
            {
                InitializeRfCommServer();
                ServerToggleButton.Content = "Disconnect Server";
               
            }
        }

But the thing is that’s just a bluetooth server. To test it out you will need a bluetooth client too.

Step 2: The Bluetooth Client

The bluetooth client is essentially a Windows Phone app. I couldve showed the XAML here but that wouldn’t look that simple. In spite of doing that, let’s break down what do we have to do now. We have to:

1. Connect to a paired device. (Yes, you have to have your devices paired)

2. Search the bluetooth service we are looking for and if we find any, we’d give a list to pick

3. Use that selected device and send data to it .

Here, definitely the paired device is a device that has our server running.

Let’s assume we have a run button that starts the search for the paired devices with the desired service. Remember I said that we will need our service GUID and other identifications to make this work even from the client side because it has to know which service it is feeding itself to.

Thus, this definitions first come in place:

private static readonly Guid RfcommChatServiceUuid = Guid.Parse("482af5ed-3faf-4a51-9dd0-718c85aa64d0");

// The Id of the Service Name SDP attribute
private const UInt16 SdpServiceNameAttributeId = 0x100;
private const byte SdpServiceNameAttributeType = (4 << 3) | 5;

private StreamSocket chatSocket;
private DataWriter chatWriter;
private RfcommDeviceService chatService;
private DeviceInformationCollection chatServiceInfoCollection;

and the run method looks like the following:


private StreamSocket chatSocket;
private DataWriter chatWriter;
private RfcommDeviceService chatService;
private DeviceInformationCollection chatServiceInfoCollection;

private async void RunButton_Click(object sender, RoutedEventArgs e)
{
// Find all paired instances of the Rfcomm chat service
chatServiceInfoCollection = await DeviceInformation.FindAllAsync(
RfcommDeviceService.GetDeviceSelector(RfcommServiceId.FromUuid(RfcommChatServiceUuid)));

if (chatServiceInfoCollection.Count > 0)
{
List<string> items = new List<string>();
foreach (var chatServiceInfo in chatServiceInfoCollection)
{
items.Add(chatServiceInfo.Name);
}
cvs.Source = items;
ServiceSelector.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
else
{
Debug.WriteLine("No chat services were found. Please pair with a device that is advertising the chat service.");

}
}

You can definitely understand DeviceInformationCollection does all the hard work here finding the device and the next thing to do would be selecting one and using it. After selecting we will start our chat session and indulge ourselves on a loop so we can keep receiving messages sent from the server, actually in our case thats not necessary, but still it’s nicer to have it on the place.


private async void ServiceList_Tapped(object sender, TappedRoutedEventArgs e)
{
try
{
RunButton.IsEnabled = false;
ServiceSelector.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

var chatServiceInfo = chatServiceInfoCollection[ServiceList.SelectedIndex];
chatService = await RfcommDeviceService.FromIdAsync(chatServiceInfo.Id);

if (chatService == null)
{
Debug.WriteLine(
"Access to the device is denied because the application was not granted access",
);
return;
}

var attributes = await chatService.GetSdpRawAttributesAsync();
if (!attributes.ContainsKey(SdpServiceNameAttributeId))
{
Debug.WriteLine(
"The Chat service is not advertising the Service Name attribute (attribute id=0x100). " +
"Please verify that you are running the BluetoothRfcommChat server.",
);
return;
}

var attributeReader = DataReader.FromBuffer(attributes[SdpServiceNameAttributeId]);
var attributeType = attributeReader.ReadByte();
if (attributeType != SdpServiceNameAttributeType)
{
Debug.WriteLine(
"The Chat service is using an unexpected format for the Service Name attribute. " +
"Please verify that you are running the BluetoothRfcommChat server.",);
return;
}

var serviceNameLength = attributeReader.ReadByte();

// The Service Name attribute requires UTF-8 encoding.
attributeReader.UnicodeEncoding = UnicodeEncoding.Utf8;
ServiceName.Text = "Service Name: \"" + attributeReader.ReadString(serviceNameLength) + "\"";

lock (this)
{
chatSocket = new StreamSocket();
}

await chatSocket.ConnectAsync(chatService.ConnectionHostName, chatService.ConnectionServiceName);

chatWriter = new DataWriter(chatSocket.OutputStream);
ControlBox.Visibility = Visibility.Visible;

DataReader chatReader = new DataReader(chatSocket.InputStream);
ReceiveStringLoop(chatReader);
}
catch (Exception ex)
{
RunButton.IsEnabled = true;
Debug.WriteLine("Error: " + ex.HResult.ToString() + " - " + ex.Message,);
}
}

The very basic thing it does it starts getting device info out and creates a String Loop that listens for a string. I wont go with that details rather I’d show how to send a bluetooth data through it.


private async void SendMessage(string message)
{
try
{
chatWriter.WriteUInt32((uint)message.Length);
chatWriter.WriteString(message);

await chatWriter.StoreAsync();

}
catch (Exception ex)
{
//MainPage.Current.NotifyUser("Error: " + ex.HResult.ToString() + " - " + ex.Message,
//    NotifyType.StatusMessage);
}
}

For the first part I’d stop here now, in the next two parts we are going to see how we are going to feed this data to vjoy and use our phone as a game controller. 🙂

Recording Audio in Windows Phone 8.1

First Words

This article points to a simple, fast way to record audio in Windows Phone 8.1. As the old Microphone class is no longer available, Windows Phone 8.1 came with new MediaCapture class to consolidate the media recording part for audio and video together. So, why waiting, let’s see how to do it!

Let’s get busy

I jotted a very simple GUI for this that has three simple buttons named “Capture Audio”, “Stop Capture” and “Play Capture”.  And it looks as follows:

CaptureAudio1

If you guys want to have a closer look at the XAML behind, peek a look:

<Page
    x:Class="CaptureSoundTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CaptureSoundTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <MediaElement x:Name="playbackElement1" ></MediaElement>
        <Button x:Name="CaptureButton" Content="Capture Audio" HorizontalAlignment="Center" Margin="0,184,0,0" VerticalAlignment="Top" Click="CaptureButton_Click" Width="145"/>
        <Button x:Name="StopCaptureButton" Content="Stop Capture" HorizontalAlignment="Center" Margin="0,284,0,0" VerticalAlignment="Top" Click="StopCaptureButton_Click" Width="145"/>
        <Button x:Name="PlayRecordButton" Content="Play Capture" HorizontalAlignment="Center" Margin="0,380,0,0" VerticalAlignment="Top" Click="PlayRecordButton_Click" Width="145"/>

    </Grid>
</Page>

It’s pretty basic, I didn’t do much here without keeping the buttons in place. There’s a MediaElement named playbackElement1 to play back the recorded video.

Now, it’s time to populate the button events here named CaptureButton_Click, StopCaptureButton_Click and PlayRecordButton_Click

Initializing and Capturing audio

To capture audio, first we need to initialize the recording. Before digging into that, let’s just initialize some variables that we might need on the journey.

private MediaCapture _mediaCaptureManager;
private StorageFile _recordStorageFile;
private bool _recording;
private bool _userRequestedRaw;
private bool _rawAudioSupported;

The MediaCapture class here is the one responsible here to capture audio and video both here. And StorageFile instance will help us store, locate and play the recorded media on our device.

So, let’s have a look how our recording initialization method looks like:

private async void InitializeAudioRecording()
{

   _mediaCaptureManager = new MediaCapture();
   var settings = new MediaCaptureInitializationSettings();
   settings.StreamingCaptureMode = StreamingCaptureMode.Audio;
   settings.MediaCategory = MediaCategory.Other;
   settings.AudioProcessing = (_rawAudioSupported &amp;&amp; _userRequestedRaw) ? AudioProcessing.Raw : AudioProcessing.Default;

   await _mediaCaptureManager.InitializeAsync(settings);

   Debug.WriteLine("Device initialised successfully");

   _mediaCaptureManager.RecordLimitationExceeded += new RecordLimitationExceededEventHandler(RecordLimitationExceeded);
    _mediaCaptureManager.Failed += new MediaCaptureFailedEventHandler(Failed);
}

So, the very first thing done here is the initialization of the MediaCapture class. Every media capture is associated with a MediaCaptureInitializationSettings instance. We’ve set the StreamingCaptureMode to Audio to make sure this is only audio recording rather than video. There were two bool variables declared before named _rawAudioSupported and _userRequestedRaw. Although I didn’t hook them up from GUI, you guys can defintiely try hooking them up from GUI and thus you can give the app a choice to record raw or pointing it to default audio processing.

The next thing to do is initializing the _mediaCaptureManager with the settings provided. I have included two event handlers for RecordingLimitExceeded and Failed. You guys can write your own handlers of course in these cases.

Now, usually you can put the InitializeAudioRecording method where your app initializes. I put it here on MainPage constructor.

Now, let’s capture audio, MediaCapture class made it really easy indeed.

private async void CaptureAudio()
{
   try
   {
     Debug.WriteLine("Starting record");
     String fileName = "record.m4a";

     _recordStorageFile = await KnownFolders.VideosLibrary.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);

     Debug.WriteLine("Create record file successfully");

     MediaEncodingProfile recordProfile = MediaEncodingProfile.CreateM4a(AudioEncodingQuality.Auto);
     await _mediaCaptureManager.StartRecordToStorageFileAsync(recordProfile, this._recordStorageFile);

     Debug.WriteLine("Start Record successful");

     _recording = true;
    }
    catch (Exception e)
    {
      Debug.WriteLine("Failed to capture audio");
    }
}

If you have a good look on the code you will see we declared a filename first. And then we created a file in our VideosLibrary using KnownFolders.VideosLibrary.CreateFileAsync that actually points to the media and video library of windows phone 8.1 with a given filename and we also gave a CreationCollisionOption.GenerateUniqueName that says it will create a unique name in case of collision. The next thing to do is creating a recording profile using MediaEncodingProfile class and you can get all possible recording profiles for videos and audios from here. I created a M4a audio profile using MediaEncodingProfile.CreateM4a(recordProfile, this._recordStorageFile) method.

All we have left to do is now start the recording, so  let’s go ahead and start the recording. And it’s easy as pie with _mediaCaptureManager.StartRecordToStorageFileAsync(recordProfile, this._recordStorageFile)

Stopping Recording

Stopping the recording is quiet easy too. All you have to do is call _mediaCaptureManager.StopRecordAsync()

private async void StopCapture()
{

     if (_recording)
     {
          Debug.WriteLine("Stopping recording");
          await _mediaCaptureManager.StopRecordAsync();
          Debug.WriteLine("Stop recording successful");
          _recording = false;
     }

}

Playing Recording

Playing a recording kind of consists of two tasks. First we have to open the recorded file and then play it. If you guys remember we used StorageFile _recordStorageFile to define our file that will hold the recording.

private async void PlayRecordedCapture()
{
     if (!_recording)
     {
         var stream = await _recordStorageFile.OpenAsync(FileAccessMode.Read);
         Debug.WriteLine(&quot;Recording file opened&quot;);
         playbackElement1.AutoPlay = true;
         playbackElement1.SetSource(stream, _recordStorageFile.FileType);
         playbackElement1.Play();
     }
}

So, let’s open up the file and assign the resultant stream to a source of a MediaElement on XAML. In this case that one is playBackElement1

 

Voila! You’re done, now you can play your recording. All you have to do now is assign these three methods to proper button events. For the full source please download the demo project above.

A little bit of fun with face detection in Windows Phone 8

To start off

If anybody is actually looking for Face Detection Libraries for Windows Phone and haven’t tried this one, this is for them. It’s a neat library popping off facedetecionwp7 library by Julia Schwarz. And you can do pretty cool stuffs with it with pretty less hassle.

Let’s have a little fun

Let’s go ahead and get ourselves started with a Windows Phone 8 Project. Our job would be try this library and detect faces in a picture and have some fun with it, say overlay the face with the classic troll face with the laugh. 😛

Troll

Now, no matter what that sounds, let’s give it a shot. I’ve jotted down a very simple GUI to do this one. And it’s as following:

FaceDetection1

If you want to have a closer look at the XAML it will look the following:

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

       
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="Face Detection in Windows Phone" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Button x:Name="CaptureImageButton" Content="Capture Image" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Top" Width="267" Click="CaptureImageButton_Click"/>
            <Image Name="facesPic" HorizontalAlignment="Center" Height="388" Margin="0,170,0,0" VerticalAlignment="Top" Width="427"/>
        </Grid>

        
    </Grid>

Now, you can definitely see that this is a very basic UI. We have a Button with a click event CaptureImageButton_Click and a Image named facesPic.

Now, before we get our hands dirty, let’s go ahead and add reference to a library we will need. We will need WritableBitmapEx from here. This lib is already available on Nuget too, so you can get it from there too.

After you added that in your project reference, you will need a bunch of libraries from FaceDetectionWP8 to make this things work. You can get the classes on there from the following link too.

After you download the .zip , unzip it and include all the classes inside in your project. I added them under a separate folder named FaceDetector.

Let’s move to our MainPage.xaml.cs

Now, the first thing to do is to pick a picture and the best thing to use for that is PhotoChooserTask . Let’s hook up the following snippet in CaptureImageButton_Click event.

private void CaptureImageButton_Click(object sender, RoutedEventArgs e)
        {
            PhotoChooserTask photo = new PhotoChooserTask();
            photo.Completed += new EventHandler&lt;PhotoResult&gt;(photoChooserTask_Completed);
            photo.ShowCamera = true;
            photo.Show();
        }

Now, the next thing to write is definitely photoChooserTask_Completed event handler. Let’s go ahead and write it. First thing to do would be get the chosen photo into a  WritableBitmap as we are going to do some changes on it.

if(e.TaskResult==TaskResult.OK)
{
    BitmapImage SourceBitmap = new BitmapImage();
    SourceBitmap.SetSource(e.ChosenPhoto);
    WriteableBitmap SourceWritableBitmap = new WriteableBitmap(SourceBitmap);

}

Now, the next thing we need to do is downsample the image a bit as most of the Windows Phone devices pack a pretty powerful camera and often the pictures are oversampled. I personally suggest you downsize the image a bit too. I din’t do it in this demo but if you want a faster performance, you really should try that too. I added the following code segment inside the if block.

byte[] downsampledImage = new byte[SourceWritableBitmap.PixelWidth / _downsampleFactor * SourceWritableBitmap.PixelHeight / _downsampleFactor];

Utils.DownSample(SourceWritableBitmap.ToByteArray(), SourceWritableBitmap.PixelWidth, SourceWritableBitmap.PixelHeight, ref downsampledImage, _downsampleFactor);

SourceWritableBitmap = SourceWritableBitmap.FromByteArray(downsampledImage);

The code segment is pretty straight forward here now. The Utils class here is from the FaceDetectionWP8 library. The first argument for Utils.DownSample used here is a Byte Array of the SourceWritableBitmap and it used the WritableBitmapEx we added here before. The second one is the width of the source bitmap and the third one is the height. The last argument passed here is the downsample factor. This was defined as the following in the MainPage class.

int _downsampleFactor = 2;

And then the SourceWritableBitmap is reinitiazed with a downsampled version of it’s own.

The next thing to do would be getting the faces detected of course. 😀 So, why wait? I added the following sinppet for that.

List<FaceDetector.Rectangle> faces = new List<FaceDetector.Rectangle>();
faces = _detector.getFaces(SourceWritableBitmap, 2f, 1.25f, 0.1f, 1, false, true);

I used the defaults here except the last argument that determines whether it should detect multiple faces and I kept it true.  You can have fun with the other params if you want to tweak your haar cascade detection parameters here.

here comes the fun part, now we are going to replace the face/faces in the picture with the troll laugh picture. :P. We can see it returns the faces as a List<FaceDetector.Rectangle>. All we have to do now is paint the Troll laugh in the rectangles returned by the method. fun, huh?

Now, before replacing the Source Image with the Troll Laugh, we need to load it in another WritableBitmap so we can blend the both of them.

StreamResourceInfo MaskImageSri = Application.GetResourceStream(new Uri("Images/Troll.png", UriKind.Relative));
BitmapImage MaskImageBitmap = new BitmapImage();
MaskImageBitmap.SetSource(MaskImageSri.Stream);

WriteableBitmap MaskWritableBitmap = new WriteableBitmap(MaskImageBitmap);

As we got our Troll.png, why don’t we paste it on the source bitmap?

foreach (var r in faces)
{
   int x = Convert.ToInt32(r.X);
   int y = Convert.ToInt32(r.Y);
   int width = Convert.ToInt32(r.Width);
   int height = Convert.ToInt32(r.Height);                    

   System.Windows.Rect destRect = new Rect(x, y, width, height);
   System.Windows.Rect srcRect = new Rect(0, 0, MaskWritableBitmap.PixelWidth, MaskWritableBitmap.PixelHeight);

   SourceWritableBitmap.Blit(destRect, MaskWritableBitmap, srcRect);

}
   SourceWritableBitmap.Invalidate();
   facesPic.Source = SourceWritableBitmap;

Now, this is pretty straight forward too. All we need is we iterated over the faces and we created to separate rect structs. One is from the MaskWritableBitmap so we can get the rect of the portion of the image we are pasting, in this case, the full one and the destination rect is the face rectangle on the source image we are pasting this troll laugh on.

The next thing to do is invoke Blit() method as this one is responsible to blend these two WritableBitmaps. It comes with the WritableBitmapEx library we added before. The first parameter is the destination rectangle where the source rectangle will be pasted. In this case the face rectangle that will be replaced by the troll laughter image. The second parameter would be the WritableBitmap where the rect that would be posted in the source is found. Here this one is the Troll Image and the last one is the rect in the Troll Image that would be transferred to the main bitmap.

We called it on SourceWritableBitmap as we want to paste the troll Laugh on it. And when all is said and done we set facesPic source to the SourceWritableBitmap.

Now all we have to do is build and test the app. And the result is as following:

FaceDetectionScreenshot

So, what are waiting for? Test the demo project here of the article and have fun!

Making a quick rating user control for Windows Phone 8.1

First Words

Usually when someone is working on Windows Phone 8.1 and usually if he’s the guy who just came from Windows Phone 8 development, the first thing he misses is all the toolkits lying around like the Windows Phone Toolkit or Coding4Fun Toolkits. Thus, missing a rating control is not new. So, let’s get our hands dirty and make a little 5 starred rating control fast enough to solve our problem at least to a minimum extent.

Design First!

Okay, to do this thing fast, we might need to make at least a little bit of groundwork in design, I’m going to use Microsoft Expression Design for it. You guys can get the free version from here. It’s pretty lightweight and we can practically export stuff straight to Blend for Visual Studio!

So, let’s go over Microsoft Expression Blend and create a new document. Take a size to your preference as you need it. Let’s draw some stars! To get started, look on the left toolbar and select Polygons.

1

After you select polygons, drag your mouse pointer in the artboard to draw anything that pops off. Now, it might be any kind of polygon, starting from a triangle, like the following came up with too much of points.

2

To rectify this, we need to make sure it has 5 points and an inner depth above 40% and close to 50% so it looks like a star, you can modify the existing one you made or set the proper settings and make a new one. To do that, look on the right on Edit Polygon dialog and change the points to 5 and move the innder depth to 47%. If you want to create a new one you will see the dialog name is Create Polygon. You can go for anything in between 45% tp 50%, I used 47% in this case.

3 4

 

 

Now, with the proper settings set with the polygon give it a yellowish shade and no borders please. And copy the stars and set them side by side so you can have 5 stars in a line that resembles a regular rating control. So, in the artboard it might look pretty close to the one following:

5

Now, what we want to do is put these together on a gradient so it resembles more to the rating control. But before we go do that, we need to make the stars act as a compound path together. So, lets go ahead and select all of them and select Object -> Compound Path -> Make so all the stars can work as a compound path.

6

Now, it’s time to put a gradient over it. Select a yellow to white gradient and put it on your rating control from left to right. Now, there comes a trick, at first you will see the color melted down in the middle, in a rating control we need a clear difference of color to make sure that it looks like one.

7

Now, click on the gradient bar in the middle to put a gradient stop in there.

8

Now, drag the midpoints (the dots over the gradient bar) and put them together to make the color difference more prominent.

9

Now, if we move the Gradient Stop now, we will see the color changes are more prominent and the starts look a lot like rating control now.

10

Now let’s go over Visual Studio and create a Windows Phone 8.1 Project. Let’s put a Right Click on the solution name and add a user control.

11

12

Now, lets select the user control and open it on blend. Let’s select all the stars, copy it and just paste it on the grid of the user control. To your surprise, this thing will just get copied to blend just fine. And let’s make the height of the user control to 40 and the width to 200 just to get the user control to appropriate proportion. I copied a set of stars and put the background to a very light gray color and put it behind the ones we copied at first so it looks a bit more prominent even in a white background.

13

Now, the xaml for the usercontrol will look somewhat like the following:

<UserControl
    x:Class="RatingControlTest.usercontrol"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
 
    d:DesignHeight="40"
    d:DesignWidth="200">

    <Grid>
        <Path Stretch="Fill"
              Data="M -1346.6,-259.247L -1358.76,-305.451L -1321.61,-335.489L -1369.31,-338.197L -1386.39,-382.816L -1403.71,-338.285L -1451.43,-335.823L -1414.43,-305.594L -1426.83,-259.454L -1386.65,-285.302L -1346.6,-259.247 Z M -1067.9,-259.247L -1080.07,-305.451L -1042.91,-335.489L -1090.61,-338.197L -1107.7,-382.816L -1125.01,-338.285L -1172.73,-335.823L -1135.73,-305.594L -1148.13,-259.453L -1107.95,-285.301L -1067.9,-259.247 Z M -1207.25,-259.247L -1219.41,-305.451L -1182.26,-335.489L -1229.96,-338.197L -1247.05,-382.816L -1264.36,-338.285L -1312.08,-335.823L -1275.08,-305.594L -1287.48,-259.453L -1247.3,-285.301L -1207.25,-259.247 Z M -928.552,-259.247L -940.718,-305.451L -903.562,-335.489L -951.264,-338.197L -968.35,-382.816L -985.666,-338.285L -1033.38,-335.823L -996.382,-305.594L -1008.79,-259.453L -968.602,-285.301L -928.552,-259.247 Z M -789.204,-259.247L -801.369,-305.451L -764.214,-335.489L -811.916,-338.197L -829.002,-382.816L -846.318,-338.285L -894.034,-335.823L -857.033,-305.594L -869.437,-259.453L -829.254,-285.301L -789.204,-259.247 Z "  
              UseLayoutRounding="False"
              HorizontalAlignment="Left"
              VerticalAlignment="Top"
              Height="Auto" Width="Auto" Fill="#FFDEDEDE"/>
        <Path Stretch="Fill"
              Data="M -1346.6,-259.247L -1358.76,-305.451L -1321.61,-335.489L -1369.31,-338.197L -1386.39,-382.816L -1403.71,-338.285L -1451.43,-335.823L -1414.43,-305.594L -1426.83,-259.454L -1386.65,-285.302L -1346.6,-259.247 Z M -1067.9,-259.247L -1080.07,-305.451L -1042.91,-335.489L -1090.61,-338.197L -1107.7,-382.816L -1125.01,-338.285L -1172.73,-335.823L -1135.73,-305.594L -1148.13,-259.453L -1107.95,-285.301L -1067.9,-259.247 Z M -1207.25,-259.247L -1219.41,-305.451L -1182.26,-335.489L -1229.96,-338.197L -1247.05,-382.816L -1264.36,-338.285L -1312.08,-335.823L -1275.08,-305.594L -1287.48,-259.453L -1247.3,-285.301L -1207.25,-259.247 Z M -928.552,-259.247L -940.718,-305.451L -903.562,-335.489L -951.264,-338.197L -968.35,-382.816L -985.666,-338.285L -1033.38,-335.823L -996.382,-305.594L -1008.79,-259.453L -968.602,-285.301L -928.552,-259.247 Z M -789.204,-259.247L -801.369,-305.451L -764.214,-335.489L -811.916,-338.197L -829.002,-382.816L -846.318,-338.285L -894.034,-335.823L -857.033,-305.594L -869.437,-259.453L -829.254,-285.301L -789.204,-259.247 Z "  
              UseLayoutRounding="False"
              HorizontalAlignment="Left"
              VerticalAlignment="Top"
              Height="Auto" Width="Auto">
            <Path.Fill>
                <LinearGradientBrush StartPoint="3.55262e-007,0.500002" EndPoint="1,0.500001">
                    <GradientStop Color="#FFFFCC00" Offset="0.6"/>
                    <GradientStop Color="#06FCFCFC" Offset="0.6"/>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>

    </Grid>
</UserControl>

Code Comes Along

Now if we remember stuff clearly, when we moved the gradient stop in blend it looked like the rating bar value is changing. We just need to emulate that in code, that’s it. So, all we need to is change the Offset of <GradientStop> ‘s in LinearGradientBrush entry inside <Path.Fill>. So, we have to databind the offset from backend so we can access it from the usercontrol. And please remember this have to be 0.0 to 1 of course as thats the range for the offset value.

I renamed my user control to RatingControl and let’s move back to the code backend of RatingControl.xaml

public partial class RatingControl : UserControl
    {
        public RatingControl()
        {
            this.InitializeComponent();
        }

        DependencyProperty RateValueProperty = DependencyProperty.Register("RateValue", typeof(double), typeof(RatingControl), new PropertyMetadata(0.1, UpdateValue));

        private static void UpdateValue(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RatingControl control = d as RatingControl;
            control.Value = (double)e.NewValue;
        }

        private double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        private static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(RatingControl), new PropertyMetadata(0.1));

        public double RateValue
        {
            get
            {
                return (double)GetValue(RateValueProperty);
            }
            set
            {
                SetValue(RateValueProperty, value);
            }
        }

    }

Now, our job was to hook up the Value provided from backend. Now I used two DependencyProperty here instead of one because we are going to attach the value in the xaml using a element binding. The first DependencyProperty RateValueProperty has a PropertyMetadata of 0.1 and a function is attached to update the value in the xaml control (update the offset from the xaml).

And Value is the actual DependencyProperty Attached to the control and as it’s private it’s accessible only from inside. So we are exposing it using the RateValueProperty and update it with the Value Dependency Property. You can only use Value and expose it if you want.

Now all we have to do is hook it up in the xaml:

Let’s add a name to the Rating Control. Lets just add x:Name=”UserControl” in the UserControl Tag. And let’s tag the GradientStop property Offset on the ElementName “UserControl”. So, let’s modify the GradientStop tags as following:

<Path.Fill>
     <LinearGradientBrush StartPoint="3.55262e-007,0.500002" EndPoint="1,0.500001">
          <GradientStop Color="#FFFFCC00" Offset="{Binding Value, ElementName=UserControl}"/>
          <GradientStop Color="#06FCFCFC" Offset="{Binding Value, ElementName=UserControl}"/>
      </LinearGradientBrush>
</Path.Fill>

And voila! We are done!

Using the control

Using the control is pretty easy, add xmlns:local=”using:RatingControlTest” in the page tag and use it just like the following:

<Page
    x:Class="RatingControlTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:RatingControlTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <local:RatingControl Width="200" Height="40" RateValue="0.5"></local:RatingControl>
    </Grid>
</Page>

And it will look like the following:

14

Demo Project:

The demo project is here so you can test it yourself. Hope this helps! 😉

সোজা সাপ্টা ম্যাশিন লার্নিং – কে নিয়ারেস্ট নেইবর (K Nearest Neighbour)

সত্যি কথা বলতে কি, আমরা সবাই একটা বেশ লম্বা থিসিস করে আসি চতুর্থ বর্ষে, আমি জানিনা অন্যদের কথা, কিন্তু অনেক কিছুই আমি ঠিকমতো বুঝিনাই, তাই ঠিকমতো বোঝার ইচ্ছা টা যায়নাই। সেই চেষ্টায় ভাবলাম, যেটুকু বুঝি , লিখে ফেলি। ম্যাশিন লার্নিং জিনিসটা ভারিক্কী শোনালেও জিনিস টার কাজ একটাই, একটা গবেট প্রকৃতির ম্যাশিন কে আপনি কোন নির্দিষ্ট ব্যাপারে ঠিক যে ভাবে চিন্তা করেন সেভাবে তাকে ভাবতে শিখানো বা অন্তত কাছাকাছি কিছু একটা চেষ্টা করা।

ম্যাশিন লার্নিং এ আমার মতো যারা নবিশ তাদের জন্য কিছু বলতে হলে বলা লাগে, যদি ম্যাশিন লার্নিং কে একটু ক্ল্যাসিফাই করার চেষ্টা করি তাহলে দুটো বড়সড় ভাগ পাবেন। প্রথমটার নাম সুপারভাইজড লার্নিং, আরেকটা আনসুপারভাইজড লার্নিং। কোনটা কি এগুলোর সংজ্ঞা নিয়ে ভটভট করার মতো জ্ঞান আমার নাই। তাই সোজা সহজ ভাবে বলি। যদি একটা ম্যাশিন কে শুরু থেকে কিছু শেখানো হয়, তারপর সে কিছু ঠিকঠাক মতো করতে পারে, সেটা দাঁড়ায় সুপারভাইজড লার্নিং এর দলে। বাকিটা হচ্ছে আনসুপারভাইজড।

ম্যাশিন লার্নিং থেকে ম্যাশিন শব্দ টা সরিয়ে ফেলুন, মনে করুন আপনি একটা ম্যাশিন। একটু সুবিধাজনক একটা পরিবেশ চিন্তা করতে গেলে আপনাকে একটু বোকা হতে হবে। ম্যাশিন সাধারণত বোকাসোকা হয়। তাহলে কি করা? চলুন ফেরত যাই ছোটবেলায়। একদম যখন ছোট ছিলেন আপনি তখন নিশ্চই আপনি এতো কিছু জানতেন না। তো সেই হিসাবে সেই সময়টায় আপনি এখনকার একটা বোকা ম্যাশিন এর মতোই ছিলেন। এখন বাকি থাকলো লার্নিং শব্দটা। সহজ কথায় শিক্ষা। আসুন ছোটবেলার আপনাকে কিছু শেখানো যাক।

ভয় নেই। বেশিদূর যাওয়া লাগবেনা। আপনার মার কথাই মনে করুন। আপনার মাই সম্ভবত আপনাকে শিখিয়েছেন সব কিছু্। চিন্তা করুন, আপনার মা আপনাকে চিনতে শিখিয়েছে কোনটা কি? ধরে নিলাম আপনার মা আপনাকে চিনিয়েছে আপেল, ধরে নিলাম, প্রথম আপেলটির রং ছিলো হালকা লাল। পরেরদিন আপনার মা আপনাকে দিলো একটি হালকা সবুজ রঙের আপেল, পরেরদিন একটু গাড় রঙের আরেকটি। চিন্তা করুন একবার, খুবই সাধারণ মনে হওয়া এই ঘটনাটি আসলে ম্যাশিন লার্নিং এর একটি অসাধারন উদাহরণ। আপনি (ম্যাশিন) প্রতিদিন দেখেছেন একটি ভিন্ন আপেল, ভিন্ন রং, ভিন্ন আকার এবং হতে পারে একটু ভিন্ন স্বাদের। কিন্তু আপনি জেনেছেন সব ই আপেল। যদি এমন হতো, প্রতিদিন এক ই রঙের, আকারের আপেল দেখতেন, আপনি হয়তো কোনদিন বিশ্বাস করতে পারতেন নাম আপেল অন্য আকার বা রঙের হয়। আপনার মা খুব নিভৃতে আপনাকে শিখিয়েছেন একটি সাধারণ আপেল দেখতে কেমন হয়। এটাকে সহজ কথায় Generalization বলে। যে কারণগুলো একটা আপেল থেকে বাকিগুলোকে আলাদা করেছে সেগুলোই Feature. একটু ভেবে দেখুন, যদি আমরা গোলকার আকৃতি এবং লাল রং কে আপেলকে অন্য সব কিছু থেকে আলাদা করার Feature হিসেবে ঘোষনা করি, তাহলে আপেলটা দিনশেষে কিছু লাল বলের সাথে গিয়ে জমা হতে পারে। এটিকে বলে Over generalization. আবার উল্টোটি হলে under generalization  ও হয়ে যেতে পারে। 🙂

সুতরাং বোঝাই যাচ্ছে ঘাপলাটা কোথায়। আপনাকে আপেল থেকে অন্য কিছু আলাদা করার জন্যে ঠিক সেই Feature গুলোই গ্রহণ করতে হবে যেগুলো over বা under generalization তৈরী করবেননা। এর মানে হচ্ছে গোলাকার আকৃতি Feature হিসেবে খুব একটা সুবিধার না, অনেক কিছুই গোলাকার হতে পারে। কিভাবে ভালো Feature বের করতে হয় সেটা নিয়ে আমরা ভটভট পরে করবো কোন এক দিন। আসেন কোন কিছু নিয়ে ভটভট করি। আমাদের আজকের লক্ষ্য K-Nearest Neighbor Algorithm দেখা, এটি আসলে একটি Classification Algorithm, সহজ কথায় যেগুলো দিয়ে অনেক কিছু থেকে জিনিসপত্র ভিন্ন ভিন্ন প্রকারে ভাগ করা হয়।

K-Nearest Neighbor Algorithm

K-Nearest Neighbour এর নাম ই যথেষ্ট আসলে এটি কি করে সেটা বোঝানোর জন্যে। সহজ বাংলায় এটি কোন কিছুকে ক্ল্যাসিফাই করে অনেকটা সে জিনিসটার আশেপাশের জিনিসগুলো কোন প্রকারের তার উপর থেকে। ধরুন আপনি একটি গোলাকার জিনিস অনেকগুলো আপেল আর বলের মধ্যে রাখলেন । কে নিয়ারেস্ট নেইবর যেটা বলে সেটা হচ্ছে  আপনার রাখা নতুন বস্তুটি আশেপাশের অধিকাংশ বস্তু যে প্রকারের সেই প্রকারের মধ্যে পরে। যদি আশেপাশের অধিকাংশ জিনিস বল হয়, তাহলে সেটি বল অথবা সেটি আপেল। আজগুবি ঠেকলেও ভুলে যাবেন না, আপনি কিন্তু যে কোন জায়গায় আপনার অজানা জিনিসটি বসাতে পারবেন না। বসাবেন আসলে তার ফিচারের ভিত্তিতে। যদি সেটি সত্যি আপেল হয় এবং আপনার ফিচার ঠিকমতো নির্ণয় করা হয়ে থাকে তাহলে সেটি অবস্থান নেবে আপেলদের পাশেই। তার মানে এই নাম এই অ্যালগরিদম একেবারেই ভুল করেনা। তবে সেটা নিয়ে পরে কথা বলি না হয়।

ছোট্ট একটি উদাহরণ:

মনে করুন, আপনার কাছে এইরকম একটি ডাটাসেট আছে।

Rooms Area Type
1 350 apartment
2 300 apartment
3 300 apartment
4 250 apartment
4 500 apartment
4 400 apartment
5 450 apartment
7 850 house
7 900 house
7 1200 house
8 1500 house
9 1300 house
8 1240 house
10 1700 house
9 1000 house
1 800 flat
3 900 flat
2 700 flat
1 900 flat
2 1150 flat
1 1000 flat
2 1200 flat
1 1300 flat

দেখেই বোঝা যাচ্ছে এটি আকার এবং রুম সংখ্যার ভিত্তিতে আপনার বাসাটি অ্যাপার্টমেন্ট, ফ্ল্যাট না হাউজ সেটার প্রকারভেদ। তাহলে এখানে সহজ কথায় রুম সংখ্যা এবং আকার আমাদের ফিচার। এই স্যাম্পল সেটটি যদি আমরা কোন ম্যাশিন কে শিখাই, তাহলে এটার উপর ভিত্তি করে সে নতুন কোন নোড এই কোন ক্যাটাগরিতে পরে সেটা আপনাকে জানিয়ে দেবে। প্রশ্ন হচ্ছে কিভাবে। আমরা যেটা করবো এখন সেটি হচ্ছে একটি দ্বিমাত্রিক কার্তেসীয় স্থানাংক ব্যবস্হায় X অক্ষ বরাবর Room সংখ্যা এবং Y অক্ষ বরাবর বসাবো Area. এখন এই ছকে নতুন কোন বাসা যখন আসবে তখন সেটিও হবে আসলে একটি ডাটা পয়েন্ট মাত্র। আমরা সেটির আশে পাশের K সংখ্যক ডাটা পয়েন্ট গুলো থেকে দেখে নিবো সেটি কোন প্রকারে পরে। সহজ বাংলায় আমাদের অ্যালগরিদম টা দাঁড়ায় এরকম :

১. স্যাম্পল ডাটা সহ যে সকল Mystery Point (যেগুলো কোন প্রকারে পরে আমরা জানিনা)  বসিয়ে দিন ছকে
২. বের করে নিন Mystery Point গুলো থেকে সকল পয়েন্ট এর দূরত্ব। ৩. এরপর বেছে নিন কাছের K সংখ্যক প্রতিবেশী পয়েন্ট কে।
৪. k সংখ্যক প্রতিবেশীদের সর্বোচ্চ সংখ্যক প্রতিবেশী যে প্রকারের আপনার Mystery Point ও সেই প্রকারের।

আসুন আমরা ছোট্ট একটা কোডে চলে যাই।

কোড:

আমি কোড করার প্ল্যাটফরম হিসেবে নিয়েছি উইন্ডোজ ফোন ৮ এবং আমি চার্ট এর জন্যে ব্যবহার করছি আমার পছন্দের Telerik Rad Controls. আপনারা চাইলে স্প্যারো টুলকিট ব্যবহার করতে পারেন। এটি ফ্রি এবং চমৎকার। চাইলে যে কোন ল্যাংগুয়েজে কোড করে ফেলতে পারেন। আমি এখানে অসম্ভব crude code করে গেছি এবং চেষ্টা করে গেছি কোড বড় হলেও বোধগম্য রাখার। আপনি আপনার মতো সাজিয়ে নেবেন। 🙂

আমরা প্রথমে Node  এবং NodeList নামের দুটি ক্লাস নিয়ে কাজ করবো।

class Node শুরুতে দেখতে অনেকটাই এরকম:

public class Node
{
public int Rooms { get; set; }
public int Area { get; set; }
private NodeType _type = NodeType.unknown;
public NodeType Type {
get
{ return _type; }
set
{
_type = value;
SetForeground();

}
}

private void SetForeground()
{
if (_type == NodeType.apartment)
{
Foreground = new SolidColorBrush(Colors.Red);
}
else if (_type == NodeType.flat)
{
Foreground = new SolidColorBrush(Colors.Green);
}
else if (_type == NodeType.house)
{
Foreground = new SolidColorBrush(Colors.Blue);
}
else
{
Foreground = new SolidColorBrush(Colors.White);
}
}

public SolidColorBrush Foreground { get; set; }

public ObservableCollection Neighbours { get; set; }

public double Distance { get; set; }

public Node()
{

}

}

এখানে প্রথমে যেটা চোখে পরে, সেটা হচ্ছে শুরুতেই Rooms এবং Area বলে দেয়া শুরুতে। এই দুটো আমাদের ফিচার, একটি Node একটি বাসাকে প্রকাশ করে এখানে। সুতরাং এগুলো শুরুতেই ডিফাইন করা। এরপর দেখা যাচ্ছে NodeType বলে দেয়া এবং NodeType এর ভিত্তিতে একটি Foreground বলে দেয়া। এটি করা হয়েছে শুরু চার্ট তৈরী করার সুবিধার্থে। আসুন দেখে নেই NodeType enum টি কিরকম।

public enum NodeType
{
[Description("apartment")]
apartment,
[Description("house")]
house,
[Description("flat")]
flat,
[Description("unknown")]
unknown
}

স্পষ্টতই দেখা যচ্ছে টাইপ গুলো আমরা আমাদের স্যাম্পল ডাটার মতো বলে নিয়েছি। শুধু unknown টাইপটি আমাদের Mystery Point এর জন্যে। যাতে আমরা চার্টে আলাদা করতে পারি কোনটার টাইপ আমি জানিনা। এবার আসুন NodeList ক্লাসটি কিরকম দেখে নেই

public class NodeList
    {
        private int MinAreas { get; set; }
        private int MinRooms { get; set; }

        private int MaxAreas { get; set; }

        private int MaxRooms { get; set; }

        public ObservableCollection<Node> Nodes { get; set; }
        public int K { get; set; }

        public NodeList()
        {
            MinAreas = MinRooms = 100000;
            MaxAreas = MaxRooms = 0;
        }

    }

এখানে দেখা যাচ্ছে MinAreas, MaxAreas এবং MinRooms, MaxRooms নামের কয়েকটি প্রোপার্টি ডিফাইন করা। এগুলো আরো সহজেই করা যায়। কিন্তু কাজের এবং বোঝার সুবিধার্থে আলাদা করে লেখা। যে দুটি প্রোপার্টি জরুরী তা হচ্ছে Nodes এবং K . Nodes হচ্ছে আপনার ম্যাশিনের শেখা সমগ্র নোড গুলো (উপরের বাসার লিস্ট) এবং K হচ্ছে কতজন প্রতিবেশী নোড কে আমরা বিবেচনায় আনবো সেটির সংখ্যা। আপনি চাইলে Nodes কালেকশনটি কনস্ট্রাকটর ডিপেন্ডেন্সি তে ঢুকিয়ে দিতে পারেন। কিন্তু আমি আগেই বলেছি, এই পুরো লেখার উদ্দেশ্য বোঝানো, তাই কোডের এই দুরবস্থা। 😛
আসুন আর একটু আগানো যাক। এখন আমাদের Mystery Point থেকে সকল পয়েন্ট এর দূরত্ব বের করার কথা। কিন্তু তার আগে কিছু কথা বলা প্রয়োজন।

নরমালাইজেশন

একটু ভালো করে দেখুন, আমাদের দেয়া স্যাম্পল ডাটাতে রুম সংখ্যা ১ থেকে ১০ এর মধ্যে এবং রুম সাইজ ২৫০ তে ১৭০০ এর মধ্যে। তার মানে আমাদের x অক্ষ বরাবর বিন্দুগুলোর দূরত্ব Y অক্ষ বরাবর বিন্দুগুলোর দূরত্ব হতে গড়ে কম হবে। এটিতে যেটা হতে পারে, একটি বাসা ছকে তার সর্ব্বোচ্চ কাছাকাছি বিন্দু হতে অপেক্ষাকৃত দূরে সরে যেতে পারে এবং ভুল বিন্দুর কাছে X অক্ষ বরাবর কাছে চলে আসতে পারে। ফলে আপনার অ্যালগরিদম এর ফলাফল ক্ষতিগ্রস্ত হতে পারে। আসুন সব ভ্যালু গুলোকে আমরা ০ হতে ১ এর মধ্যে নিয়ে আসি। সহজ বাংলায় আমরা আমাদের গ্রাফটিকে বর্গাকার করে নিচ্ছি যাতে X অক্ষ এবং Y অক্ষ এর গুরুত্ব সমান থাকে। আপনি যদি চান আপনার ক্ল্যাসিফিকেশনে রুম সংখ্যা বেশি গুরুত্ব পাবে। তাহলে আপনি যেকোন অক্ষের দুরত্ব কমিয়ে আনতে পারেন। একে Weighting বলে। সেটা না হয় পরে।

দুই অক্ষের সকল ভ্যালু ০ হতে ১ এর মধ্যে আনতে হলে দুই অক্ষের সর্ব্বোচ্চ এবং সর্বনিম্ন মান আমাদের জানতে হবে এবং সেটাকে অক্ষভেদে মানের পার্থক্য দিয়ে ভাগ দিতে হবে। আসুন কোড দেখে বুঝে নেই:

        private void CalculateRanges()
        {
            if(Nodes!=null && Nodes.Count>0)
            {

                foreach (var node in Nodes)
                {
                    if (node.Rooms < MinRooms)
                        MinRooms = node.Rooms;
                    if (node.Rooms > MaxRooms)
                        MaxRooms = node.Rooms;

                    if (node.Area < MinAreas)
                        MinAreas = node.Area;
                    if (node.Area > MaxAreas)
                        MaxAreas = node.Area;
                }
            }
        }

CalculateRanges() মেথডটি NodeList ক্লাসে যোগ করা। দেখেই বোঝা যাচ্ছে খুবই আনাড়ি কোড কিন্তু বোঝার জন্যে এটি অসাধারণ। খেয়াল করে দেখুন CalculateRanges() শুধুমাত্র room এবং area (আমাদের x এবং y অক্ষ) এর সর্ব্বোচ্চ এবং সর্বনিম্ন মান খুঁজে বের করছে। আর কিছু নয়। এখন যেহেতু আমাদের হাতে দুই অক্ষের সর্ব্বোচ্চ এবং সর্বিনম্ন মান আছে সেহেতু আমরা মূল অ্যালগরিদমে প্রবেশ করতে পারি।

public void DetermineUnknown()
        {
            //start the calculation of range;
            this.CalculateRanges();

            foreach (var node in Nodes)
            {
                if(node.Type == NodeType.unknown)
                {
                    node.Neighbours = new ObservableCollection<Node>();
                    foreach (var NNode in Nodes)
                    {
                        if (NNode.Type == NodeType.unknown)
                            continue;
                        node.Neighbours.Add(NNode);

                    }

                    //Measure Distance Now

                    node.MeasureDistances(MaxRooms - MinRooms, MaxAreas - MinAreas);

                    //Sort it out
                    node.Neighbours = new ObservableCollection<Node>(node.Neighbours.OrderBy(x => x.Distance).ToList());

                    //Guess the type
                    node.GuessType(this.K);
                }
            }
        }

এক বস্তা জিনিস এক সাথে লিখে ফেললাম, এই তো? আসুন আস্তে আস্তে আগাই। আমরা CalculateRanges() আগেই দেখেছি। শুরুতেই লুপে ঘুরছে আমাদের সব স্যাম্পল ডাটা (যেখানে আমাদের Classification না জানা Mystery Points গুলো, মানে যে বাসাগুলো কোন প্রকারের আমরা জানিনা সেগুলোও আছে এবং অবশ্যই স্যাম্পল ডাটা সেট ও আছে)। দেখা হয়েছে NodeType unknown কিনা, হলে সেটিই সেই বাসা যেটির প্রকার আমরা জানিনা, তার মানে সেটি Mystery Point গুলোর একটি। আসুন সেটিকে classify এর ব্যবস্থা করি। শুরুতে ই লাগবে সকল প্রতিবেশী সকল নোড হতে দূরত্ব্। না হলে কাছের গুলো বের করবেন কিভাবে। সেজন্যে MeasureDistances() ব্যবহার করা। দেখে আসি মেথডটি দেখতে কেমন হতে পারে। খেয়াল করে দেখুন, আরগুমেন্ট দুটি হচ্ছে Rooms এবং Areas এর মানের পার্থক্য। এটি আমাদের Normalize করতে সাহায্য করবে।

        internal void MeasureDistances(int RoomRange, int AreaRange)
        {
            foreach (var neighbour in Neighbours)
            {
                double delta_rooms = neighbour.Rooms - this.Rooms;
                delta_rooms = delta_rooms / (double)RoomRange;

                double delta_area = neighbour.Area - this.Area;
                delta_area = delta_area / (double)AreaRange;

                neighbour.Distance = Math.Sqrt(delta_rooms * delta_rooms + delta_area * delta_area);

            }
        }

এখানে দূরত্ব বের করার আগে Normalization করো হয়েছে। তার মানে আমরা প্রতিটি প্রতিবেশীর Room এবং Area এর মানের পার্থক্য কে Room এবং Area এর সর্বোচ্চ এবং সর্বনিম্ন মানের পার্থক্য দিয়ে ভাগ দিয়ে এই মানগুলোকে ০ হতে ১ এর মধ্যে নিয়ে আসছি। এর পর আমরা পিথাগোরাসীয় দূরত্ব বের করছি। একটি জিনিস লক্ষ্য করে দেখুন। আমাদের ফিচার সংখ্যা দুইটি হওয়ায় আমরা দ্বিমাত্রিক কার্তেসীয় স্থানাংক ব্যবস্থায় আমরা বিন্দুগুলোকে X অক্ষ এবং Y অক্ষে আলাদা করে লিখেছি। এইজন্য দ্বিমাত্রিক দুটি বিন্দুর দূরত্ব বের করার সূত্র হচ্ছে Math.Sqrt(x * x + y * y)
যদি আপনার ফিচার তিনটি হতো তাহলে ছকটি ত্রিমাত্রিক স্থানাংক ব্যবস্থায় চলে আসতো। তখন দুই বিন্দুর দূরত্ব হতো Math.Sqrt(x * x + y * y+ z*z)
এইভাবে n সংখ্যক ফিচারের জন্য n মাত্রিক স্থানাংক ব্যবস্থায় দুই বিন্দুর দূরত্ব হতো Math.Sqrt(x * x + y * y+ z*z+ …..+ n*n)
আপনি চাইলে একটি n মাত্রিক স্থানাংক ব্যবস্থাকে কমিয়ে এনে দ্বিমাত্রিক ব্যবস্থায় প্রোজেক্ট করতে পারেন। তবে সেগুলো অন্য কোন দিন। যেটা আমি এখানে বোঝাতে চেয়েছিলাম সেটি হচ্ছে এটিই KNN এর ক্ষমতার পরিচায়ক, আপনার ফিচার সংখ্যা যাই হোক না কেন, সেটি আরামসে কাজ করতে পারে। তবে মনে রাখবেন বেশি ফিচার মানেই ভালো ক্ল্যাসিফিকেশন তা নয়। তাতে সেই generalization সংক্রান্ত জটিলতায় পড়তে পারেন।

তো MeasureDistances() এর পরে আমাদের কাজ হচ্ছে দূরত্বের ভিত্তিতে প্রতিটি নোডের প্রতিবেশীর লিস্ট কে সর্ট করা। কারণ আমরা k সংখ্যক কাছের প্রতিবেশী নিয়ে আগ্রহী। এর পরেই চলে আসে Mystery Point টি যেটির প্রকার আমরা জানিনা (যে বাসাটি ফ্ল্যাট, অ্যাপার্টমেন্ট না হাউজ আমরা জানিনা) সেটির প্রকার খুঁজে বের করা। সেই কাজটি করে GuessType()

internal void GuessType(int k)
        {
            int[] TypeVotes = new int[4]; //There are three types;

            var EligibleNeighbours = this.Neighbours.Take(k).ToList();

            foreach(var eligibleNode in EligibleNeighbours)
            {
                TypeVotes[(int)eligibleNode.Type] ++;

            }

            NodeType GuessedType = (NodeType)TypeVotes.Max();

            MessageBox.Show("Guessed type for node is " + GuessedType.ToString());

            this.Type = GuessedType;
        }

আমরা চলে এসেছি প্রায় শেষে, দেখুন এটি কোন নোডের প্রতিবেশী হতে খুঁজে বের করে সর্বোচ্চ কাছের k সংখ্যক প্রতিবেশী কে । এবং এদের থেকে সর্বোচ্চ সংখ্যক প্রতিবেশী যে প্রকারের আপনার Mystery Point টি ও সেই প্রকারের বলে ঘোষনা করে। মানে এখানেই আপনি জেনে যাবেন আপনার বাসাটি কোন প্রকারের।

উইন্ডোজ ফোনে উদাহরণ:

‌উইন্ডোজ ফোনে আমি একটি ছোট্ট MVVM অ্যাপ বানিয়ে পরীক্ষা করেছি অ্যালগরিদম টি। পুরো কোড শেষে দেয়া আছে। তাই MainviewModel এর কিছু অংশ আমি তুলে দিচ্ছি:

        public MainViewModel()
        {

            TestKnnCommand = new RelayCommand(TestKnnAction);
            LoadSamples();
            AddASample();
        }

        private void TestKnnAction()
        {
            NodeCollection.DetermineUnknown();
        }

        private void LoadSamples()
        {

            using(StreamReader reader= new StreamReader("SampleData.txt"))
            {
                var data = reader.ReadToEnd();
                List<Node> datArray = JsonConvert.DeserializeObject<List<Node>>(data, new StringToEnumConverter());
                var _sampleCollection = new ObservableCollection<Node>(datArray);

                NodeCollection = new NodeList();
                NodeCollection.K = 3;
                NodeCollection.Nodes = _sampleCollection;

            }

        }

        private void AddASample()
        {
            Node newNode = new Node() { Area = 500, Rooms = 2, Type = NodeType.unknown };
            NodeCollection.Nodes.Add(newNode);
        }

আমি শুধু শুরুতে লোড করে নিয়েছি আমার স্যাম্পল ডাটা গুলো। এরপর যোগ করে নিয়েছি একটি অজানা Mystery Point , মানে এমন একটি বাসা যেটি কোন প্রকারের আমি জানিনা। K এখানে ৩, তার অর্থ হচ্ছে আমি ৩ টি কাছের প্রতিবেশী ব্যবহার করবো classification এর জন্যে।
TestKnnAction() একটি action যেটি একটি বাটন হতে command এর সাহায্যে invoke করা হয়। এবং এটি অজানা পয়েন্ট টির ক্ল্যাসিফিকেশন বের করার জন্য DetermineUnknown() ব্যবহার করে। আপনি চাইলে একাধিক স্যাম্পল যোগ করে অ্যালগরিদম টি চালিয়ে দেখতে পারেন।

জেনে রাখা ভালো:

অন্য সকল ক্লাসিফিকেশন অ্যালগরিদম এর মতোই এটিতেও সমস্যা আছে। আপনার ডাটা সেট যদি পৃথক করার যোগ্য (Separable) হয় তবেই এটি বেশ ভালো কাজ করে। আর যদি তা না হয় আপনি এমন একটি ছক পাবেন যেটায় বিন্দু গুলো সব জায়গায় ছড়িয়ে আছে এবং ফলশ্রুতিতে আপনি ভালো ফলাফল পাবেন না। বরং আপনি যদি ছকে দেখেন বিন্দুগুলো cluster বা গুচ্ছে গুচ্ছে বিভক্ত তাহলে বুঝবেন এটি Separable বা পৃথক করার যোগ্য। আরেকটি জিনিস, নোডসংখ্যা বাড়লে আপনার ক্যালকুলেশন টাইম ও বাড়বে। তাই আপনি চাইলে Pruning করতে পারেন। যেমন যে বাসার Room সংখ্যা ২ তার জন্যে যে সকল বাসা যাদের room সংখ্যা ৬ এর অধিক তাদের সাথে দূরত্ব বের করা অর্থহীন।

আশা করি সবার ভালো লাগবে। অ্যাপ এর দুটি স্ক্রিনশট তুলে দিলাম।

Initial KNN Graph

classification result

কোডটুকু পাওয়া যাবে এখানে। যদিও আপনার Telerik Rad Controls for Windows Phone 8 এর লাইসেন্স থাকা লাগবে এটি বিল্ড করার জন্যে। তবুও দিয়ে দিলাম : http://1drv.ms/1DOTVu8