【问题标题】:Visual select image part from image?从图像中视觉选择图像部分?
【发布时间】:2014-08-30 22:24:17
【问题描述】:

您好!

我有特殊图片:

使用哪种算法,我可以从该图像中选择图像部分并直观地查看我当前选择的内容? 每个图像部分通过特殊颜色(例如紫红色)与其他部分分隔 1 个像素。

补充:

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

【问题讨论】:

  • AFAIK,没有从图像中存储或读取子图像的标准,但这是大多数皮肤库所做的任务之一。因此,您可以阅读其中一个的源代码(如 Vcl 样式)并检查其工作原理。基本思想是存储每个图像的边界,然后将该图像映射到一个对象
  • -1 子图像(或其中任何一个)可能有任何形式。在你得到答案后添加严格的约束并不酷

标签: image delphi delphi-xe6


【解决方案1】:

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

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

这里是代码:

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.

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 任何岛屿都可以有任何形式。好主意,我会尝试使用 rigions
    • 问题表示矩形岛。无论如何,扩展到任意形状的岛屿都不是太难。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 1970-01-01
    相关资源
    最近更新 更多