I am receiving an image from an external system in the form of a sequence of BGR values followed by an empty byte. The sequence looks sort of like...
[B,G,R,0,B,G,R,0,...,B,G,R,0] where each BGR0 is a single pixel in an image.
I need this in a .NET Bitmap so I can perform some manipulations on it and have come up with the following function to do so:
private Bitmap fillBitmap(byte[] data, int width, int height)
{
Bitmap map = new Bitmap(width, height);
for (int i = 0; i < data.Length; i += 4)
{
int y = ((i / 4) / width);
int x = ((i / 4) - (y * width));
int b = data[i];
int g = data[i + 1];
int r = data[i + 2];
Color pixel = Color.FromArgb(r, g, b);
map.SetPixel(x, y, pixel);
}
return map;
}
This would normally be ok except that most of my images are 1920x1200... so I have a loop that's iterating over 2 million times. Even then that wouldn't be so bad as 2 million iterations shouldn't be very taxing on any modern processor.
But for some reason, this loop can take upwards of 5-15 seconds to run on a pretty beefy Xeon on my server. It would be trivial to parallelize the loop but I suspect there is an even better way of going about this. Any help would be greatly appreciated!
The description of the Bitmap.LockBits Method says,
You can change the color of an image with the SetPixel method, although the LockBits method offers better performance for large-scale changes.
An alternative, I'd guess, might be to use the Bitmap(Stream) Constructor, after you create a Stream which matches the file format of a bitmap.
Check some FastBitmap implementation that will help you set the array from which you will be able to generate the image.
A call to GetPixel and SetPixel makes an interop call to native functions. Each time you call one of the functions the Bitmap image is locked the corresponding pixel/bytes is modified and then the image is finally unlocked. You can imagine performing this repeatedly is highly inefficient.
As others have suggested use the LockBits method, although you will have to use unsafe code I imagine. Now if this is allowed, use it. It allows direct access to the Bitmap's pixels in an unmanaged memory buffer.
Related
I've picked up a project that creates a noise map using setPixel(). The majority of the runtime of this application is spent within the setPixel() function so I was looking to increase the speed at which the function executes.
I have done some research into this and found that this:
int index = x + (y * Width);
int col = color.ToArgb();
if (this.Bits == null)
{
this.Bits = new Int32[Width * Height];
}
Bits[index] = col;
has been recommended as a quicker approach. However, this is generating a completely black image.
I don't understand 100% how image manipulation and memory pointers work to be able to understand the code completely and refactor it to something better.
Here's the original code the person before implemented:
unsafe
{
var scan0 = (byte*)Iptr;
int bitmapStride = Stride;
int bitmapPixelFormatSize = Depth / 8;
index = (bitmapStride * y) + (x * bitmapPixelFormatSize);
if (bitmapPixelFormatSize == 4)
{
scan0[index + 3] = color.A;
scan0[index + 2] = color.R;
scan0[index + 1] = color.G;
scan0[index] = color.B;
}
else if (bitmapPixelFormatSize == 1)
{
scan0[index] = color.R;
}
else
{
scan0[index + 2] = color.R;
scan0[index + 1] = color.G;
scan0[index] = color.B;
}
}
Iptr is just an IntPtr
Stride is an int, the only place I can find this being set is Stride = (PixelCount/Height) * (Depth / 8)
x is width
y is height
Would I be able to get an explanation of what is happening in the original block of code and possibly some help in understanding how to convert that to something that executes quicker, currently it takes around 500,000ms to finish this function due to a nested for loop of width * height.
Note: The following information was originally created by Bob Powell. The original link is no longer functional, so I have copied this information from the Internet Archive at https://web.archive.org/web/20120330012542/http://bobpowell.net/lockingbits.htm. It's a bit long, but I think it's worth preserving.
I'm not sure if this will serve as a direct answer to your question, but perhaps it will help you in finding a solution.
Using the LockBits method to access image data
Many image processing tasks and even file type conversions, say from 32 bit-per-pixel to 8 bit-per-pixel can be speeded up by accessing the pixel data array directly, rather than relying on GetPixel and SetPixel or other methods.
You will be aware that .NET is a managed code system which most often uses managed data so it's not often that we need to gain access to bytes stored in memory anymore however, image manipulation is one of the few times when managed data access is just too slow and so we need to delve once again into the knotty problems of finding the data and manipulating it.
Before I start on the subject in hand, I'll just remind you that the methods used to access any unmanaged data will be different depending on the language in which your program is written. C# developers have the opportunity, via the unsafe keyword and use of pointers, to access data in memory directly. Visual basic programmers should access such data through the Marshal class methods which may also show a small performance loss.
Lock up your bits
The Bitmap class provides the LockBits and corresponding UnlockBits methods which enable you to fix a portion of the bitmap pixel data array in memory, access it directly and finally replace the bits in the bitmap with the modified data. LockBits returns a BitmapData class that describes the layout and position of the data in the locked array.
The BitmapData class contains the following important properties;
Scan0 The address in memory of the fixed data array
Stride The width, in bytes, of a single row of pixel data. This width
is a multiple, or possibly sub-multiple, of the pixel dimensions of
the image and may be padded out to include a few more bytes. I'll
explain why shortly.
PixelFormat The actual pixel format of the data. This is important
for finding the right bytes
Width The width of the locked image
Height The height of the locked image
The relationship of Scan0 and Stride to the array in memory is shown in figure1.
The Stride property, as shown in figure 1, holds the width of one row in bytes. The size of a row however may not be an exact multiple of the pixel size because for efficiency, the system ensures that the data is packed into rows that begin on a four byte boundary and are padded out to a multiple of four bytes. This means for example that a 24 bit per pixel image 17 pixels wide would have a stride of 52. The used data in each row would take up 317 = 51 bytes and the padding of 1 byte would expand each row to 52 bytes or 134 bytes. A 4BppIndexed image of 17 pixels wide would have a stride of 12. Nine of the bytes, or more properly eight and a half, would contain data and the row would be padded out with a further 3 bytes to a 4 byte boundary.
The data carrying portion of the row, as has been suggested above, is laid out according to the pixel format. A 24 bit per pixel image containing RGB data would have a new pixel every 3 bytes, a 32 bit per pixel RGBA every four bytes. Pixel formats that contain more than one pixel per byte, such as the 4 bit per pixel Indexed and 1 bit per pixel indexed, have to be processed carefully so that the pixel required is not confused with it's neigbour pixels in the same byte.
Finding the right byte.
Because the stride is the width of a row, to index any given row or Y coordinate you can multiply the stride by the Y coordinate to get the beginning of a particular row. Finding the correct pixel within the row is possibly more difficult and depends on knowing the layout of the pixel formats. The following examples show how to access a particular pixel for a given pixel format.
Format32BppArgb Given X and Y coordinates, the address of the first
element in the pixel is Scan0+(y * stride)+(x*4). This Points to the
blue byte. The following three bytes contain the green, red and alpha
bytes.
Format24BppRgb Given X and Y coordinates, the address of the first
element in the pixel is Scan0+(yStride)+(x3). This points to the
blue byte which is followed by the green and the red.
Format8BppIndexed Given the X and Y coordinates the address of the
byte is Scan0+(y*Stride)+x. This byte is the index into the image
palette.
Format4BppIndexed Given X and Y coordinates the byte containing the
pixel data is calculated as Scan0+(y*Stride)+(x/2). The corresponding
byte contains two pixels, the upper nibble is the leftmost and the
lower nibble is the rightmost of two pixels. The four bits of the
upper and lower nibble are used to select the colour from the 16
colour palette.
Format1BppIndexed Given the X and Y coordinates, the byte containing
the pixel is calculated by Scan0+(y*Stride)+(x/8). The byte contains
8 bits, each bit is one pixel with the leftmost pixel in bit 8 and
the rightmost pixel in bit 0. The bits select from the two entry
colour palette.
Iterating through the pixels
For pixel formats with one or more bytes per pixel, the formula is simple and can be accomplished by looping through all Y and X values in order. The code in the following listings sets the blue component of a 32 bit per pixel image to 255. In both cases bm is a bitmap previously created.
BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
int PixelSize=4;
for(int y=0; y<bmd.Height; y++)
{
byte* row = (byte *)bmd.Scan0+(y*bmd.Stride);
for(int x = 0; x<bmd.Width; x++)
{
row[x * PixelSize] = 255;
}
}
In VB this operation would be treated a little differently because VB has no knowledge of pointers and requires the use of the marshal class to access unmanaged data.
Dim x As Integer
Dim y As Integer
Dim PixelSize As Integer = 4
Dim bmd As BitmapData = bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat)
For y = 0 To bmd.Height - 1
For x = 0 To bmd.Width - 1
Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + (4 * x) , 255)
Next
Next
Sub-byte pixels.
The Format4BppIndexed and Format1BppIndexed pixel formats mentioned earlier both have more than one pixel stored in a byte. In such cases, it's up to you to ensure that changing the data for one pixel does not effect the other pixel or pixels held in that byte.
The method for indexing a 1 bit per pixel image relies on using bitwise logical operations And and Or to reset or set specific bits in the byte. After using the formula shown above for 1 bit per pixel images, the lower 3 bits of the X coordinate is used to select the bit required. The listings below show this process in C# and VB. In both examples bmd is a bitmap data extracted from a 1 bit per pixel image.
C# code uses pointers and requires compiling with unsafe code
byte* p=(byte*)bmd.Scan0.ToPointer();
int index=y*bmd.Stride+(x>>3);
byte mask=(byte)(0x80>>(x&0x7));
if(pixel)
p[index]|=mask;
else
p[index]&=(byte)(mask^0xff);
VB code uses the marshal class
Dim mask As Byte = 128 >> (x And 7)
Dim offset As Integer = (y * bmd.Stride) + (x >> 3)
Dim currentPixel As Byte = Marshal.ReadByte(bmd.Scan0, offset)
If pixel = True Then
Marshal.WriteByte(bmd.Scan0, offset, currentPixel Or mask)
Else
Marshal.WriteByte(bmd.Scan0, offset, CByte(currentPixel And (mask Xor 255)))
End If
Note, it's quite valid to use the Marshal class from C# code. I used pointers because it offers the best performance.
Accessing individual pixels in a 4 bit per pixel image is handled in a similar manner. The upper and lower nibble of the byte must be dealt with separately and changing the contents of the odd X pixels should not effect the even X pixels. The code below shows how to perform this in C# and VB.
C#
int offset = (y * bmd.Stride) + (x >> 1);
byte currentByte = ((byte *)bmd.Scan0)[offset];
if((x&1) == 1)
{
currentByte &= 0xF0;
currentByte |= (byte)(colorIndex & 0x0F);
}
else
{
currentByte &= 0x0F;
currentByte |= (byte)(colorIndex << 4);
}
((byte *)bmd.Scan0)[offset]=currentByte;
VB
Dim offset As Integer = (y * bmd.Stride) + (x >> 1)
Dim currentByte As Byte = Marshal.ReadByte(bmd.Scan0, offset)
If (x And 1) = 1 Then
currentByte = currentByte And &HF0
currentByte = currentByte Or (colorIndex And &HF)
Else
currentByte = currentByte And &HF
currentByte = currentByte Or (colorIndex << 4)
End If
Marshal.WriteByte(bmd.Scan0, offset, currentByte)
Using LockBits and UnlockBits
The LockBits method takes a rectangle which may be the same size or smaller than the image being processed, a PixelFormat which is usually the same as that of the image being processed and a ImageLockMode value that specifies whether the data is read-only, write-only, read-write or a user allocated buffer. This last option cannot be used from C# or VB because the method overload for LockBits that specifies a user buffer is not included in the GDI+ managed wrapper.
It is very important that when all operations are complete the BitmapData is put back into the bitmap with the UnlockBits method. The snippet of code below illustrates this.
Dim bmd As BitmapData = bm.LockBits(New Rectangle(0, 0, 10, 10), ImageLockMode.ReadWrite, bm.PixelFormat)
' do operations here
bm.UnlockBits(bmd)
Summary
That just about covers the aspects of accessing the most popular and most difficult pixel formats directly. Using these techniques instead of the GetPixel and SetPixel methods provided by Bitmap will show a marked performance boost to your image processing and image format conversion routines.
Is it possible to do Unsafe Image Processing in Python?
As with C# I encountered a hard wall with my pixel processing in Python as getPixel method from Image is simply running too slow.
Is it possible to get a direct access to the image in memory like LockBits in c# does? It will make my program run much faster.
Thanks,
Mark
There is nothing "unsafe" about this.
Once you understand how Python works, it becomes patent that calling a method to retrieve information on each pixel is going to be slow.
First of all, although you give no information on it, I assume you are using "Pillow" - the Python Image Library (PIL) which is the most well known library for image manipulation using Python. As it is a third party package, nothing obliges you to use that. (PIL does have a getpixel method on images, but not a getPixel one)
One straightforward way to have all data in a manipulable way is to create a bytearray object of the image data - given an image in a img variable you can do:
data = bytearray(img.tobytes())
And that is it, you have linear access to all data on your image. To get to an specific Pixel in there, you need to get the image width , heigth and bytes-per-pixel. The later one is not a direct Image attribute in PIL, so you have to compute it given the Image's mode. The most common image types are RGB, RGBA and L.
So, if you want to write out a rectangle at "x, y, width, size" at an image, you can do this:
def rectangle(img, x,y, width, height):
data = bytearray(img.tobytes())
blank_data = (255,) * witdh * bpp
bpp = 3 if data.mode == 'RGB' else 4 if data.mode == 'RGBA' else 1
stride = img.width * bpp
for i in range(y, y + height):
data[i * stride + x * bpp: i * stride + (x + width) * bpp)] = blank_data
return Image.frombytes(img.mode, (img.width, img.height), bytes(data))
That is not much used, but for simple manipulation. People needing to apply filters and other more complicated algorithms in images with Python usually access the image using numpy - python high-performance data manipulation package, wich is tightly coupled with a lot of other packages that have things specific for images - usually installed as scipy.
So, to have the image as an ndarray, which already does all of the above coordinate -> bytes conversion for you, you can use:
import scipy.misc
data = scipy.misc.imread(<filename>)
(Check the docs at https://docs.scipy.org/doc/scipy/reference/)
I'm trying to do some image processing with C# using that same old GDI techniques, iterating through every pixel with a nested for-loop, then using the GetPixel and SetPixel methods on that (Bitmap) image.
I have already got the same results with the pointers approach (using unsafe context) but I'm trying now to do the old-school Get/Set-Pixel Methods to play with my Bitmaps ...
Bitmap ToGrayscale(Bitmap source)
{
for (int y = 0; y < source.Height;y++ )
{
for (int x = 0; x < source.Width; x++)
{
Color current = source.GetPixel(x, y);
int avg = (current.R + current.B + current.G) / 3;
Color output = Color.FromArgb(avg, avg, avg);
source.SetPixel(x, y, output);
}
}
return source;
}
considering performance with the code above ... it takes just tooooo much to finish while stressing the user out waiting for his 1800x1600 image to finish processing.
So i thought that i could use the technique that we use working with HLSL, running a seperate function for each pixel (Pixel Shader engine (as i was tought) copies the function returning the float4 (Color) thousands of times on GPU to do the processing parallel).
So I tried to run a separate Task (function) for each pixel, putting these Task variables into a List and the 'await' for List.ToArray(). But I failed doing that as every new Task 'awaits' to be finished before the next one runs.
I wanted to call a new Task for each pixel to run this :
Color current = source.GetPixel(x, y);
int avg = (current.R + current.B + current.G) / 3;
Color output = Color.FromArgb(avg, avg, avg);
source.SetPixel(x, y, output);
At the end of the day I got my self an async non-blocking code but not parallel ...
Any suggestions guys?
GetPixel and SetPixel are likely the main bottleneck here.
Instead of trying to parallelize this, I would recommend using Bitmap.LockBits to handle the parsing much more efficiently.
That being said, you can parallelize your current version via:
Bitmap ToGrayscale(Bitmap source)
{
Parallel.For(0, source.Height, y =>
{
for (int x = 0; x < source.Width; x++)
{
Color current = source.GetPixel(x, y);
int avg = (current.R + current.B + current.G) / 3;
Color output = Color.FromArgb(avg, avg, avg);
source.SetPixel(x, y, output);
}
});
return source;
}
However, the Bitmap class is not thread safe, so this will likely cause issues.
A better approach would be to use LockBits, then parallelize working on the raw data directly (as above). Note that I'm only parallelizing the outer loop (on purpose) as this will prevent over saturation of the cores with work items.
Using tasks will just set up multiple threads on the CPU - it won't use the graphics processor. Also, I'm pretty sure that the Bitmap objects you are working with are not thread safe, so you won't be able to use multiple threads to access them anyway.
If all you are trying to do is convert an image to grayscale, I would look at built-in functionality first. In general, something built into the .NET framework can use lower level 'unsafe' code to do things more efficiently than would be possible otherwise, without being unsafe. Try How to: Convert an Image to Greyscale.
If you really want to use multiple threads for your custom bitmap processing, I think you will have to make a byte array, modify it in a multithreaded way, then create a bitmap object at the end from the byte array. Take a look at https://stackoverflow.com/a/15290190/1453269 for some pointers on how to do that.
A good way to parallelize your work is not to dispatch a task per pixel, but to dispatch as many threads as your processor cores.
You also say you are able to manipulate your pixels through pointers, so if you take that route, here goes another important advice: Have each thread work on neighboring pixels.
A valid scenario would be thread 1 working with the first 25% pixels, thread 2 with the next 25% and so on until thread 4.
The above is very important to avoid False Sharing, in which you are effectively dismissing your cache's services, making your algorithm a lot slower.
Other than this, you could probably work with your graphics card, but that is totally out of my league.
EDIT: As noted by Panagiotis in the comments, a task may not correlate to a thread, and as such you have to be cautious about what API you'll use to parallelize your work and how you will do it.
I'm writing a tool to automate some of our asset making for a game. What I want to do is take a folder of PNG files, combine them into a texture atlas and then export the atlas as a TGA and the UV coords to XML.
I'm not sure which method I should use to load the PNG files in C# as there seem to be several. What is the recommended method to load images in C# that gives access to the colour/alpha data so I can extract it to the TGA?
I also already have TGA creation code in C++ which I plan to move to C# but I'm wondering if there is anything already available in .Net to create/save TGAs?
Thanks for reading.
Loading a PNG file into a .Net Bitmap is easy:
Bitmap bmp = (Bitmap)Bitmap.FromFile("c:\wherever\whatever.png");
// yes, the (Bitmap) cast is necessary. Don't ask me why.
Once you have the Bitmap loaded, you can access all of its info (including alpha channel info) most efficiently using the Bitmap's LockBits method (there are many LockBits code samples on StackOverflow).
Update: here's a code sample that shows how to use LockBits to access the Bitmap's data pixel-by-pixel:
System.Drawing.Imaging.BitmapData data =
bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
unsafe
{
// important to use the BitmapData object's Width and Height
// properties instead of the Bitmap's.
for (int x = 0; x < data.Width; x++)
{
int columnOffset = x * 4;
for (int y = 0; y < data.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
byte B = row[columnOffset];
byte G = row[columnOffset + 1];
byte R = row[columnOffset + 2];
byte alpha = row[columnOffset + 3];
}
}
}
bmp.UnlockBits(data);
You need to set the "Allow unsafe code" compiler option for your project to use this code. You could also use the GetPixel(x, y) method of the Bitmap, but this is amazingly slow.
I dont have a code sample, but i can give you a guideline
Load PNG using Image.FromFile(). .NET supports loading PNG
Open a file handle to your targa. Create an XmlDocument. .NET doesnt support targa, so you have to manually write it.
Lock the bitmap to get at the pixels. GetPixel() is very slow. I think the method is named LockBits(). You get a pointer to the surface to read the pixels
Write to targa. Targa is a container format, so any bitmap should fit.
Save the UV as Xml.
Targa format
Do you want to use a palette ? Since your making a game, i would recommend you compute a palette for your bitmaps and put that into the targa to reduce the file size.
Oh , before i forget, .NET doesnt use RGBA, instead, the pixels are BGRA. Dont ask me why, but its like that.
I'm currently using Brendan Tompkins ImageQuantization dll.
http://codebetter.com/blogs/brendan.tompkins/archive/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness.aspx
But it doesn't run in medium trust in asp.net.
Does anyone know of a Image Quantization library that does run in medium trust?
Update
I don't care if the solution is slow. I just need something that works.
You should be able to replace the code using Marshal with explicit reading of the underlying stream via something like BinaryReader. This may be slower since you must read the stream entirely into your managed memory or seek into it rather than relying on the copy already in unmanaged memory being quickly accessible but is fundamentally your only option.
You simply cannot go spelunking into unmanaged memory from a medium trust context, even if only performing read operations.
Having looked at the linked code there's a reason you're not allowed to do this sort of thing. For starters he's ignoring the 64/32bit aspect of the IntPtr!
The underlying BitMapData class he's using is utterly predicated on having unfettered read access to arbitrary memory, this is never happening under medium trust.
A significant rewrite of his base functionality will be required to either use BitMap's directly (with the slow GetPixel calls) or read the data directly via conventional stream apis, dropping it into an array(s) and then parse it out yourself. Neither of these are likely to be pleasant. The former will be much slower (I would expect order of magnitude due to the high overhead per pixel read), the later less slow (though still slower) but has much more associated effort in terms of rewriting the low level parsing of the image data.
Here's a rough guide to what you need to change based on the current code:
from Quantizer.cs
public Bitmap Quantize(Image source)
{
// Get the size of the source image
int height = source.Height;
int width = source.Width;
// And construct a rectangle from these dimensions
Rectangle bounds = new Rectangle(0, 0, width, height);
// First off take a 32bpp copy of the image
Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb);
// And construct an 8bpp version
Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
// Now lock the bitmap into memory
using (Graphics g = Graphics.FromImage(copy))
{
g.PageUnit = GraphicsUnit.Pixel;
// Draw the source image onto the copy bitmap,
// which will effect a widening as appropriate.
g.DrawImage(source, bounds);
}
//!! BEGIN CHANGES - no locking here
//!! simply use copy not a pointer to it
//!! you could also simply write directly to a buffer then make the final immage in one go but I don't bother here
// Call the FirstPass function if not a single pass algorithm.
// For something like an octree quantizer, this will run through
// all image pixels, build a data structure, and create a palette.
if (!_singlePass)
FirstPass(copy, width, height);
// Then set the color palette on the output bitmap. I'm passing in the current palette
// as there's no way to construct a new, empty palette.
output.Palette = GetPalette(output.Palette);
// Then call the second pass which actually does the conversion
SecondPass(copy, output, width, height, bounds);
//!! END CHANGES
// Last but not least, return the output bitmap
return output;
}
//!! Completely changed, note that I assume all the code is changed to just use Color rather than Color32
protected virtual void FirstPass(Bitmap source, int width, int height)
{
// Loop through each row
for (int row = 0; row < height; row++)
{
// And loop through each column
for (int col = 0; col < width; col++)
{
InitialQuantizePixel(source.GetPixel(col, row));
} // Now I have the pixel, call the FirstPassQuantize function...
}
}
you would need to do roughly the same in the other functions.
This removes any need for Color32, the Bitmap class will deal with all that for you.
Bitmap.SetPixel() will deal with the second pass. Note that this is the easiest way to port things but absolutely not the fastest way to do it within a medium trust environment.