I want to copy a 24 bit Color bitmap data onto a Int32 array and i wrote the following code to do it.
Width of image = 200 pixels
Height of image = 150 pixels
public static Int32[] readBitmap()
{
int rows = 150;
int columns = 200;
Bitmap myBmp = new Bitmap("puppy.bmp");
BitmapData bmd = myBmp.LockBits(new Rectangle(0, 0, columns, rows), ImageLockMode.ReadWrite, myBmp.PixelFormat);
Console.WriteLine(myBmp.PixelFormat.ToString());
int fileSize = 30000; // 200 * 150
Int32[] fileBufferArray = new Int32[fileSize];
unsafe
{
for (int j = 0; j < rows; j++)
{
Int32* row = (Int32*)bmd.Scan0 + (j * bmd.Stride);
for (int i = 0; i < columns; i++)
{
try
{
fileBufferArray[j * columns + i] = row[i];
}
catch (Exception e)
{
Console.WriteLine(e.ToString() + " " + i.ToString() + " " + j.ToString());
break;
}
}
}
myBmp.UnlockBits(bmd);
return fileBufferArray;
} //unsafe
}
But i get a access violation exception.
Unhandled Exception: System.AccessViolationException: Attempted to read or write
protected memory. This is often an indication that other memory is corrupt.
Could someone please help me correct this error ?
The problem is that a bitmap that has 30,000 pixels requires 3 * 30,000 bytes to represent in 24-bit color. Each pixel is represented by three bytes. Your loop is copying bytes to integers. And since your integer array is only 30,000 integers in length, it's going to fail.
It's if you had written:
var fileBufferArray = new int[30000];
for (int i = 0; i < 90000; ++i)
{
fileBufferArray[i] = bitmapData[i];
}
Obviously, that's going to fail.
You need to combine each three bytes into a single 24-bit value. One way to do that would be to change the assignment in your inner loop to:
int r = i * 3;
int pixelvalue = row[r];
pixelValue = (pixelValue << 8) | row[r+1];
pixelValue = (pixelValue << 8) | row[r+2];
fileBufferArray[j * columns + i] = pixelValue;
That's not the most concise or most efficient way to do it, but it illustrates the concept. I also might not have the order of the values right. They might be stored with the low byte first rather than the high byte. Regardless, the concept is the same.
You overrunning fileBufferArray. See if this more generic approach can help you. The source:
private unsafe byte[] BmpToBytes_Unsafe (Bitmap bmp)
{
BitmapData bData = bmp.LockBits(new Rectangle (new Point(), bmp.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
// number of bytes in the bitmap
int byteCount = bData.Stride * bmp.Height;
byte[] bmpBytes = new byte[byteCount];
// Copy the locked bytes from memory
Marshal.Copy (bData.Scan0, bmpBytes, 0, byteCount);
// don't forget to unlock the bitmap!!
bmp.UnlockBits (bData);
return bmpBytes;
}
There is also safer ways to get the byte array using streams:
private byte[] BmpToBytes(Bitmap bmp)
{
MemoryStream ms = new MemoryStream();
// Save to memory using the bmp format
bmp.Save (ms, ImageFormat.Bmp);
// read to end
byte[] bmpBytes = ms.GetBuffer();
bmp.Dispose();
ms.Close();
return bmpBytes;
}
Related
I am trying to generate 16bit grayscale Bitmap in C# from a random data.But it crashed on Marshal.Copy.
Here is my code:
Bitmap b16bpp;
private void GenerateDummy16bitImage()
{
b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT * 2;
var bitmapBytes = new short[numberOfBytes];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < IMAGE_WIDTH; x++)
{
for (int y = 0; y < IMAGE_HEIGHT; y++)
{
var i = ((y * IMAGE_WIDTH) + x) * 2; // 16bpp
// Generate the next random pixel color value.
var value = (short)random.Next(5);
bitmapBytes[i] = value; // BLUE
bitmapBytes[i + 1] = value; // GREEN
bitmapBytes[i + 2] = value; // RED
// bitmapBytes[i + 3] = 0xFF; // ALPHA
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);//crashes here
// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);
b16bpp.Save("random.bmp", ImageFormat.Bmp);
Debug.WriteLine("saved");
}
The exception is:
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
This is not my code.I found it in relation to 32bit Bitmaps and modified.But I guess I have missed something as I am pretty new to C#.
Basically,all I need is to wrap into BitmapData an arrays of shorts.
This works for System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
private static void SaveBmp(Bitmap bmp, string path)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);
BitmapSource source = BitmapSource.Create(bmp.Width,
bmp.Height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
pixelFormats,
null,
bitmapData.Scan0,
bitmapData.Stride * bmp.Height,
bitmapData.Stride);
bmp.UnlockBits(bitmapData);
FileStream stream = new FileStream(path, FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
stream.Close();
}
private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
{
System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;
switch (pixelformat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
pixelFormats = PixelFormats.Bgr32;
break;
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
pixelFormats = PixelFormats.Gray8;
break;
case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
pixelFormats = PixelFormats.Gray16;
break;
}
return pixelFormats;
}
I have corrected some of your mistakes (mostly wrong sizes). But it will still crash on b16bpp.Save(), because GDI+ does not support saving 16bit grayscale images.
Bitmap b16bpp;
private void GenerateDummy16bitImage()
{
b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT;
var bitmapBytes = new short[IMAGE_WIDTH * IMAGE_HEIGHT];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < IMAGE_WIDTH; x++)
{
for (int y = 0; y < IMAGE_HEIGHT; y++)
{
var i = ((y * IMAGE_WIDTH) + x); // 16bpp
// Generate the next random pixel color value.
var value = (short)random.Next(5);
bitmapBytes[i] = value; // GRAY
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, bitmapBytes.Length);
// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);
b16bpp.Save("random.bmp", ImageFormat.Bmp);
Debug.WriteLine("saved");
}
Explanation of my changes:
bitmapData.Stride is already IMAGE_WIDTH * BytesPerPixel so you don't need to multiply by 2
as you declared bitmapBytes as short[] it has to have the size of the image in pixels not in bytes
that means you also do not need to multiply i by 2
since you have a grayscale image it does not have a blue, green and red channel, but one single 16bit gray channel
Marshal.Copy takes the length in "array units" not in bytes
All in all you tried to copy an array 8 times to large into the bitmap.
I have a bitmap that I want to convert into a color array, faster than if I had used the GetPixel function.
So far I have seen people convert the bitmap into a byte array first then into the color array:
Bitmap bmBit = (Bitmap)bit;
var bitmapData = bmBit.LockBits(new Rectangle(0, 0, bmBit.Width, bmBit.Height),
ImageLockMode.ReadWrite, bmBit.PixelFormat);
var length = bitmapData.Stride * bitmapData.Height;
byte[] bytes = new byte[length];
Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
bmBit.UnlockBits(bitmapData);
But this returns a bmBit with incorrect numbers.
What am I doing wrong, and is there a better way to do this without converting to a byte array first?
There are different ways for pixels to be stored in a bitmap (16 color, 256 color, 24 bits per pixel, etc...), and rows are padded to multiples of 4 bytes.
for (int i = 0; i < bitmapData.Height; i++)
{
//the row starts at (i * bitmapData.Stride).
//we must do this because bitmapData.Stride includes the pad bytes.
int rowStart = i * bitmapData.Stride;
//you need to use bitmapData.Width and bitmapData.PixelFormat
// to determine how to parse a row.
//assuming 24 bit. (bitmapData.PixelFormat == Format24bppRgb)
if (bitmapData.PixelFormat == PixelFormat.Format24bppRgb)
{
for (int j = 0; j < bitmapData.Width; j++)
{
//the pixel is contained in:
// bytes[pixelStart] .. bytes[pixelStart + 2];
int pixelStart = rowStart + j * 3;
}
}
}
I am trying to combine 3 grayscale bitmaps into one color bitmap. All three grayscale images are the same size (this is based off of data from the Hubble). My logic is:
Load "blue" image and convert to PixelFormat.Format24bppRgb. Based off of that create a new byte array that is 4 times as large as the blue data array length/3 (so it will be one byte for blue, one byte for green, one byte for red, one byte for alpha per pixel since my system is little endian). Populate the "blue" bytes of the array from the "blue" bytes of the blue image (and in this first loop set the alpha byte to 255). I then load the green and red bitmaps, convert them to PixelFormat.Format24bppRgb, and pull the g/r value and add it to the correct place in the data array. The final data array then has the bgra bytes set correctly from what I can tell.
When I have the data array populated, I have used it to:
Create a PixelFormats.Bgra32 BitmapSource then convert that to a Bitmap.
Create a PixelFormat.Format32bppArgb Bitmap using the Bitmap constructor (width, height, stride, PixelForma, IntPtr)
Create a PixelFormat.Format32bppArgb Bitmap using pointers
All three ways of creating a return bitmap result in the image being "skewed" (sorry, I don't know of a better word).
The actual output (of all three ways of generating the final bitmap) is: Actual output
The desired output is something like (this was done in photoshop so it is slightly different): Desired output
The three file names (_blueFileName, _greenFileName, _redFileName) are set in the constructor and I check to make sure the files exist before creating the class. I can post that code if anyone wants it.
Can anyone tell me what I am doing wrong? I am guessing that is is due to the stride or something like that?
Note: I can't post the links to the images I am using as input as I don't have 10 reputation points. Maybe I could send the links via email or something if someone wants them as well.
Here is my code (with some stuff commented out, the comments describe what happens if each commented out block is used instead):
public Bitmap Merge()
{
// Load original "blue" bitmap.
Bitmap tblueBitmap = (Bitmap)Image.FromFile(_blueFileName);
int width = tblueBitmap.Width;
int height = tblueBitmap.Height;
// Convert to 24 bpp rgb (which is bgr on little endian machines)
Bitmap blueBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(blueBitmap))
{
gr.DrawImage(tblueBitmap, 0, 0, width, height);
}
tblueBitmap.Dispose();
// Lock and copy to byte array.
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly,
blueBitmap.PixelFormat);
int numbBytes = blueData.Stride*blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
Marshal.Copy(blueData.Scan0, blueBytes, 0, numbBytes);
blueBitmap.UnlockBits(blueData);
blueData = null;
blueBitmap.Dispose();
int mult = 4;
byte[] data = new byte[(numbBytes/3)*mult];
int count = 0;
// Copy every third byte starting at 0 to the final data array (data).
for (int i = 0; i < data.Length / mult; i++)
{
// Check for overflow
if (blueBytes.Length <= count*3 + 2)
{
continue;
}
// First pass, set Alpha channel.
data[i * mult + 3] = 255;
// Set blue byte.
data[i*mult] = blueBytes[count*3];
count++;
}
// Cleanup.
blueBytes = null;
int generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tgreenBitmap = (Bitmap)Image.FromFile(_greenFileName);
Bitmap greenBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(greenBitmap))
{
gr.DrawImage(tgreenBitmap, 0, 0, width, height);
}
tgreenBitmap.Dispose();
BitmapData greenData = greenBitmap.LockBits(new Rectangle(0, 0, greenBitmap.Width, greenBitmap.Height), ImageLockMode.ReadOnly,
greenBitmap.PixelFormat);
numbBytes = greenData.Stride * greenBitmap.Height;
byte[] greenBytes = new byte[numbBytes];
Marshal.Copy(greenData.Scan0, greenBytes, 0, numbBytes);
greenBitmap.UnlockBits(greenData);
greenData = null;
greenBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (greenBytes.Length <= count * 3 + 1)
{
continue;
}
// Set green byte
data[i * mult + 1] = greenBytes[count * 3 + 1];
count++;
}
greenBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tredBitmap = (Bitmap)Image.FromFile(_redFileName);
Bitmap redBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(redBitmap))
{
gr.DrawImage(tredBitmap, 0, 0, width, height);
}
tredBitmap.Dispose();
BitmapData redData = redBitmap.LockBits(new Rectangle(0, 0, redBitmap.Width, redBitmap.Height), ImageLockMode.ReadOnly,
redBitmap.PixelFormat);
numbBytes = redData.Stride * redBitmap.Height;
byte[] redBytes = new byte[numbBytes];
Marshal.Copy(redData.Scan0, redBytes, 0, numbBytes);
redBitmap.UnlockBits(redData);
redData = null;
redBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (redBytes.Length <= count * 3+2)
{
count++;
continue;
}
// set red byte
data[i * mult + 2] = redBytes[count * 3 + 2];
count++;
}
redBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
int stride = (width*32 + 7)/8;
var bi = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, data, stride);
// uncomment out below to see what a bitmap source to bitmap does. So far, it is exactly the same as
// the uncommented out lines below.
// ---------------------------------------------------------------------------------------------------
//return BitmapImage2Bitmap(bi);
unsafe
{
fixed (byte* p = data)
{
IntPtr ptr = (IntPtr)p;
// Trying the commented out lines returns the same bitmap as the uncommented out lines.
// ------------------------------------------------------------------------------------
byte* p2 = (byte*)ptr;
Bitmap retBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData fData = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
unsafe
{
for (int i = 0; i < fData.Height; i++)
{
byte* imgPtr = (byte*)(fData.Scan0 + (fData.Stride * i));
for (int x = 0; x < fData.Width; x++)
{
for (int ii = 0; ii < 4; ii++)
{
*imgPtr++ = *p2++;
}
//*imgPtr++ = 255;
}
}
}
retBitmap.UnlockBits(fData);
//Bitmap retBitmap = new Bitmap(width, height, GetStride(width, PixelFormat.Format32bppArgb),
// PixelFormat.Format32bppArgb, ptr);
return retBitmap;
}
}
}
private Bitmap BitmapImage2Bitmap(BitmapSource bitmapSrc)
{
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSrc));
enc.Save(outStream);
Bitmap bitmap = new Bitmap(outStream);
return new Bitmap(bitmap);
}
}
private int GetStride(int width, PixelFormat pxFormat)
{
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
int validBitsPerLine = width * bitsPerPixel;
int stride = ((validBitsPerLine + 31) / 32) * 4;
return stride;
}
You are missing the gap between the lines. The Stride value is not the amount of data in a line, it's the distance between the start of one line to the next. There may be a gap at the end of each line to align the next line on an even address boundary.
The Stride value can even be negative, then the image is stored upside down in memory. To get the data without the gaps and to handle all cases you need to copy one line at a time:
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly, blueBitmap.PixelFormat);
int lineBytes = blueBitmap.Width * 3;
int numbBytes = lineBytes * blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
for (int y = 0; y < blueBitmap.Height; y++) {
Marshal.Copy(blueData.Scan0 + y * blueData.Stride, blueBytes, y * lineBytes, lineBytes);
}
blueBitmap.UnlockBits(blueData);
blueBitmap.Dispose();
I'm trying to determine the optimal way to flip an image across the Y axis. For every pixel, there are 4 bytes, and each set of 4 bytes needs to remain together in order but get shifted. Here's the best I've come up with so far.
This only takes .1-.2s for a 1280x960 image, but with video such performance is crippling. Any suggestions?
Initial implementation
private void ReverseFrameInPlace(int width, int height, int bytesPerPixel, ref byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
int stride = width * bytesPerPixel;
int halfStride = stride / 2;
int byteJump = bytesPerPixel * 2;
int length = stride * height;
byte pix;
for (int i = 0, a = stride, b = stride - bytesPerPixel;
i < length; i++)
{
if (b % bytesPerPixel == 0)
{
b -= byteJump;
}
if (i > 0 && i % halfStride == 0)
{
i = a;
a += stride;
b = a - bytesPerPixel;
if (i >= length)
{
break;
}
}
pix = framePixels[i];
framePixels[i] = framePixels[b];
framePixels[b++] = pix;
}
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace: {0}", s.Elapsed);
}
Revision #1
Revised with indexes and Buffer.BlockCopy per SLaks and Alexei. Also added a Parallel.For since the indexes allow for it.
int[] pixelIndexF = null;
int[] pixelIndexB = null;
private void ReverseFrameInPlace(int width, int height, int bytesPerPixel, byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
if (pixelIndexF == null)// || pixelIndex.Length != (width * height))
{
int stride = width * bytesPerPixel;
int length = stride * height;
pixelIndexF = new int[width * height / 2];
pixelIndexB = new int[width * height / 2];
for (int i = 0, a = stride, b = stride, index = 0;
i < length; i++)
{
b -= bytesPerPixel;
if (i > 0 && i % (width / 2 )== 0)
{
//i = a;
i += width / 2;
a += stride;
b = a - bytesPerPixel;
if (index >= pixelIndexF.Length)
{
break;
}
}
pixelIndexF[index] = i * bytesPerPixel;
pixelIndexB[index++] = b;
}
}
Parallel.For(0, pixelIndexF.Length, new Action<int>(delegate(int i)
{
byte[] buffer = new byte[bytesPerPixel];
Buffer.BlockCopy(framePixels, pixelIndexF[i], buffer, 0, bytesPerPixel);
Buffer.BlockCopy(framePixels, pixelIndexB[i], framePixels, pixelIndexF[i], bytesPerPixel);
Buffer.BlockCopy(buffer, 0, framePixels, pixelIndexB[i], bytesPerPixel);
}));
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace: {0}", s.Elapsed);
}
Revision #2
private void ReverseFrameInPlace(int width, int height, System.Drawing.Imaging.PixelFormat pixelFormat, byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
System.Drawing.Rectangle imageBounds = new System.Drawing.Rectangle(0,0,width, height);
//create destination bitmap, get handle
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, pixelFormat);
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(imageBounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptr = bitmapData.Scan0;
//byte[] to bmap
System.Runtime.InteropServices.Marshal.Copy(framePixels, 0, ptr, framePixels.Length);
bitmap.UnlockBits(bitmapData);
//flip
bitmap.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX);
//get handle for bitmap to byte[]
bitmapData = bitmap.LockBits(imageBounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
ptr = bitmapData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, framePixels, 0, framePixels.Length);
bitmap.UnlockBits(bitmapData);
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace: {0}", s.Elapsed);
}
I faced almost the same issue but in my case I needed to flip the image for saving it to an .avi container.
I used the Array.Copy() method instead and suprisingly it seems faster than the others (at least, on my machine). The source image that I used was 720 x 576 pixels with 3 bytes per pixel. This method took between .001 - 0.01 seconds versus about 0.06 seconds for both your revisions.
private byte[] ReverseFrameInPlace2(int stride, byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
var reversedFramePixels = new byte[framePixels.Length];
var lines = framePixels.Length / stride;
for (var line = 0; line < lines; line++)
{
Array.Copy(framePixels, framePixels.Length - ((line + 1) * stride), reversedFramePixels, line * stride, stride);
}
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace2: {0}", s.Elapsed);
return reversedFramePixels;
}
Try calling Buffer.BlockCopy on each range of 4 bytes; that should be faster.
You could parallelize execution on the CPU using any technique or use a pixel shader and do it on the GPU. If you only do that to display flipped video - you would best use DirectX and simply do a transformation on the GPU.
Couple more random things to try and measure:
pre-build array of indexes to copy to for a line (like [12,13,14,15, 8,9,10,11, 4,5,6,7, 0,1,2,3] instead of some complicated ifs executed on each line.
try copying to new destination instead of in-place.
Use one of the many transforms supplied by the .NET library:
http://msdn.microsoft.com/en-us/library/aa970271.aspx
Edit: Here's another example:
http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-rotate
Another option might be to use the XNA framework to do manipulations on images. There is a small example of How to resize and save a Texture2D in XNA?. I have no idea how fast it is, but I could see how it should be pretty fast considering the functions are suppose to be used in games with high fps.
What is the fastest way to get column of pixels from bitmap?
I don't consider two loops because it could take to long. I will appreciate for any idea. Thanks!
I would suggest to convert your bitmap into a vector of bytes using the following code:
Bitmpa myImage=new Bitmap("myImage.bmp");
BitmapData bmpData1=myImage.LockBits(new Rectangle(0,0,myImage.Width,myImage.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, myImage.PixelFormat);
byte[] myImageData = new byte[bmpData1.Stride * bmpData1.Height];
System.Runtime.InteropServices.Marshal.Copy(bmpData1.Scan0,myImageData,0
,myImageData.Length);
myImgage.UnlockBits(bmpData1);
Then it becomes easy to retrieve the pixels on the column you want. Just loop through the vector myImageData with a step equals to the number of pixels per rows * the number of bytes per pixel.
Just be careful about the number of bytes per pixel are used for the representation of your bitmap.
This depends on the PixelFormat .
Assuming you only need a single column, the following routines should help you.
First here is the standard solution using verifiable C# code
static Color[] GetPixelColumn(Bitmap bmp, int x)
{
Color[] pixelColumn = new Color[bmp.Height];
for (int i = 0; i < bmp.Height; ++i)
{
pixelColumn[i] = bmp.GetPixel(x, i);
}
return pixelColumn;
}
Here is a faster alternative, but this requires that you enable unsafe support for the C# compiler
static Color[] GetPixelColumnFast(Bitmap bmp, int x)
{
Color[] pixelColumn = new Color[bmp.Height];
BitmapData pixelData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
unsafe
{
int* pData = (int*)pixelData.Scan0.ToPointer();
pData += x;
for (int i = 0; i < bmp.Height; ++i)
{
pixelColumn[i] = Color.FromArgb(*pData);
pData += bmp.Width;
}
}
bmp.UnlockBits(pixelData);
return pixelColumn;
}
Both functions will return a Color array with the colors of the pixels for a specific column in the bitmap.
Convert it to a array of bytes
private static byte[, ,] BitmapToBytes(Bitmap bitmap)
{
BitmapData bitmapData =
bitmap.LockBits(new Rectangle(new Point(), bitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte[] bitmapBytes;
var stride = bitmapData.Stride;
try
{
int byteCount = bitmapData.Stride * bitmap.Height;
bitmapBytes = new byte[byteCount];
Marshal.Copy(bitmapData.Scan0, bitmapBytes, 0, byteCount);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
byte[, ,] result = new byte[3, bitmap.Width, bitmap.Height];
for (int k = 0; k < 3; k++)
{
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
result[k, i, j] = bitmapBytes[j * stride + i * 3 + k];
}
}
}
return result;
}