72

I have BitmapImage in C#. I need to do operations on image. For example grayscaling, adding text on image, etc.

I have found function in stackoverflow for grayscaling which accepts Bitmap and returns Bitmap.

So I need to convert BitmapImage to Bitmap, do operation and convert back.

How can I do this? Is this best way?

Tigran Tokmajyan
  • 1,937
  • 7
  • 25
  • 36
  • If you do not want to create a copy in memory a sharedbitmapsource is what you want. stackoverflow.com/a/32841840/690656 – Andreas Oct 28 '15 at 10:14

8 Answers8

105

There is no need to use foreign libraries.

Convert a BitmapImage to Bitmap:

private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
    // BitmapImage bitmapImage = new BitmapImage(new Uri("../Images/test.png", UriKind.Relative));

    using(MemoryStream outStream = new MemoryStream())
    {
        BitmapEncoder enc = new BmpBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(bitmapImage));
        enc.Save(outStream);
        System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);

        return new Bitmap(bitmap);
    }
}

To convert the Bitmap back to a BitmapImage:

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
    IntPtr hBitmap = bitmap.GetHbitmap();
    BitmapImage retval;

    try
    {
        retval = (BitmapImage)Imaging.CreateBitmapSourceFromHBitmap(
                     hBitmap,
                     IntPtr.Zero,
                     Int32Rect.Empty,
                     BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(hBitmap);
    }

    return retval;
}
Aric
  • 319
  • 7
  • 27
Sascha Hennig
  • 2,556
  • 1
  • 19
  • 22
  • I agree using foreign libraries is not always recommended, but i think in this case "my 5 lines of code" answer (which also does the gray scale) is better (not to mention the other bitmap functionalities inside...) – Hertzel Guinness Jun 26 '11 at 16:32
  • @Hertzel - Using a foreign library only for conversion into a different property is overkill. If the OP believes that the lib you linked offers a lot more than just that, thats cool, I would be the last to recommend to him not to use it. The OP specified also that he already has the code to convert the Bitmap into a grayscale version. Imho it would be best to actually use [FormatConvertedBitmap](http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.formatconvertedbitmap.aspx) to convert the BitmapImage to grayscale - but thats not what was asked, hence my answer. – Sascha Hennig Jun 26 '11 at 16:46
  • 3
    A more concise implementation for Bitmap2BitmapImage would be: return Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); – Steven Jan 12 '13 at 21:29
  • Can anybody tell me why the first part of code in Bitmap2BitmapImage is necessary? From my testing, just the last line code (Imaging.CreateBitmapSourceFromHBitmap) produces the same result. – newman Apr 01 '13 at 21:11
  • @miliu: it is not necessary - I just failed to incorporate Stevens changes properly. Its fixed now. – Sascha Hennig Apr 02 '13 at 09:19
  • Doesn't bitmap.GetHbitmap() produce a memory leak unless it is cleaned up? – BVB May 01 '13 at 17:26
  • 1
    @BVB: the handle does indeed need to get released. I fixed it. Did it out of my head, so I'm not guaranteeing anything :) – Sascha Hennig May 02 '13 at 12:21
  • Just get rid of the using block around the MemoryStream, it doesn't do anything anyway and it solves your disposal problem. – Jeff May 20 '14 at 02:19
  • 1
    @Jeff: There is no problem disposing anything. The comment actually comments the commented out 'return bitmap;' Also, why doesn't it do anything? Afaik, MemoryStream implements IDisposable and should therefore be disposed. – Sascha Hennig Jun 04 '14 at 10:42
  • @SaschaHennig You're right, it probably doesn't solve the problem. However, disposing MemoryStream is effectively a no-op in .NET since all it does is remove the reference to the internal buffer, which is what will happen from the GC's perspective when the stream goes out of scope anyway. The only reason it implements IDisposable is because the abstract base does. – Jeff Jun 04 '14 at 12:41
  • @Jeff: Thanks for the explanation, never cared to look at the implementation of MemoryStream myself. Nevertheless, I will leave the using construct in its place, simply because it is best practice and because in the case that someone does not know that it is not really necessary to call dispose here or other instances, its always a safe bet to simply do it anyway. I have removed the comment from the code snippet though, as it did not really improve the answer. – Sascha Hennig Jun 05 '14 at 07:46
  • 4
    @SaschaHennig: in `BitmapImage2Bitmap`: In the last line, why `return new Bitmap(bitmap)` instead of just `return bitmap`? – arnobpl Jun 23 '18 at 05:15
  • 5
    The Bitmap2BitmapImage function is not working for me. I get System.InvalidCastException Unable to cast object of type 'System.Windows.Interop.InteropBitmap' to type 'System.Windows.Media.Imaging.BitmapImage' – n.jmurov Dec 17 '19 at 07:38
  • @n.jmurov I have the same problem. Do you have a solution? – daniel Mar 03 '20 at 23:13
  • @daniel I don't remember exactly but I don't think I was able to solve it. – n.jmurov Mar 05 '20 at 00:12
  • BMP format seems not to handle transparency correctly. Makes all black. – david.pfx Oct 07 '20 at 23:17
  • @david.pfx BMP format doesn't have an alpha channel AFAIK. – Brian A. Henning Aug 13 '21 at 16:23
  • @BrianA.Henning: Of course not. The PNG has transparency, and this converts it to black. The convention is to allocate a pure colour (often magenta) to represent transparency, but nothing here will do that AFAICT. This is a serious issue IRL. – david.pfx Aug 15 '21 at 00:34
60

Here's an extension method for converting a Bitmap to BitmapImage.

    public static BitmapImage ToBitmapImage(this Bitmap bitmap)
    {
        using (var memory = new MemoryStream())
        {
            bitmap.Save(memory, ImageFormat.Png);
            memory.Position = 0;

            var bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = memory;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.Freeze();

            return bitmapImage;
        }
    }
LawMan
  • 3,469
  • 1
  • 29
  • 32
9

If you just need to go from BitmapImage to Bitmap it's quite easy,

private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
    {
        return new Bitmap(bitmapImage.StreamSource);
    }
C0bra5
  • 91
  • 1
  • 3
5

using System.Windows.Interop; ...

 private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
        {                
            BitmapSource i = Imaging.CreateBitmapSourceFromHBitmap(
                           bitmap.GetHbitmap(),
                           IntPtr.Zero,
                           Int32Rect.Empty,
                           BitmapSizeOptions.FromEmptyOptions());
            return (BitmapImage)i;
        }
dreamcrash
  • 47,137
  • 25
  • 94
  • 117
  • 7
    maybe i miss something but you can't cast "_System.Windows.Interop.InteropBitmap_" to "_System.Windows.Media.Imaging.BitmapImage_" based on the **InvalidCastException** which popsup – WiiMaxx Jul 23 '13 at 14:47
  • 3
    @WiiMaxx It does throw an exception but if you want to convert the `System.Drawing.Image` to be able to show it in the WPF `Image` control you can return `BitmapSource` instead of `BitmapImage` and remove the cast. It works perfectly then. – MasterMastic Sep 09 '13 at 19:59
  • 1
    Don't return image just return BitmapSource – DotNetRussell Jan 09 '15 at 14:02
  • This CreateBitmapSourceFromHBitmap version is 10 times faster compared to the MemoryStream version. – informatorius Feb 05 '19 at 14:40
  • While this does work, it has a memory leak. Use "Sascha Hennig" solution listed above, as they dispose of the unmanaged memory properly. – BlueFuzzyThing Jul 29 '19 at 16:02
3

I've just been trying to use the above in my code and I believe that there is a problem with the Bitmap2BitmapImage function (and possibly the other one as well).

using (MemoryStream ms = new MemoryStream())

Does the above line result in the stream being disposed of? Which means that the returned BitmapImage loses its content.

As I'm a WPF newbie I'm not sure that this is the correct technical explanation, but the code didn't work in my application until I removed the using directive.

Wolfshead
  • 540
  • 6
  • 8
  • Using a "using" block does indeed dispose the object. Thats what the using block is for. And you really do want to dispose the streams once you do not need them any longer. Therefore the edits in my post like 2 years ago. The BitmapImage2Bitmap-Fix does create a new object of type Bitmap() before closing the stream. For Bitmap2BitmapImage() you want to try the implementation posted as comment to my answer posted by Steven. Basically what you want to do is create a new object from the data in the stream, close the stream and return the created object instead. – Sascha Hennig Mar 07 '13 at 13:33
2

Here the async version.

public static Task<BitmapSource> ToBitmapSourceAsync(this Bitmap bitmap)
{
    return Task.Run(() =>
    {
        using (System.IO.MemoryStream memory = new System.IO.MemoryStream())
        {
            bitmap.Save(memory, ImageFormat.Png);
            memory.Position = 0;
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = memory;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.Freeze();
            return bitmapImage as BitmapSource;
        }
    });

}
Andreas
  • 3,843
  • 3
  • 40
  • 53
1

Thanks Guillermo Hernandez, I created a variation of your code that works. I added the namespaces in this code for reference.

System.Reflection.Assembly theAsm = Assembly.LoadFrom("My.dll");
// Get a stream to the embedded resource
System.IO.Stream stream = theAsm.GetManifestResourceStream(@"picture.png");

// Here is the most important part:
System.Windows.Media.Imaging.BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.StreamSource = stream;
bmi.EndInit();
Lawrence
  • 11
  • 1
  • 2
0

This converts from System.Drawing.Bitmap to BitmapImage:

MemoryStream ms = new MemoryStream();
YOURBITMAP.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
BitmapImage image = new BitmapImage();
image.BeginInit();
ms.Seek(0, SeekOrigin.Begin);
image.StreamSource = ms;
image.EndInit();
Guillermo Hernandez
  • 1,114
  • 1
  • 7
  • 6