UWP / C# Rotating BMP - c#

The question seems to be asked already, however I cannot find a relevant answer.
I am loading a BMP image to memory in a UWP app, and I would like to rotate it by either 90, 180 or 270, but I just cannot find the way to do this.
The imgSource.rotate() does not seem to exist anymore
The RotateTransform works with xaml
....
Could anyone add the missing code by a chance please?
public async Task LoadImage()
{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("test.bmp");
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
var decoder = await BitmapDecoder.CreateAsync(stream);
bitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
var imgSource = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight);
// Code to rotate image by 180 to be added
bitmap.CopyToBuffer(imgSource.PixelBuffer);
}
}

The RotateTransform works with xaml
As you known, RotateTransform is for rotate transform in uwp app XAML. A RotateTransform is defined by an Angle that rotates an object through an arc around the point CenterX, CenterY. But a transform is typically used to fill the UIElement.RenderTransform property, so if you load the image source to an ImageControl, you can rotate the ImageControl since it is a UIElement. For example, if we have ImageControl as follows:
<Image x:Name="PreviewImage" Height="400" Width="300" AutomationProperties.Name="Preview of the image" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center"/>
We can simply rotate it by angle property by code as:
RotateTransform m_transform = new RotateTransform();
PreviewImage.RenderTransform = m_transform;
m_transform.Angle = 180;
If you need rotate an image file not a UIElement, you may need to decode the image file as what you already did and then encode the file with setting the BitmapTransform.Rotation property. Code as follows:
double m_scaleFactor;
private async void btnrotatefile_Click(object sender, RoutedEventArgs e)
{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("test.bmp");
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.ReadWrite),
memStream = new InMemoryRandomAccessStream())
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
uint originalWidth = decoder.PixelWidth;
uint originalHeight = decoder.PixelHeight;
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(memStream, decoder);
if (m_scaleFactor != 1.0)
{
encoder.BitmapTransform.ScaledWidth = (uint)(originalWidth * m_scaleFactor);
encoder.BitmapTransform.ScaledHeight = (uint)(originalHeight * m_scaleFactor);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
}
//Rotate 180
encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise180Degrees;
await encoder.FlushAsync();
memStream.Seek(0);
fileStream.Seek(0);
fileStream.Size = 0;
await RandomAccessStream.CopyAsync(memStream, fileStream);
}
}
More features about the image file rotation you can use other APIS under Windows.Graphics.Imaging namespace. And the scenario 2 of SimpleImaging official sample provides a complete sample about image rotation you can reference.

Related

Is there a way to lower the resolution of a UWP InkCanvas to 28x28?

In an attempt to create a neural network program to detect handwritten digits, I first need to capture the handwritten digit on an InkCanvas. The problem is that when I save the InkCanvas as a Bitmap, the resolution is much greater than 28x28. I'm favoring 28x28 because the input layer to my neural network will have 784 nodes. The size of my input layer will skyrocket if I use the full resolution of the InkCanvas.
As it stands, I draw the handwritten digits in a 28x28 canvas within MS Paint.
I then save the image as a bmp file in my programs project folder.
From there, my program sees the bmp file and processes it accordingly.
I just need help lowering the resolution of the UWP InkCanvas.
I do not simply want to edit the width and height, but the actual pixel count/zoom.
Main Page
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
// sets initial window size
ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(1000, 800);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
// sets supported inking device types
inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse;
// sets initial ink stroke attributes
InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
drawingAttributes.Color = Windows.UI.Colors.White;
drawingAttributes.Size = new Windows.Foundation.Size(2, 2);
drawingAttributes.IgnorePressure = false;
drawingAttributes.FitToCurve = true;
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}
private async void saveInkCanvasBitmap()
{
RenderTargetBitmap bitmap = new RenderTargetBitmap();
await bitmap.RenderAsync(grid);
var pixelBuffer = await bitmap.GetPixelsAsync();
byte[] pixels = pixelBuffer.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
StorageFolder pictureFolder = KnownFolders.SavedPictures;
var file = await pictureFolder.CreateFileAsync("image.bmp", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)bitmap.PixelWidth,
(uint)bitmap.PixelHeight,
displayInformation.RawDpiX,
displayInformation.RawDpiY,
pixels);
await encoder.FlushAsync();
}
}
private void clearButton_Click(object sender, RoutedEventArgs e)
{
// clears the ink canvas of any pen strokes
inkCanvas.InkPresenter.StrokeContainer.Clear();
}
private void submitButton_Click(object sender, RoutedEventArgs e)
{
saveInkCanvasBitmap(); // try to lower resolution of ink canvas to 28 x 28
// other stuff
}
}
Based on the description of BitmapEncoder.SetPixelData, the parameter called dpiX and dpiY are the resolution values for the bitmap. Please try to change your code like
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)bitmap.PixelWidth,
(uint)bitmap.PixelHeight,
28,
28,
pixels);

win2D uwp won't save a highlight stroke in an inkcanvas

I'm desperately trying to save a highlight stroke as a png. I'm using win2d for UWP to make this work.
It works well for strokes with 100% opacity, but when I set DrawAsHighlighter = true;, the saved png is empty, fully transparent.
Here's my code :
private void SetHighLight()
{
InkDrawingAttributes attributes = new InkDrawingAttributes();
attributes.DrawAsHighlighter = true;
attributes.PenTip = PenTipShape.Rectangle;
attributes.Size = new Size(4, 10);
attributes.Color = currentColor;
SetAttribute(attributes);
}
private void GetCanvasRender(out CanvasRenderTarget renderTarget)
{
CanvasDevice device = CanvasDevice.GetSharedDevice();
renderTarget = new CanvasRenderTarget(device, (int)ink.ActualWidth, (int)ink.ActualHeight, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.Transparent); //I already tried to delete this but it doesn't change anything
ds.DrawInk(ink.InkPresenter.StrokeContainer.GetStrokes());
}
}
private async void SavePicture()
{
CanvasRenderTarget renderTarget;
Image img = new Image();
GetCanvasRender(out renderTarget);
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile noteFile = await storageFolder.CreateFileAsync(i.ToString() + ".png", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await noteFile.OpenAsync(FileAccessMode.ReadWrite))
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
img.Source = new BitmapImage(new Uri(storageFolder.Path + "/" + i++ + ".png"));
img.VerticalAlignment = VerticalAlignment.Stretch;
ContainerCanvas.Children.Add(img);
Canvas.SetTop(img, ScrollViewerContainer.VerticalOffset);
Canvas.SetZIndex(img, 5);
}
I've read it might be because of the highlight not being present in the visual tree but I'm really not sure about it.
By the way when I try to change the opacity of a color(attributes.Color = Color.FromArgb(128, 255, 0, 0)), the inkcanvas doesn't apply the alpha, why ? Am I missing something ?
You can't save DrawAsHighlighter ink to a bitmap format like .png - that's just fundamentally not a meaningful operation to be attempting.
Regular non-highlighter ink is drawn using standard alpha blending, so it is reasonable to write just these ink shapes into a bitmap format. You can later blend that bitmap over some other background image, and get the same result as if the ink had been drawn directly over that background.
For highlighter ink, however, the "blend over background" is a more complex operation, not just standard sourceover blending. So there is no such thing as a bitmap image that contains just this ink - you also have to provide a background in order for the appropriate blend to be carried out.
You have three options here:
Don't use highligher ink mode.
Instead of saving out your ink as a bitmap image, save the original ink stroke data, and use inking APIs to later blend these strokes directly to their final location.
Include the background as well as the ink strokes in the same bitmap.
Try clearing the background of the canvas with:
ds.Clear(Colors.White);
The highlighter isn't visible on transparent backgrounds as it seems to multiply its value with the background color.
I finally figured out how to make it work.
I simply added a new layer before calling DrawInk and gave it an opacity, and got rid of the attributes.DrawAsHighlighter = true;. Instead, I've made 1 inkCanvas with 0.5 opacity specially for the highlighter, looking like you're using a highlighter.
Here's the code :
private void SetHighLight()
{
InkDrawingAttributes attributes = new InkDrawingAttributes();
attributes.PenTip = PenTipShape.Rectangle;
attributes.Size = new Size(4, 10);
attributes.Color = currentColor;
SetAttribute(attributes);
}
private void GetCanvasRender(out CanvasRenderTarget renderTarget, float opacity)
{
CanvasDevice device = CanvasDevice.GetSharedDevice();
renderTarget = new CanvasRenderTarget(device, (int)ink.ActualWidth, (int)ink.ActualHeight, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.Transparent);
using (ds.CreateLayer(opacity))
{
ds.DrawInk(ink.InkPresenter.StrokeContainer.GetStrokes());
}
}
}
private async void SavePicture(float opacity)
{
CanvasRenderTarget renderTarget;
Image img = new Image();
GetCanvasRender(out renderTarget, opacity);
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile noteFile = await storageFolder.CreateFileAsync(i.ToString() + ".png", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await noteFile.OpenAsync(FileAccessMode.ReadWrite))
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
img.Source = new BitmapImage(new Uri(storageFolder.Path + "/" + i++ + ".png"));
img.VerticalAlignment = VerticalAlignment.Stretch;
ContainerCanvas.Children.Add(img);
Canvas.SetTop(img, ScrollViewerContainer.VerticalOffset);
Canvas.SetZIndex(img, 5);
}

How to export part of a huge Canvas to PNG

I need to export a Canvas to multiple PNG files. Trying to export the whole canvas to an image and then crop the image failed due to the size of the resulting image (something like 20.000px x 5.000px).
Now the strategy is to split the main canvas into smaller parts and then export each individual part to an image. The resulting image though, doesn't show the image from the main canvas below it.
Any ideas? Here is the code used to test the splitting of the Canvas (the real one is much larger than 1024x1024):
public void TestPrintPartOfCanvas()
{
//the main canvas
var main = new Canvas();
main.Width = 1024;
main.Height = 1024;
main.Background = Brushes.Blue;
//place something inside the canvas
var redRect = new System.Windows.Shapes.Rectangle();
redRect.Fill = Brushes.OrangeRed;
redRect.Width = 128;
redRect.Height = 128;
main.Children.Add(redRect);
// reset current transform (in case it is scaled or rotated)
var transform = main.LayoutTransform;
main.LayoutTransform = null;
// Get the size of canvas
var size = new Size(main.Width, main.Height);
//representing the first part of the main canvas
var part = new Canvas();
part.Width = 256;
part.Height = 256;
part.Background = Brushes.Transparent;
main.Children.Add(part);
// Measure and arrange the surface
main.Measure(size);
main.Arrange(new Rect(size));
// Create a render bitmap and push the part to it
var renderBitmap = new RenderTargetBitmap((int)part.Width, (int)part.Height, 96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(part);
// Create a file stream for saving image
using (var outStream = new FileStream("p:/part.png", FileMode.Create))
{
// Use png encoder for our data
var encoder = new PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
// Restore previously saved layout
main.LayoutTransform = transform;
}
You may easily crop a part of the Canvas by adjusting the Arrange rectangle:
var size = new Size(main.Width, main.Height);
main.Measure(size);
var cropOffset = new Point(-256, -256);
main.Arrange(new Rect(cropOffset, size));
var renderBitmap = new RenderTargetBitmap(256, 256, 96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(main);
The above crops a 256x256 rectangle at position 256,256.

how to render canvas to bitmap excluding out of bounds elements (C# WinRT)

I have a simple paiting app and I'm trying to render a canvas into a bitmap using the RenderTargetBitmap.RenderAsync() method.
If a child element of the canvas exceeds canvas boundaries, the rendered bitmap is bigger than the canvas area... How can I avoid that and get only in-bounds elements rendered?
I tried the clip property of the canvas but it only works for the UI , not for the rendering.
ClipToBounds is not available in WinRT...
I've ran into same problem. Here is what worked for me:
Rect r = new Rect(new Point(0, 0), new Point(canvas.Width, canvas.Height));
foreach (var c in canvas.Children)
c.Clip = new RectangleGeometry() { Rect = r };
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(canvas, (int)canvas.Width, (int)canvas.Height);
FileSavePicker picker = new FileSavePicker();
picker.FileTypeChoices.Add("JPEG Image", new string[] { ".jpg" });
StorageFile file = await picker.PickSaveFileAsync();
if (file != null)
{
var pixels = await renderTargetBitmap.GetPixelsAsync();
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await
BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
byte[] bytes = pixels.ToArray();
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)canvas.Width, (uint)canvas.Height,
96, 96, bytes);
await encoder.FlushAsync();
}
}
Basically, you just add Clip to all Canvas's children, so everything what is out of bounds won't be rendered by RenderTargetBitmap.
ClipToBounds functionality can be easily added via AttachedProperty: details

How to crop a rotated portion of a WriteableBitmap in Windows Store app and C#

In a Windows Store app I want to crop a rotated rectangle part of a WriteableBitmap like Case 2 in the following image.
I have P0, Width, Height and P1 and rotation angle of the rectangle.
Rotation is based on center of each rectangle
I am using Crop extension method available in WriteableBitmapEx.WinRT for Cropping.
In Case 1 I am doing these:
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(PhotoGrid);
WriteableBitmap bitmapImage = new WriteableBitmap(renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight);
IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
using (var stream = new InMemoryRandomAccessStream())
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight, 96, 96, pixelBuffer.ToArray());
await encoder.FlushAsync();
stream.Seek(0);
bitmapImage.SetSource(stream);
}
// Redraw the WriteableBitmap
bitmapImage.Invalidate();
SampleImage.Source = bitmapImage.Crop(new Rect(p0.X, p0.Y, width, height));
But when rotation come in place I don't know what calculation should be applied do to crop like case 2.
Would any of you be kind enough to assist?
Thanks!
The WriteableBitmapEx library implements the RotateFree method.
bitmapImage.RotateFree(70);
http://writeablebitmapex.codeplex.com/SourceControl/changeset/82055

Categories

Resources