I'm dealing with Bitmaps in my application and for some purposes I need to create a deep copy of the Bitmap. Is there an elegant way how to do it?
I tried
Bitmap deepCopy = original.Clone();
,well apparently this doesn't create a deep copy, but shallow one.
My next attempt was to create a new Bitmap
Bitmap deepCopy = new Bitmap(original);
Unfortunately this constructor is Bitmap(Image), not Bitmap(Bitmap) and Bitmap(Image) will convert my nice 8bppIndexed Pixelformat into a different one.
Another attempt was to use of a MemoryStream
public static Bitmap CreateBitmapDeepCopy(Bitmap source)
{
Bitmap result;
using (MemoryStream stream = new MemoryStream())
{
source.Save(stream, ImageFormat.Bmp);
stream.Seek(0, SeekOrigin.Begin);
result = new Bitmap(stream);
}
return result;
}
Well, this doesn't work either, since the MemoryStream has to be opened during the whole lifetime of Bitmap.
So, I've summed up all my deadends and I'd really like to see a nice elegant way of creating a Bitmap deep copy. Thanks for that :)
B.Clone(new Rectangle(0, 0, B.Width, B.Height), B.PixelFormat)
Another way I stumbled on that achieves the same thing is to rotate or flip the image. Under the hood that seems to create a completely new copy of the bitmap. Doing two rotations or flips lets you end up with an exact copy of the original image.
result.RotateFlip(RotateFlipType.Rotate180FlipX);
result.RotateFlip(RotateFlipType.Rotate180FlipX);
My environment:Windows 10, Visual Studio 2015, Framework 4.5.2
It works for me.
Bitmap deepCopy = new Bitmap(original);
You could serialize the bitmap and then deserialize it. Bitmap is serializable.
Suppose you already have a bitmap called original with something in it
Bitmap original = new Bitmap( 200, 200 );
Bitmap copy = new Bitmap(original.Width, original.Height);
using (Graphics graphics = Graphics.FromImage(copy))
{
Rectangle imageRectangle = new Rectangle(0, 0, copy.Width, copy.Height);
graphics.DrawImage( original, imageRectangle, imageRectangle, GraphicsUnit.Pixel);
}
This should create a copy of the same size, and draw the original into the copy.
You can place the following class in your code:
public static class BitmapExtensions
{
public static Bitmap DeepClone(this Bitmap source)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (Bitmap)formatter.Deserialize(stream);
}
}
}
This method is adjusted to work with Bitmap. If you want to have a generic method look into the link below. By naming the method DeepClone() we remove the risk of breaking already existing code (in the answer below the method is called Clone(), so it's a override method).
Original from here: https://stackoverflow.com/a/43042865/13574233
This method takes a lot of time though. If you're looking for a good performing method, this probably isn't what you're looking for.
Related
What I want to do:
Given a simple Bitmap (System.Drawing.Bitmap) I am willing to output it to my window in WPF application. I am also willing to do it frequently, creating a frame stream.
What I've used:
Firstly, I've been converting Bitmap to a BitmapImage and then assigning it to Source field of an Image control.
The problem with this method is the conversion itself. It's very slow. I haven't found a way that works fast enough, the best one took around 20 ms for a 640x480 Bitmap, which is slow. I am hoping to find a way that would do this for less than 5 ms for any common resolution or a different approach to the whole problem. Perhaps, there is a different control other than Image that would work with pure Bitmap and I wouldn't need to convert. I am also not tied to using WPF, does UWP or the new WinUI 3 have this problem?
I have checked UWP uses WriteableBitmap, which would also require conversion, but maybe there is a different way as well?
I have found various conversions some of which are slow and some of which just produce a white image as a result for some reason. I am providing a list below (I've tried more but I don't remember what exactly):
Using the method below. This conversion works but it takes around 20 ms to convert 640x480 ms Bitmap.
Method (source):
public BitmapImage ToBitmapImage(Bitmap bitmap)
{
using (MemoryStream memory = new 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;
}
}
Using Asmak9.EssentialToolKit library (source), but that conversion took around 27 ms, so that was not an option.
Using the method below. This one does not work for me for some strange reason. It runs with no problem, but the result of the conversion is a blank (white) image and not something that was fed into it.
Method (source):
private BitmapSource Convert(Bitmap bmp)
{
var bitmapData = bmp.LockBits(
new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bmp.UnlockBits(bitmapData);
return bitmapSource;
}
Using the method below.This produces the same result as the previous conversion - blank BitmapImage. I am also not sure what could possibly go wrong here.
Method (source):
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private BitmapSource Bitmap2BitmapImage(Bitmap bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapSource retval;
try
{
retval = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
return retval;
}
Perhaps, the last 2 conversions are better, but they do not convert properly, or I am doing something wrong? Or is there a better way in general to discard the conversion step and display the Bitmap straight?
As Clement pointed out above, Method 3 is the fastest, but it requires 2 things:
Correct pixel format set.
The Convert method should be run in the main thread.
Images extracted using PdfPig are the type of XObject Image or InlineImage (both inherit from IPdfImage). I would like to save and display them in a simple WPF application. In order to do so, I would need to have them in more accessible form, for example BitmapImage format. What is the correct way to achieve that? Library documentation does not help here and my miserable attempts were unsuccessful.
I haven't tested any of this, but it should at least put you on the right path if it doesn't work.
Looking at the PdfPig source on GitHub I can see both XObjectImage and InlineImage have a function TryGetPng. From the looks of it, I would assume that this byte array would match up with the contents of a normal PNG file, which means you should be able to load it straight into a BitmapImage.
Taking some code from this answer. Something like this might work:
InlineImage pdfImage;
byte[] png;
if (pdfImage.TryGetPng(out png))
{
var bitmap = (BitmapSource)new ImageSourceConverter().ConvertFrom(png);
}
Note: both classes also have a TryGetBytes method, which might work in place of TryGetPng. I'm just not sure what format the output of TryGetBytes is in, so I'd be more confident with TryGetPng. Still, I'd try both if one doesn't work.
FWIW, by trial and error, my current approach is to start with TryGetPng and fall back to RawBytes if it fails. I then interpret the extracted bytes as a System.Drawing.Image. I don't use TryGetBytes at all. Here's my code (F#, but should be easy to convert to C#):
let bytes =
match pdfImage.TryGetPng() with
| true, bytes -> bytes
| _ -> Seq.toArray pdfImage.RawBytes
use stream = new MemoryStream(bytes)
use image = Image.FromStream(stream)
I find the following code for me works in most cases. It simply tries all three options available to extract an image (TryGetPng, TryGetBytes and rawBytes) and converts those to an BmpSource.
private static BitmapSource TryGetImage(IPdfImage image)
{
BitmapSource bmp;
byte[] bytes;
if (image.TryGetPng(out bytes))
{
bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
Debug.WriteLine("Converted using TryGetPng.");
}
else
{
IReadOnlyList<byte> iroBytes;
if (image.TryGetBytes(out iroBytes))
{
bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
Debug.WriteLine("Converted using TryGetBytes.");
}
else
{
var rawB=image.RawBytes.ToArray<Byte>();
Bitmap nbmp;
using (var ms = new MemoryStream(rawB))
{
nbmp = new Bitmap(ms);
}
bmp = ConvertBmpToBmpSource(nbmp);
Debug.WriteLine("Converted using RawBytes.");
}
}
return bmp;
}
public static BitmapSource ConvertBmpToBmpSource(Bitmap bitmap)
{
var bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bitmap.UnlockBits(bitmapData);
return bitmapSource;
}
I have the Image of a PictureBox pointing to a certain file "A". At execution time I want to change the Image of the PictureBox to a different one "B" but I get the following error:
"A first chance exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file "A" because it is being used by another process."
I'm setting the Image as follows:
pbAvatar.Image = new Bitmap(filePath);
How can I unlock the first file?
Here is my approach to opening an image without locking the file...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
UPDATE: I did some perf tests to see which method was the fastest. I compared it to #net_progs "copy from bitmap" answer (which seems to be the closest to correct, though does have some issues). I loaded the image 10000 times for each method and calculated the average time per image. Here are the results:
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
The results seem to make sense since you have to create the image twice using the copy from bitmap method.
UPDATE:
if you need a BitMap you can do:
return (Bitmap)Image.FromStream(ms);
This is a common locking question widely discussed over the web.
The suggested trick with stream will not work, actually it works initially, but causes problems later. For example, it will load the image and the file will remain unlocked, but if you try to save the loaded image via Save() method, it will throw a generic GDI+ exception.
Next, the way with per pixel replication doesn't seem to be solid, at least it is noisy.
What I found working is described here: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
This is how the image should be loaded:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
I was looking for a solution to this problem and this method works fine for me so far, so I decided to describe it, since I found that many people advise the incorrect stream approach here and over the web.
Using a filestream will unlock the file once it has been read from and disposed:
using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}
Edit: Updated to allow the original bitmap to be disposed, and allow the FileStream to be closed.
THIS ANSWER IS NOT SAFE - See comments, and see discussion in net_prog's answer. The Edit to use Clone does not make it any safer - Clone clones all fields, including the filestream reference, which in certain circumstances will cause a problem.
You can't dispose / close a stream while a bitmap object is still using it. (Whether the bitmap object will need access to it again is only deterministic if you know what type of file you are working with and exactly what operations you will be performing. -- for example for SOME .gif format images, the stream is closed before the constructor returns.)
Clone creates an "exact copy" of the bitmap (per documentation; ILSpy shows it calling native methods, so it's too much to track down right now) likely, it copies that Stream data as well -- or else it wouldn't be an exact copy.
Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
static Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap b = new Bitmap(stream))
{
retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
using (Graphics g = Graphics.FromImage(retval))
{
g.DrawImage(b, Point.Empty);
g.Flush();
}
}
return retval;
}
And then you can invoke it like such:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
As far as I know, this is 100% safe, since the resulting image is 100% created in memory, without any linked resources, and with no open streams left behind in memory. It acts like any other Bitmap that's created from a constructor that doesn't specify any input sources, and unlike some of the other answers here, it preserves the original pixel format, meaning it can be used on indexed formats.
Based on this answer, but with extra fixes and without external library import.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
To call, simply use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
Or, from bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Here's the technique I'm currently using, and seems to work best. It has the advantage of producing a Bitmap object with the same pixel format (24-bit or 32-bit) and resolution (72 dpi, 96 dpi, whatever) as the source file.
// ImageConverter object used to convert JPEG byte arrays into Image objects. This is static
// and only gets instantiated once.
private static readonly ImageConverter _imageConverter = new ImageConverter();
This can be used as often as needed, as follows:
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
Edit:
Here's an update of the above technique: https://stackoverflow.com/a/16576471/253938
(The accepted answer is wrong. When you try to LockBits(...) on the cloned bitmap eventually you will encounter GDI+ errors.)
I see only 3 ways to get out of this:
copy your file to a temporary file and open that the easy way new Bitmap(temp_filename)
open your file, read image, create a pixel-size-pixelformat copy (don't Clone()) and dispose the first bitmap
(accept the locked-file-feature)
I suggest to use PixelMap (available on NuGet)
or Github
Very easy to use and much faster than standard Bitmap from .NET
PixelMap pixelMap = new PixelMap(bild);
pictureBox1.Image = pixelMap.GetBitmap();
Read it into the stream, create bitmap, close the stream.
I need to make copy of Image class. Tried clone(), but didn't work. Also googled about it and found MemberwiseClone(), but i think i can't access Image class. Also tried this solution, guess it didn't work. Any ideas how to copy Image class without reference?
Bitmap (which inherits from Image has a constructor overload that creates a copy:
Bitmap copy = new Bitmap(image);
My bad. There was silly mistake. Solution below also works fine.
private Image DeepCopy(Image other)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, other);
ms.Position = 0;
return (Image)formatter.Deserialize(ms);
}
}
.
Image tempImg = DeepCopy(img);
Also, this works too:
Image tempImg = (Image)img.Clone();
I'm taking a Stream convert it to Image, process that image, then return a FileStream.
Is this a performance problem? If not, whats the optimized way to convert and return back a stream?
public FileStream ResizeImage(int h, int w, Stream stream)
{
var img = Image.FromStream(stream);
/* ..Processing.. */
//converting back to stream? is this right?
img.Save(stream, ImageFormat.Png);
return stream;
}
The situation in which this is running: User uploads image on my site (controller gives me a Stream, i resize this, then send this stream to rackspace (Rackspace takes a FileStream).
You basically want something like this, don't you:
public void Resize(Stream input, Stream output, int width, int height)
{
using (var image = Image.FromStream(input))
using (var bmp = new Bitmap(width, height))
using (var gr = Graphics.FromImage(bmp))
{
gr.CompositingQuality = CompositingQuality.HighSpeed;
gr.SmoothingMode = SmoothingMode.HighSpeed;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.DrawImage(image, new Rectangle(0, 0, width, height));
bmp.Save(output, ImageFormat.Png);
}
}
which will be used like this:
using (var input = File.OpenRead("input.jpg"))
using (var output = File.Create("output.png"))
{
Resize(input, output, 640, 480);
}
That looks as simple as it can be. You have to read the entire image contents to be able to process it and you have to write the result back.
FileStreams are the normal .NET way to handle files, so for normal purposes your approach is okay.
The only thing I don't understand is why you return the FileStream again - it is the same object as was passed by a parameter.
If you are doing a lot of images and only modify parts of the data, memory mapped files could improve performance. However it is a more advanced concept to use.