Reputation: 37
I am trying to convert an RGB image to a grayscale image. The RGB image is 160*120*4, while my grayscale is 160*120*1. However, it just give me plain black instead of what I want and takes a very long time.
This is what I wrote, please advise. Thanks
int i, j, sum;
Image = new unsigned char [ 160 * 120 * 1 ];
for( int j = 0; j < 120; j++ ) {
for( int i = 0; i < 160; i++ ) {
sum=0;
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
*Image = sum/4;
PaintBox2->Canvas->Pixels[ i ][ j ] = *Image;
Image++;
}
}
Upvotes: 0
Views: 722
Reputation: 51903
The VCL canvas use TColor
to hold color. However internal format is defined by the PixelFormat
format of the Canvas
... The usual is pf32bit
or pf24bit
... The TColor
is always 32bit however regardless of the image PixelFormat
as a lot of conversions happens along the way (that is why it is so slow) but beware different PixelFormat
settings can reverse the RGB order making also predefined colors like clYellow,...
reversed too (rendering as clAqua,...
) !!!
For example if you use VCLs
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->PixelFormat=pf32bit;
then each pixel is a 32bit value. You can do this safely for bitmaps but I am not sure about Form/PaintBox Canvas as they are tied to window I suspect your PixelFormat
is similar (most likely 24bit) so you need to change your code slightly:
int x,y,xs=160,ys=120;
DWORD rgb,bw;
Image = new unsigned char [ xs*ys*1 ];
for(y=0;y<ys;y++)
for(x=0;x<xs;x++)
{
rgb = PaintBox1->Canvas->Pixels[x][y];
bw = ( rgb &255) // sum r,g,b
+((rgb>> 8)&255)
+((rgb>>16)&255);
bw/=3; // normalize
*Image = bw; // write to your array
Image++;
bw*=0x00010101; // convert intensity to grayscale RGB color (you can shift and or instead)
PaintBox2->Canvas->Pixels[x][y]=TColor(bw); // write back
} Image-=xs*ys;
The Pixels
property is sloooow I would use VCL bitmap instead with use of its ScanLine
property that is ~1000 and more times faster and also have load/save functions implemented so you can use bmp files easily. For more info see:
Also there are more ways to convert RGB to intensity see:
Upvotes: 0
Reputation: 19347
There's several problem in the code that I can see immediately. The most striking comes from within the innermost for loop:
sum=0;
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ];
*Image = sum/4;
Here, you have simply added the same value to sum
four times over, and then divided by four. This makes these six lines equivalent to
*Image = PaintBox1->Canvas->Pixels[ i ][ j ];
Clearly, you actually wanted to average each channel. If your RGB image were implemented as a three-dimensional array, this would probably look something like:
sum = 0;
sum += PaintBox1->Canvas->Pixels[ i ][ j ][ 0 ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ][ 1 ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ][ 2 ];
sum += PaintBox1->Canvas->Pixels[ i ][ j ][ 3 ];
*Image = sum/4;
However, from your code example, it looks like your RGB image is actually implemented as a two-dimensional array of (un)signed integers. In that case, the following code should suffice (provided integers are four bytes on your machine):
sum = 0;
unsigned int pixel = PaintBox1->Canvas->Pixels[ i ][ j ];
for(int k = 0; k < 4; ++k)
{
sum += pixel & 0xFF;
pixel >>= 1;
}
*Image = sum/4;
The other major problem I see is that you do not keep a pointer to the beginning of your grayscale array. You initialize it as
Image = new unsigned char[ 160 * 120 * 1 ];
which is good. But then each time through the loop, you've written
Image++;
Rather, you should keep a pointer to the beginning of the array, and have a temporary pointer which acts as an iterator:
// before the for loops
Image = new unsigned char[ 160 * 120 * 1 ];
unsigned char * temp = Image;
// at the end of the inner for loop:
temp++;
So you only move around the temp
pointer, while Image
stays fixed.
Upvotes: 1