<<

NAME

Image::BoxFind - scan for rectangles inside of images

SYNOPSIS

   use Image::BoxFind;

   my $bf = Image::BoxFind->new({ image_file = "/tmp/menu.jpeg" });

   my $count = $bf->count_rectangles; # NOT YET IMPLEMENTED



   my ($image_width, $image_height) = $bf->dimensions();

   my $bg_color = $bf->background_color();

   TODO demo the other routines: "scan"s and so on.

DESCRIPTION

Image::BoxFind is an OOP module to pick out rectangular shapes from inside of an image.

The theory is that scanning for rectangles inside of images is useful for automating tests of graphical user interfaces.

Note: the "rectangles" of interest here are aligned with the x and y axes.

concepts

rectangle (or "box") data structure

For convenience, a rectangle is represented here as a list of the four corners, where each corner is a point (i.e. a list of the two x and y values).

The corners are listed in the following order:

   1                2
    ---------------
    |             |
    |             |
    |             |
    ---------------
   4                3

So, a "list of rectangles" is an aref of arefs of arefs.

To verify that one of these figures truly is a rectangle, we first check that the corners are (roughly) lined up horizontally and vertically:

   y1 == y2
   x2 == x3
   y3 == y4
   x1 == x4

(and if the corners were found with the "follow*" routines the edges should be simple straight horizontals and verticals).

point

By a "point", we mean two x and y values that specify a pixel in the image. An array reference.

Image::Magick color representation

Where possible, we work with color values in the native Image::Magick format: a comma-seperated string of decimals:

   rr,gg,bb,t

(The fourth entry is 'alpha' or 'transparency' or something like that: often it's just zero).

location cursor

This code maintains a "cursor" location inside of the image: cursor_x and cursor_y, though most routines accept explicit coordinates as arguments.

spot

The "cursor" points to a single pixel, but we often focus on a larger area near the cursor, a rectangular region called the spot. This is a rectangle as defined by two settable parameters "spotsize_x" and "spotsize_y" (though the spot may be truncated when near an image boundary: spot_bounds_truncated).

For the sake of simplicity: the cursor is the upper-left hand corner of the spot.

threshold

There are multiple threshold settings for determining whether a difference in color is significance. Different values are needed for different ways of measuring color difference (luminence, color distance, etc.).

fuzziness

Spatial "fuzziness": when two things are almost on top of each other, we'll consider them to be in the same place. This parameter controls the cut-off for significance in spatial difference.

pixpat or "pixel pattern"

The pixpat (short for "pixel pattern") summarizes the matrix of colors in the spot by averaging them in the direction of travel to get an array of colors. So, the number of colors in the pixpat array is the width of the spot in the transverse direction. The family of "follow_*" methods typically look for changes in the "pixpat" in an attempt at tracking the edge of a rectangle.

horizon

Rather than work with a fixed width and height spotsize, some methods here can use a "forward_horizon" and "transverse_horizon" to get an assymetric spotsize who's orientation flips depending on the direction the cursor is being scanned.

METHODS

initialization methods

new

Creates a new Image::BoxFind object, taking a hashref as an argument, with named fields identical to the names of the object attributes. Either the attribute image_file or imh is required.

Inputs:

An optional hashref, with named fields identical to the names of the object attributes. The attributes, in order of likely utility:

image_file

The name of the file you intend to scan. This is required, unless an Image::Magick image object is passed in instead (see imh attribute)

imh

The "image handle": an Image::Magick image object either created internally using the image_file attribute, or passed in as an argument. (( This is stupid. Require the fucking filename! ))

luminance_threshold

Cutoff for significance in color luminance differences. Defaults to 3500.

color_distance_threshold

Cutoff for significance in color distance differences. Defaults to 10.

pixpat_threshold

Threshold of ignorable change in the "pixel pattern". Used by has_pixpat_changed_past_threshold

ignore_subtle_pixpat_change

With this flag on and a smaller pixpat_threshold, *sudden* changes should be detected but a gradual drift in the appearence of a border (e.g. a shading effect) might be ignored.

The various 'follow_*' methods that look for changes in the "pixpat" by

can compare either (1) the current one to the original one at the start of the follow operation or (2) (the default) with "ignore_subtle_pixpat_change" set to a true value, then instead it will compare the current pixpat to the immediately previous one. Since a threshold of ignorable change is allowed (see "pixpat_threshold" above), this allows for a shifting standard with a smaller defined threshold.

Default: on.

edge_contrast_cutoff

Minimum difference between min and max color distance in the colors of a pixpat before it becomes at all plausible the pixpat represents an edge. Used by looks_like_edge.

direction

A direction code: 'x_plus', 'x_minus', 'y_plus', or 'y_minus'

forward_horizon

Size of the spotsize in the direction of travel of the cursor. (Only used by some methods). Overrides spotsize_x and spotsize_y settings.

transverse_horizon

Size of the spotsize in the transverse direction. (Only used by some methods). Overrides spotsize_x and spotsize_y settings.

spotsize_x

Width of the "spot", the region examined at the cursor. INTERNAL USE ONLY. ("horizon" values are swapped in by "set_direction").

spotsize_y

Height of the "spot", the region examined at the cursor. INTERNAL USE ONLY. ("horizon" values are swapped in by "set_direction").

fuzziness

A spatial fuzziness parameter. Default: 4.

refocus_factor

The routine center_on_edge looks beyond the boundaries of the spot in an attempt at repositioning the spot with any nearby edge moved toward it's center. The refocus_factor is the factor applied to the spot dimension, to determine how widely the center_on_edge method (and similar methods) will range in looking for the edge. Default: 4 ((TODO still? Make smaller?))

delta_colordist_threshold

Much like the various other thresholds: used by center_on_edge to skip recentering if there isn't very much color variation going on nearby. Default: 10

step_back

Some operations here back off slightly from an edge (to stay away from blurred corners and so on). This is a standard step size for this purpose. Default: 3.

Note: there's no association between step_back and the following two "step_*".

step_x

Horizontal step size for routines that raster across the entire image, ala roughly_raster_for_rectangular_regions.

step_y

Vertical step size for routines that raster across the entire image, ala roughly_raster_for_rectangular_regions.

beware

When you keep running into the wall despite your best efforts to stop short, you can deploy "beware" throughout your code to gain an extra margin of safety. I.e. this is a total hack.

Defaults to 10, unless I change it.

Empirically determined it needs to be at least 8 (for *some* settings, e.g. spotsize of 5?) or "roughly_raster" doesn't.

cursor_x

X-coordinate of an internally used "cursor", pointing at a pixel of the image.

cursor_y

Y-coordinate of an internally used "cursor", pointing at a pixel of the image.

image_height

Image_Height of the image (i.e. the maximum "y" value plus one)

image_width

Image_Width of the image (i.e. the maximum "x" value plus one)

background_color

The background color of the image, as determined by the background_color method.

minimum_height

Minimum allowed width for a rectangle. Default 12.

minimum_width

Minimum allowed height for a rectangle. Default 12.

color_diff

The name of the method used internally to determine if color has changed significantly. May be one of:

has_changed_luminence (default)
has_changed_distance
has_changed_peculiar (deprecated)
rectangle_finder

The name of a method used internally to find a rectangle somewhere in the image. Used by routines such as roughly_raster_for_rectangular_regions. The value may be one of:

look_down_boxfind
boxfind_downward_via_pixpat
boxfind_upward_via_pixpat
boxfind_downward_purely_via_pixpat
boxfind_downward_recenter
finder_of_rectangles

The name of a method used internally to find some rectangles (plural) somewhere in the image. A variant of the above rectangle_finder that holds open the possibility of finding more than one rectangle at once.

Used by routines such as smarter_sweep_for_squarish_shapes, but not it's predecessor: smart_sweep_for_squarish_shapes

The value may be one of:

boxfind_downward_recenter
change_detector

A further generalization of the notion behind the above color_diff. The name of the method to be used to detect a change. EXPERIMENTAL.

Used by detected_change, which is not yet in use (and probably never will be). TODO

previous_state

This may be used by the above change_detector, a way to keep the data that change_detector will compare the current state to. Note: may be anything, a scalar value or a ref to any data structure.

color

Color of annotations made to images. Typically 'red' or 'green'.

init

Method that initializes object attributes and then locks them down to prevent accidental creation of new ones.

Any class that inherits from this one should have an init of it's own that calls this init. Otherwise, it's an internally used routine that is not of much interest to client coders.

init_imh

Internally used routine. Initialize a new Image::Magick object using the file specified by the "image_file" field.

whole image transforms

apply_edge_detect

Applies the Image::Magick "Edge" image filter to the current image, using the value of the edge_detect attribute as a "radius" argument.

save_image_using_suffix

Saves the current image under a modified name using the given string as a "suffix" for a new file name, and placing the new file in a sub-directory named "output".

image info

Routines that get information about the entire image.

dimensions

Determine (and stash) width and height of the image.

Returns array of width and height values.

background_color

Determines the most common color in an image, and returns it in the native Image::Magick form, a comma-seperated string of decimals:

   rr,gg,bb,t

geometry

Utility routines to do geometric calculations

spot_bounds

Returns the coordinates of the upper-left and lower-right corners of the spot at the given x, y location, or at the spot at the cursor if the location is not specified.

Note: The spot is prevented from extending past the image boundaries, erroring out if asked to do so.

spot_bounds_truncated

Returns the coordinates of the upper-left and lower-right corners of the spot at the given x, y location, or at the spot at the cursor if the location is not specified.

Note: The spot is prevented from extending past the image boundaries, it is silently truncated to keep it from doing so.

Example usage:

  my ($x1, $y1, $x2, $y2) = $self->spot_bounds_truncated( $x0, $y0 );
main_bounding

Calculate the effective x and y boundaries to be used by a routines that move the "spot" though the entire image.

Example:

  my ($x_bound, $y_bound) = $self->main_bounding;
looks_rectangular

Given four points, tries to determine if they (roughly) define the corners of a rectangle, and if the rectangle is of a significant size.

The object attribute "fuzziness" is used to determine how close two locations need to be to be considered the same: the differences in the x and y components both need to be less than the fuzziness value.

The object attributes "minimum_width" and "minimum_height" determine how wide and tall a rectangle is required to be.

looks_rectangular_any_size

Given four points, tries to determine if they (roughly) define the corners of a rectangle.

This version makes no effort to throw away small fry. Rectangles of any size qualify.

color arithmetic

Utility routines to do color calculations

luminence

Given a list of the RR GG BB values for a color, calculates the luminence using the weighting factors defined in the object: weights. Note: luminence is an easily calculated value that approximates the subjective impression of brightness.

Example:

   $l = luminence( $rr, $gg, $bb );
has_changed

Compares the two given colors, and returns true if they're significantly different, and false otherwise.

This is a wrapper method that makes it eaisier to swap in different ways of calculating color differences. It defaults to has_changed_distance.

has_changed_luminence

Compares the given two colors, and returns true if they're luminence is significantly different, and false otherwise.

 Example usage:

  if(
    $self->has_changed_luminence( $color, $ref_color);
  ) { print "Color has changed significantly\n";
      last;
    }

A "color" is the color string (as used by Image::Magick).

As written, this routine checks if the difference in luminence exceeds the luminance_threshold. It returns the value of the luminance difference, or 0 (to indicate "false"), so this can be thought of as a "luminence difference" routine, which rounds down to 0 if below the luminance_threshold.

Note: as written, this ignores any changes in "alpha".

has_changed_distance

Compares the given two colors, and returns true if they're color distance is significantly different, and false otherwise.

Uses the color_distance_threshold settting to determine significance.

Note: as written, this ignores any changes in "alpha".

has_changed_peculiar

Compares the given two colors, and returns true if they're color distance is significantly (?) different, and false otherwise.

This is an older, poorly implemented version of a method intended to use color distance. (Note: in perl '**' is expotentiation, not '^'). It remains slightly possible that it does something useful (for the wrong reasons).

Still uses the color_distance_threshold settting to determine significance.

Note: as written, this ignores any changes in "alpha".

probes

A probe examines conditions in a particular location.

major_color

Returns the most common color of all the pixels in the spot. Note: this need not be the "majority", just the "plurality".

average_color

Determine the average color of the spot at the given x,y location, or at the current spot by default.

analyze_pixpat_for_edge

Given a pixpat, this looks through it to find the location of an "edge" in the image. Determines the offset of the edge inside the pixpat by finding location of the maximum delta colordistance value, and returns both offset and "max_delta_colordist".

Example usage:

  my ($offset, $max_delta_colordist) = $bf->analyze_pixpat_for_edge( $pixpat );

Note: this uses an empirically determined technique for picking the edge out of the field of colors: it finds the place where the change of the color-distance is maximized (loosely speaking, this is a maximum of the rate of change of color).

This appears to be slightly better than the more conventional approaches of looking for a steep color gradient (the maximum color-distance), or of looking for inflection points (zero-crossings in the rate of change of the color distance, taking it as an approximation of the second-derivative).

location adjustment

center_on_edge

Adjusts the location of the cursor to center the spot on any nearby edge.

Optionally takes a pair of x, y values, otherwise it uses the object cursor. Returns the new point, after setting the object cursor to the adjusted location.

Looks beyond the spot by temporarily expanding the size of the spot in the transverse direction, by multiplying the spotsize by the refocus_factor object attribute.

Also relies on the object-attribute: delta_colordist_threshold (a lower threshold would mean greater sensitivity to changes).

Uses analyze_pixpat_for_edge to do the work of picking out an edge from inside of a pixpat.

scans

A scan sweeps in a particular direction looking for some feature (e.g. some sort of change, e.g. a change in the average color of the spot, as in the "*_spotcolor" methods).

scan_down_spotcolor

Scans vertically, starting at the given x, y location, defaulting to the the cursor location -- until a significant change in the average color is detected.

Sets the cursor at new location.

Detects a change in the average color larger than the "threshold" setting.

scan_right_spotcolor

Scans horizontally -- starting at the cursor location -- until a significant change in the average color is detected.

Sets the cursor at new location.

Detects a change in the average color larger than the "threshold" setting.

scan_up_spotcolor

Scans vertically -- starting at the cursor location -- until a significant change in the average color is detected.

Sets the cursor at new location.

Detects a change in the average color larger than the "threshold" setting.

scan_left_spotcolor

Scans horizontally backwards (toward the left edge), starting at the cursor location, until a significant change in the average color is detected.

Sets the cursor at new location.

Detects a change in the average color larger than the "threshold" setting.

scan_for_edgey_pixpat

Takes a location as a pair of x/y values as arguments. If omitted uses the cursor_x and cursor_y object data.

Sweeps downward looking for something that looks like an edge, as defined by looks_like_edge, with determine_pixpat_forward.

Returns the y value if it finds an edge, or undef it it doesn't see one before the image boundary.

look_down_boxfind

Scan downward and attempt to find a rectangle. Looks for changes of the average spot color.

Begins searching at the given x, y location (but defaults to the cursor location).

If found, returns a data structure containing the four points at (or near) the corners of the rectangle.

Otherwise, returns undef.

Example:

      if ( my $corners = $self->look_down_boxfind( $x, $y) ) {
        push @raw_rectangles, $corners;
      }

Note: the edge of the image is never taken as the edge of a "rectangle", (we're interested in GUI applications, where the fashion is to have boxes and buttons offset from the window borders).

whole image analysis

roughly_raster_for_rectangular_regions

Scan the image for rectangles.

Returns a list of rectangles (see concepts section above).

bang_along_from_below_for_boxes {

Scan the image for rectangles.

Returns a list of rectangles (see concepts section above).

Like roughly_raster_for_rectangular_regions, but it works from the lower-right hand corner, stepping backwards through the image; which is presumably this is better for using the "rectangle_finder": boxfind_upward_via_pixpat.

smart_sweep_for_squarish_shapes

Scan the image for rectangles.

Returns an array reference, a list of rectangles (see concepts section above).

Like roughly_raster_for_rectangular_regions, but smarter.

Can not find nested rectangles, however.

peer_under_rect_for_rects

Given a rectangle, looks at the region below it to find any rectangles down there (it calls itself recursively to find rectangles below rectangles).

Returns list of rectangles found.

Note: first time, can be given a dummy "degenerate" rectangle zero pixels high, to trick it into scanning the whole image from the top.

Example usage:

  my $new_rects = $self->peer_under_rect_for_rects( $given_rect );

boxing

uniq_boxes

Uniquifies a list of sorted rectangles: by scanning through the list, comparing all pairs of them.

We use the is_dupe_box method to determine if they're roughly identical, and if so we keep only one of them.

Note: Instead of just choosing one member of a duplicate pair, it might be better to take a geometric average of the corners of the near duplicates. TODO

If the list is empty, is should also return an empty list without error, but if it looks like something else has been passed in, this should 'confess' (i.e. die with a stack trace).

sort_boxes

Given a list of rectangles, sorts them in the obvious numeric order.

If the list is empty, is should also return an empty list without error, but if it looks like something else has been passed in, this should 'confess' (i.e. die with a stack trace).

is_dupe_box

Returns true if the two given rectangles are (roughly) duplicates of each other (i.e. their corners coincide with a delta-x and delta-y smaller than the "fuzziness" parameter, an object attribute).

Note: instead of looking at delta-x and delta-y, it might be more rigorous to calculate the distance between the two points (using the geometric_distance method)... but I suspect thinking in terms of x any y errors maps more closely to the way this data is determined, so I'm sticking with this simpler calculation.

is_dupe_box_list

Compares lists of rectangles, returns true if they're effectively identical (within the limits of "fuzziness", as in is_dupe_box.

is_dupe_box_list_croaker

Compares lists of rectangles, returns true if they're effectively identical (within the limits of "fuzziness", as in is_dupe_box. If they're not identical, it croaks, returning a message explaining at what point they diverge.

diff_box_list

Compares two lists of rectangles, and reports on the place where they diverge. Returns an empty string if they match each other, within the limits of "fuzziness", as in is_dupe_box.

is_aref_three_deep

A rough check to see if we have a ref to an array of arrays of arrays.

Note: can yield false positives.

is_aref_two_deep

A rough check to see if we have a ref to an array of arrays, as opposed to an array of arrays of arrays.

Note: can yield false positives.

geometric_distance

Given two points, calculates the distance between them.

As written, restricted to 2D (x,y) points.

count_boxes_from_list

Given a list of rectangles, returns the number of rectangles.

TODO: not yet in use. But the goal is a count_rectangles command, remember? This is a (trivial) piece of the problem.

follow

A "follow" method moves forward and looks to one or both sides, trying to maintain some condition as it proceeds (e.g. a pixel pattern indicating an edge).

follow_pixel_pattern

Given a direction code: 'x_plus', 'x_minus', 'y_plus', or 'y_minus' Moves in that direction as far as it can go while maintaining the same pattern of pixels throughout a certain width (the spotsize in the direction transverse, for now).

Returns the point where it stops (x, y).

Sets the cursor to that location.

follow pixel patterns

The 'follow_pixpat_*' methods below move in the direction indicated by their suffixes:

  'x_plus', 'x_minus', 'y_plus', or 'y_minus'

Each of them makes use of the "spot", averaging the color of the rows of pixels in the direction of travel, but preserving differences in the transverse direction. They look for a change in this array of average colors (using the has_pixpat_changed method), and then stop, setting the cursor and returning the x and y values of the point (as an aref).

If no change is detected the extreme limit at the edge of the image is returned.

(( TODO review that design decision -- the main idea is that it saves me from propagating an undef return and handling it in a special way, or alternately from trapping an error. ))

follow_pixpat_x_plus

Starting at the given (x, y) location (defaulting to the cursor) moves in the "x_plus" direction until a change in the pixpat is detected.

Example usage:

  my $end_point = $bf->follow_pixpat_x_plus( $x0, $y0 );
follow_pixpat_x_minus
follow_pixpat_y_plus
follow_pixpat_y_minus
has_pixpat_changed

Given two "pixpats", the current and the previous one, this returns true if there's a difference between the two.

If the second argument is omitted, this will instead use the "previous_state" value from the object data.

Ex. usage:

    if ($self->has_pixpat_changed( $current, $previous ) ){
      my $self->set_cursor_y( $y );
      return ($y, $x);
    }

Note that this is a comparison sensitive to the slightest change (no "fuzziness" or "threshold" concept applies here).

has_pixpat_changed_past_threshold
color_distance

Calculate the difference in "color distance" from two Image::Magick color strings.

Ex. usage:

  my $color_distance = $self->color_distance( $color1, $color2 );

Note, any difference in the fourth parameter (alpha) is ignored.

If the second color is skipped, the distance to the origin is returned.

determine_pixpat_forward

See "pixpat" in concepts.

Averages colors in the spot, in stripes aligned across the direction of travel: this determines a "pixel pattern" that can be used to detect an edge. This is used to look ahead for an edge, in the "forward" direction.

Ex. usage:

  $self->set_direction('x_plus');
  my @colors = $self->determine_pixpat_forward( $x, $y );

(Remember, "set_direction" has a side-effect: it sets the spotsize in the forward direction to the "forward_horizon", and the spotsize in the transverse direction to the "transverse_horizon".)

This uses a "spot" (see concepts) that is silently truncated to fit the image boundaries.

determine_pixpat_transverse

See "pixpat" in concepts.

Averages colors in the spot, in stripes aligned with the direction of travel, to determine a "pixel pattern" that can be used to follow an edge. (This looks sideways for an edge, hence the name "transverse".)

Ex. usage:

  $self->set_direction('x_plus');
  my @colors = $self->determine_pixpat_transverse( $x, $y );

(Remember, "set_direction" has a side-effect: it sets the spotsize in the forward direction to the "forward_horizon", and the spotsize in the transverse direction to the "transverse_horizon".)

This uses a "spot" (see concepts) that is silently truncated to fit the image boundaries.

average_array_of_colors

Given an array of color strings, returns the color string of the average.

  Ex. my $ave = $self->average_array_of_colors( \@colors );
looks_like_edge

Given a pixpat, returns true if there's enough variation in color (as measured by color distance) over the pattern so that it's plausible it represents an "edge" of a GUI element.

Uses the object setting edge_contrast_cutoff to determine if there's enough difference bettween min and max color distance.

Looks at the change in color distance between adjacent colors in the "pixpat" array.

detected_change

A very general routine to compare a saved state to the current state, using a method of detecting change defined in the object data.

Ex. usage (TODO)

   $self->set_change_detector( "null_change_detector" );
   for my $x (0 .. $xmin) {
     for my $y (0 .. $ymin) {
       ($mark_x, $mark_y) = $self->follow_y_plus( $x, $y);
       $self->set_cursor_x( $x );
       $self->set_cursor_y( $y );
     }
   }

   $self->set_previous_state( $current_state );
boxfind_downward_via_pixpat

Scan downward and attempt to find a rectangle. Begins from the current cursor location, or from a given location if the x/y values have been supplied.

If found, returns a data structure containing the four points at (or near) the corners of the rectangle.

Otherwise, returns undef.

Example:

      $self->set_forward_horizon( 3 );
      $self->set_transverse_horizon( 4);

      if ( my $corners = $self->boxfind_downward_via_pixpat( $x, $y) ) {
        push @raw_rectangles, $corners;
      }

Note: this routine tries to use the "follow_*" family of methods, which scan using "pixel_patterns". As written, these routines might actually treat the edge of the image as the edge of a rectangle.

boxfind_downward_purely_via_pixpat

This is much like boxfind_downward_via_pixpat, except that instead of scanning down for a change in spotcolor, it scans downward for something that looks like it might be an edge pixpat (uses scan_for_edgey_pixpat).

Begins from the current cursor location, or from a given location if the x/y values have been supplied.

If found, returns a data structure containing the four points at (or near) the corners of the rectangle.

Otherwise, returns undef.

Example:

      $self->set_forward_horizon( 3 );
      $self->set_transverse_horizon( 4);

      if ( my $corners = $self->boxfind_downward_purely_via_pixpat( $x, $y) ) {
        push @raw_rectangles, $corners;
      }

Note: this routine tries to use the "follow_*" family of methods, which scan using "pixel patterns". As written, these routines might actually treat the edge of the image as the edge of a rectangle.

boxfind_upward_via_pixpat

Scan upward and attempt to find a rectangle. Begins from the current cursor location, or from a given location if the x/y values have been supplied.

If found, returns a data structure containing the four points at (or near) the corners of the rectangle.

Otherwise, returns undef.

Example:

      $self->set_forward_horizon( 3 );
      $self->set_transverse_horizon( 4);

      if ( my $corners = $self->boxfind_upward_via_pixpat( $x, $y) ) {
        push @raw_rectangles, $corners;
      }

Note: this routine tries to use the "follow_*" family of methods, which scan using "pixel_patterns" instead of simple average spot color differences.

As written, these routines might actually treat the edge of the image as the edge of a rectangle.

boxfind_downward_recenter

This is much like boxfind_downward_via_pixpat, except that instead of scanning down for a change in spotcolor, it scans downward for something that looks like it might be an edge pixpat (uses scan_for_edgey_pixpat), and further, it uses calls to "center_on_edge" to try to improve it's precision in edge following.

Scan downward and attempt to find a rectangle. Begins from the current cursor location, or from a given location if the x/y values have been supplied.

If found, returns a data structure containing the four points at (or near) the corners of the rectangle.

Otherwise, returns undef.

Example:

      $self->set_forward_horizon( 3 );
      $self->set_transverse_horizon( 4);

      if ( my $corners = $self->boxfind_downward_recenter( $x, $y) ) {
        push @raw_rectangles, $corners;
      }

Note: this routine tries to use the "follow_*" family of methods, which scan using "pixel_patterns" instead of simple average spot color differences.

As written, these routines might actually treat the edge of the image as the edge of a rectangle.

boxfind_here_or_downward

Looks at both the current location, and also looks some indefinite distance below, to attempt to find a rectangle.

This is like boxfind_downward_recenter above, EXCEPT: since this may return more than one rect, it must use a "list of rects" data structure for it's return.

boxfind utilities

Common code factored out from the above boxfind_* methods.

trace_box

Given an x and y value, presumes that that location is right on the top edge of a rectangle. It traces the sides of the rectangle (internally using the "follow_pixpat_*" routines and "center_on_edge") and returns a rect data structure or undef if one is not found.

Debugging Utilities

dump_boxes

Takes a list of rectangles and pretty-prints them to STDOUT.

dump_boxes_to_string

Takes a list of rectangles and pretty-prints them to a string.

print_box

Given a single rectangle, pretty-prints it to STDOUT.

print_box_to_string

Given a single rectangle, returns a pretty-printed string.

adjustable_parameter_report

Dumps a report of the "adjustable parameters" in the object data.

draw_rects_corners

Applies the Image::Magick "Edge" image filter to the current image, using the value of the edge_detect attribute as a "radius" argument.

draw_rects

Applies the Image::Magick "Edge" image filter to the current image, using the value of the edge_detect attribute as a "radius" argument.

DEPRECATED

Methods that might not be that useful, but are reasonably well tested.

scan_down_one_pix

Scans vertically, until a change in color is detected.

Sets the cursor at new location.

Detects a single-pixel change of any quantity.

scan_right_one_pix

Scans horizontally, until a change in color is detected.

Sets the cursor at new location.

Detects a single-pixel change of any quantity.

special setters with side-effects

set_direction

Setter for object attribute "direction".

Side-effect: sets the spotsize in the forward direction to the "forward_horizon", and the spotsize in the transverse direction to the "transverse_horizon".

setters and getters

The naming convention used:

  setters begin with "set_", I<but> getters have *no* prefix.

This is on the principle that the most commonly used case deserves the simplest syntax

(Note: in general mutators are now deprecated, see Conway "Best Practices").

These accessors exist for all of the object attributes (documented above) irrespective of whether they're expected to be externally useful. Note: no leading underscores have been used to indicate "internal" use.

special getters

direction_axis

A wrapper around the getter for direction, that returns only the first portion of it, the axis: "x" or "y".

direction_sign

A wrapper around the getter for direction, that returns only the second portion of it, the sign "+" or "-".

Note: the internal codes 'plus' and 'minus' are converted to the mathematical symbols.

automatic generation of accessors

AUTOLOAD

Discussion

Representing a rectangle as four points may seem excessive: mathematically, this is twice as much information as is necessary (e.g. postgresql's "box" geometric datatype uses only two diagonally opposite points, alternately a rectangle could be represented by a single point plus width and height).

Using all four corners is a convenience suited to the way this code tries to identify rectangles: crawling along the edges from corner-to-corner, only checking later to make sure they line up with each other. Also, the redundant specification allows a certain amount of spatial fuzziness: the alignment need not be perfect in order for us to call it "rectangular". To reduce our rectangles to postgresql's box-type, we would need to throw away some information (possibly by taking averages of each of the x and y coordinates).

data structures

A rectangle example:

$rect = [ [ 20, 66 ], [ 20, 99 ], [ 130, 99 ], [ 130, 66 ] ];

(( TODO add an example of a list of rectangles? ))

SEE ALSO

http://obsidianrook.com/Perl/image-boxfind

AUTHOR

Joseph Brenner, <doom@kzsu.stanford.edu>, 28 Sep 2007

COPYRIGHT AND LICENSE

Copyright (C) 2007 by Joseph Brenner

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.2 or, at your option, any later version of Perl 5 you may have available.

BUGS

None reported... yet.

<<