Rotate writeable bitmap without creating a new bitmap - c#

Ok so here's how this works:
camera sends me an image as a writeable bitmap
client.WhenMasterFrameCaptured().ObserveOn(SynchronizationContext.Current).Subscribe(frame => UpdateMasterCameraPreview(frame));
I set that pointer to an image source(wpf Image control) in the method that this is subscribed to
if (cameraOneBitmap != null)
{
frame.Image.CopyTo(cameraOneBitmap);
}
else
{
cameraOneBitmap = frame.Image.ToWriteableBitmap();
}
cameraOneBitmap.Lock();
cameraOneBitmap.AddDirtyRect(new Int32Rect(0, 0, cameraOneBitmap.PixelWidth, cameraOneBitmap.PixelHeight));
cameraOneBitmap.Unlock();
cameraOneImage.Source = cameraOneBitmap;
The Problem: The method feeds me a bitmap that isn't the correct angle and i have no way of changing that. I must rotate it. I'm currently using a render transform in xaml. the problem is it shapes the image all weird in certain window sizes it hangs over the grid into the bottom of the window so i don't want to use that. I've tried using writeablebitmapex library but it creates a new bitmap and that gives me an out of memory exception in about 10 seconds... Is there a way to rotate this bitmap with out have to make a new one every time? Somehow the render transform does it without giving me problems. I was doing this to set the source but it still gives me an exception because it makes a new one.
public static RenderTargetBitmap RotateImage(double angle, WriteableBitmap sourceBitmap)
{
TransformedBitmap tb = new TransformedBitmap(sourceBitmap, new RotateTransform(angle));
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawImage(tb, new Rect(0, 0, tb.PixelWidth, tb.PixelHeight));
//drawingContext.PushTransform(new RotateTransform(270, .5, .5));
drawingContext.Close();
System.Windows.Media.Imaging.RenderTargetBitmap bmp = new System.Windows.Media.Imaging.RenderTargetBitmap(tb.PixelWidth, tb.PixelHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
return bmp;
}

Related

Resize Bitmap without losing original ratio on C#

This code basically takes an image and crops it according to a detected image (in this case, it detects a decentralized fingerprint, and returns a new Bitmap with the fingerprint centered and cropped).
It turns out that, depending on the image, each resulting Bitmap will have a different size (for example, my test is returning a 425x448 Bitmap, since the identified image has that size), when in fact I need the image to return with a specific size (512x512).
I've already tried to change all the height and width variables of the code, but none satisfy this desired condition. Either it creates a Bitmap with the size of 512x512 and stretches the original image (violating the original ratio), or it creates a 512x512 Bitmap with the cropped image but with a black border on the right and bottom sides.
Any hints of what can be changed or included in the code?
Edit: More clearly, I need to create a 512x512 canvas for the 425x448 image without changing the size or dimensions of the image (it should be 425x448 inside a 512x512 canvas).
private byte[] GetAndCropImage(byte[] image, IEnumerable<YoloItem> yoloItems)
{
byte[] imageRet = null;
var topYoloItem = yoloItems?.Where(x => x.Confidence >= 0.30).OrderByDescending(x => x.Confidence).First();
MemoryStream ms = new MemoryStream(image);
Bitmap src = new Bitmap(ms);
Rectangle cropRect = new Rectangle(topYoloItem.X, topYoloItem.Y, topYoloItem.Width, topYoloItem.Height);
Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
target.SetResolution(512, 512);
ImageConverter converter = new ImageConverter();
imageRet = (byte[])converter.ConvertTo(target, typeof(byte[]));
return imageRet;
}

Very slow scrolling performance, using a wide canvas with a scrollviewer

I have a C# WPF application with a Canvas. The Canvas is wider then the screen, so I use a ScrollViewer on the Canvas. And everything is working fine.
Here comes the problem:
With a much wider Canvas (width=44655), the performance of the scrollviewer is really slow. Changing the scrollposition takes a couple of seconds.
How can I increase the performance for this wide Canvas?
The Canvas I use = 44655 x 969
The width of the ScrollViewer = 1488
I've tried to place the children elements (total of 602 children elements) of the Canvas in one Bitmap, and place this one Bitmap on the same Canvas (after first removing all the children elements). This improves the performance big-time and it works as needed. But the memory-usage is huge, causing Out-Of-Memory exceptions many times. (I've also written the Bitmap as PNG to a file and this results in a file of 6MB, with 44655x969 pixels).
Is there another way to increase the scroll-performance with the Canvas? Or is there a way to reduce the memory use with the following code.
This is the code for making Bitmap from the Canvas and placing this Bitmap on the Canvas again:
double cnvsW = Cnvs.Width;
double cnvsH = realCnvsHeight;
if (double.IsNaN(cnvsW))
cnvsW = 0;
Size size = new Size(cnvsW, cnvsH);
// Measure and arrange the Canvas
Cnvs.Measure(size);
Cnvs.Arrange(new Rect(size));
// Create a render bitmap and push the canvas to it
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Default);//.Pbgra32);
renderBitmap.Render(Cnvs);
// Save to bitmap opbject
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Interlace = PngInterlaceOption.Off;
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
MemoryStream stream = new MemoryStream();
encoder.Save(stream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(stream);
// Save to file for debugging
System.IO.File.WriteAllBytes("testCanvas.png", stream.ToArray());
// Now place the bitmap to the canvas on the screen
Cnvs.Children.Clear();
IntPtr hBitmap = bitmap.GetHbitmap();
try
{
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
Canvas.SetTop(image, 0);
Cnvs.Children.Add(image);
Cnvs.Width = size.Width;
}
finally
{
DeleteObject(hBitmap);
}

Take a screenshot from canvas with size of bigger than 20,000 pixels

I need take a screenshot from a part of Canvas but because RenderTargetBitmap
doesn't support take a screenshot from a region first I should take a screenshot from total of the Canvas and after that crop it with CroppedBitmap.
But problem is size of the canvas is bigger than 20000px and when I use from RenderTargetBitmap to take a screenshot sometimes I have an error about out of memory.
Do you have any idea to take a screenshot just with a start point and an end point instead of take a screenshot from total of my canvas in WPF?
You can render a cropped area from a UI element into a bitmap by a method like shown below.
It uses a VisualBrush with an appropriate Viewbox to draw the crop into a DrawingVisual, which is subsequently drawn into a RenderTargetBitmap.
private BitmapSource RenderCrop(Visual element, Rect crop)
{
var visualBrush = new VisualBrush
{
Visual = element,
ViewboxUnits = BrushMappingMode.Absolute,
Viewbox = crop,
Stretch = Stretch.None
};
var drawingVisual = new DrawingVisual();
using (var dc = drawingVisual.RenderOpen())
{
dc.DrawRectangle(visualBrush, null, new Rect(0, 0, crop.Width, crop.Height));
}
var bitmap = new RenderTargetBitmap(
(int)Math.Round(crop.Width), (int)Math.Round(crop.Height),
96, 96, PixelFormats.Default);
bitmap.Render(drawingVisual);
return bitmap;
}

WPF thumbnail image is blurry

I have a WPF app which saves out a thumbnail image as png. The code works well enough but when I open the image is very blurry. The image that it grabs comes from the canvas itself. The canvas changes its width and height depending on the image I'm loading. The desired thumbnail size will be 200 x 200 (pixels).
Here is my code
public void CreateThumbail(Canvas canvas, string filename)
{
RenderTargetBitmap rtb = new RenderTargetBitmap(
(int)canvas.ActualWidth,
(int)canvas.ActualHeight,
96, //dip X
96, //dpi Y
PixelFormats.Pbgra32);
rtb.Render(canvas);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(CreateResizedImage(rtb, 200, 200, 0));
using (var filestream = System.IO.File.Create(filename))
{
pngImage.Save(filestream);
}
}
private static BitmapFrame CreateResizedImage(ImageSource source, int width, int height, int margin)
{
var rect = new Rect(margin, margin, width, height);
var group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, BitmapScalingMode.HighQuality);
group.Children.Add(new ImageDrawing(source, rect));
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
drawingContext.DrawDrawing(group);
var resizedImage = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height,// Resized dimensions
96, 96, // Default DPI values
PixelFormats.Pbgra32); // Default pixel format
resizedImage.Render(drawingVisual);
return BitmapFrame.Create(resizedImage);
}
I saved out the image before I resize it and it looks crisp and sharp. yet when I save out the thumbnail it's ugly and blurry. What am I doing wrong? Am I over-engineering this? Many thanks in advance.
Probably you need to respect the original dimensions of the image. For instance an image that is 400x400 will downscale to 200x200 quite nicely, but an image that is 235x235 will not.
This doesn't consider images that are not square to begin with.
You might try reducing the image height and width by a good factor (I would start by halving) repeatedly until the image is smaller than 200x200 and then padding it with white or transparent.
Image processing can be quite hard. It's not something I'm an expert in either so I'd probably try a 3rd party library like this one I just found on google: https://imageprocessor.org/

How to use canvas content as a bitmap?

I'm writing a paint application in WPF, and when I want make color picker or fill tool i get a problem. I dont know how to use canvas content as bitmap, the only one solution I invent is save it, and open as bitmap then I can easy operate on pixels to get and sets pixels, but I want do it in another way. Any suggestions?
If you're looking for converting your canvas UIElement to a BitmapSource so that you can manipulate it then you can take a look at this post on MSDN MSDN.
Here is the helper function to get the BitmapSource from your canvas:
public static BitmapSource CreateBitmapSourceFromVisual(
Double width,
Double height,
Visual visualToRender,
Boolean undoTransformation)
{
if (visualToRender == null)
{
return null;
}
RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)Math.Ceiling(width), (Int32)Math.Ceiling(height), 96,96, PixelFormats.Pbgra32);
if (undoTransformation)
{
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(visualToRender);
dc.DrawRectangle(vb, null, new Rect(new Point(), new Size(width, height)));
}
bmp.Render(dv);
}
else
{
bmp.Render(visualToRender);
}
return bmp;
}
I didn't get what do want to say by a color picker or fill tool problem, so I can't help you with that. Good luck!

Categories

Resources