Bitmap <-> ArrayList of points

I

Iwanow

Hello!


My goal is to develop a program that opens a bitmap, copies its pixels
to an ArrayList in order to perform some complex calculations (edge
detection, Hough transform etc.), and save resulting image back in some
other bitmap.

It works pretty fast, except for stages of coping pixels between bitmap
(object of type Bitmap) and the ArrayList. I'm using the trivial
solution with two loops:

for (int y=0; y<bmp.Height; y++)
for (x=0; x<bmp.Width; x++)

which seems to be very time-consuming -- for any image I tried, copying
each pixel from Bitmap to the ArrayList (and vice-versa) takes more
time than performing edge detection on pixels stored in the ArrayList!

Is there any way to make that process faster?
 
T

Tamas Demjen

Iwanow said:
It works pretty fast, except for stages of coping pixels between bitmap
(object of type Bitmap) and the ArrayList. I'm using the trivial
solution with two loops:

for (int y=0; y<bmp.Height; y++)
for (x=0; x<bmp.Width; x++)

And what's inside those loops? How do you access the pixels?

Try to use a DIB instead of a bitmap. The terminology seems to be a bit
confusing, but a DIB (created using CreateDIBSection) resides in the
physical memory, and is as fast as memory allocated with malloc. On the
other hand, a bitmap (created using CreateBitmap) doesn't have a pointer
in the RAM. I believe it resides in the video memory, and yes, it takes
enormous emount of time to transfer data between the RAM and the VRAM
(in most cases much more time than to process the image itself).

I personally don't care about bitmaps unless I really need to display
them on the screen. Even then, instead of using bitmaps, I prefer the
DrawDibDraw API, which allows me to use my own data structures. I almost
never use Windows bitmap functions. Windows can't load and save my image
files anyway.

In .NET, I achieved better results with cli::array<> than with List<>,
and ArrayList is a huge no no. ArrayList stores Object^ handles, not
numeric values, which means each of your pixel will silently allocate an
managed object of its own. Don't ever use ArrayList, it's evil! That
would explain why it's so slow for you. If it's an RGB image, than each
pixel will allocate 3 managed objects. That means 200 million .NET
objects will be allocated with an 11x17" 600 DPI color drawing!

I strongly recommend that you start using cli::array. In image
processing, you always know the size of the image in advance, and it
doesn't grow. But even if you must use a dynamic array, please stay away
from ArrayList at all times.

Tom
 
I

Iwanow

Tamas said:
I strongly recommend that you start using cli::array. In image
processing, you always know the size of the image in advance, and it
doesn't grow. But even if you must use a dynamic array, please stay away
from ArrayList at all times.

Thank you for that hint. Up to now, I've been using... ArrayLists to
store my data. Therefore, I'll need to change it to the cli::array.

I use Bitmap objects as sources of original images, and a destinations
after processing. I usually access them through either:
- bmp->GetPixel(x, y).GetBrightnes() or
- bmp->SetBixel(x, y, Color)
Both functions are called within 'for' loops mentioned in my first
post. Will they work faster with cli::array?
 
T

Tamas Demjen

Iwanow said:
Thank you for that hint. Up to now, I've been using... ArrayLists to
store my data.

ArrayList stores System::Object^ managed handles. If you add an integer
value to an ArrayList, it wraps that value into a managed object. This
is called boxing. The C++/CLI compiler automatically generates code that
wraps your integer value into a garbage collected object. In the old
MC++, you had to explicitly __box() them. Either way, this is a major
performance penalty, and a waste of memory too. That's why I suggested
using array<unsigned char>, or even List<unsigned char> (if you're
programming in .NET 2.0). List has an overhead to array, but if used
correctly, it's almost negligible. It's like the difference between
char* and std::string.
I use Bitmap objects as sources of original images, and a destinations
after processing. I usually access them through either:
- bmp->GetPixel(x, y).GetBrightnes() or
- bmp->SetBixel(x, y, Color)
Both functions are called within 'for' loops mentioned in my first
post. Will they work faster with cli::array?

I'm pretty sure that grabbing a pointer to the raw image data would be
many times faster than GetPixel. I don't know how GetPixel works, but it
must do at least a multiplication and an addition (usually TopLeftPtr +
x - y * BytesPerLine, assuming the image is upside down). This is many
times slower than incrementing a pointer.

Even if you use array<unsigned char>, using the array syntax happens to
be much slower for me than interior_ptr<unsigned char>. What I do is the
following:

array<unsigned char> buffer = gcnew array<unsigned char>(width * height);
interior_ptr<unsigned short> ptr = &buffer[0];
while(/*...*/)
{
*ptr++ = /*...*/;
}

This trick alone made my code about twice as fast than using array
syntax, like buffer[offset++].

I'm not familiar with the Bitmap class, but I know it has a way to lock
its pixels and get a byte pointer to its raw underlying data. Here's an
example:

http://vbforums.com/showthread.php?t=260903

I think the C# byte* is the same as the C++/CLI interior_ptr<unsigned char>.

I recommend that you research Bitmap::LockBits and BitmapData a bit:
http://msdn2.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx

Note that if you do this, you must deal with the following:
- windows bitmaps are upside down
- the raw data depends on the bits per pixel value. There are 1, 4, 8,
16, 24, 32-bit images. You must decode the color data from the raw bits
on your own (which is very easy with 8 and 24-bit images, a bit tricky
with 16-bit images, but I refuse to work directly with 16-bit bitmap)
- each line begins on a DWORD boundary, so usually there are some
padding bytes after the last pixel in each line. Here's how to calculate
the number of bytes per line:
BytesPerLine = (Width * BitsPerPixel + 31) / 32 * 4 * Height;

Since images are upside down, the last line is in fact the first, and
instead of incrementing the line pointer by BytesPerLine, you have to
decrement it.

Note that I have very little .NET experience compared to unmanaged code.
99.99% of my code is native C++. You should really do your own
benchmarking with your own algorithm, but you should get much better
result with Bitmap::LockBits than with GetPixel.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top