从图像中选择图像部分?

时间:2014-08-30 22:24:17

标签: image delphi delphi-xe6

问候!

我有特殊形象:
enter image description here
在哪个alghoritm我可以从这个图像中选择图像部分&直观地看到我选择的是什么? 每个图像部分通过特殊颜色与其他图像部分隔开1个像素(例如,紫红色)。

加入:

子图像(或其中任何一种)可以具有任何形式。

2 个答案:

答案 0 :(得分:3)

以下是在图像中查找子图像的示例。

此示例能够找到具有凸形(矩形,三角形,圆形等)的任何子图像。但它在凹形上无法正常工作。对于那些你需要修改算法的人来说,一旦你找到第一个像素,你就可以用与flod fill类似的算法扫描所有附近的人。

以下是代码:

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls;

type
  TSubImage = record
    LeftBound: Integer;
    RightBound: Integer;
    TopBound: Integer;
    BottomBound: Integer;
  end;

  ASubImages = Array of TSubImage;

  TForm2 = class(TForm)
    Button1: TButton;
    SourceImage: TImage;
    ListView1: TListView;
    SelectionImage: TImage;
    procedure Button1Click(Sender: TObject);
    procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure SelectionImageMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;
  SubImages: ASubImages;

implementation

{$R *.dfm}

procedure FindSubimages(Bitmap: TBitmap; var Subimages: ASubImages);
var X,Y,I: Integer;
    //2D array we use to store to which image does which pixel belong
    SubImagesMap: Array of Array of Integer;
begin
  //Set the map dimension to the same dimension of TBitmap we scan
  SetLength(SubImagesMap,Bitmap.Width+1,Bitmap.Height+1);
  for Y := 0 to Bitmap.Height-1 do
  begin
    for X := 0 to Bitmap.Width-1 do
    begin
      //Check to see if current pixel color is not of background color.
      if Bitmap.Canvas.Pixels[X,Y] <> clFuchsia then
      begin
        //Check if we already moved rightward (current pixel X postion > 0)
        if X > 0 then
        begin
          //Check if pixel to the left has already been assigned to a subimage number
          //and assign current pixel to the same subimage number since they are adjenct
          if SubImagesMap[X-1,Y] <> 0 then
          begin
            SubImagesMap[X,Y] := SubImagesMap[X-1,Y];

            //Here we are checking to see if current pixel is placed outside of subimage
            //bonds and adjust them acordingly
            //Check to se if pixel X position is leftwards to subimages left bound
            if Subimages[SubImagesMap[X,Y]-1].LeftBound > X then
              //Move subimage left bound to match pixel X position
              Subimages[SubImagesMap[X,Y]-1].LeftBound := X;
            //Check to se if pixel X position is rightwards to subimages right bound
            if Subimages[SubImagesMap[X,Y]-1].RightBound < X then
              //Move subimage right bound to match pixel X position
              Subimages[SubImagesMap[X,Y]-1].RightBound := X;
            //Check to se if pixel Y position is upwards to subimages top bound
            if Subimages[SubImagesMap[X,Y]-1].TopBound > Y then
              //Move subimage top bound to match pixel Y position
              Subimages[SubImagesMap[X,Y]-1].TopBound := Y;
            //Check to se if pixel Y position is downwards to subimages bottom bound
            if Subimages[SubImagesMap[X,Y]-1].BottomBound < Y then
              //Move subimage bottom bound to match pixel Y position
              Subimages[SubImagesMap[X,Y]-1].BottomBound := Y;
          end;
        end;
        //Check if we already moved downward (current pixel Y position > 0)
        if Y > 0 then
        begin
          //Check if pixel above has already been assigned to a subimage number
          //and assign current pixel to the same subimage number since they are adjenct
          if SubImagesMap[X,Y-1] <> 0 then
          begin
            SubImagesMap[X,Y] := SubImagesMap[X,Y-1];

            //Here we are checking to see if current pixel is placed outside of subimage
            //bonds and adjust them acordingly
            //Check to se if pixel X position is leftwards to subimages left bound
            if Subimages[SubImagesMap[X,Y]-1].LeftBound > X then
              //Move subimage left bound to match pixel X position
              Subimages[SubImagesMap[X,Y]-1].LeftBound := X;
            //Check to se if pixel X position is rightwards to subimages right bound
            if Subimages[SubImagesMap[X,Y]-1].RightBound < X then
              //Move subimage right bound to match pixel X position
              Subimages[SubImagesMap[X,Y]-1].RightBound := X;
            //Check to se if pixel Y position is upwards to subimages top bound
            if Subimages[SubImagesMap[X,Y]-1].TopBound > Y then
              //Move subimage top bound to match pixel Y position
              Subimages[SubImagesMap[X,Y]-1].TopBound := Y;
            //Check to se if pixel Y position is downwards to subimages bottom bound
            if Subimages[SubImagesMap[X,Y]-1].BottomBound < Y then
              //Move subimage bottom bound to match pixel Y position
              Subimages[SubImagesMap[X,Y]-1].BottomBound := Y;
          end;
        end;
        //Check to see if current pixel has already been asigned a sibimage number
        //I not we create a new subimage entry and assign its number to current pixel
        if SubImagesMap[X,Y] = 0 then
        begin
          //Increase the size of dynamic array storing subimage records
          SetLength(SubImages,Length(SubImages)+1);

          //Assing current pixel the number of newly created subimage
          SubImagesMap[X,Y] := Length(SubImages);

          //Set subimage initial bounds which are coordinates of one pixel
          //since we created new subimage for this pixel
          SubImages[SubImagesMap[X,Y]-1].LeftBound := X;
          SubImages[SubImagesMap[X,Y]-1].RightBound := X;
          SubImages[SubImagesMap[X,Y]-1].TopBound := Y;
          SubImages[SubImagesMap[X,Y]-1].BottomBound := Y;
        end;
      end;
    end;
  end;
  //Reduce the size of SubImageMap array to free its memory
  //Since SubImageMap is local array this is optional
  SetLength(SubImagesMap,0,0);
end;

procedure TForm2.Button1Click(Sender: TObject);
var I: Integer;
    ListItem: TListItem;
    ListColumn: TListColumn;
begin
  //Our procedure for finding subimages. It accepts two parameters
  //First parameter is reference to TBitmap object containing original image
  //Second is reference to variable in which subimage bouns will be stored to
  FindSubimages(SourceImage.Picture.Bitmap, Subimages);
  //Lets show our results in more readable format
  //First we change the ListView style to vsReport so we can show our results
  //in multiple columns
  ListView1.ViewStyle := vsReport;
  //Then we add necessary columns
  ListColumn := ListView1.Columns.Add;
  ListColumn.Caption := 'Subimage number';
  ListColumn.Width := 100;
  ListColumn := ListView1.Columns.Add;
  ListColumn.Caption := 'Left Bound';
  ListColumn.Width := 80;
  ListColumn := ListView1.Columns.Add;
  ListColumn.Caption := 'Right Bound';
  ListColumn.Width := 80;
  ListColumn := ListView1.Columns.Add;
  ListColumn.Caption := 'Top Bound';
  ListColumn.Width := 80;
  ListColumn := ListView1.Columns.Add;
  ListColumn.Caption := 'Bottom Bound';
  ListColumn.Width := 80;
  //Iterate through all subimages and add data to ListView
  for I := 0 to Length(Subimages)-1 do
  begin
    //Ad new item to list view
    ListItem := ListView1.Items.Add;
    //Use the reference of newly added item to set caption which will be the text
    //in first column
    ListItem.Caption := IntToStr(I+1);
    //Add aditional subitems. Each of this subitems is shown in its own column
    //NOTE: Make sure to have enough columns to show all subitems
    //If you wanna field in certain column to be empty just pass an empty string ''
    ListItem.SubItems.Add(IntToStr(SubImages[I].LeftBound));
    ListItem.SubItems.Add(IntToStr(SubImages[I].RightBound));
    ListItem.SubItems.Add(IntToStr(SubImages[I].TopBound));
    ListItem.SubItems.Add(IntToStr(SubImages[I].BottomBound));
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  //Make selection image 2 pixels larger so we will never draw right to the edge
  //and therefore can easily use its defult transparency
  SelectionImage.Width := SourceImage.Width+2;
  SelectionImage.Height := SourceImage.Height+2;
  //Shift selector image position one to the left and one up to be centered above
  //SourceIMage.
  SelectionImage.Left := SourceImage.Left-1;
  SelectionImage.Top := SourceImage.Top-1;
end;

procedure TForm2.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
var Rect: TRect;
begin
  //Use SubImage bounds to form rectagnle we will use for our selection
  Rect.Left := SubImages[Item.Index].LeftBound+1;
  Rect.Right := SubImages[Item.Index].RightBound+2;
  Rect.Top := SubImages[Item.Index].TopBound+1;
  Rect.Bottom := SubImages[Item.Index].BottomBound+2;
  //Clear previous selection
  SelectionImage.Canvas.Brush.Color := clFuchsia;
  SelectionImage.Canvas.FillRect(SelectionImage.Canvas.ClipRect);
  //Draw new selection rectangle
  SelectionImage.Canvas.Brush.Color := clLime;
  SelectionImage.Canvas.FrameRect(Rect);
end;

procedure TForm2.SelectionImageMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Form2.Caption := IntToStr(X);
end;

end.

答案 1 :(得分:2)

在我看来,你有一个长方形的紫红色的海洋,其中包含许多非紫红色的岛屿。你想要识别那些岛屿。一个简单的算法如下:

  1. 从图像的一角开始,比如左上角。
  2. 逐行逐像素地处理图像。因此,从左到右处理第一行,然后处理下一行,依此类推。
  3. 当您找到非紫红色像素时,它就是岛屿的左上角。现在找到岛上的其他地方。继续沿着岛的顶行,直到你到达行的末端,或找到一个紫红色像素。现在你知道了宽度。通过向下移动其中一列来查找高度,直到到达底行,或找到一个紫红色像素。
  4. 现在您知道该岛的左上角坐标以及宽度和高度。使用该信息捕获您需要的内容,并用紫红色替换源图像中的岛状矩形,以指示这些像素都已处理完毕。
  5. 继续从刚捕获的岛屿右上角寻找下一个岛屿。
  6. 当您到达图像的右下角时,您已识别出所有岛屿。