In my code, I'm receiving WriteableBitmaps from a byte array (in turn from a Kinect) and I'd like to turn them into bitmaps for use with EmguCV. Currently this is the code I have:
// Copy the pixel data from the image to a temporary array
colorFrame.CopyPixelDataTo(this.colorPixels);
// Write the pixel data into our bitmap
this.colorBitmap.WritePixels(
new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
this.colorPixels,
this.colorBitmap.PixelWidth * colorFrame.BytesPerPixel,
0);
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(colorBitmap));
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
Bitmap b=new Bitmap(ms);
Image<Gray, Byte> img = new Image<Gray, Byte>(b);
img = img.ThresholdBinary(new Gray(200), new Gray(255));
I got the bottom half of the code from here.The code compiles and everything, but hangs when I'm trying to run the program (it's supposed to perform some operations on the image and then convert it back to a format that can be presented as an image.) Pausing my code and then using IntelliTrace in VS 2013, I get the following Exception at Image<Gray, Byte> img = new Image<Gray, Byte>(b); "A System.ArgumentException was thrown: URI formats are not supported." Using alternate code, from where I go directly from byte to bitmap gives me the same error. (Code can be found here.)
Anyone got tips on how to resolve this error, or alternate ways of casting to bitmap? I'm a newbie with C# & EmguCV and I'd greatly appreciate it.
Turns out all the code is fine. I'm not too sure on the technical details of the error, but the error is received when trying to write a Gray16 image in the WriteableBitmap (which is to be transformed into a Emgu Image.) Bgr565 or other formats are supported and I believe Gray16 wasn't fully implemented by MS. If doing a WinForms application, Format16bppGray will also give the same error.
I resolved to using a Gray Emgu image while writing the Bitmap as a Bgr555, which is a lot more noisy, but better than nothing.
I had the same issue. The exception with "URI formats are not supported" had nothing to do with the bitmap but with loading needed opencv dlls. I just copied the x86 and x64 folders including opencv_core290.dll and others to my executable directory.
Related
We have a system that provides images in 8-bit grayscale either tiff or jpg formats. However, the component we have to process the images expects image to be in 8-bit jpg format.
When I use .Net to save the tiff images as jpg it convets it to 24-bit image.
Is there a way, hopefully simple and fast, to convert 8-bit grayscale tiff images to equivalent jpg?
I tried and tried just to conclude that I'm sorry: .Net library's Bitmap class DOES NOT save JPEG as 8bpp even when explicitly stated and data is in grayscale.
(note: although stated in some places, JPEG format DOES support 8bpp).
At Convert an image to grayscale you may find code snipet to convert to grayscale any Image.
Using that code, I was able to save a 8bpp grayscale Image instance with '.jpeg' extension, but stating ImageFormat.Gif... that's a cheat...
My findings show as solution an entirely different approach.
The FreeImage library offers powerful APIs, including the feature needed to solve your problem.
It's home page is at http://freeimage.sourceforge.net/faq.html
But, I could not easily compile it in my Win2008 + VS 2010 machine.
One ought to sweat a lot to make it run on modern environments.
Some hints on how to accomplish that are found at http://www.sambeauvois.be/blog/2010/05/freeimage-and-x64-projects-yes-you-can/
Good luck!
Image img = Image.FromFile(filePathOriginal);
Bitmap bmp = ConvertTo8bpp(img);
EncoderParameters parameters = new EncoderParameters();
parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);
bmp.Save(filePathNew, jpgCodec, parameters);
bmp.Dispose();
img.Dispose();
...
private static Bitmap ConvertTo8bpp(Image img) {
var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
using (var gr = Graphics.FromImage(bmp))
{
gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
}
return bmp;
}
I'm using SharpDX and its accompanying WIC and Direct2D wrappers to do some serverside image manipulation.
The following code works great with JPEG images and is modeled after the SharpDX docs and this Microsoft sample using D2D directly via C++.
However, I get a BadImage error when I try to load a TIFF CCITT (bitonal 1bpp) image. The BadImage error is only thrown at EndDraw, (which happens later on in the commented DrawEndorsement function), or at this line of code which I inserted to make the point at which the issue occurs more obvious:
SharpDX.Direct2D1.Bitmap bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_renderTarget, _wicBitmap);
The JPEG image I pass in gets to this point and continues with no issues, but the TIFF I pass in gets to this point and causes FromWicBitmap to barf with a BadImage error.
I'm using FormatConverter to convert the TIFF/JPEG pixel formats to an appropriate and supported D2D pixel format, and the converter does change the pixel format GUID for both images, but, again, FromWicBitmap barfs only on the TIFF.
I assumed I was doing something wrong with conversion or misusing SharpDX/D2D, but when I built and ran the aforementioned Microsoft C++ D2D image viewer sample, it loaded and rendered this same TIFF file with no errors. I double checked the sample's code to verify that I was using all the same pixel formats, options, etc, and it looks like I'm doing almost exactly the same thing with SharpDX that the sample is doing with D2D directly.
Clearly Direct2D doesn't like the pixel format of the TIFF image that WIC is handing it, but why didn't the MS sample exhibit the same behavior, and why didn't FormatConverter fix it?
Am I missing something that the D2D sample code is doing?
Am I missing some trick with SharpDX?
Is this a SharpDX bug?
Thanks!
public byte[] BuildImage(byte[] image, Format saveFormat)
{
SharpDX.WIC.Bitmap _wicBitmap;
WicRenderTarget _renderTarget;
BitmapFrameDecode bSource;
FormatConverter converter = new FormatConverter(_factoryManager.WicFactory);
using (MemoryStream systemStream = new MemoryStream(image))
using (WICStream wicStream = new WICStream(_factoryManager.WicFactory, systemStream))
{
BitmapDecoder inDecoder = new BitmapDecoder(_factoryManager.WicFactory, wicStream, DecodeOptions.CacheOnLoad);
if (inDecoder.FrameCount > 0)
{
bSource = inDecoder.GetFrame(0);
converter.Initialize(bSource, SharpDX.WIC.PixelFormat.Format32bppPRGBA, BitmapDitherType.Solid, null, 0.0f, BitmapPaletteType.MedianCut);
_imageWidth = bSource.Size.Width;
_imageHeight = bSource.Size.Height;
}
else
{
throw new Exception("No frames found!");
}
}
_wicBitmap = new SharpDX.WIC.Bitmap(
_factoryManager.WicFactory,
converter,
BitmapCreateCacheOption.CacheOnDemand
);
_renderTarget = new WicRenderTarget(_factoryManager.D2DFactory, _wicBitmap, new RenderTargetProperties());
SharpDX.Direct2D1.Bitmap bitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap(_renderTarget, _wicBitmap);
//DrawEndorsement(_renderTarget);
_renderTarget.Dispose();
bSource.Dispose();
converter.Dispose();
return SaveImage(saveFormat, _wicBitmap);
}
As xoofx pointed out, turns out that this was caused by my disposing of the WIC/MemoryStreams underlying the FormatConverter while it was still in use.
This was causing JPEGs to be corrupted on write, and weirdly causing the TIFFs to fail even before that.
Extended the using scope accordingly and that fixed it.
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 have a Bitmap with 16bpp. I want to convert that image in my ASP.NET side in a 8bpp image.
I tried a lot of options which I found in the internet but nothing works for me.
I also tried that way: C# Converting 32bpp image to 8bpp
But if I want to save the file, I get the following error:
Exception Details: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
Line 278: System.Drawing.Image img2 = Convert(bm_resize);//byteArrayToImage(gray);
Line 279:
Line 280: img2.Save(helper+"grey2.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
Line 281: }
Line 282:
Is there any correct way for my problem?
Full Code:
System.Drawing.Image img2 = Convert(bm_resize);
img2.Save(path+"test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
public static System.Drawing.Image Convert(Bitmap oldbmp)
{
using (MemoryStream ms = new MemoryStream())
{
oldbmp.Save(ms, ImageFormat.Gif);
ms.Position = 0;
return System.Drawing.Image.FromStream(ms);
}
}
The problem is caused by disposing the memory stream before the image is saved.
I believe GDI+ requires the memory stream to persist while you are still working with an Image created from the memory stream.
See the Microsoft Support article.
I use a little helper function to create 1 bpp monochrome bitmaps in .NET. Check out this link, it also works great for 8 bpp
http://www.wischik.com/lu/programmer/1bpp.html
hope this could help!
AForge.Net has a good collection of free (under Lesser GPL License) routines for such changes and a lot more. Conversion of 16bpp to 8bpp is as simple as this.
I am trying to save an image into a MemoryStream but it is failing under certain conditions.
Here is the code:
The following code succeeds:
Image img = Bitmap.FromStream(fileStream);
MemoryStream ms = new MemoryStream();
img.Save(ms, img.RawFormat); // This succeeds.
The following code fails:
Image img = Bitmap.FromStream(fileStream);
Image thumb = img.GetThumbnailImage(thumbWidth, thumbHeight, null, System.IntPtr.Zero);
MemoryStream ms = new MemoryStream();
thumb.Save(ms, thumb.RawFormat); // This fails.
Notice that the second snippet is using an image created using Image.GetThumbnailImage.
What is the difference? Does anyone have any idea why is it failing?
I believe the problem has to do with this part of the GetThumbnailImage documentation:
If the Image contains an embedded thumbnail image, this method retrieves the embedded thumbnail and scales it to the requested size. If the Image does not contain an embedded thumbnail image, this method creates a thumbnail image by scaling the main image.
This probably accounts for the intermittent behaviour (AKA "certain conditions"). The explanation is in the following Microsoft Connect ticket:
The underlying API is not able to locate an encoder for the MemoryBmp image type. We will need to investigate this will the GDI+ team. In the meantime, you should be able to simply change your ImageFormat to ImageFormat.Bmp rather than ImageFormat.MemoryBmp and it should work. It will still be saved to the MemoryStream using the BMP format.
In all likelihood, if there is no embedded thumbnail, the new thumbnail generated by the GetThumbnailImage API is in fact going to have a RawFormat of MemoryBmp which has no associated encoder - thus the specific error message you're seeing.
Just don't use thumb.RawFormat; since you know it's a bitmap anyway, use ImageFormat.Bmp instead.
P.S. Please note that although I deleted my earlier answer because it turned out to not to be relevant to this particular issue, it is still important to use the GetThumbnailImage API properly as the documentation specifies; you must pass a valid delegate for the callback parameter instead of null, otherwise it can fail, and you still need to be wrapping disposables in using clauses.