Delphi> Vertical scanline? (Get columns instead of rows?)
I'm trying to write an 开发者_Python百科algorithm that will detect the space between 'RF' and 'WOOLF in the image below. I need something like Scanline for COLUMNS not rows. My algorithm will be scanning each column for the presence of black pixels, if it finds any it will store '1', else it will store '0', so for example the image above might be: 000001111111111111111111100000000000000000000000011111111111111111111111111111111111111111111111111 so I will know that the space starts on pixel 30.
Why can't you just access the pixels through Scanline?
for i := 0 to (column count)-1 do begin //enumerate columns
black_pixels:=false
for j := 0 to (row count)-1 do //enumerate scanlines
if PByte(integer(Scanline[j]) + i)^=0 then begin
//black pixels in column i
black_pixels:=true;
break;
end;
end;
(just an example, I don't remember the specifics of using scanline)
Even if you're concerned with locality, you can just setup an array of size (column count), and update it while scanning through j, i:
for j := 0 to (row count)-1 do
for i := 0 to (column count)-1 do
if PByte(integer(Scanline[j]) + i)^=0 then
blackpixels[i] := true;
For 1-bit images the data is stored in scanline by bits, though (see this document). To access bit k of BYTE_VALUE (counting from 0), use:
((BYTE_VALUE shr k) and 1)
As requested, additional explanations on how this works.
"Shl" and "shr" are "shift left" and "shift right" operations. What they do is shift the bits inside of the byte to the left (highest bit side) and to the right (lowest bit side). For instance:
01101101 shr 0 = 01101101
01101101 shr 1 = 00110110
01101101 shr 2 = 00011011
01101101 shr 3 = 00001101
In the same way
01101101 shl 0 = 01101101
01101101 shl 1 = 11011010
01101101 shl 2 = 10110100
01101101 shl 3 = 01101000
Binary AND ("C := A and B") is an operation which for every bit, takes it's values from A and from B and sets it's value in C to 1 only when A and B have 1's at the same place.
For instance:
01101101 and
00000000 =
00000000 (B has no 1's at all)
01101101 and
11111111 =
01101101 (B has all 1's, so every 1 from A is transferred to C)
01101101 and
11001100 =
01001100
Therefore what ((BYTE_VALUE shr (i-1)) and 1) does is:
First, shifts BYTE_VALUE (i-1) bits to the right, thus making it's i-th bit the rightmost. But the new BYTE_VALUE can still have unrelated bits to the left of it.
Then, zeroes out all the bits except for the new rightmost.
For instance, if we want to know the 5th rightmost bit of 01101101, we shr it by 4:
01101101 shr 4 = 00000110
But although the rightmost bit is zero, the whole value is still not zero. We AND it with 1 == 00000001:
00000110 and 00000001 = 00000000 = 0
Back to the topic. When your columns are packed by bits, this is how you enumerate them:
setLength(blackpixels, ColumnCount);
for i := 0 to ColumnCount - 1 do
blackpixels[i] := false;
for j := 0 to RowCount-1 do
for i := 0 to ColumnCount div 8-1 do begin
byte_value := PByte(integer(Bitmap.Scanline[j]) + i)^; //read the byte which contains 8 columns
for k := 0 to 7 do
if ((byte_value shr (7-k)) and 1)=0 then
blackpixels[i*8+k] := true;
end;
Here's the working example, just in case:
//Assuming img is declared as img: TImage;
//mm is declared as mm: TMemo;
var blackpixels: array of boolean;
i, j, k: integer;
byte_value: byte;
RowCount, ColumnCount: integer;
Bitmap: TBitmap;
s: string;
begin
RowCount := img.Picture.Bitmap.Height;
ColumnCount := img.Picture.Bitmap.Width;
Bitmap := img.Picture.Bitmap;
setLength(blackpixels, ColumnCount);
for i := 0 to ColumnCount - 1 do
blackpixels[i] := false;
if Bitmap.PixelFormat=pf1Bit then begin
for j := 0 to RowCount-1 do
for i := 0 to ColumnCount div 8-1 do begin
byte_value := PByte(integer(Bitmap.Scanline[j]) + i)^; //read the byte which contains 8 columns
for k := 0 to 7 do
if ((byte_value shr (7-k)) and 1)=0 then //(7-k) because pixels seems to be stored inside of the byte "left-to-right" (highest to lowest)
blackpixels[i*8+k] := true;
end;
end else
raise Exception.Create('Invalid pixel format');
//Print array
if ColumnCount > 0 then
s := BoolToStr(blackpixels[0], false)
else s := '';
for i := 1 to ColumnCount - 1 do
s := s + ', ' + BoolToStr(blackpixels[i], false);
s := '(' + s + ')';
mm.Lines.Add(s);
精彩评论