Using below code you can make drawing by moving Mobile phone in the Air. This application was done for Windows Phone 8.1 during a project, however its not a complete code, you can add more methods and can apply different techniques to smooth accelerometer values.
MainPage.XAML
This is not complete code of xaml file, this code is just to give you an idea, xaml file which is layout
file contains Canvas like below, application will draw hand movements on canvas. You can also add a pencil in canvas and move it / change its position within canvas as accelerometer values changes.
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="root_layout" Tapped="root_layout_Tapped"
Margin="19,9.5,19,0">
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Canvas Background="White" Name="MyCanvas" Height="500" Width="500">
</Canvas>
</StackPanel>
</Grid>
---------------------------------------------------------------------------------------------------------------
MainPage.XAML.CS
This file contains actual logic,
Below is accelerometer reading change event, this will be called when you want app to start detecting hand movments, you can initialize accelerometer object.
public async void _accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
_num variable is used for, how many previous values you want to consider for calculating hand movment.
if (_index >= _num)
{
_index = 0;
}
As line is 2-D object, so we need only 2 values for x and y, but accelerometer gives us three values for X,Y,Z axis. so you can transform three values into 2. but I am using X and Z axis just to give you an Idea,
X and Z axis values of accelerometer will be saved in an array so that application can calculate average of values for noise removal and smoothing values of accelerometer.
As you may have an idea that accelerometer gives us almost 180 values in a second for all the three axis and application cannot process all values, so it needs to remove noise and smooth values.
_x[_index] = (_accelerometer.GetCurrentReading().AccelerationX);
_y[_index] = (_accelerometer.GetCurrentReading().AccelerationZ);
_index++;
for (int i = 0; i < _num; i++)
{
_sumx += _x[i];
_sumy += _y[i];
}
y and x variable below contains smoothed values after averaging, which we will use for drawing.
double y = _sumx / _num;
double x = _sumy / _num;
_currentposition.X = (x * 200);
_currentposition.Y = (y * 200);
// boundry detect
If you need to make sure that line which you are drawing donot cross Canvas boundary, you can //add a check like below.
if (_currentposition.X > MyCanvas.ActualWidth - 30 || _currentposition.Y > MyCanvas.ActualHeight - 30)
{
}
Here we will make a line object like below by giving it current and previous values of acceleromter
and then we will add this line object in Canvas.
_drawLine = new Line()
{
X1 = _previousposition.X,
Y1 = _previousposition.Y,
X2 = _currentposition.X,
Y2 = _currentposition.Y,
StrokeThickness = _strokrthick,
Stroke = new SolidColorBrush(Colors.Blue)
};
_previousposition.X = _currentposition.X;
_previousposition.Y = _currentposition.Y;
If you need to detect pause, in accelerometer values, or hand movement, you can use below code.
it will calculate distance between values, if distance is below a certain threshold, you can make sure that user is not moving mobile phone or not drawing. You can find distance function below.
//if (gloabal_length > 0)
//{
// gloabal_array[cc] = _previousposition.X;
// gloabal_array[++cc] = _previousposition.Y;
// cc++;
// gloabal_length = gloabal_length - 2;
//}
//else if (gloabal_length == 0)
//{
// gloabal_length = 6;
// cc = 0;
// int i = 0;
// g1 = gloabal_array[i];
// g2 = gloabal_array[i + 1];
// g3 = gloabal_array[i + 2];
// g4 = gloabal_array[i + 3];
// g5 = gloabal_array[i + 4];
// g6 = gloabal_array[i + 5];
//}
MyCanvas.Children.Add(_drawLine);
double dist = Distance(g1, g2, g3, g4, g5, g6);
if (flag == 0)
{
MyCanvas.Children.Remove(_drawLine);
flag = 1;
}
});
}
function for calculating distance between accelerometer values.
/*CALCULATING DISTANCE*/
private double Distance(double x1, double y1, double x2, double y2, double x3, double y3)
{
double d = 0;
double[] dist_values = new double[200];
d = Math.Sqrt(Math.Pow((x2 - x1), 2) + Math.Pow((y2 - y1), 2) + Math.Pow((x3 - x2), 2) + Math.Pow((y3 - y2), 2));
return d;
}/*DISTANCE FUNCTION ENDS HERE */
If you want to convert this drawing into an image and want to store it on your device or want to send it, you can convert canvas into an image using below code.
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(MyCanvas, (int)MyCanvas.Width, (int)MyCanvas.Height);
IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
byte[] pixels = null;
pixels = pixelBuffer.ToArray();
// 2. Write the pixels to a InMemoryRandomAccessStream
var stream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
WB = new WriteableBitmap(renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight);
WB.SetSource(stream);
if (WB == null)
return;
string filename = "Image-" + DateTime.Now.ToFileTime() + ".jpeg";
await SaveWriteableBitmapAsJpeg(WB, filename);
StorageFile userPhoto = await KnownFolders.CameraRoll.GetFileAsync(filename);
BitmapImage image = new BitmapImage();
byte[] fileBytes = null;
using (IRandomAccessStreamWithContentType _stream = await userPhoto.OpenReadAsync())
{
fileBytes = new byte[_stream.Size];
using (DataReader reader = new DataReader(_stream))
{
await reader.LoadAsync((uint)_stream.Size);
reader.ReadBytes(fileBytes);
}
}
// If you want to convert it into string for serializing/streaming. than use below code
string encodedImage = Convert.ToBase64String(fileBytes);
MainPage.XAML
This is not complete code of xaml file, this code is just to give you an idea, xaml file which is layout
file contains Canvas like below, application will draw hand movements on canvas. You can also add a pencil in canvas and move it / change its position within canvas as accelerometer values changes.
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="root_layout" Tapped="root_layout_Tapped"
Margin="19,9.5,19,0">
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Canvas Background="White" Name="MyCanvas" Height="500" Width="500">
</Canvas>
</StackPanel>
</Grid>
---------------------------------------------------------------------------------------------------------------
MainPage.XAML.CS
This file contains actual logic,
Below is accelerometer reading change event, this will be called when you want app to start detecting hand movments, you can initialize accelerometer object.
public async void _accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
_num variable is used for, how many previous values you want to consider for calculating hand movment.
if (_index >= _num)
{
_index = 0;
}
As line is 2-D object, so we need only 2 values for x and y, but accelerometer gives us three values for X,Y,Z axis. so you can transform three values into 2. but I am using X and Z axis just to give you an Idea,
X and Z axis values of accelerometer will be saved in an array so that application can calculate average of values for noise removal and smoothing values of accelerometer.
As you may have an idea that accelerometer gives us almost 180 values in a second for all the three axis and application cannot process all values, so it needs to remove noise and smooth values.
_x[_index] = (_accelerometer.GetCurrentReading().AccelerationX);
_y[_index] = (_accelerometer.GetCurrentReading().AccelerationZ);
_index++;
for (int i = 0; i < _num; i++)
{
_sumx += _x[i];
_sumy += _y[i];
}
y and x variable below contains smoothed values after averaging, which we will use for drawing.
double y = _sumx / _num;
double x = _sumy / _num;
_currentposition.X = (x * 200);
_currentposition.Y = (y * 200);
// boundry detect
If you need to make sure that line which you are drawing donot cross Canvas boundary, you can //add a check like below.
if (_currentposition.X > MyCanvas.ActualWidth - 30 || _currentposition.Y > MyCanvas.ActualHeight - 30)
{
}
Here we will make a line object like below by giving it current and previous values of acceleromter
and then we will add this line object in Canvas.
_drawLine = new Line()
{
X1 = _previousposition.X,
Y1 = _previousposition.Y,
X2 = _currentposition.X,
Y2 = _currentposition.Y,
StrokeThickness = _strokrthick,
Stroke = new SolidColorBrush(Colors.Blue)
};
_previousposition.X = _currentposition.X;
_previousposition.Y = _currentposition.Y;
If you need to detect pause, in accelerometer values, or hand movement, you can use below code.
it will calculate distance between values, if distance is below a certain threshold, you can make sure that user is not moving mobile phone or not drawing. You can find distance function below.
//if (gloabal_length > 0)
//{
// gloabal_array[cc] = _previousposition.X;
// gloabal_array[++cc] = _previousposition.Y;
// cc++;
// gloabal_length = gloabal_length - 2;
//}
//else if (gloabal_length == 0)
//{
// gloabal_length = 6;
// cc = 0;
// int i = 0;
// g1 = gloabal_array[i];
// g2 = gloabal_array[i + 1];
// g3 = gloabal_array[i + 2];
// g4 = gloabal_array[i + 3];
// g5 = gloabal_array[i + 4];
// g6 = gloabal_array[i + 5];
//}
MyCanvas.Children.Add(_drawLine);
double dist = Distance(g1, g2, g3, g4, g5, g6);
if (flag == 0)
{
MyCanvas.Children.Remove(_drawLine);
flag = 1;
}
});
}
function for calculating distance between accelerometer values.
/*CALCULATING DISTANCE*/
private double Distance(double x1, double y1, double x2, double y2, double x3, double y3)
{
double d = 0;
double[] dist_values = new double[200];
d = Math.Sqrt(Math.Pow((x2 - x1), 2) + Math.Pow((y2 - y1), 2) + Math.Pow((x3 - x2), 2) + Math.Pow((y3 - y2), 2));
return d;
}/*DISTANCE FUNCTION ENDS HERE */
If you want to convert this drawing into an image and want to store it on your device or want to send it, you can convert canvas into an image using below code.
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(MyCanvas, (int)MyCanvas.Width, (int)MyCanvas.Height);
IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
byte[] pixels = null;
pixels = pixelBuffer.ToArray();
// 2. Write the pixels to a InMemoryRandomAccessStream
var stream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
WB = new WriteableBitmap(renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight);
WB.SetSource(stream);
if (WB == null)
return;
string filename = "Image-" + DateTime.Now.ToFileTime() + ".jpeg";
await SaveWriteableBitmapAsJpeg(WB, filename);
StorageFile userPhoto = await KnownFolders.CameraRoll.GetFileAsync(filename);
BitmapImage image = new BitmapImage();
byte[] fileBytes = null;
using (IRandomAccessStreamWithContentType _stream = await userPhoto.OpenReadAsync())
{
fileBytes = new byte[_stream.Size];
using (DataReader reader = new DataReader(_stream))
{
await reader.LoadAsync((uint)_stream.Size);
reader.ReadBytes(fileBytes);
}
}
string encodedImage = Convert.ToBase64String(fileBytes);
No comments:
Post a Comment