[Lazarus] Object detection

Leonardo M. Ramé l.rame at griensu.com
Mon Nov 7 01:26:38 CET 2011


On 2011-11-02 15:26:11 -0300, Leonardo M. Ramé wrote:
> Hi, I'm looking for a library/routine for object detection in bitmaps.
> 
> I need to process a black and white image (1bit) in low resolution and
> detect all white objects over a black background, as the attached image
> shows.
> 
> Apart from OpenCV, do you know anything similar but simplier?.
> Preferably in Pascal?.
> 
> Thanks in advance,
> -- 
> Leonardo M. Ramé
> http://leonardorame.blogspot.com

Well, it turned out to be a wrong decision to choose OpenCV. To make a
long story short, it worked on Linux but failed on Windows.

Here's a post I made on StackOverflow hoping for help:

http://stackoverflow.com/questions/8015084/opencv-pointer-to-bitmap-processing

Also asked in OpenCV chat room, on FreeNode, but nobody faced the same
issue as me.

So, I've started to google for a Blob Detection library and found this:
http://www.labbookpages.co.uk/software/imgProc/blobDetection.html

Took a look at the code, and it wasn't too complicated to migrate to
ObjectPascal, so, I did it.

I attached the resulting conversion for all those looking for it.

P.S.: The class uses TBGRABitmap, but it can be easily replaced by
TBitmap or similar.

-- 
Leonardo M. Ramé
http://leonardorame.blogspot.com
-------------- next part --------------
unit blobdetection;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils,
  BGRABitmap,
  BGRABitmapTypes;

type

  { TBlob }
  TBlob = class
  private
    FxMin: Integer;
    FxMax: Integer;
    FyMin: Integer;
    FyMax: Integer;
    FMass: Integer;
  public
    constructor create( xMin, xMax, yMin, yMax, Mass: Integer);
    property xMin: Integer read FxMin;
    property yMin: Integer read FyMin;
    property xMax: Integer read FxMax;
    property yMax: Integer read FyMax;
    property Mass: Integer read FMass;
  end;

  { TBlobBoundaries }
  TBlobBoundaries = class
  private
    FBmp: TBGRABitmap;
    FX1: Integer;
    FX2: Integer;
    FY1: Integer;
    FY2: Integer;
    FBlobList: TBlobList;
    procedure DoCalcs;
    procedure GrayToBinary;
  public
    destructor Destroy; override;
    class procedure Execute(ABmp: TBGRABitmap; var AX1, AY1, AX2, AY2: Integer);
  end;


implementation

const
  cInitialColor = 0;

class procedure TBlobBoundaries.Execute(ABmp: TBGRABitmap; var AX1, AY1, AX2, AY2: Integer);
var
  lBlobBoundaries: TBlobBoundaries;
  lBlob: TBlob;
  I: Integer;
begin
  lBlobBoundaries := TBlobBoundaries.Create;
  try
    with lBlobBoundaries do
    begin
      FBlobList := TObjectList.create(True);
      FBmp := ABmp.FilterPixelate(5, False) as TBGRABitmap;
      GrayToBinary;
      FX1:= AX1;
      FX2:= AX2;
      FY1:= AY1;
      FY2:= AY2;
      DoCalcs;
      for I := 0 to FBlobList.Count - 1 do
      begin
        if I = 0 then
        begin
          lBlob := TBlob(FBlobList[0]);
        end
        else
        if TBlob(FBlobList[I]).Mass > lBlob.Mass then
          lBlob := TBlob(FBlobList[I]);
      end;
      AX1:= lBlob.xMin;
      AX2:= lBlob.xMax;
      AY1:= lBlob.yMin;
      AY2:= lBlob.yMax;
      ABmp.Assign(FBmp);
    end;
  finally
    lBlobBoundaries.Free;
  end;
end;

{ TBlob }

constructor TBlob.create(xMin, xMax, yMin, yMax, Mass: Integer);
begin
  FxMin := xMin;
  FxMax := xMax;
  FyMin := yMin;
  FyMax := yMax;
  FMass := Mass;
end;

procedure TBlobBoundaries.DoCalcs;
var
  X, Y: Integer;
  p: PBGRAPixel;
  tableSize: Integer;
  srcPtr: Integer;
  lLabel: Integer;
  aLabel: Integer;
  bLabel: Integer;
  cLabel: Integer;
  dLabel: Integer;
  aPtr: Integer;
  bPtr: Integer;
  cPtr: Integer;
  dPtr: Integer;
  lMin: Integer;
  I: Integer;
  L: Integer;
  LabelBuffer: array of Integer;
  labelTable: array of Integer;
  xMinTable: array of Integer;
  xMaxTable: array of Integer;
  yMinTable: array of Integer;
  yMaxTable: array of Integer;
  massTable: array of Integer;
  lBlob: TBlob;

begin
  SetLength(LabelBuffer, FBmp.Width * FBmp.Height);
  tableSize := FBmp.Width * FBmp.Height div 4;

  SetLength(labelTable, tableSize);
  SetLength(xMinTable, tableSize);
  SetLength(xMaxTable, tableSize);
  SetLength(yMinTable, tableSize);
  SetLength(yMaxTable, tableSize);
  SetLength(massTable, tableSize);

  srcPtr := 0;
  aPtr := -FBmp.Width - 1;
  bPtr := -FBmp.Width;
  cPtr := -FBmp.Width + 1;
  dPtr := -1;
  lLabel := 1;

  for Y := 0 to FBmp.Height - 1 do
  begin
    p := FBmp.ScanLine[Y];
    for X := 0 to FBmp.Width - 1 do
    begin
      LabelBuffer[srcPtr] := 0;
      if p^.red = 255 then
      begin
        // Find label for neighbours (0 if out of range)
        aLabel := 0;
        bLabel := 0;
        cLabel := 0;
        dLabel := 0;

        if (x > 0) and (y > 0) then
          aLabel := labelTable[LabelBuffer[aPtr]];

        if (y > 0) then
          bLabel := labelTable[LabelBuffer[bPtr]];

        if (x < FBmp.Width - 1) and (y > 0) then
          cLabel := labelTable[LabelBuffer[cPtr]];

        if (x > 0) then
          dLabel := labelTable[LabelBuffer[dPtr]];

        // Look for label with least value
        lMin := MaxInt;
        if (aLabel <> 0) and (aLabel < lmin) then
          lMin := aLabel;

        if (bLabel <> 0) and (bLabel < lmin) then
          lMin := bLabel;

        if (cLabel <> 0) and (cLabel < lmin) then
          lMin := cLabel;

        if (dLabel <> 0) and (dLabel < lmin) then
          lMin := dLabel;

        // If no neighbours in foreground
        if (lMin = MaxInt) then
        begin
          labelBuffer[srcPtr] := lLabel;
          labelTable[lLabel] := lLabel;

          // Initialise min/max x,y for label
          yMinTable[lLabel] := y;
          yMaxTable[lLabel] := y;
          xMinTable[lLabel] := x;
          xMaxTable[lLabel] := x;
          massTable[lLabel] := 1;

          inc(lLabel);
        end
        else
        // Neighbour found
        begin
          // Label pixel with lowest label from neighbours
          labelBuffer[srcPtr] := lMin;

          // Update min/max x,y for label
          yMaxTable[lMin] := y;
          inc(massTable[lMin]);

          if (x < xMinTable[lMin]) then
                   xMinTable[lMin] := x;

          if (x > xMaxTable[lMin]) then
                   xMaxTable[lMin] := x;

          if (aLabel <> 0) then
                   labelTable[aLabel] := lMin;

          if (bLabel <> 0) then
                   labelTable[bLabel] := lMin;

          if (cLabel <> 0) then
                   labelTable[cLabel] := lMin;

          if (dLabel <> 0) then
                   labelTable[dLabel] := lMin;
        end;
      end;
      inc(srcPtr);
      inc(aPtr);
      inc(bPtr);
      inc(cPtr);
      inc(dPtr);
      inc(p);
    end;
  end;

  for I := lLabel - 1 downto 0 do
  begin
    if (labelTable[i] <> i) then
    begin
      if (xMaxTable[i] > xMaxTable[labelTable[i]]) then
         xMaxTable[labelTable[i]] := xMaxTable[i];

      if (xMinTable[i] < xMinTable[labelTable[i]]) then
         xMinTable[labelTable[i]] := xMinTable[i];

      if (yMaxTable[i] > yMaxTable[labelTable[i]]) then
         yMaxTable[labelTable[i]] := yMaxTable[i];

      if (yMinTable[i] < yMinTable[labelTable[i]]) then
         yMinTable[labelTable[i]] := yMinTable[i];

      massTable[labelTable[i]] := massTable[labelTable[i]] + massTable[i];

      L := I;
      while (l <> labelTable[l]) do
        L := labelTable[l];

      labelTable[i] := L;
    end
    else
    begin
      lBlob := TBlob.create(xMinTable[i], xMaxTable[i], yMinTable[i], yMaxTable[i], massTable[i]);
      FBlobList.Add(lBlob);
    end;
  end;
end;

procedure TBlobBoundaries.GrayToBinary;
var
  I: Integer;
  A: Integer;
  n: Integer;
  psrc: PBGRAPixel;
  newintensity: single;
begin
  psrc := FBmp.Data;
  for n := 0 to FBmp.NbPixels-1 do
  begin
    if psrc^.red > 25 then
    begin
      psrc^.red := 255;
      psrc^.green := psrc^.red;
      psrc^.blue := psrc^.red;
    end
    else
    begin
      psrc^.red := 0;
      psrc^.green := psrc^.red;
      psrc^.blue := psrc^.red;
    end;

    inc(psrc);
  end;
end;

destructor TBlobBoundaries.Destroy;
begin
  FBlobList.Free;
  FBmp.Free;
  inherited Destroy;
end;

end.



More information about the Lazarus mailing list