开发者

Delphi> Vertical scanline? (Get columns instead of rows?)

开发者 https://www.devze.com 2023-01-23 06:51 出处:网络
I\'m trying to write an 开发者_Python百科algorithm that will detect the space between \'RF\' and \'WOOLF in the image below.

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);
0

精彩评论

暂无评论...
验证码 换一张
取 消