In the code below I want to convert an image to a byte array, modify the value of some pixels, and convert it back to an image.
Everything works fine if I don't modify any pixel value. The two conversions work. But if I try to modify a pixel value between the two conversions I get an exception. Can you tell me what I am doing wrong ?
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.ShowDialog();
FileStream bmpstream = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read);
Bitmap spotImage = new Bitmap(bmpstream);
ImageConverter imgCon = new ImageConverter();
byte[] rgbValues = (byte[])imgCon.ConvertTo(spotImage, typeof(byte[]));
for (int counter = 2; counter < rgbValues.Length; counter += 3)
{
Buffer.SetByte(rgbValues, counter, 0x00);
//rgbValues[counter] = 0x00; <--I tried also this (not sure of the difference) and I get the same result
}
Image image2 = null;
using (MemoryStream mStream = new MemoryStream(rgbValues, 0, rgbValues.Length))
{
image2 = Image.FromStream(mStream, true); //this line gives the exception
}
pictureBox2.Image = image2;
I also tried to open the file using Bitmap.LockBits and got the same result. Here is the second version of the code (again, it worked as long as I didn't modify the byte array):
Rectangle rect = new Rectangle(0, 0, spotImage.Width, spotImage.Height);
BitmapData bmpData = spotImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, spotImage.PixelFormat);
IntPtr pointeur = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * bmpData.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(pointeur, rgbValues, 0, bytes);
for (int counter = 2; counter < rgbValues.Length; counter += 3)
{
Buffer.SetByte(rgbValues, counter, 0x00);
//rgbValues[counter] = 0x00;
}
spotImage.UnlockBits(bmpData);
Image image2 = null;
using (MemoryStream mStream = new MemoryStream(rgbValues, 0, rgbValues.Length))
{
image2 = Image.FromStream(mStream, true); //this line gives the exception
}
pictureBox2.Image = image2;
Your both attempts failed, because as in the comments:
First attempt - you mess up bytes in file header
Second attempt - you try to decode raw pixel data as Image
Your second attempt is much closer to correct solution. Lock bits with Write enabled and stay with the image you already open - no need to rebuild new bitmap (if there is actually such need in your specific case then I leave this to you as an exercise):
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.ShowDialog();
FileStream bmpstream = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read);
Bitmap spotImage = new Bitmap(bmpstream);
Rectangle rect = new Rectangle(0, 0, spotImage.Width, spotImage.Height);
BitmapData bmpData = spotImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, spotImage.PixelFormat);
IntPtr pointeur = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * bmpData.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(pointeur, rgbValues, 0, bytes);
var pixelSize = Image.GetPixelFormatSize(bmpData.PixelFormat);
for (int counter = 2; counter < rgbValues.Length; counter += 3)
{
rgbValues[counter] = 0x00;
}
// copy buffer back to image
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, pointeur, bytes);
spotImage.UnlockBits(bmpData);
pictureBox2.Image = spotImage;
This should work. I don't know what effect on the image you wanted to achieve though.
Related
I have a System.Windows.Media.Drawing object that I am wanting to convert into a Bitmap object, and then from there extract the bytes that represent the image. I've looked about the internet and I can't seem to find how to do what I need, so any help would be appreciated.
So I finally found a way to convert a System.Windows.Media.Drawing object to a System.Drawing.Bitmap object, and from that get a byte[] object representing the image data. The following is not pretty but does actually work.
public static byte[] DrawingToBytes(Drawing drawing)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext context = visual.RenderOpen())
{
// If using the BitmapEncoder uncomment the following line to get a white background.
// context.DrawRectangle(Brushes.White, null, drawing.bounds);
context.DrawDrawing(drawing);
}
int width = (int)(drawing.Bounds.Width)
int height = (int)(drawing.Bounds.Height)
Bitmap bmp = new Bitmap(width, height);
Bitmap bmpOut;
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(System.Drawing.Color.White);
RenderTargetBitmap rtBmp = new RenderTargetBitmap(width, height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
PixelFormats.Pbgra32);
rtBmp.Render(visual);
// Alternative using BmpBitmapEncoder, use in place of what comes after if you wish.
// MemoryStream stream = new MemoryStream();
// BitmapEncoder encoder = new BmpBitmapEncoder();
// encoder.Frames.Add(BitmapFrame.Create(rtBmp));
// encoder.save(stream);
int stride = width * ((rtBmp.Format.BitsPerPixel + 7) / 8);
byte[] bits = new byte[height * stride];
bitmapSource.CopyPixels(bits, stride, 0);
unsafe
{
fixed (byte* pBits = bits)
{
IntPtr ptr = new IntPtr(pBits);
bmpOut = new Bitmap(width, height, stride,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb, ptr);
}
}
g.DrawImage(bmpOut, 0, 0, bmp.Width, bmp.Height);
}
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.bmp);
data = ms.ToArray();
}
return bytes;
}
So yeah, it's horrible but it actually works.
You can try that:
byte[] ImageToByte(Image image)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
This work also for Bitmap.
I want to read a dicom image using simpleitk, convert it into a bitmap and then display the result in a pictureBox. But when I'm trying to do this, an ArgumentException is thrown. How can I solve this?
Here is my code:
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Open";
dialog.Filter = "DICOM Files (*.dcm;*.dic)|*.dcm;*.dic|All Files (*.*)|*.*";
dialog.ShowDialog();
if (dialog.FileName != "")
{
using (sitk.ImageFileReader reader = new sitk.ImageFileReader())
{
reader.SetFileName(dialog.FileName);
reader.SetOutputPixelType(sitk.PixelIDValueEnum.sitkFloat32);
sitk.Image image = reader.Execute();
var castedImage = sitk.SimpleITK.Cast(image,
sitk.PixelIDValueEnum.sitkFloat32);
var size = castedImage.GetSize();
int length = size.Aggregate(1, (current, i) => current * (int)i);
IntPtr buffer = castedImage.GetBufferAsFloat();
// Declare an array to hold the bytes of the bitmap.
byte[] rgbValues = new byte[length];
// Copy the RGB values into the array.
Marshal.Copy(buffer, rgbValues, 0, length);
Stream stream = new MemoryStream(rgbValues);
Bitmap newBitmap = new Bitmap(stream);
//I have tried in this way, but it generated ArgumentException too
//Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetDepth(), PixelFormat.Format8bppIndexed, buffer);
Obraz.pic.Image = newBitmap;
}
}
Thank you for your comments and attempts to help. After consultations and my own searching on the internet I solved this issue. The first problem was the inadequate representation of pixel image. I had to change Float32 to UInt8 to provide an eight-bit for pixel.
var castedImage = sitk.SimpleITK.Cast(image2, sitk.PixelIDValueEnum.sitkUInt8);
Then I would already create a Bitmap using the constructor that was was commented out in question, but with (int)image.GetWidth() instead of (int)image.GetDepth().
Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetWidth(), PixelFormat.Format8bppIndexed, buffer);
Unfortunately, a new problem appeared. The image, that was supposed to be in gray scale, was displayed in strange colors. But I found the solution here
ColorPalette pal = newBitmap.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
newBitmap.Palette = pal; // you need to re-set this property to force the new ColorPalette
I'm using a C# library for reading of QRCodes. A lot of the samples I've found are based off the old version of zxing where the RGBLuminanceSource constructor still takes in bitmap. In the latest version RGBLuminanceSource only takes byte[]. I've tried to convert bitmap to byte[], but the decode result is always null.
Here's the code I used for conversion:
private byte[] GetRGBValues(Bitmap bmp)
{
// Lock the bitmap's bits.
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
return rgbValues;
}
and for decode:
Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap;
LuminanceSource source = new RGBLuminanceSource(GetRGBValues(bitmap), bitmap.Width, bitmap.Height);
var binarizer = new HybridBinarizer(source);
var binBitmap = new BinaryBitmap(binarizer);
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(binBitmap);
Somehow the result is always null.
Also, our requirement is that we have to use image taken by camera. I've tried this:
Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap;
BarcodeReader reader = new BarcodeReader { AutoRotate = true, TryHarder = true };
Result result = reader.Decode(bitmap);
It only works for the QR image I download online, but if I print out that image and take a picture of that with my phone, then try to process that image, the result is back to null.
Any suggestions would be appreciated.
Here's the image I'm using:
A user selects a portion of an image for a cut and paste operation. I create a new bitmap, paste the selected portion in the new image, wipe the source array and paste it back into the old image. Works but at least half the time it hangs with Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Any thoughts or help?
public BitmapSource CutToNew(double left, double top, double width, double height, double pageWidth, double pageHeight)
{
var destBmp = new Bitmap((int)pageWidth, (int)pageHeight);
var g = Graphics.FromImage(destBmp);
g.FillRectangle(new SolidBrush(Color.White), 0, 0,
(int)pageHeight, (int)pageWidth);
g.Dispose();
var croppedArea = new Rectangle((int)left, (int)top, (int)width, (int)height);
BitmapData croppedSource = _bitmapImage.LockBits(croppedArea,
ImageLockMode.ReadWrite, BitmapImage.PixelFormat);
var croppedDestArea = new Rectangle((int)left, (int)top, (int)width, (int)height);
BitmapData croppedDest = destBmp.LockBits(croppedDestArea,
ImageLockMode.WriteOnly, BitmapImage.PixelFormat);
// Create data array to hold bmpSource pixel data
int stride = croppedSource.Stride;
int numBytes = stride * (int)height;
var srcData = new byte[numBytes];
var destData = new byte[numBytes];
Marshal.Copy(croppedSource.Scan0, srcData, 0, numBytes);
//Tried creating a separate array in case that helped.
Array.Copy(srcData, destData, srcData.Length);
//Often hangs here with Attempted to read or write protected memory.
Marshal.Copy(destData, 0, croppedDest.Scan0, numBytes);
destBmp.UnlockBits(croppedDest);
var retVal = new DocAppImage {BitmapImage = destBmp};
destBmp.Dispose();
//Blank the source area
for (int y = 0; y < srcData.Length; y++)
srcData[y] = 0xFF;
Marshal.Copy(srcData, 0, croppedSource.Scan0, numBytes);
_bitmapImage.UnlockBits(croppedSource);
return retVal.bmpSource;
}
private Bitmap _bitmapImage;
public Bitmap BitmapImage
{
get
{
if (_bitmapImage != null)
return _bitmapImage;
if (FileImage != null)
{
var stream = new MemoryStream(FileImage); //Fileimage=TIFF read from file.
_bitmapImage = new Bitmap(stream);
return _bitmapImage;
}
return null;
}
set
{
if (value != null)
{
ImageCodecInfo codecInfo = GetImageCodecInfo("TIFF");
... implementation to set the bitmap image.
You may want to try specifying your PixelFormat when you create the new object.
For example:
var destBmp = new Bitmap((int)pageWidth, (int)pageHeight, PixelFormat.Format24bppRgb);
I have doubts that this part of code causes memory leak:
public FileResult ShowCroppedImage(int id, int size)
{
string path = "~/Uploads/Photos/";
string sourceFile = Server.MapPath(path) + id + ".jpg";
MemoryStream stream = new MemoryStream();
var bitmap = imageManipulation.CropImage(sourceFile, size, size);
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
Byte[] bytes = stream.ToArray();
return File(bytes, "image/png");
}
How could I make a test to see if this piece of code is the cause?
EDIT:
public Image CropImage(string sourceFile, int newWidth, int newHeight)
{
Image img = Image.FromFile(sourceFile);
Image outimage;
int sizeX = newWidth;
int sizeY = newHeight;
MemoryStream mm = null;
double ratio = 0;
int fromX = 0;
int fromY = 0;
if (img.Width < img.Height)
{
ratio = img.Width / (double)img.Height;
newHeight = (int)(newHeight / ratio);
fromY = (img.Height - img.Width) / 2;
}
else
{
ratio = img.Height / (double)img.Width;
newWidth = (int)(newWidth / ratio);
fromX = (img.Width - img.Height) / 2;
}
if (img.Width == img.Height)
fromX = 0;
Bitmap result = new Bitmap(sizeX, sizeY);
//use a graphics object to draw the resized image into the bitmap
Graphics grPhoto = Graphics.FromImage(result);
//set the resize quality modes to high quality
grPhoto.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
//now do the crop
grPhoto.DrawImage(
img,
new System.Drawing.Rectangle(0, 0, newWidth, newHeight),
new System.Drawing.Rectangle(fromX, fromY, img.Width, img.Height),
System.Drawing.GraphicsUnit.Pixel);
// Save out to memory and get an image from it to send back out the method.
mm = new MemoryStream();
result.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
img.Dispose();
result.Dispose();
grPhoto.Dispose();
outimage = Image.FromStream(mm);
return outimage;
}
I would write it as
public FileResult ShowCroppedImage(int id, int size)
{
string path = "~/Uploads/Photos/";
string sourceFile = Server.MapPath(path) + id + ".jpg";
using (MemoryStream stream = new MemoryStream())
{
using (Bitmap bitmap = imageManipulation.CropImage(sourceFile, size, size))
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
Byte[] bytes = stream.ToArray();
return File(bytes, "image/png");
}
}
}
to ensure that stream.Dispose & bitmap.Dispose are called.
Might want to call stream.dispose(); after Byte[] bytes = stream.ToArray();.
Given the question was how to detect memory leaks/usage, I'd recommend writing a method that calls your function recording the memory usage before and after:
public void SomeTestMethod()
{
var before = System.GC.GetTotalMemory(false);
// call your method
var used = before - System.GC.GetTotalMemory(false);
var unreclaimed = before - System.GC.GetTotalMemory(true);
}
Before will measure the memory usage before your function runs. The used variable will hold how much memory your function used before the garbage collector was run and unreclaimed will tell you how many bytes your function used even after trying to clean up your objects.
I suspect used will be high and unreclaimed will not - putting a using around your memory stream as the other posters suggest should make them closer although bear in mind you still have a byte array holding on to memory.