Get BitmapSource From Image Control - c#

why the following code is returning null for btmSrc ?
DrawingImage drawingElement =(DrawingImage)System.Windows.Application.Current.TryFindResource(name);
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = drawingElement as ImageSource;
BitmapSource btmSrc = image.Source as BitmapSource;

Simplifying your code :
DrawingImage drawingElement = (DrawingImage)System.Windows.Application.Current.TryFindResource(name);
BitmapSource btmSrc = drawingElement as BitmapSource;
As DrawingImage doesn't inherit from BitmapSource, the result will be null.
I don't have a DrawingImage to test (so take this as a pseudocode not as a copy-paste solution) but the conversion code should look something like this :
// Create a visual from a drawing
DrawingVisual drawingVisual = new DrawingVisual();
drawingVisual.Drawing.Children.Add(drawingImage.Drawing);
// Render it to a WPF bitmap
var renderTargetBitmap = new RenderTargetBitmap(
drawingVisual.Drawing.Bounds.Right,
drawingVisual.Drawing.Bounds.Bottom, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(drawingVisual);
// Create a bitmap with the correct size
Bitmap bmp = new Bitmap(renderTargetBitmap.PixelWidth,
renderTargetBitmap.PixelHeight, PixelFormat.Format32bppPArgb);
BitmapData data = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);
renderTargetBitmap.CopyPixels(Int32Rect.Empty, data.Scan0,
data.Height * data.Stride, data.Stride);
bmp.UnlockBits(data);
The last part being taken from Is there a good way to convert between BitmapSource and Bitmap?

Related

WPF .png overlay using DrawingVisual

I'm using this example - I found around here - to overlay two .png images and then save the result as a third .png image.
The input images are:
The output image should (in my dreams) be:
And instead I get this:
Here is the code:
public static void Test()
{
// Loads the images to tile (no need to specify PngBitmapDecoder, the correct decoder is automatically selected)
BitmapFrame frame1 = BitmapDecoder.Create(new Uri(#"D:\_tmp_\MaxMara\Test\Monoscope.png"), BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames.First();
BitmapFrame frame2 = BitmapDecoder.Create(new Uri(#"D:\_tmp_\MaxMara\Test\OverlayFrame.png"), BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames.First();
// Gets the size of the images (I assume each image has the same size)
int imageWidth = 1920;
int imageHeight = 1080;
// Draws the images into a DrawingVisual component
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawImage(frame1, new Rect(0, 0, imageWidth, imageHeight));
drawingContext.DrawImage(frame2, new Rect(0, 0, imageWidth, imageHeight));
}
// Converts the Visual (DrawingVisual) into a BitmapSource
RenderTargetBitmap bmp = new RenderTargetBitmap(imageWidth, imageHeight, 300, 300, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
// Creates a PngBitmapEncoder and adds the BitmapSource to the frames of the encoder
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
// Saves the image into a file using the encoder
using (Stream stream = File.Create(#"D:\_tmp_\MaxMara\Test\Result.png"))
encoder.Save(stream);
}
Note: if i use 100 dpi as in:
RenderTargetBitmap bmp = new RenderTargetBitmap(imageWidth, imageHeight, 100, 100, PixelFormats.Pbgra32);
I get the correct result (meaning: the result I want).
I don't understand why. All images are 300 DPI
Can anyone shed some light on the topic please?
Thank you for your time
Orf
Do not use the PixelWidth and PixelHeight (i.e. your imageWidth and imageHeight values) of the bitmaps for drawing them into a DrawingContext.
Use their Width and Height values instead, because these give the bitmap size in device-independent units (1/96th inch per unit) as required for drawing.
using (var drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawImage(frame1, new Rect(0, 0, frame1.Width, frame1.Height));
drawingContext.DrawImage(frame2, new Rect(0, 0, frame2.Width, frame2.Height));
}

Large image update in WPF is delayed for real time monitoring

I am getting Sequence of images from USB and with each image grabbed I convert grabbed result to System.Drawing.Bitmap and after that I convert it to System.Windows.Mesia.Imging.BitmapImage to be able to assign it to Imagesource and finally update UI in dispatcher thread, all this process takes time and it doesn't go live, the sample codes of the camera company (Basler) has used C# and directly assigns System.Drawing.Bitmap to picture box and can show live view without delay.
What is the best solution to handle it ? it worth mentioning that with 2048*2000 pixel size the frame rate is almost 50 fps
PixelDataConverter converter = new PixelDataConverter();
Bitmap bitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format32bppRgb);
BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
converter.OutputPixelFormat = PixelType.BGRA8packed;
IntPtr ptrBmp = bmpData.Scan0;
converter.Convert(ptrBmp, bmpData.Stride * bitmap.Height, grabResult);
bitmap.UnlockBits(bmpData);
BitmapImage bitmapimage = new BitmapImage();
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Bmp);
memory.Position = 0;
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
bitmapimage.Freeze();
}
Dispatcher.Invoke(new Action(() =>
{
imgMain.Source = bitmapimage;
}));
And this is the sample code for c# by company :
Bitmap bitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format32bppRgb);
// Lock the bits of the bitmap.
BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
// Place the pointer to the buffer of the bitmap.
converter.OutputPixelFormat = PixelType.BGRA8packed;
IntPtr ptrBmp = bmpData.Scan0;
converter.Convert(ptrBmp, bmpData.Stride * bitmap.Height, grabResult); //Exception handling TODO
bitmap.UnlockBits(bmpData);
// Assign a temporary variable to dispose the bitmap after assigning the new bitmap to the display control.
Bitmap bitmapOld = pictureBox.Image as Bitmap;
// Provide the display control with the new bitmap. This action automatically updates the display.
pictureBox.Image = bitmap;
if (bitmapOld != null)
{
// Dispose the bitmap.
bitmapOld.Dispose();
}
}
enter code here
Use Dispatcher.BeginInvoke instead of Dispatcher.Invoke(...to set the image asynchronous.
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
{
imgMain.Source = bitmapimage;
}));

How do I correctly transform an image in WPF?

I want to write program that functions something like PhotoShop.
1.upload image
2. Then I want to do skew transform, But when I do transform I have a problem, my picture goes beyond the edge of the workspace.
How do I transform without this problem(I think I should create a new Image every time I do a transform).
Then I crop, but crop makes the picture without transform. I think if I create a new image every time I do a transform, the problem will be fixed.
How do I do this correctly?
How do I correctly create this image in WPF? How to do transform and save an image? I am using(System.Drawing.Bitmap, System.Windows.Media.Imaging) Maybe, can someone show me experiences, code or useful material?
For the skew tranformation you may use MatrixTranform. Basic idea is described here
Below is the code that transforms an image located at "D:\input.png", attaches transformation result to the source of Image that defined in the .xaml file:
<Image Name="imgProcess" />
and writes result to the file "D:\skew.png"
double skewX = .0;
double skewY = Math.Tan(Math.PI / 18);
MatrixTransform transformation = new MatrixTransform(1, skewY, skewX, 1, 0, 0)
BitmapImage image = new BitmapImage(new Uri(#"D:\input.png"));
var boundingRect = new Rect(0, 0, image.Width + image.Height * skewX, image.Height + image.Width * skewY);
DrawingGroup dGroup = new DrawingGroup();
using (DrawingContext dc = dGroup.Open())
{
dc.PushTransform(transformation);
dc.DrawImage(image, boundingRect);
}
DrawingImage imageSource = new DrawingImage(dGroup);
imgProcess.Source = imageSource;
SaveDrawingToFile(ToBitmapSource(imageSource), #"D:\skew.png", (int)boundingRect.Width, (int)boundingRect.Height);
private BitmapSource ToBitmapSource(DrawingImage source)
{
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawImage(source, new Rect(new Point(0, 0), new Size(source.Width, source.Height)));
drawingContext.Close();
RenderTargetBitmap bmp = new RenderTargetBitmap((int)source.Width, (int)source.Height, 96, 96, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
return bmp;
}
private void SaveDrawingToFile(BitmapSource image, string fileName, int width, int height)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
using (var stream = new FileStream(fileName, FileMode.Create))
{
encoder.Save(stream);
}
}
Results

Resize bitmap image

I want to have smaller size at image saved.
How can I resize it?
I use this code for redering the image:
Size size = new Size(surface.Width, surface.Height);
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height, 96d, 96d,
PixelFormats.Default);
renderBitmap.Render(surface);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
public static Bitmap ResizeImage(Bitmap imgToResize, Size size)
{
try
{
Bitmap b = new Bitmap(size.Width, size.Height);
using (Graphics g = Graphics.FromImage((Image)b))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, size.Width, size.Height);
}
return b;
}
catch
{
Console.WriteLine("Bitmap could not be resized");
return imgToResize;
}
}
The shortest way to resize a Bitmap is to pass it to a Bitmap-constructor together with the desired size (or width and height):
bitmap = new Bitmap(bitmap, width, height);
Does your "surface" visual have scaling capability? You can wrap it in a Viewbox if not, then render the Viewbox at the size you want.
When you call Measure and Arrange on the surface, you should provide the size you want the bitmap to be.
To use the Viewbox, change your code to something like the following:
Viewbox viewbox = new Viewbox();
Size desiredSize = new Size(surface.Width / 2, surface.Height / 2);
viewbox.Child = surface;
viewbox.Measure(desiredSize);
viewbox.Arrange(new Rect(desiredSize));
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)desiredSize.Width,
(int)desiredSize.Height, 96d, 96d,
PixelFormats.Default);
renderBitmap.Render(viewbox);

Convert RenderTargetBitmap into System.Drawing.Image

I have 3D WPF visual that I want to pass into an Excel cell (via clipboard buffer).
With "normal" BMP images it works but I do not know how to convert a RenderTargetBitmap.
My code looks like this:
System.Windows.Media.Imaging.RenderTargetBitmap renderTarget = myParent.GetViewPortAsImage(DiagramSizeX, DiagramSizeY);
System.Windows.Controls.Image myImage = new System.Windows.Controls.Image();
myImage.Source = renderTarget;
System.Drawing.Bitmap pg = new System.Drawing.Bitmap(DiagramSizeX, DiagramSizeY);
System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(pg);
gr.DrawImage(myImage, 0, 0);
System.Windows.Forms.Clipboard.SetDataObject(pg, true);
sheet.Paste(range);
My problem is that gr.DrawImage does not accept a System.Windows.Controls.Image or a System.Windows.Media.Imaging.RenderTargetBitmap; only a System.Drawing.Image.
How do I convert the Controls.Image.Imaging.RenderTargetBitmap into an Image, or are there any easier ways?
You can copy the pixels from the RenderTargetBitmap directly into the pixel buffer of a new Bitmap. Note that I've assumed that your RenderTargetBitmap uses PixelFormats.Pbrga32, as use of any other pixel format will throw an exception from the constructor of RenderTargetBitmap.
var bitmap = new Bitmap(renderTarget.PixelWidth, renderTarget.PixelHeight,
PixelFormat.Format32bppPArgb);
var bitmapData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size),
ImageLockMode.WriteOnly, bitmap.PixelFormat);
renderTarget.CopyPixels(Int32Rect.Empty, bitmapData.Scan0,
bitmapData.Stride*bitmapData.Height, bitmapData.Stride);
bitmap.UnlockBits(bitmapData);
This was the solution I came up with
System.Windows.Media.Imaging.RenderTargetBitmap renderTarget = myParent.GetViewPortAsImage(DiagramSizeX, DiagramSizeY);
System.Windows.Media.Imaging.BitmapEncoder encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
MemoryStream myStream = new MemoryStream();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(renderTarget));
encoder.Save(myStream);
//
System.Drawing.Bitmap pg = new System.Drawing.Bitmap(DiagramSizeX, DiagramSizeY);
System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(pg);
//
// Background
//
gr.FillRectangle(new System.Drawing.SolidBrush(BKGC), 0, 0, DiagramSizeX, DiagramSizeY);
//
gr.DrawImage(System.Drawing.Bitmap.FromStream(myStream), 0, 0);
System.Windows.Forms.Clipboard.SetDataObject(pg, true);
sheet.Paste(range);
Maybe I don't understand the question right, but you want to copy a RenderTargetBitmap to the clipboard, couldn't you just call SetImage ?:
Dim iRT As RenderTargetBitmap = makeImage() //this is what you do to get the rendertargetbitmap
If iRT Is Nothing Then Exit Sub
Clipboard.SetImage(iRT)

Categories