I'm using the Microsoft.MixedReality.WebRTC library and I am planing on using it for my next project - a Real-Time video chatting app.
I have been able to establish a connection and pass video frames around.
How would I properly render those Frames and display them as Video?
Using WPF's MediaElement seems pretty easy, but I can only input an Uri object as source, I cannot feed it single frames, AFAIK.
I have read that drawing Bitmaps is a possible solution, but I am sure this would mean many hours reinventing the wheel and testing, which I am not a fan of doing, unless there is no other way.
The library works as follows:
Each time a new frame is received by the client the Argb32VideoFrameReady event is raised. A Argb32VideoFrame struct object is then passed to the callback, which contains an IntPtr to the raw data. Height, Width and Stride are also provided.
More Information on the specific struct here
What would be some ways I could achieve this?
I am planning on using WPF.
The solution should target Windows 7+ and .Net Framework 4.6.2.
Thanks in advance.
With an Image element in XAML
<Image x:Name="image"/>
the simple method below would directly copy the frame into a WriteableBitmap that is assigned to the Image's Source property.
private void UpdateImage(Argb32VideoFrame frame)
{
var bitmap = image.Source as WriteableBitmap;
var width = (int)frame.width;
var height = (int)frame.height;
if (bitmap == null ||
bitmap.PixelWidth != width ||
bitmap.PixelHeight != height)
{
bitmap = new WriteableBitmap(
width, height, 96, 96, PixelFormats.Bgra32, null);
image.Source = bitmap;
}
bitmap.WritePixels(
new Int32Rect(0, 0, width, height),
frame.data, height * frame.stride, frame.stride);
}
ARGBVideoFrame from here: https://github.com/microsoft/MixedReality-WebRTC/blob/master/libs/Microsoft.MixedReality.WebRTC/VideoFrame.cs
PixelFormats.Bgra32 seems to be the proper format, due to this comment on the struct:
The ARGB components are in the order of a little endian 32-bit integer, so 0xAARRGGBB, or (B, G, R, A) as a sequence of bytes in memory with B first and A last.
Related
I need to show the preview thumbnails of high resolution images in a control for user selection. I currently use ImageListView to load images.
This works fine for low to medium resolution images.But when it comes to showing thumbnails of very high resolution images there is a noticeable delay.Sample image can be downloaded from https://drive.google.com/open?id=1Qgu_aVXBiMlbHluJFU4fBvmFC45-E81C
The image size is around 5000x3000 pixels and size is around 12 MB.The issue can be replicated by using 1000 copies of this image.
The issue screen capture is uploaded here
https://giphy.com/gifs/ZEH3T3JTfN42OL3J1A
The images are loaded using a background worker
foreach (var f in filepaths)
{
imageListView1.Items.Add(f);
}
1. In order to solve this issue I tried resizing large resolution images and adding the resized image to ImageListView ... but for resizing there is a heavy time consumption and thumbnail generation is slow.
Bitmap x = UpdatedResizeImage2(new Bitmap(f), new Size(1000, 1000));
string q = Path.GetTempPath() + Path.GetFileName(f);
x.Save(Path.GetTempPath() + Path.GetFileName(f));
x.Dispose();
imageListView1.Items.Add(Path.GetTempPath() + Path.GetFileName(f));
2. I have also tried Image.CreateThumbnail Method but this is also quite slow.
Is there a better way to solve this issue?
I would suggest using image processing library such ImageMagick.
ImageMagick has optimized this feature and you have Magick.NET a nuget package for .NET.
It is simple and straight forward:
var file = new FileInfo(#"c:\temp\input.jpg");
using (MagickImage image = new MagickImage(file))
{
{
image.Thumbnail(new MagickGeometry(100, 100));
image.Write(#"C:\temp\thumbnail.jpg");
}
}
example I made:
Here is some documentation and references that might be useful:
https://imagemagick.org/Usage/thumbnails/#creation
http://www.imagemagick.org/Usage/thumbnails/
https://github.com/dlemstra/Magick.NET
https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
https://devblogs.microsoft.com/dotnet/net-core-image-processing/
https://weblogs.asp.net/bleroy/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi
Alternatives to System.Drawing for use with ASP.NET?
You could use WPF interop and use the DecodePixelWidth/Height properties. They use underlying Windows imaging layer technology ("Windows Imaging Component") to create an optimized thumbnail, saving lots of memory (and possibly CPU): How to: Use a BitmapImage (XAML)
You can also use WPF/WIC by code, with a code like this (adapted from this article The fastest way to resize images from ASP.NET. And it’s (more) supported-ish.. You just need to add a reference to PresentationCore and WindowsBase which shouldn't be an issue for a desktop app.
// needs System.Windows.Media & System.Windows.Media.Imaging (PresentationCore & WindowsBase)
public static void SaveThumbnail(string absoluteFilePath, int thumbnailSize)
{
if (absoluteFilePath == null)
throw new ArgumentNullException(absoluteFilePath);
var bitmap = BitmapDecoder.Create(new Uri(absoluteFilePath), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None).Frames[0];
int width;
int height;
if (bitmap.Width > bitmap.Height)
{
width = thumbnailSize;
height = (int)(bitmap.Height * thumbnailSize / bitmap.Width);
}
else
{
width = (int)(bitmap.Width * thumbnailSize / bitmap.Height);
height = thumbnailSize;
}
var resized = BitmapFrame.Create(new TransformedBitmap(bitmap, new ScaleTransform(width / bitmap.Width * 96 / bitmap.DpiX, height / bitmap.Height * 96 / bitmap.DpiY, 0, 0)));
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(resized);
var thumbnailFilePath = Path.ChangeExtension(absoluteFilePath, thumbnailSize + Path.GetExtension(absoluteFilePath));
using (var stream = File.OpenWrite(thumbnailFilePath))
{
encoder.Save(stream);
}
}
Otherwise there are lots of tools out there like MagicScaler, FreeImage ImageSharp, ImageMagick, Imazen, etc. Most were written for ASP.NET/Web server scenarios (for which WPF is officially not supported but works, read the article) and are also cross-platform which you don't seem to need. I'm not sure they're generally faster or use less memory than builtin Windows technology, but you should test all this in your context.
PS: otherwise there's no magic bullet, bigger images take more time.
There's also NetVips, the C# binding for libvips.
It's quite a bit quicker than Magick.NET: between 3x and 10x faster, depending on the benchmark.
Thumbnailing is straightforward:
using NetVips;
var image = Image.Thumbnail("some-image.jpg", 128);
image.WriteToFile("x.jpg");
There's an introduction in the documentation.
Most of answers approach is to resize bitmap and then save it. Its a bit offcourse slow, specially if you say very high resolution.
Why not use existing thumbnail created by windows explorer ? This is fastest way of all (specially if you use smaller thumbnails).
//https://stackoverflow.com/a/1751610
using Microsoft.WindowsAPICodePack.Shell;
var shellFile = ShellFile.FromFilePath(pathToYourFile); Bitmap
Image image = shellFile.Thumbnail.LargeBitmap;
Nuget : https://www.nuget.org/packages/WindowsAPICodePack-Shell (around 600KB)
Note: Its same as others, if thumbnail arent cached already.
How can I convert a System.Drawing.Bitmap to GDK# Image so that I can set to the image widget.
I have tried this...
System.Drawing.Bitmap b = new Bitmap (1, 1);
Gdk.Image bmp = new Gdk.Image (b);
UPDATE:
Bitmap bmp=new Bitmap(50,50);
Graphics g=Graphics.FromImage(bmp);
System.Drawing.Font ff= new System.Drawing.Font (System.Drawing.FontFamily.GenericMonospace, 12.0F, FontStyle.Italic, GraphicsUnit.Pixel);
g.DrawString("hello world",ff,Brushes.Red,new PointF(0,0));
MemoryStream ms = new MemoryStream ();
bmp.Save (ms, ImageFormat.Png);
Gdk.Pixbuf pb= new Gdk.Pixbuf (ms);
image1.Pixbuf=pb;
Exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> GLib.GException: Unrecognized image file format
at Gdk.PixbufLoader.Close()
at Gdk.PixbufLoader.InitFromStream(Stream stream)
at Gdk.PixbufLoader..ctor(Stream stream)
at Gdk.Pixbuf..ctor(Stream stream)
One ugly, but working, way is to store the bitmap as a PNG in a MemoryStream.
To save the Bitmap, you can use the Save method:
b.Save(myMemoryStream, ImageFormat.Png);
That was easy enough. Loading the PNG data into the Gdk# Pixbuf is also rather easy; you can use the appropriate constructor:
Pixbuf pb = new Gdk.Pixbuf(myMemoryStream);
You may need to reset the memory stream so the reading position is at the start of the stream before creating the Pixbuf.
A word of caution: I do not consider this the best, or even a "good" solution. Transferring data between two object-oriented data structures by serializing and deserializing the data has a certain code smell to it. I genuinely hope someone else can come up with a better solution.
EDIT: As for the used libraries: This answer uses only plain GDI+ (System.Drawing.Bitmap) and Gdk# (Gdk.Pixbuf). Note that a Gtk.Image is a widget that displays a Gdk.Pixbuf. As such, Gtk.Image is the equivalent of Windows Forms' PictureBox, whereas Gdk.Pixbuf is roughly equivalent to Windows Forms' System.Drawing.Bitmap.
EDIT2: After testing your code, I have found that there are three additional preconditions to ensure before you can run your minimum example:
As suspected above, you must reset the stream position to the beginning of the after saving your Bitmap and before loading your Pixbuf: ms.Position = 0;
You must compile the application for x86 CPUs.
You must invoke Gtk.Application.Init(); before you do anything with Pixbuf.
You may draw in Gtk# like in Winforms. For this you must obtain System.Drawing.Graphics object and then you may draw lines, images and text on it. You may do it like this: 1. Create new Widget. 2. Subscribe on ExposeEvent. 3. On event handler write some code:
protected void OnExposeEvent(object o, ExposeEventArgs e)
{
Gdk.Window window = e.Event.Window;
using (System.Drawing.Graphics graphics =
Gtk.DotNet.Graphics.FromDrawable(window))
{
// draw your stuff here...
graphics.DrawLine(new System.Drawing.Pen(System.Drawing.Brushes.Black), 0, 0, 30, 40);
}
}
Also you need to add reference on gtk-dotnet.dll.
try this ....
Gdk.Pixbuf pixbufImage = mew Gdk.Pixbuf(#"images/test.png");
Gtk.Image gtkImage = new Gtk.Image(pixbufImage);
Gdk.Image gdkImage = gtkImage.ImageProp;
I want to draw lines as fast as possible. For that reason I implemented a method using InteropBitmap. This works quite good. Next step was to compare with ShardDX. Basically what I want to do is:
Running the following code in a BackgroundWorker. This does inform the WPF about an update of WIC. I found out that this code (creating all needed for ShapeDX and draw line) takes about 10ms longer than doing the same using InteropBitmap.
My question now is simply, how to speed this up? Can I change the code somehow that I only have to call BeginDraw, create lines and EndDraw, not always doing all of this Image Encoding/Decoding stuff? Or is there a better approach?
var wicFactory = new ImagingFactory();
var d2dFactory = new SharpDX.Direct2D1.Factory();
const int width = 800;
const int height = 200;
var wicBitmap = new Bitmap(wicFactory, width, height, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);
var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new PixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
var d2dRenderTarget = new WicRenderTarget(d2dFactory, wicBitmap, renderTargetProperties);
var solidColorBrush = new SharpDX.Direct2D1.SolidColorBrush(d2dRenderTarget, SharpDX.Color.White);
d2dRenderTarget.BeginDraw();
//draw whatever you want
d2dRenderTarget.EndDraw();
// Memorystream.
MemoryStream ms = new MemoryStream();
var stream = new WICStream(wicFactory, ms);
// JPEG encoder
var encoder = new SharpDX.WIC.JpegBitmapEncoder(wicFactory);
encoder.Initialize(stream);
// Frame encoder
var bitmapFrameEncode = new BitmapFrameEncode(encoder);
bitmapFrameEncode.Initialize();
bitmapFrameEncode.SetSize(width, height);
var pixelFormatGuid = SharpDX.WIC.PixelFormat.FormatDontCare;
bitmapFrameEncode.SetPixelFormat(ref pixelFormatGuid);
bitmapFrameEncode.WriteSource(wicBitmap);
bitmapFrameEncode.Commit();
encoder.Commit();
ms.Seek(0, SeekOrigin.Begin);
// JPEG decoder
var decoder = new System.Windows.Media.Imaging.JpegBitmapDecoder(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
// Write to wpf image
_WIC = decoder.Frames[0];
// Tell WPF to update
RaisePropertyChanged("WIC");
bitmapFrameEncode.Dispose();
encoder.Dispose();
stream.Dispose();
With:
System.Windows.Media.Imaging.BitmapFrame _WIC;
public System.Windows.Media.Imaging.BitmapSource WIC
{
get
{
return (System.Windows.Media.Imaging.BitmapSource)_WIC.GetAsFrozen();
}
}
And:
<StackPanel>
<Image Name="huhu1" Source="{Binding WIC}" />
</StackPanel>
SharpDX Toolkit has support for WPF via a Direct3D11-to-Direct3D9 shared texture. It is implemented in the SharpDXElement class.
You may not be able to reuse this as is because Direct2D (which you are using to draw) can interop either with Direct3D11.1 or Direct3D10 and SharpDX uses Direct3D11 for WPF support, so you will need to tweak the solution a little bit.
Basically you need to do the following:
Initialize Direct3D (10 or 11.1).
Initialize Direct2D.
Create the D3D render target with Direct2D support and the Shared flag (here is how it is done in SharpDX).
Initialize Direct3D9.
Create the shared texture.
Bind the texture to an D3DImage.
Do not forget to call D3DImage.AddDirtyRect when the contents of the render target are updated.
From the code you provided it is not clear if you are doing all initializations only once or not, so try to call any initialization code only once and reuse the render target - just clear it at the beginning of every frame. This is mandatory to get a decent performance.
Update: SharpDX.Toolkit has been deprecated and it is not maintained anymore. It is moved to a separate repository.
If you want to share a directx surface with WPF, the best option is using a WPF D3DImage. It promises to work without copying if you use the right color format.
I have only used it with Directx9, but it is possible that its compatible with Direct2D too, but if it isn't D3d9 can draw lines too.
There's a great codeproject article with examples. From managed code using slimdx or sharpdx the only nonobvious caveat is that D3DImage retains a reference count to your DirectX surface, so you need to null the backbuffer expicitly when you want to reset your d3d device.
You can use Smartrak/WpfSharpDxControl: Provides WPF control to host SharpDx content.
It uses sharpDx in WPF and the wpf can add Win32HwndControl to HwndHost.
We have an image processing windows application where we are using lead tools for converting and images from 24/48 bit images to 8 bit images.
As an experiment I'm porting the application to iPad using MonoTouch and C#, now the LeadTools components are incompatible with Monotouch. Is there any alternate I can use? if not how can I convert 24/48 bit images to 8 bit?
To use Apple's imaging tools here is where I would start:
Convert your raw bytes into a pixel format supported by the platform. See the Quartz 2D documentation on supported pixel formats.
Note that iOS doesn't currently have a 24 or 48 bit format. However, if your 24 bit format is 8 bits per channel (RGB) you could add 8 bits of ignored alpha. (Alpha options are in MonoTouch.CoreGraphics.CGImageAlphaInfo)
Convert your raw bytes into a CGImage. Here is an example of how to do that
var provider = new CGDataProvider(bytes, 0, bytes.Length);
int bitsPerComponent = 8;
int components = 4;
int height = bytes.Length / components / width;
int bitsPerPixel = components * bitsPerComponent;
int bytesPerRow = components * width; // Tip: When you create a bitmap graphics context, you’ll get the best performance if you make sure the data and bytesPerRow are 16-byte aligned.
bool shouldInterpolate = false;
var colorSpace = CGColorSpace.CreateDeviceRGB();
var cgImage = new CGImage(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow,
colorSpace, CGImageAlphaInfo.Last, provider,
null, shouldInterpolate, CGColorRenderingIntent.Default);
Use a Core Image Filter to convert to Monochrome
var mono = new CIColorMonochrome
{
Color = CIColor.FromRgb(1, 1, 1),
Intensity = 1.0f,
Image = CIImage.FromCGImage(image)
};
CIImage output = mono.OutputImage;
var context = CIContext.FromOptions(null);
var renderedImage = context.CreateCGImage(output, output.Extent);
Finally you can retrieve the raw bytes of that image by drawing into a CGBitmapContext constructed according to your desired parameters.
I suspect this pipeline could be optimized, but it is a place to start. I'd be interested to hear what you end up with.
I think your best option will be to do native calls to the LeadTools libraries - any image manipulation in C# that I can think of is going to rely on components like GDI+ and the System.Drawing namespace which isn't supported by monotouch.
You can call native objective-C code from your monotouch project by creating a Binding project - http://docs.xamarin.com/ios/advanced_topics/binding_objective-c_types
This should allow you to port your code in a way that will produce the exact same image/quality/format without really having to rework your current conversion code.
There are numerous tutorial on the web on how to capture and save a screenshot using C#.
For example, I used this website to obtain my solution:
using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(screenshot))
{
g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
screenshot.Save("screenshot.png", ImageFormat.Png);
}
It works fine in most programs, but the program I want to capture uses an 8 bits indexed color table. Screenshots of that program taken with this code are strange. My question is if someone can point me in the right direction for capturing screenshots of programs with indexed color tables in C#?
To help you help me, I will describe my findings and attempted solutions below.
The screenshots captured with this code of a full screen program using an 8 bits indexed color table is mostly black(for ~88 %) and there are only 17 other colors in it. I don't see a pattern in those 17 colors. In the program itself almost all 256 colors are used. I was hoping to find 256 colors in the black screenshots as well, which could indicate a simple one-on-one relationship, but that is not the case.
I would also like to note that screenshots taken manually are perfect(when I paste them in MS Paint for example). For that reason I tried fetching the image from System.Windows.Forms.Clipboard.GetImage() after taking a screenshot manually, but that returns a COMobject and I wouldn't know what to do with that. Surely that must contain the information of a valid screenshot, since MS Paint knows how to extract it from that COM object. How can I extract that myself, preferably in C#?
But my main question: Can someone point me in the right direction for capturing screenshots of programs with indexed color tables with C#?
If I understand correctly, the problem here is that you are copying the pixel values (the palette indexes) but not the palette itself. I didn't find a way to copy the palette with pure C#, but with P/Invoke and an alternative with DirectX... since both adds a dependency to Windows i've opted for P/Invoke as it's easier and doesn't depend on DirectX.
I have two methods to offer you...
The first Method:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern IntPtr SelectPalette(
IntPtr hdc,
IntPtr htPalette,
bool bForceBackground);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern int RealizePalette(IntPtr hdc);
private void ScreenShot()
{
IntPtr htPalette = Graphics.GetHalftonePalette();
using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var graphics = Graphics.FromImage(screenshot))
{
IntPtr hdc = graphics.GetHdc();
SelectPalette(hdc, htPalette, true);
RealizePalette(hdc);
graphics.ReleaseHdc(hdc);
graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
}
screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
This method depends on GetHalftonePalette giving you the right palette. I have never tested with palettes (I'm too young)... so I decided to write a second method based in that windows should handle palettes automatically in some conditions. Note: not sure if moving the call to CopyFromScreen to the begin of the using block is better, or ignoring the call to RealizePalette (I'm too young).
The second Method:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
private void ScreenShot()
{
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
using (var screenshot = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var fromHwnd = Graphics.FromHwnd(new IntPtr(0)))
using (var graphics = Graphics.FromImage(screenshot))
{
IntPtr hdc_screen = fromHwnd.GetHdc();
IntPtr hdc_screenshot = graphics.GetHdc();
BitBlt(hdc_screenshot, 0, 0, width, height, hdc_screen, 0, 0, 0x00CC0020); /*SRCCOPY = 0x00CC0020*/
graphics.ReleaseHdc(hdc_screenshot);
fromHwnd.ReleaseHdc(hdc_screen);
}
screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
Both methods works on normal conditions. Please do your testing, it may be that you will need to do the second method with the addition of duplicating the original palette (that is... when windows doesn't handle them automatically), but I'm not sure on how to do that (I'm too young) and hope this method works.
Lastly, if you are to afford an extra dependency and want it to be different from Windows, maybe GTK# (there is a version available form Windows) is a good option.
Please see How to take a screenshot with Mono C#? as it addresses a similar problem, and provides example code for GTK#.
Note:
The following code seems to work well here, do you get any exceptions by this code?
System.Drawing.Image image = Clipboard.GetImage();
image.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
That's not how it works. You are capturing the screen, not directly the output of the program. The setting of the video adapter matters, it will be 32bpp for any recent machine. Maybe 16bpp for old ones. Trying to copy such a non-indexed pixel format into a bitmap with a palette isn't supported. The algorithm to create a palette that provides the best color fidelity is computationally quite non-trivial.
Just don't bother, the 32bpp image will be indistinguishable from the program's output. If squeezing the file is really important then store it as a .gif. It isn't going to look great, the GIF encoder uses dithering to compress the color table.
The suggested solutions did not work and I only recently solved this problem.
I took a screenshot by sending a PrintScreen key press and release to windows, and acquired the data from the clipboard in the form of a MemoryStream and figured out how the screenshot was decoded in that stream and converted that to a bitmap. Not very appealing, but at least it works...