MemoryStream to BitmapImage

2.5k Views Asked by At

I am having a bit of a hard time converting MemoryStream into BitmapImage. There are a lot of questions on SO regarding similar situations, but after trying everything on them, I've been unable to fix this, so I turn to you. Note that I'm working with Magick.NET (ImageMagick.NET) and Tessnet2 -- that is what some of that code is.

I use Bitmap class to do most of the work in Magick.NET and Tessnet2. BitmapImage is used for displaying purposes.

First, I load up the PDF and extract a cropped bitmap from its first page:

public Task PdfToBmp(string path)
{
    return Task.Run(() =>
    {
        using (var image = new MagickImage())
        {
            MagickNET.SetGhostscriptDirectory("./");

            var settings = new MagickReadSettings
            {
                Density = new MagickGeometry(300, 300),
                FrameCount = 1
            };

            image.Read(path, settings);
            image.Crop(new MagickGeometry(1850, 200, 600, 140));

            // ImageStream is a MemoryStream property.
            ImageStream = new MemoryStream();
            image.Write(ImageStream, MagickFormat.Bmp);
            ImageStream.Position = 0;
        }
    });
}

That is when I save the bitmap into the MemoryStream. Once I have MemoryStream loaded up, I move onto working with it. I instantiate a Bitmap, so that I may use it for Tessnet2 related work and then try to instantiate a BitmapImage.

public Task DoOcr()
{
    if (ImageStream == null)
    {
        return null;
    }

    TargetImage = new Bitmap(ImageStream);
    ImageStream.Position = 0;

    // ----------------------- Problem Area ----------------------- //
    DisplayImage = new BitmapImage();
    DisplayImage.BeginInit();
    DisplayImage.StreamSource = ImageStream;
    DisplayImage.CacheOption = BitmapCacheOption.OnLoad;
    DisplayImage.EndInit();

    //ImageStream.Close();
    // ------------------------------------------------------------ //

    return Task.Run(() =>
    {
        var ocr = new Tesseract();
        ocr.Init("tessdata", "eng", false);
        var results = ocr.DoOCR(TargetImage, Rectangle.Empty);
        Dispatcher.Invoke(() =>
        {
            Results = new ObservableCollection<Word>(results);
        });
    });
}

This is where I'm having a problem. Without that DisplayImage block, the program runs fine and I just don't get the displayed image. I'm even able to save the Bitmap (TargetImage) to a file with no problems. However, with the DisplayImage block, I get System.NullReferenceException:

System.NullReferenceException occurred
_HResult=-2147467261
_message=Object reference not set to an instance of an object.
HResult=-2147467261
IsTransient=false
Message=Object reference not set to an instance of an object.
Source=System
StackTrace:
     at System.Uri.CreateThisFromUri(Uri otherUri)
InnerException: 

I'm unable to pinpoint where it occurs exactly, because the ImageStream object looks "fine" upon inspection. It contains data and is at position 0. If I try to close it, or do anything with it, after assigning it as the StreamSource to DisplayImage, I get a null exception on the line that attempts to perform such action. I even tried creating two different streams, to see if that's the problem; however, I was getting the exact same behavior. Debugging this is kind of a pain, considering it doesn't point to any one specific line. There's obviously an issue between this MemoryStream and BitmapImage. Could it be possible that there's some sort of format/conversion problem between the two, but not between MemoryStream and Bitmap in this particular situation?

I tried the file route, where I save MagickImage to a file and load it up into BitmapImage through Uri and it worked flawlessly; however, I would like to be able to perform this in-memory. By the way, setting position to 0 on the MemoryStream did not seem to affect either Bitmap (loads properly) or BitmapImage (same exception).

The temporary fix I currently use is to make DisplayImage a BitmapSource, rather than BitmapImage:

DisplayImage = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
    TargetImage.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
    BitmapSizeOptions.FromWidthAndHeight(TargetImage.Width, TargetImage.Height));
1

There are 1 best solutions below

0
On

The Magick.NET's Write() method has some bugs, so we have to use ToBitmap().

image.ToBitmap().Save(ImageStream, System.Drawing.Imaging.ImageFormat.Bmp);