c# - memory usage during filestream.seek and marshal.copy - c#

I have a running code, which gets any filetypes and show that file in a picturebox full of black and white pixels representing opened file bits
////////////////
int bmpWidth = 128000;
int startIndex = 0;
Int64 tempz = Convert.ToInt64(bmpWidth);
long tmp2 = fs.Length / tempz + 1;
int bmpHeight = Convert.ToInt32(tmp2);
Bitmap bmp = new Bitmap(bmpWidth, bmpHeight, PixelFormat.Format1bppIndexed);
////////////////
long all = 0;
byte[] array = new byte[bmpWidth];
int read = 0;
fs.Seek(startIndex, SeekOrigin.Begin);
all += startIndex;
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
Int64 ptrFirstPixel = bmpData.Scan0.ToInt64();
int y = 0;
while ((read = fs.Read(array, 0, array.Length)) > 0)
{
if (read < array.Length)
{
byte[] arrayNew = new byte[read];
Array.Copy(array, arrayNew, read);
array = arrayNew;
}
Marshal.Copy(array, 0, new IntPtr(ptrFirstPixel + y * bmpData.Stride), array.Length/8);
y++;
all += read;
}
bmp.UnlockBits(bmpData);
imageBox.Image = bmp;
I have a 4GB ram, how its possible that I'm opening a 4.5GB file?
And also, when I try opening 2 or 4 GB file, only 200MB of memory is used, where are the rest of the data coming from?
During marshal.copy and filestream.seek the system use memory for byte by byte for hole filestream and bitmap or its coming from hard disk?
I have a logical and semantic misunderstanding in my mind, any help!?

Related

What is the most efficient way to loop through picture pixels

I've 2 images that I need to compare of 1280x800 each and I'm too worried about the efficiency as I'll have to do the same operations that include looping each second
I can think of so many ways to loop through a Bitmap object pixels but I don't know which would be more efficient, for now I am using simple for loop but it uses too much memory and processing which I could not afford at this point
Some tweaks here and there and all it does is less memory for more processing or the other way around
Any tips, information or experience is highly appreciated, I'm also open to use external libraries if they have much better efficiency.
from https://stackoverflow.com/a/6094092/1856345
Bitmap bmp = new Bitmap("SomeImage");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
// 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];
byte[] r = new byte[bytes / 3];
byte[] g = new byte[bytes / 3];
byte[] b = new byte[bytes / 3];
// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);
int count = 0;
int stride = bmpData.Stride;
for (int column = 0; column < bmpData.Height; column++)
{
for (int row = 0; row < bmpData.Width; row++)
{
b[count] = rgbValues[(column * stride) + (row * 3)];
g[count] = rgbValues[(column * stride) + (row * 3) + 1];
r[count++] = rgbValues[(column * stride) + (row * 3) + 2];
}
}

treshould filter in Aforge doesn't seem to work properly

hope you all doing well. I did write a bit of codes in C# using Aforge library. I wanted to crop my main image captured from webcam so as to have a nice ROI. When I use threshold value of 0 everything should be in white pixels (total of lets say 26880 pixels) but it seems that I have some black pixels (578 pixels) within my cropped image. any idea of what may caused it? when I don't crop my image everything is fine.
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
Bitmap bmp = new Bitmap(x2box, y2box);
bmp = img.Clone(new Rectangle(x1box, y1box, x2box, y2box), eventArgs.Frame.PixelFormat);
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
Bitmap img1 = filter.Apply(bmp);
Threshold tresh = new Threshold((int)tresh1); // tresh1 is 0-255 but is set to zero here
tresh.ApplyInPlace(img1);
int iterator = 1; int xrow = 0; // here i use these constant to calculate location of the pixels
byte[] arraybyte = BitmapToByteArray(img1);
for (int i = 0; i < arraybyte.Length; i++)
{
if (i - iterator * img1.Width == 0)
{
xrow++;
iterator++;
}
if (arraybyte[i] == 0) // if pixel is black
{
X_val.Add(i - xrow * img1.Width);
Y_val.Add(iterator);
}
}
for (int i = 0; i < X_val.Count; i++)
{
YAve += Y_val[i];
XAve += X_val[i];
}
MessageBox.Show(X_val.Count.ToString()); // shows non-zero value!
the BitmapToByteArray method is as follow:
public static byte[] BitmapToByteArray(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
The number of bytes for each row of the Bitmap will be enforced to be a multiple of 4. If roi width * bytes per pixel is not a multiple of 4, you will have padding bytes at the end of each row.
They will not be thresholded as they aren't really part of the Bitmap, so their value may be 0. Your BitmapToByteArray method might not be padding-aware and read every byte.

BitLock-GetPixels. Time and PixelFormat

Okay, I've created two programs. One that uses GetPixels and another that uses LockBits. My GetPixels program is as follows...
The stripe picture referred is a 200x200 jpg
Stopwatch GetTime = new Stopwatch();
Bitmap img = new Bitmap("stripe.jpg");
GetTime.Start();
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color pixel = img.GetPixel(i, j);
output += " " + pixel;
}
}
GetTime.Stop();
Now this one reads out about 20 secs to process this image and output all the pixels. Great, but my LockBits one should, theoretically be faster. My code for the LockBits is...
Bitmap bmp = new Bitmap("stripe.jpg");
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height); //Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, Pixels); //Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0; //Scans the first line of data
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
string Pix = " ";
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
bmp.UnlockBits(bmpData);
Stopwatch Timer = new Stopwatch();
pictureBox1.Image = bmp;
Timer.Start();
for (int p = 0; p < DataBytes; p++)
{
Pix += " " + rgbValues[p];
}
Timer.Stop();
and the time on that is 37secs. Now I dont understand why my time is longer for the Lockbits than it is for the GetPixels.
Also my output files don't match up in terms of where they are listed. It is almost as if they are out of order.
This is a big problem to tackle so thank you all in advance for reading and trying to solve my problem.
You have a few problems that I can see. The biggest issue is that your image has a width of 200, but in memory, its stride is 600 (for me - probably similar for you). This means you are writing out a lot more data, because you don't ignore the 400 padding pixels per row.
Other issues:
You're timing string concatenation only. By the time you start your timer, the lockbits stuff has finished.
Your string concatenation would be faster using StringBuilder.
You are locking the bitmap for read/write access, when you only need read. No discernible effect on performance for me, with this image, but still might as well change it to ReadOnly.
Most of your comments are unnecessary at best (// creates array)- some are misleading (//Scans the first line of data - no, it returns a pointer to the data which has already been loaded).
The following code completes in only a few milliseconds on my machine.
Bitmap bmp = new Bitmap(#"d:\stripe.jpg");
//pictureBox1.Image = bmp;
Stopwatch Timer = new Stopwatch();
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(
bmpRec, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr Pointer = bmpData.Scan0;
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[DataBytes];
Marshal.Copy(Pointer, rgbValues, 0, DataBytes);
bmp.UnlockBits(bmpData);
StringBuilder pix = new StringBuilder(" ");
Timer.Start();
for (int i = 0; i < bmpData.Width; i++)
{
for (int j = 0; j < bmpData.Height; j++)
{
// compute the proper offset into the array for these co-ords
var pixel = rgbValues[i + j*Math.Abs(bmpData.Stride)];
pix.Append(" ");
pix.Append(pixel);
}
}
Timer.Stop();
Console.WriteLine(Timer.Elapsed);

Byte Array to Bitmap Image

I made this code to receive an image and convert it to bitmap image but it doesn't work.
Here is the code:
public void ReceiveImage()
{
NetworkStream stream = new NetworkStream(socket);
byte[] data = new byte[4];
stream.read(data,0,data.length,0)
int size = BitConverter.ToInt32(data,0);
data = new byte[size];
stream.read(data,0,data.length)
MemoryStream imagestream = new MemoryStream(data);
Bitmap bmp = new Bitmap(imagestream);
picturebox1.Image = bmp;
}
It gets to:
Bitmap bmp = new Bitmap(imagestream);
And gives me this error:
Parameter is not valid
This is an alternative method
int w= 100;
int h = 200;
int ch = 3; //number of channels (ie. assuming 24 bit RGB in this case)
byte[] imageData = new byte[w*h*ch]; //you image data here
Bitmap bitmap = new Bitmap(w,h,PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(imageData,0,pNative,w*h*ch);
bitmap.UnlockBits(bmData);
You are probably not receiving enough bytes in stream.read(data,0,data.length) since Read does not ensure that it will read data.length bytes. you have to check its return value and continue to read till data.Length bytes are read.
See : Stream.Read Method's return value
int read = 0;
while (read != data.Length)
{
read += stream.Read(data, read, data.Length - read);
}
PS: I am assuming lengths and reads are typos.
I assume you have a table and want to receive the picture from database.
int cout = ds.Tables["TableName"].Rows.Count;
if (cout > 0)
{
if (ds.Tables["TableName"].Rows[cout - 1]["Image"] != DBNull.Value)
{
var data = (byte[])(ds.Tables["TableName"].Rows[cout - 1]["Image"]);
var stream = new MemoryStream(data);
pictureBox1.Image = Image.FromStream(stream);
}
else
{
pictureBox1.Image = null;
}
}
Try this:
int size = BitConverter.ToInt32(data.Reverse().ToArray(),0);

24-bit- to 1-bit-bitmap-convertion

For a mobile application I have to generate a 1-bit-bitmap out of a 24-bit-bitmap. The problem is, that the result isn't correct, so i made this little project to try it on my desktop-pc. the creation works, but the result isn't ok, as you can see.
You can hardly read anything because a lot of bits aren't at the right position anymore but moved some pixels left or right.
This is the code i use for creation:
int z = 0;
int bitNumber = 0;
//the new 1Bit-byte-Array
byte[] oneBitImage = new byte[(bmp.Height * bmp.Width) / 8];
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* p = (byte*)(void*)bmData.Scan0.ToPointer();
int stopAddress = (int)p + bmp.Height * bmData.Stride;
while ((int)p != stopAddress)
{
if (*p < 128) // is black
oneBitImage[z] = (byte)(oneBitImage[z] | Exp(bitNumber)); //Set a Bit on the specified position
p += 3;
bitNumber++;
if (bitNumber == 8)
{
bitNumber = 0;
z++;
}
}
}
bmp.UnlockBits(bmData);
//Convert into 1-bit-bmp to check result
Bitmap newbmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format1bppIndexed);
BitmapData bmpData = newbmp.LockBits(
new Rectangle(0, 0, newbmp.Width, newbmp.Height),
ImageLockMode.WriteOnly, newbmp.PixelFormat);
Marshal.Copy(oneBitImage, 0, bmpData.Scan0, oneBitImage.Length);
newbmp.UnlockBits(bmpData);
newbmp.Save(fileName, ImageFormat.Bmp);
Short explanation:
I run through every third byte, and if this byte - the first one of a 3-byte-group (pixel in 24-bit) - is lower than 128 I put a bit at the specified position.
EXP gives me the exponent...
Switch the bits in every output byte around. Bit 7 should be bit 0, etc.
I would convert three bytes to a "real" color (long value or similar) and see if the result is bigger than half of 16.7m .

Categories