Groups | Blog | Home
all groups > dotnet drawing api > january 2006 >

dotnet drawing api : 1bpp to 8bpp grayscale smoothing (performance)


Anders Spliid Hansen
1/24/2006 12:00:00 AM
I've got a bunch of scanned microfiches in 1bpp format, all rather big,
about 6000 x 4000 pixels. Some of the fiches are in handwriting, and this
is why I want to make an algorithm that converts and smooths them into 8bpp
grayscale. After reading Bob Powells excellent "Understanding LockBits" I
was able to come up with this (initialization details omitted):

Bitmap bmOrg; // Format1bppIndexed image
Bitmap bmGray = new Bitmap(bmOrg.Width, bmOrg.Height,
PixelFormat.Format8bppIndexed);
// convert 1bpp to 8bpp, palette of bmGray is set to a grayscale one.
unsafe
{
for (y = 0; y < bmGray.Height; y++)
{
byte* pixelGray = (byte*)(void*)Scan0_gray + (y * strideGray);
byte* pixelOrg = (byte*)(void*)Scan0_org + (y * strideOrg);

for (x = 0; x < bmGray.Width; x++)
{
//ANDs between 128, 64, 32, 16, 8, 4, 2, 1 and the original image byte,
//to obtain the individual bits.
if ((1 << (7 - (x & 7)) & pixelOrg[x / 8]) != 0)
pixelGray[x] = 255;
else
pixelGray[x] = 0;
}
}
}
// smoothing algorithm. Idea is to calculate a mean value for a 2x2
// window, and set the four pixels to this mean value. In this way every
// pixel except those in the first row and the first column, will have it
// value changed twice.
unsafe
{
for (y = 0; y < bmGray.Height - 1; y++)
{
byte* pixel = (byte*)(void*)Scan0_gray + (y * strideGray);
for (x = 0; x < bmGray.Width - 1; x++)
{
mean = (pixel[x] + pixel[x + 1] + pixel[x + skip] + pixel[x + 1 +
skip]) / 4;
pixel[x] = pixel[x + 1] = pixel[x + skip] = pixel[x + 1 + skip] =
(byte)mean;
}
}
}

It works as intended, OK, but I'm not happy with the performance. On an old
Athlon 1800XP bitbucket the algorithm's running time on a 6000x4000 image
is about 8 seconds.

Any ideas how to tweak this mother?


Thanks in advance,
Lloyd Dupont
1/25/2006 12:00:00 AM
oh yeah...
I have a very quick fix.
chache bitmap.Width and bitmap.Height and use the cached value.
as follow
int w = img.Width;
int h = img.Height;

from now on, never again access the Width and Height property and use the w
& h variable.
try it on, you'll be surprise by the results.

In such cases you thanks your perfmance tool for finding this out!

--
Regards,
Lloyd Dupont

NovaMind development team
NovaMind Software
Mind Mapping Software
<www.nova-mind.com>
[quoted text, click to view]

Anders Spliid Hansen
1/25/2006 7:38:47 PM
Whoah, that did the trick. Running time is now down to 0.3 seconds,
compared to 9.6 seconds before. Thanks a lot for the great advice!!

I wonder why the compiler don't figure out by it self that the for-loop
bound is a constant?

Regards,
Anders Spliid Hansen



"Lloyd Dupont" <net.galador@ld> wrote in
news:#EbyX2TIGHA.2064@TK2MSFTNGP09.phx.gbl:

[quoted text, click to view]
Lloyd Dupont
1/26/2006 12:00:00 AM
[quoted text, click to view]
;-)

[quoted text, click to view]
well, how should it?
..Width is a property, that is, it comes from a function call...
worst than that, if you look at the code with Reflector, you sure begin =
to understand why it's so heavy duty:
=3D=3D=3D
public int get_Width()
{
int num1;
int num2 =3D SafeNativeMethods.Gdip.GdipGetImageWidth(new =
HandleRef(this, this.nativeImage), out num1);
if (num2 !=3D 0)
{
throw SafeNativeMethods.Gdip.StatusException(num2);
}
return num1;
}=20

=3D=3D=3D

[quoted text, click to view]
AddThis Social Bookmark Button