Reference
Range
Base.contains — Methodcontains(x::UnitRange{Int}, y::Integer)Test if range x contains value y.
In addition to the contains method, you can also use the ∈ operator.
julia> using Regions
julia> contains(0:10, 5)
true
julia> contains(0:10, 15)
false
julia> 0 ∈ 0:10
true
julia> 100 ∈ 0:10
falseRegions.invert — Methodinvert(x::UnitRange{Int})Invert a range. Inversion mirrors a range at the origin. A range is inverted by reversing and inverting each of its coordinates.
In addition to the invert method, you can also use the - operator to invert a range.
julia> using Regions
julia> invert(5:10)
-10:-5
julia> -(5:10)
-10:-5
julia> invert(invert(0:100))
0:100
julia> invert(-(0:100))
0:100Regions.isclose — Methodisclose(x::UnitRange{Int}, y::UnitRange{Int}, distance::Integer)Test whether the gap between x and y is at most distance integers wide. Equivalently, isclose(x, y, d) returns true iff growing the smaller range by d units toward the other range makes them overlap.
The distance parameter generalizes the related range predicates:
distance == 0is equivalent toisoverlapping— the ranges share at least one integer.distance == 1is equivalent toistouching— the ranges overlap or are immediately adjacent.distance > 1permits a gap of up todistance - 1integers between them.
julia> using Regions
julia> isclose(0:10, 15:25, 5)
true
julia> isclose(0:10, 15:25, 4)
falseRegions.isoverlapping — Methodisoverlapping(x::UnitRange{Int}, y::UnitRange{Int})Test if two ranges overlap.
julia> using Regions
julia> isoverlapping(0:10, 5:15)
true
julia> isoverlapping(0:10, 20:30)
falseRegions.istouching — Methodistouching(x::UnitRange{Int}, y::UnitRange{Int})Test if two ranges touch.
julia> using Regions
julia> istouching(0:10, 11:21)
true
julia> istouching(0:10, 12:22)
falseRegions.translate — Methodtranslate(x::UnitRange{Int}, y::Integer)Translate a range. Translation moves a range. A range is translated by adding an offset to each of its coordinates.
In addition to the translate method, you can also use the + or - operators to translate a range.
julia> using Regions
julia> translate(0:10, 5)
5:15
julia> (5:15) + 10
15:25
julia> 10 + (5:15)
15:25
julia> (5:15) - 10
-5:5Run
Regions.Run — TypeRunA run is a set of consecutive coordinates within a column (possibly partial) of a region. It consists of a discrete column coordinate (of type Int) and a range of discrete row coordinates (of type UnitRange{Int}).
Runs within a region specify a sort order: one run is smaller than the other if it starts before the other run modeling the coordinates from left to right and top to bottom.
Base.contains — Methodcontains(r::Run, x::Integer, y::Integer)
contains(r::Run, a::Vector{Int})Test if run r contains position (x, y).
julia> using Regions
julia> contains(Run(7, 2:8), 7, 5)
true
julia> contains(Run(7, 2:8), [7, 5])
true
julia> contains(Run(7, 2:8), 7, 10)
false
julia> contains(Run(7, 2:8), 3, 5)
falseBase.isempty — Methodisempty(x::Run)Discover whether the run is empty.
julia> using Regions
julia> isempty(Run(1, 1:10))
false
julia> isempty(Run(2, 1:1))
false
julia> isempty(Run(3, 1:0))
trueBase.isless — Methodisless(x::Run, y::Run)Compare two runs according to their natural sort order. First, their columns are compared, and if they are equal, their row ranges are compared.
julia> using Regions
julia> isless(Run(0, 1:10), Run(1, 0:10))
true
julia> isless(Run(1, 1:10), Run(1, 2:10))
trueRegions._minkowski_addition — Method_minkowski_addition(a::Run, b::Run)Compute the Minkowski sum of two runs.
Each run represents a vertical segment at a given column: Run(col, rows). The Minkowski sum is the segment whose column is a.column + b.column and whose row range is the interval sum (a.rows.start + b.rows.start):(a.rows.stop + b.rows.stop).
This is the primitive used by _minkowski_addition(::Region, ::Region).
Regions._minkowski_subtraction — Method_minkowski_subtraction(a::Run, b::Run)Compute the Minkowski difference of two runs.
Each run represents a vertical segment at a given column: Run(col, rows). The Minkowski difference a ⊖ b is the segment whose column is a.column - b.column and whose row range is (a.rows.start - b.rows.start):(a.rows.stop - b.rows.stop). The result may be empty when b's row range is wider than a's.
This is the primitive used by _minkowski_subtraction(::Region, ::Region).
Regions.invert — Methodinvert(x::Run)
-(x::Run) = invert(x)Invert a run. Inversion mirrors a run at the origin. A run is inverted by negating its column and inverting its rows.
In addition to the invert method, you can also use the unary - operator.
julia> using Regions
julia> invert(Run(1, 20:30))
Run(-1, -30:-20)
julia> -Run(-1, -30:-20)
Run(1, 20:30)Regions.isclose — Methodisclose(a::Run, b::Run, x::Integer, y::Integer)
isclose(a::Run, b::Run, d::Integer)
isclose(x::Run, y::Run, distance::Vector{Int})Test if two runs are close.
If distance == 0 this is the same as isoverlapping(). If distance == 1 this is the same as istouching(). If distance > 1 this is testing of closeness.
julia> using Regions
julia> isclose(Run(5, 0:10), Run(8, 2:15), 5, 3)
true
julia> isclose(Run(5, 0:10), Run(8, 15:20), 2, 2)
falseRegions.isoverlapping — Methodisoverlapping(x::Run, y::Run)Test if two runs overlap. Two runs can only overlap if they share the same column.
julia> using Regions
julia> isoverlapping(Run(3, 0:10), Run(3, 5:15))
true
julia> isoverlapping(Run(3, 0:10), Run(3, 20:30))
false
julia> isoverlapping(Run(3, 0:10), Run(4, 0:10))
falseRegions.istouching — Methodistouching(x::Run, y::Run)Test if two runs touch.
julia> using Regions
julia> istouching(Run(5, 0:10), Run(6, 5:15))
true
julia> istouching(Run(5, 0:10), Run(7, 5:15))
falseRegions.translate — Methodtranslate(r::Run, x::Integer, y::Integer)
translate(r::Run, a::Vector{Int})Translate a run. Translation moves a run. A run is translated by adding offsets to its column and rows.
In addition to the translate method, you can also use the + or - operators to translate a run.
julia> using Regions
julia> translate(Run(1, 20:30), 10, 20)
Run(11, 40:50)
julia> translate(Run(3, 2:5), [4, 10])
Run(7, 12:15)
julia> Run(3, 2:5) + [4, 10]
Run(7, 12:15)
julia> [4, 3] + Run(0, 0:10)
Run(4, 3:13)
julia> Run(0, 0:100) - [5, 25]
Run(-5, -25:75)Region
Regions.DownsampleRoundingMode — TypeDownsampleRoundingModeEnum that selects how non-integer scaled coordinates are rounded by downsample:
RoundNearestMode: round each coordinate to the nearest integer.CastToIntMode: truncate toward zero (matches C++(int)cast).ShrinkMode: round each coordinate toward the region's centroid, so the result is a (possibly strict) subset of what nearest-rounding would yield.GrowMode: round each coordinate away from the region's centroid, so the result covers at least what nearest-rounding would yield.
Regions.Region — TypeRegionA region is a discrete set of coordinates in two-dimensional euclidean space, represented as a sorted vector of Runs plus a boolean complement flag.
Sort invariant
The runs are sorted first by column, then by rows.start. Many functions in the package rely on this invariant — out-of-order runs will silently produce wrong results. If you mutate r.runs directly, restore the order with sort!(r.runs) before passing the region to other functions.
The complement flag
When complement == false (the usual case), the runs enumerate the pixels contained in the region. When complement == true, the runs enumerate the pixels excluded from an otherwise infinite plane — i.e. the region is the set-theoretic complement of those runs. This flag is what allows the package to represent infinite regions (such as the result of inverting a finite region) without storing infinite memory, and it is what enables union, intersection, and difference to handle complement operands correctly via De Morgan's laws. Use complement to construct a complement region rather than setting the field directly.
Examples
julia> using Regions
julia> Region() # create an empty region
Region(Run[], false)
julia> Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]) # create a region with 3 runs
Region(Run[Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)], false)
julia> Region([Run(col, -10:10) for col=-10:10]); # create a region with many runs using comprehensionBase.:(==) — Method==(a::Region, b::Region)Equality operator for two regions. Two regions are equal, if both their runs and their complement flags are equal.
julia> using Regions
julia> a = Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]);
julia> b = Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]);
julia> a == b
trueBase.contains — Methodcontains(r::Region, x::Integer, y::Integer)
contains(r::Region, a::Vector{Int})Test if region r contains position (x, y). x is the column coordinate, y is the row coordinate.
julia> using Regions
julia> r = Region([Run(2, 1:4)]);
julia> contains(r, 2, 3)
true
julia> contains(r, 2, 5)
false
julia> contains(r, 3, 3)
false
julia> [2, 3] ∈ r
trueBase.copy — Methodcopy(x::Region)Create a copy of a region.
julia> using Regions
julia> a = Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]);
julia> b = copy(a)
Region(Run[Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)], false)Base.hash — Methodhash(r::Region, h::UInt) -> UIntHash a region by combining the hashes of its runs vector and complement flag. Consistent with ==: regions that compare equal hash equal, so Regions can be used as keys in Dict / Set.
julia> using Regions
julia> a = Region([Run(0, 0:2), Run(1, 0:2)]);
julia> b = Region([Run(0, 0:2), Run(1, 0:2)]);
julia> hash(a) == hash(b)
true
julia> d = Dict(a => "first"); d[b]
"first"Base.isempty — Methodisempty(x::Region)Discover whether the region is empty.
julia> using Regions
julia> isempty(Region(Run[]))
true
julia> isempty(Region([Run(2, 1:1)]))
falseBase.show — MethodBase.show(io, mime::MIME"image/png", r::Region)MIME"image/png" show method for a Region. Renders the region as a PNG via region_to_image with a half-transparent blue color (RGBA(0, 0, 1, 0.5)). This is what produces an inline graphic when a region is the value of the last expression in notebook environments such as Pluto, Jupyter, or VS Code, or when a region is passed to display. Empty regions render to nothing.
Base.union — Methodunion(a::Region, b::Region)Returns the set-theoretic union of a and b — every pixel contained in a, in b, or in both. The operation is commutative and associative.
Complement regions are handled transparently: any combination of regular and complement operands is mapped via De Morgan's laws to operations on regular runs, and the result is returned with the appropriate complement flag.
julia> using Regions
julia> a = region_from_box(0, 0, 4, 3);
julia> b = region_from_box(3, 2, 7, 5);
julia> area(union(a, b))
36Regions._difference — Method_difference(a::Vector{Run}, b::Vector{Run})Calculate the difference of two sorted vectors of runs (elements in a not in b).
Regions._intersect! — Method_intersect!(a::Vector{Run})Reduce a sorted array of runs to only the pairwise-intersecting portions.
Regions._intersection — Method_intersection(a::Vector{Run}, b::Vector{Run})Calculate the intersection of two sorted arrays of runs.
Regions._merge — Method_merge(a::Vector{Run}, b::Vector{Run})Merge sorted vectors a and b. Assumes that a and b are sorted and does not check whether a or b are sorted.
Regions._pack! — Method_pack!(a::Vector{Run})Pack consecutive runs in the same column that touch or overlap into a single run.
Regions._union — Method_union(a::Vector{Run}, b::Vector{Run})Calculate the union of two sorted arrays of runs.
Regions.bottom — Methodbottom(x::Region)Returns the largest row coordinate of x. Under the image-coordinate convention used by the package this is the bottommost row of the region (the row with the largest index).
Only valid for non-complement, non-empty regions.
julia> using Regions
julia> bottom(region_from_box(2, 1, 7, 5))
5Regions.bounds — Methodbounds(x::Region)Returns the bounding box of x as (left, top, right, bottom) — equivalently (min column, min row, max column, max row). Under the image-coordinate convention used by the package, top < bottom (rows increase downward).
Only valid for non-complement, non-empty regions.
julia> using Regions
julia> bounds(region_from_box(2, 1, 7, 5))
(2, 1, 7, 5)Regions.center_region — Methodcenter_region(r::Region) -> RegionTranslate a non-empty, non-complement region so that its bounding-box centre lands as close to the origin as possible.
The translation amounts are computed from the integer midpoints of the column and row extents:
Δcol = (left(r) + right(r)) ÷ 2
Δrow = (bottom(r) + top(r)) ÷ 2Because pixel coordinates are integers, even-width or even-height regions cannot be placed symmetrically; the standard integer (floor) division places the origin one pixel left of / below the geometric centre in those cases.
julia> using Regions
julia> r = Region([Run(3, 2:4), Run(4, 2:4), Run(5, 2:4)]);
julia> c = center_region(r);
julia> left(c), right(c), bottom(c), top(c)
(-1, 1, 1, -1)Regions.complement — Methodcomplement(x::Region)Calculates the set-theoretic complement of a region.
With a non-complemented region, the runs specify the contained pixels, i.e. they specify what is included within the region. With a complemented region, the runs specify the non-contained pixels, i.e. they specify what is not included within the region.
julia> using Regions
julia> r = Region([Run(0, 0:5)]);
julia> c = complement(r);
julia> c.complement
true
julia> contains(r, 0, 3)
true
julia> contains(c, 0, 3)
false
julia> contains(c, 0, 9)
trueRegions.difference — Methoddifference(a::Region, b::Region)Returns the set-theoretic difference a \ b — every pixel contained in a but not in b. The operation is asymmetric: difference(a, b) and difference(b, a) are generally different regions.
Complement regions are handled transparently via De Morgan's laws.
julia> using Regions
julia> a = region_from_box(0, 0, 4, 3);
julia> b = region_from_box(3, 2, 7, 5);
julia> area(difference(a, b))
16
julia> area(difference(b, a))
16Regions.downsample — Functiondownsample(r::Region, fx::Real, fy::Real, mode=RoundNearestMode) -> RegionScale a region geometrically by factors fx (columns) and fy (rows) and quantise back to an integer-coordinate region. Both factors must be positive; they may be greater than 1 (geometric upsample) or smaller than 1 (geometric downsample).
mode controls how non-integer scaled coordinates are rounded — see DownsampleRoundingMode. For ShrinkMode and GrowMode the reference point is the region's centroid (computed before scaling), so the rounding direction is determined per-coordinate by whether the value sits above or below the centroid.
After rounding, the new runs are sorted and packed, so adjacent or overlapping runs that result from quantisation are merged into single runs.
julia> using Regions
julia> a = region_from_box(0, 0, 9, 9); # 10×10 box
julia> ds = downsample(a, 0.5, 0.5);
julia> width(ds), height(ds)
(5, 5)
julia> area(downsample(a, 0.5, 0.5, ShrinkMode)) ≤ area(downsample(a, 0.5, 0.5, GrowMode))
trueRegions.intersection — Methodintersection(a::Region, b::Region)Returns the set-theoretic intersection of a and b — only the pixels contained in both. Useful for masking: intersect a segmented region with a geometric region of interest (a box, circle, polygon, …) to restrict subsequent analysis to that area.
Complement regions are handled transparently via De Morgan's laws.
julia> using Regions
julia> a = region_from_box(0, 0, 4, 3);
julia> b = region_from_box(3, 2, 7, 5);
julia> area(intersection(a, b))
4Regions.invert — Methodinvert(x::Region)
-(x::Region)Invert a region. Inversion mirrors a region at the origin. A region is inverted by inverting each of its runs. Since the runs of a region are sorted by their column and row coordinates, the order of the runs is inversed as well.
In addition to the invert method, you can also use the unary - operator.
julia> using Regions
julia> a = Region([Run(0, 0:2), Run(1, 0:2), Run(2, 0:2)]);
julia> b = invert(a)
Region(Run[Run(-2, -2:0), Run(-1, -2:0), Run(0, -2:0)], false)
julia> c = -(a)
Region(Run[Run(-2, -2:0), Run(-1, -2:0), Run(0, -2:0)], false)
julia> a == invert(-a)
trueRegions.is_centered — Methodis_centered(r::Region) -> BoolReturn true if the bounding-box midpoint of r is already at the origin, i.e. if center_region would leave r unchanged.
Uses the same integer midpoint arithmetic as center_region: Δcol = (left + right) ÷ 2, Δrow = (bottom + top) ÷ 2.
julia> using Regions
julia> is_centered(Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]))
true
julia> is_centered(Region([Run(3, 2:4), Run(4, 2:4), Run(5, 2:4)]))
false
julia> is_centered(center_region(Region([Run(3, 2:4), Run(4, 2:4), Run(5, 2:4)])))
trueRegions.left — Methodleft(x::Region)Returns the smallest column coordinate of x — its leftmost column.
Only valid for non-complement, non-empty regions.
julia> using Regions
julia> left(region_from_box(2, 1, 7, 5))
2Regions.region_from_box — Methodregion_from_box(left::Integer, top::Integer, right::Integer, bottom::Integer)Create a filled rectangular region from its bounding-box coordinates. The argument order is left, top, right, bottom, and the package's image-coordinate convention requires top < bottom and left < right (rows increase downward, columns increase to the right). The result contains one vertical run per column, each spanning rows top:bottom.
julia> using Regions
julia> r = region_from_box(1, 1, 3, 4);
julia> bounds(r)
(1, 1, 3, 4)
julia> area(r)
12Regions.region_from_circle — Methodregion_from_circle(cx::Integer, cy::Integer, radius::Integer)Create a filled circular region with center (cx, cy) and the given radius.
All integer coordinates (x, y) satisfying (x − cx)² + (y − cy)² ≤ radius² are included. Integer square root is used to avoid floating-point rounding issues.
julia> using Regions
julia> region_from_circle(0, 0, 0)
Region(Run[Run(0, 0:0)], false)
julia> region_from_circle(0, 0, 2)
Region(Run[Run(-2, 0:0), Run(-1, -1:1), Run(0, -2:2), Run(1, -1:1), Run(2, 0:0)], false)
julia> contains(region_from_circle(0, 0, 5), 0, 5)
true
julia> contains(region_from_circle(0, 0, 5), 4, 4)
falseRegions.region_from_ellipse — Methodregion_from_ellipse(cx, cy, rx, ry, phi)
region_from_ellipse(e::NamedTuple)Create a filled region from an ellipse with center (cx, cy), semi-axes rx (along phi) and ry (perpendicular), and rotation angle phi in radians.
The second form accepts a named tuple (center, semi_axes, angle) as returned by equivalent_ellipse, enabling round-trip conversion.
julia> using Regions
julia> r = region_from_ellipse(0.0, 0.0, 2.0, 1.0, 0.0);
julia> area(r)
4
julia> contains(r, 0, 0)
trueRegions.region_from_line_segment — Methodregion_from_line_segment(x0::Real, y0::Real, x1::Real, y1::Real)Create a region containing all pixels on the 8-connected Bresenham line from (x0, y0) to (x1, y1). Endpoints are rounded to the nearest integer.
julia> using Regions
julia> area(region_from_line_segment(0.0, 0.0, 3.0, 0.0))
4
julia> area(region_from_line_segment(0.0, 0.0, 0.0, 3.0))
4
julia> area(region_from_line_segment(2.0, 2.0, 2.0, 2.0))
1Regions.region_from_point — Methodregion_from_point(x::Real, y::Real)Create a single-pixel region at the nearest integer coordinate to (x, y). Rounding uses trunc(Int, v + 0.5), matching C++ (int)(v + 0.5).
julia> using Regions
julia> r = region_from_point(1.4, 2.6);
julia> area(r)
1
julia> contains(r, 1, 3)
trueRegions.region_from_point_list — Methodregion_from_point_list(points::Vector{Tuple{Int,Int}})Create a region from a list of integer pixel coordinates (column, row). Each point becomes one pixel; duplicate points are merged.
julia> using Regions
julia> r = region_from_point_list([(0,0),(1,0),(1,1)]);
julia> area(r)
3
julia> contains(r, 1, 1)
trueRegions.region_from_polygon — Methodregion_from_polygon(vertices::Vector{Tuple{Int,Int}})Create a filled region from a polygon defined by its vertices as (column, row) pairs, given in either clockwise or counter-clockwise order.
The fill uses the half-open interval convention: for each edge the column with the smaller x-coordinate is included and the column with the larger x-coordinate is excluded. As a result, the rightmost column of the polygon is not filled. Horizontal edges are ignored.
julia> using Regions
julia> r = region_from_polygon([(0,0), (4,0), (2,4)]);
julia> length(r.runs)
4
julia> r.runs
4-element Vector{Run}:
Run(0, 0:0)
Run(1, 0:2)
Run(2, 0:4)
Run(3, 0:2)
julia> contains(r, 2, 2)
true
julia> contains(r, 0, 2)
falseRegions.region_from_ring — Methodregion_from_ring(cx::Integer, cy::Integer, outer_radius::Integer, inner_radius::Integer)Create a ring-shaped region: the set difference of two concentric circles with the same center (cx, cy). outer_radius must be ≥ inner_radius ≥ 0.
julia> using Regions
julia> r = region_from_ring(0, 0, 5, 3);
julia> area(r) == area(region_from_circle(0, 0, 5)) - area(region_from_circle(0, 0, 3))
trueRegions.region_to_image — Functionregion_to_image(r::Region, color=Gray(true))Render a region to a 2D Array whose element type matches color. The image is sized to the region's bounding box: (bottom - top + 1, right - left + 1) rows by columns. Pixels outside the region are zero (e.g. Gray(0), RGB(0,0,0), or RGBA(0,0,0,0)); pixels inside are set to color. The element type of the returned array is typeof(color), so calling with Gray, RGB, or RGBA selects the output format.
Some examples of colors you can pass:
Gray(0.5)— mid grayRGB(1, 0, 0)— bright redRGBA(0, 0.5, 0, 0.5)— half-transparent mid green
Use this when you need a raster image (for saving, displaying, or interop with image libraries). For inline display in REPL/notebook contexts the Base.show method registered for MIME"image/png" calls this function automatically with a half-transparent blue color.
Regions.right — Methodright(x::Region)Returns the largest column coordinate of x — its rightmost column.
Only valid for non-complement, non-empty regions.
julia> using Regions
julia> right(region_from_box(2, 1, 7, 5))
7Regions.top — Methodtop(x::Region)Returns the smallest row coordinate of x. Under the image-coordinate convention used by the package this is the topmost row of the region (the row with the smallest index).
Only valid for non-complement, non-empty regions.
julia> using Regions
julia> top(region_from_box(2, 1, 7, 5))
1Regions.translate — Methodtranslate(r::Region, x::Integer, y::Integer)
translate(r::Region, a::Vector{Int})Translate a region. Translation moves a region. A region is translated by translating each of its runs.
In addition to the translate method, you can also use the + or - operators to translate a region.
julia> using Regions
julia> a = Region([Run(0, 0:2), Run(1, 0:2), Run(2, 0:2)]);
julia> b = translate(a, -1, -1)
Region(Run[Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)], false)Regions.upscale — Methodupscale(r::Region, fx::Integer, fy::Integer) -> RegionUpscale a region by integer factors fx ≥ 1 and fy ≥ 1. Every original pixel (c, r) becomes a block of fx × fy pixels at columns c·fx : c·fx + fx - 1 and rows r·fy : r·fy + fy - 1, so the result has exactly fx · fy · area(r) pixels.
After expansion the runs are sorted and packed.
julia> using Regions
julia> r = region_from_box(0, 0, 2, 2); # 3×3 box, area 9
julia> u = upscale(r, 2, 3);
julia> area(u)
54
julia> width(u), height(u)
(6, 9)Base.show — MethodBase.show(io, mime::MIME"image/png", regions::Vector{Region})MIME"image/png" show method for a Vector{Region}. Renders the components as a PNG via regions_to_image, cycling through six half-transparent colors (blue, green, red, cyan, magenta, yellow) so that adjacent components are visually distinct. This is what produces inline graphics for the result of components in notebook environments.
Regions._minkowski_addition — Method_minkowski_addition(regions::Vector{Region}, se::Region)Apply _minkowski_addition to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.
Regions._minkowski_subtraction — Method_minkowski_subtraction(regions::Vector{Region}, se::Region)Apply _minkowski_subtraction to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.
Regions.bottom — Methodbottom(x::Vector{Region})Calculates the maximum row coordinate across a vector of regions — the bottommost row under the package's image-coordinate convention. Returns missing for an empty vector.
julia> using Regions
julia> bottom([Region([Run(2, 1:3)]), Region([Run(7, 5:8)])])
8
julia> ismissing(bottom(Region[]))
trueRegions.bounds — Methodbounds(x::Vector{Region})Calculates (left, top, right, bottom) — the minimum column, minimum row, maximum column, and maximum row — across all regions in the vector. Under the image-coordinate convention, top < bottom. Returns missing for an empty vector.
julia> using Regions
julia> bounds([Region([Run(2, 1:3)]), Region([Run(7, 2:6)])])
(2, 1, 7, 6)
julia> ismissing(bounds(Region[]))
trueRegions.closing — Methodclosing(regions::Vector{Region}, se::Region)Apply closing to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.
julia> using Regions
julia> gapped = Region([Run(-1, 0:0), Run(1, 0:0)]);
julia> se = region_from_box(-1, -1, 1, 1);
julia> c = closing([gapped], se);
julia> contains(c[1], 0, 0)
trueRegions.dilation — Methoddilation(regions::Vector{Region}, se::Region)Apply dilation to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.
julia> using Regions
julia> regions = [region_from_box(-1, -1, 1, 1)];
julia> se = region_from_box(-1, -1, 1, 1);
julia> dilation(regions, se) == [region_from_box(-2, -2, 2, 2)]
trueRegions.erosion — Methoderosion(regions::Vector{Region}, se::Region)Apply erosion to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.
julia> using Regions
julia> regions = [region_from_box(-2, -2, 2, 2), Region([Run(0, 0:0)])];
julia> se = region_from_box(-1, -1, 1, 1);
julia> length(erosion(regions, se))
1Regions.fill_holes — Methodfill_holes(regions::Vector{Region})Apply fill_holes to each region in regions, returning a new vector with all holes filled.
julia> using Regions
julia> frame = difference(region_from_box(-3, -3, 3, 3), region_from_box(-1, -1, 1, 1));
julia> filled = fill_holes([frame]);
julia> contains(filled[1], 0, 0)
trueRegions.filter_area — Methodfilter_area(regions::Vector{Region}, op, value::Real) -> Vector{Region}Return the regions whose area satisfies op(area(r), value). op is any comparison function — typically one of <, <=, ==, !=, >=, >.
julia> using Regions
julia> small = Region([Run(0, 0:0)]);
julia> big = region_from_box(0, 0, 5, 5);
julia> length(filter_area([small, big], >, 10))
1
julia> filter_area([small, big], >, 10)[1] == big
trueRegions.inner_boundary — Methodinner_boundary(regions::Vector{Region})Apply inner_boundary to each region in regions, returning a new vector of non-empty results.
julia> using Regions
julia> box = region_from_box(-2, -2, 2, 2);
julia> ib = inner_boundary([box]);
julia> contains(ib[1], -2, 0) && !contains(ib[1], 0, 0)
trueRegions.left — Methodleft(x::Vector{Region})Calculates the leftmost coordinate of a vector of regions. Returns missing for an empty vector.
julia> using Regions
julia> left([Region([Run(2, 1:3)]), Region([Run(7, 1:3)])])
2
julia> ismissing(left(Region[]))
trueRegions.morphological_gradient — Methodmorphological_gradient(regions::Vector{Region}, se::Region)Apply morphological_gradient to each region in regions with structuring element se, returning a new vector of non-empty results.
julia> using Regions
julia> box = region_from_box(-2, -2, 2, 2);
julia> se = region_from_box(-1, -1, 1, 1);
julia> grad = morphological_gradient([box], se);
julia> contains(grad[1], -2, 0) && !contains(grad[1], 0, 0)
trueRegions.opening — Methodopening(regions::Vector{Region}, se::Region)Apply opening to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.
julia> using Regions
julia> large = region_from_box(-2, -2, 2, 2);
julia> pixel = Region([Run(0, 0:0)]);
julia> se = region_from_box(-1, -1, 1, 1);
julia> length(opening([large, pixel], se))
1Regions.outer_boundary — Methodouter_boundary(regions::Vector{Region})Apply outer_boundary to each region in regions, returning a new vector of non-empty results.
julia> using Regions
julia> box = region_from_box(-2, -2, 2, 2);
julia> ob = outer_boundary([box]);
julia> contains(ob[1], -3, 0) && !contains(ob[1], -2, 0)
trueRegions.regions_to_image — Functionregions_to_image(regions::Vector{Region}, colors=[Gray(true)])Render a vector of regions to a single 2D Array sized to the combined bounding box. Each region is drawn in turn, taking its color from colors; when there are more regions than colors, the color list is cycled. Where two regions overlap, the previously drawn pixel is alpha-composited under the new pixel — pass colors with < 1 alpha (e.g. RGBA) to make overlaps visible.
This is the natural rendering for the output of components, where each connected blob can be tinted differently.
Some examples of color lists you can pass:
[Gray(0.5)]— flat mid gray for every region[RGB(1, 0, 0), RGB(0, 1, 0), RGB(0, 0, 1)]— cycle red/green/blue per region[RGBA(0, 0.5, 0, 0.5), RGBA(0.5, 0, 0, 0.5)]— half-transparent green and red
Compare with region_to_image, which renders a single Region with a single color.
Regions.right — Methodright(x::Vector{Region})Calculates the rightmost coordinate of a vector of regions. Returns missing for an empty vector.
julia> using Regions
julia> right([Region([Run(2, 1:3)]), Region([Run(7, 1:3)])])
7
julia> ismissing(right(Region[]))
trueRegions.set_intersection — Methodset_intersection(regions::Vector{Region}) -> RegionReturn the set-theoretic intersection of every region in the vector. Equivalent to reduce(intersection, regions). The input must be non-empty.
julia> using Regions
julia> a = region_from_box(0, 0, 5, 5);
julia> b = region_from_box(3, 3, 8, 8);
julia> c = region_from_box(4, 4, 9, 9);
julia> area(set_intersection([a, b, c]))
4Regions.set_union — Methodset_union(regions::Vector{Region}) -> RegionReturn the set-theoretic union of every region in the vector. Equivalent to reduce(union, regions). An empty input vector yields an empty Region.
julia> using Regions
julia> a = region_from_box(0, 0, 2, 2);
julia> b = region_from_box(5, 5, 7, 7);
julia> area(set_union([a, b])) == area(a) + area(b)
true
julia> isempty(set_union(Region[]))
trueRegions.top — Methodtop(x::Vector{Region})Calculates the minimum row coordinate across a vector of regions — the topmost row under the package's image-coordinate convention. Returns missing for an empty vector.
julia> using Regions
julia> top([Region([Run(2, 1:3)]), Region([Run(7, 2:6)])])
1
julia> ismissing(top(Region[]))
trueRegions.Regions — ModuleRegionsMain module for Regions.jl - a set of types that model a discrete 2-dimensional region concept.
Exports
- Run
- Region
- binarize
- components
Dependencies
- Images.jl
ImageBinarization.BinarizationAPI.binarize — Methodbinarize(image, predicate::Function)Extend Images.binarize with a predicate-based method that returns a Region.
Scans image column by column and includes every pixel for which predicate returns true. Because the second argument is typed ::Function, Julia's dispatch selects this method when a plain function or lambda is passed, while algorithm-based calls such as binarize(img, Otsu()) continue to resolve to the Images.binarize method unchanged.
For images with at least 1024 columns and multiple threads available, the work is distributed across threads (one per column). Smaller images use a single-threaded path to avoid thread overhead.
reg = binarize(img, x -> x > 0.3) # all pixels above 30 % brightness
reg = binarize(img, x -> x <= 0.5) # all pixels at most 50 % brightness
reg = binarize(img, x -> 0.3 < x < 0.8) # pixels in the 30–80 % rangeRegions.components — Functioncomponents(region::Region, dx::Unsigned=1, dy::Unsigned=1)Split region into its connected components and return them as a Vector{Region} — one element per blob.
Two runs are considered part of the same component when their column distance is at most dx and their row gap is at most dy. The defaults dx = dy = 1 give standard 8-connected labelling; larger values intentionally bridge small gaps between nearby objects (useful for joining slightly-fragmented blobs into a single component before measurement).
Because the algorithm walks the run list rather than the pixel grid, it scales as O(n_runs) — independent of image area. On sparse industrial images this is typically orders of magnitude faster than pixel-based connected-component labelling.
julia> using Regions
julia> b1 = region_from_circle(-20, 0, 5);
julia> b2 = region_from_circle( 0, 0, 5);
julia> b3 = region_from_circle( 20, 0, 5);
julia> length(components(union(union(b1, b2), b3)))
3Regions.segment_multi_threshold — Methodsegment_multi_threshold(image, thresholds::AbstractVector) -> Vector{Region}Segment image into one Region per intensity bin, using a sorted vector of thresholds as bin boundaries. The result has length(thresholds) + 1 elements:
- Bin 1: pixels with value
< thresholds[1] - Bin k (1 < k ≤ length(thresholds)): pixels with
thresholds[k-1] ≤ value < thresholds[k] - Bin
length(thresholds)+1: pixels withvalue ≥ thresholds[end]
thresholds must be strictly increasing. Empty bins return empty regions but the result length is always length(thresholds) + 1, so callers can index by bin index without bounds checks.
julia> using Regions
julia> img = [0.1 0.4 0.7;
0.2 0.5 0.8;
0.3 0.6 0.9];
julia> regs = segment_multi_threshold(img, [0.4, 0.7]);
julia> length(regs)
3
julia> area(regs[1]) + area(regs[2]) + area(regs[3])
9Morphological Operations
Regions._minkowski_addition — Method_minkowski_addition(a::Region, b::Region)Compute the Minkowski sum of two regions, with complement handling via DeMorgan's rules. Both arguments being complements is not supported.
Regions._minkowski_subtraction — Method_minkowski_subtraction(a::Region, b::Region)Compute the Minkowski difference of two regions, with complement handling via DeMorgan's rules. The structuring element being a complement is not supported.
Regions.closing — Methodclosing(a::Region, b::Region)Morphological closing: dilation of a by b, followed by Minkowski subtraction with b.
Closing fills gaps and holes smaller than the structuring element and smoothes the region boundary. The result always contains a as a subset.
Structuring elements should be centred on the origin.
julia> using Regions
julia> gapped = Region([Run(-1, 0:0), Run(1, 0:0)]);
julia> se = region_from_box(-1, -1, 1, 1);
julia> c = closing(gapped, se);
julia> contains(c, 0, 0)
true
julia> contains(c, -1, 0) && contains(c, 1, 0)
trueRegions.dilation — Methoddilation(a::Region, b::Region)Dilate region a with structuring element b.
Dilation grows a: the result contains every point reachable by placing b centred at any pixel of a. Dilation may merge disconnected parts.
Structuring elements should be centred on the origin. Complement regions are handled via DeMorgan's rules.
julia> using Regions
julia> small = region_from_box(-1, -1, 1, 1);
julia> se = region_from_box(-1, -1, 1, 1);
julia> dilation(small, se) == region_from_box(-2, -2, 2, 2)
trueRegions.erosion — Methoderosion(a::Region, b::Region)Erode region a with structuring element b.
Erosion shrinks a: the result contains every point c such that the reflected (inverted) b centred at c fits entirely within a. Erosion may split a connected region into disconnected parts.
Structuring elements should be centred on the origin. Complement regions are handled via DeMorgan's rules.
julia> using Regions
julia> big = region_from_box(-2, -2, 2, 2);
julia> se = region_from_box(-1, -1, 1, 1);
julia> erosion(big, se) == se
true
julia> isempty(erosion(Region([Run(0, 0:0)]), se))
trueRegions.fill_holes — Methodfill_holes(region::Region)Fill the holes of a region.
Returns a new region equal to region with all enclosed holes filled in. If the region has no holes the original region is returned unchanged.
Only non-complement regions are supported.
julia> using Regions
julia> frame = difference(region_from_box(-3, -3, 3, 3), region_from_box(-1, -1, 1, 1));
julia> filled = fill_holes(frame);
julia> contains(filled, 0, 0)
true
julia> contains(filled, -3, 0)
trueRegions.holes — Methodholes(region::Region)Extract the holes of a region.
A hole is a connected component of the complement-within-bounding-box that does not touch the bounding box boundary. Returns a Vector{Region}, one element per hole.
Only non-complement regions are supported.
julia> using Regions
julia> frame = difference(region_from_box(-3, -3, 3, 3), region_from_box(-1, -1, 1, 1));
julia> hs = holes(frame);
julia> length(hs)
1
julia> contains(hs[1], 0, 0)
trueRegions.inner_boundary — Methodinner_boundary(a::Region)Compute the inner boundary of a region.
The inner boundary is the set of pixels that belong to a but would be removed by a 3×3 erosion — i.e. the outermost layer of pixels strictly inside a.
julia> using Regions
julia> box = region_from_box(-2, -2, 2, 2);
julia> ib = inner_boundary(box);
julia> contains(ib, -2, 0)
true
julia> contains(ib, 0, 0)
falseRegions.morphological_gradient — Methodmorphological_gradient(a::Region, b::Region)Morphological gradient: difference of the dilation and erosion of a by b.
The result is a ring around the boundary of a, lying partly inside and partly outside.
Structuring elements should be centred on the origin.
julia> using Regions
julia> box = region_from_box(-2, -2, 2, 2);
julia> se = region_from_box(-1, -1, 1, 1);
julia> grad = morphological_gradient(box, se);
julia> contains(grad, -2, 0)
true
julia> contains(grad, 0, 0)
falseRegions.opening — Methodopening(a::Region, b::Region)Morphological opening: erosion of a by b, followed by Minkowski addition with b.
Opening removes structures smaller than the structuring element and smoothes the region boundary. The result is always a subset of a.
Structuring elements should be centred on the origin.
julia> using Regions
julia> big = region_from_box(-2, -2, 2, 2);
julia> se = region_from_box(-1, -1, 1, 1);
julia> opening(big, se) == big
true
julia> isempty(opening(Region([Run(0, 0:0)]), se))
trueRegions.outer_boundary — Methodouter_boundary(a::Region)Compute the outer boundary of a region.
The outer boundary is the set of pixels that do not belong to a but are added by a 3×3 dilation — i.e. the innermost layer of pixels strictly outside a.
julia> using Regions
julia> box = region_from_box(-2, -2, 2, 2);
julia> ob = outer_boundary(box);
julia> contains(ob, -3, 0)
true
julia> contains(ob, -2, 0)
falseBlob Analysis Features
Regions._raw_moments — Method_raw_moments(r::Region) -> NamedTupleCompute all 10 raw spatial moments in a single pass over runs. Column = x, row = y. Returns (m00, m10, m01, m20, m11, m02, m30, m21, m12, m03).
Regions.area — Methodarea(r::Region) -> IntReturn the number of pixels in a non-complement region.
julia> using Regions
julia> area(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
9
julia> area(Region([Run(2, 3:3)]))
1Regions.area_of_holes — Methodarea_of_holes(r::Region) -> IntReturn the total pixel count of all holes in the region.
julia> using Regions
julia> r = Region([Run(c, 0:4) for c in 0:4]); # solid 5×5 square — no holes
julia> area_of_holes(r)
0Regions.aspect_ratio — Methodaspect_ratio(r::Region) -> Float64Return width / height of the bounding box.
julia> using Regions
julia> aspect_ratio(Region([Run(0, 0:3), Run(1, 0:3)]))
0.5Regions.bounds_center — Methodbounds_center(r::Region) -> Tuple{Float64,Float64}Return the center of the bounding box as (column, row).
julia> using Regions
julia> bounds_center(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
(1.0, 0.0)Regions.central_moments — Methodcentral_moments(r::Region) -> NamedTupleReturn the central moments (μ20, μ11, μ02, μ30, μ21, μ12, μ03) of a region. Central moments are computed relative to the region's centroid; μ10 = μ01 = 0 by definition and are not included. μ00 equals area(r) and is also omitted.
The standard relations to the raw moments mij (with x̄ = m10/m00, ȳ = m01/m00) are:
μ20 = m20 - x̄·m10μ11 = m11 - x̄·m01μ02 = m02 - ȳ·m01μ30 = m30 - 3·x̄·m20 + 2·x̄²·m10μ21 = m21 - 2·x̄·m11 - ȳ·m20 + 2·x̄²·m01μ12 = m12 - 2·ȳ·m11 - x̄·m02 + 2·ȳ²·m10μ03 = m03 - 3·ȳ·m02 + 2·ȳ²·m01
julia> using Regions
julia> μ = central_moments(Region([Run(c, -1:1) for c in -1:1])); # 3×3 box centred at origin
julia> μ.μ11 ≈ 0.0 && μ.μ30 ≈ 0.0 && μ.μ03 ≈ 0.0
true
julia> μ.μ20 ≈ 6.0 && μ.μ02 ≈ 6.0
trueRegions.centroid — Methodcentroid(r::Region) -> Tuple{Float64,Float64}Return the centroid (column, row) of the region.
julia> using Regions
julia> centroid(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
(1.0, 0.0)
julia> centroid(Region([Run(2, 3:3)]))
(2.0, 3.0)Regions.circularity — Methodcircularity(r::Region) -> Float64Return 2·√(π·area) / perimeter. Equals 1 for an ideal disk and decreases towards 0 for shapes with a longer boundary relative to their area. This is different from compactness, which is the isoperimetric ratio perimeter² / (4π·area).
julia> using Regions
julia> 0.0 < circularity(region_from_circle(0, 0, 50)) ≤ 1.0
trueRegions.compactness — Methodcompactness(r::Region) -> Float64Return the isoperimetric ratio perimeter² / (4π·area). Equals 1 for a circle, greater than 1 for less compact shapes.
julia> using Regions
julia> r = region_from_circle(0, 0, 50);
julia> compactness(r) < 1.7
trueRegions.contour — Methodcontour(r::Region) -> PointListReturn the first (outer) boundary polygon of r as a PointList. Equivalent to vectorized_boundaries(r)[1].
julia> using Regions
julia> length(contour(Region([Run(0, 0:0)])))
4Regions.convex_area — Methodconvex_area(r::Region) -> Float64Return the area of the convex hull (in pixel units).
julia> using Regions
julia> convex_area(Region([Run(0, 0:1), Run(1, 0:1)]))
4.0Regions.convex_hull — Methodconvex_hull(r::Region) -> Vector{Tuple{Float64,Float64}}Return the convex hull of the region as an ordered list of vertices (CCW). Coordinates are half-integer pixel corners.
julia> using Regions
julia> h = convex_hull(Region([Run(0, 0:1), Run(1, 0:1)]));
julia> length(h)
4Regions.convex_perimeter — Methodconvex_perimeter(r::Region) -> Float64Return the perimeter of the convex hull.
julia> using Regions
julia> convex_perimeter(Region([Run(0, 0:1), Run(1, 0:1)]))
8.0Regions.convexity — Methodconvexity(r::Region) -> Float64Return area / convex_area. Equals 1 for convex regions, less than 1 for concave ones.
julia> using Regions
julia> convexity(Region([Run(0, 0:1), Run(1, 0:1)]))
1.0Regions.equivalent_ellipse — Methodequivalent_ellipse(r::Region) -> NamedTupleReturn the equivalent ellipse of a region as a named tuple (center, semi_axes, angle), where:
centeris aTuple{Float64,Float64}(column, row),semi_axesis aTuple{Float64,Float64}(major, minor),angleis in radians, measured from the column (x) axis towards the row (y) axis.
julia> using Regions
julia> e = equivalent_ellipse(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]));
julia> e.center
(1.0, 0.0)
julia> round(e.angle; digits=4)
0.0Regions.feret_diameters — Methodferet_diameters(r::Region) -> Tuple{Float64,Float64}Return (min_feret, max_feret) — the minimum and maximum caliper widths — computed via rotating calipers on the convex hull.
julia> using Regions
julia> mn, mx = feret_diameters(Region([Run(0, 0:0)]));
julia> mn ≈ 1.0 && mx ≈ sqrt(2)
trueRegions.fiber_length — Methodfiber_length(r::Region) -> Float64Return half of the 4-connected perimeter — 0.5 · perimeter(r). For elongated, fiber-shaped blobs this approximates the length along the fiber axis (the perimeter traces one side of the fiber going out and the other side coming back).
For a circle this gives π·r rather than the diameter, so the name "length" only carries its intended meaning on truly fiber-shaped regions.
julia> using Regions
julia> fiber_length(Region([Run(0, 0:0)])) # single pixel: perimeter 4
2.0
julia> fiber_length(region_from_box(0, 0, 2, 2)) ≈ 0.5 * perimeter(region_from_box(0, 0, 2, 2))
trueRegions.fiber_width — Methodfiber_width(r::Region) -> Float64Return 2.0 · maximum(distance_transform(r)) — twice the largest city-block distance from any region pixel to the background. Geometrically this is the diameter of the biggest city-block diamond that fits inside the region; for thin, fiber-shaped blobs it closely tracks the cross-sectional width.
Because the underlying distance_transform uses 4-connected (Manhattan) distance, fiber_width overestimates the Euclidean width for non-thin shapes — e.g. a 3×3 square has fiber_width = 4.0, not 3.0. This matches the C++ reference implementation; if you need an Euclidean width, use feret_diameters instead.
julia> using Regions
julia> fiber_width(Region([Run(0, 0:0)])) # single pixel
2.0
julia> fiber_width(region_from_box(0, 0, 2, 2)) # 3×3 square: max DT = 2
4.0Regions.flusser_moments — Methodflusser_moments(r::Region) -> NTuple{4,Float64}Return the first four Flusser–Suk affine moment invariants (I1, I2, I3, I4) (Flusser & Suk, 1993). These are invariant under general affine transformations (translation, scaling, rotation, shear), which makes them more robust than hu_moments when the imaged object can be viewed under perspective change.
The invariants are formed from central moments μij and the area μ00:
I1 = (μ20·μ02 − μ11²) / μ00^4I2 = (μ30²·μ03² − 6·μ30·μ21·μ12·μ03 + 4·μ30·μ12³ + 4·μ21³·μ03 − 3·μ21²·μ12²) / μ00^10I3 = (μ20·(μ21·μ03 − μ12²) − μ11·(μ30·μ03 − μ21·μ12) + μ02·(μ30·μ12 − μ21²)) / μ00^7I4 = (μ20³·μ03² − 6·μ20²·μ11·μ12·μ03 − 6·μ20²·μ02·μ21·μ03 + 9·μ20²·μ02·μ12² + 12·μ20·μ11²·μ21·μ03 + 6·μ20·μ11·μ02·μ30·μ03 − 18·μ20·μ11·μ02·μ21·μ12 − 8·μ11³·μ30·μ03 − 6·μ20·μ02²·μ30·μ12 + 9·μ20·μ02²·μ21² + 12·μ11²·μ02·μ30·μ12 − 6·μ11·μ02²·μ30·μ21 + μ02³·μ30²) / μ00^11
julia> using Regions
julia> F = flusser_moments(region_from_circle(0, 0, 20));
julia> F[1] > 0 # I1 strictly positive for any non-degenerate shape
trueRegions.height — Methodheight(r::Region) -> IntReturn the height (row extent) of the bounding box of a non-empty, non-complement region.
julia> using Regions
julia> height(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
3Regions.hu_moments — Methodhu_moments(r::Region) -> NTuple{7,Float64}Return Hu's seven moment invariants (I1, I2, I3, I4, I5, I6, I7) computed from the normalized central moments. Hu moments are invariant under translation, uniform scaling and rotation, so they form a classic shape descriptor for rotation-and-scale-tolerant matching.
The formulas (Hu, 1962):
I1 = η20 + η02I2 = (η20 − η02)² + 4·η11²I3 = (η30 − 3·η12)² + (3·η21 − η03)²I4 = (η30 + η12)² + (η21 + η03)²I5 = (η30 − 3·η12)·(η30 + η12)·[(η30 + η12)² − 3·(η21 + η03)²] + (3·η21 − η03)·(η21 + η03)·[3·(η30 + η12)² − (η21 + η03)²]I6 = (η20 − η02)·[(η30 + η12)² − (η21 + η03)²] + 4·η11·(η30 + η12)·(η21 + η03)I7 = (3·η21 − η03)·(η30 + η12)·[(η30 + η12)² − 3·(η21 + η03)²] − (η30 − 3·η12)·(η21 + η03)·[3·(η30 + η12)² − (η21 + η03)²]
julia> using Regions
julia> I = hu_moments(region_from_circle(0, 0, 20));
julia> abs(I[2]) < 1e-3 # circle: rotational symmetry → I2 ≈ 0
trueRegions.minimum_area_bounding_rectangle — Methodminimum_area_bounding_rectangle(r::Region) -> NamedTupleReturn the minimum-area bounding rectangle of r as (corners, width, height, angle, area). Delegates to minimum_area_bounding_rectangle(::PointList) on the region's convex hull.
julia> using Regions
julia> rect = minimum_area_bounding_rectangle(region_from_box(0, 0, 4, 2));
julia> rect.area ≈ 5.0 * 3.0
trueRegions.minimum_bounding_circle — Methodminimum_bounding_circle(r::Region) -> NamedTupleReturn the minimum enclosing circle of r as (center, radius). Delegates to minimum_bounding_circle(::PointList) on the region's convex hull.
julia> using Regions
julia> c = minimum_bounding_circle(region_from_box(0, 0, 4, 4));
julia> c.center[1] ≈ 2.0 && c.center[2] ≈ 2.0 && c.radius ≈ 2.5 * sqrt(2)
trueRegions.minimum_perimeter_bounding_rectangle — Methodminimum_perimeter_bounding_rectangle(r::Region) -> NamedTupleReturn the minimum-perimeter bounding rectangle of r as (corners, width, height, angle, perimeter). Delegates to minimum_perimeter_bounding_rectangle(::PointList) on the region's convex hull.
julia> using Regions
julia> rect = minimum_perimeter_bounding_rectangle(region_from_box(0, 0, 4, 2));
julia> rect.perimeter ≈ 2 * (5.0 + 3.0)
trueRegions.moments — Methodmoments(r::Region) -> NamedTupleReturn all 10 raw spatial moments of the region as a named tuple (m00, m10, m01, m20, m11, m02, m30, m21, m12, m03).
Column is the x-axis, row is the y-axis.
julia> using Regions
julia> m = moments(Region([Run(2, 3:3)]))
(m00 = 1.0, m10 = 2.0, m01 = 3.0, m20 = 4.0, m11 = 6.0, m02 = 9.0, m30 = 8.0, m21 = 12.0, m12 = 18.0, m03 = 27.0)
julia> m.m00
1.0Regions.normalized_moments — Methodnormalized_moments(r::Region) -> NamedTupleReturn the scale-normalized central moments (η20, η11, η02, η30, η21, η12, η03). Each η_pq = μ_pq / μ00^γ with γ = (p + q) / 2 + 1, so η_pq is invariant under uniform scaling of the region.
scale_invariant_moments is an alias for this function.
julia> using Regions
julia> η = normalized_moments(region_from_circle(0, 0, 30));
julia> abs(η.η11) < 1e-3
trueRegions.number_of_holes — Methodnumber_of_holes(r::Region) -> IntReturn the number of holes (connected background components fully enclosed by the region).
julia> using Regions
julia> r = Region([Run(c, 0:4) for c in 0:4]); # solid 5×5 square — no holes
julia> number_of_holes(r)
0Regions.perforation — Methodperforation(r::Region) -> Float64Return (convex_area - area) / convex_area.
julia> using Regions
julia> perforation(Region([Run(0, 0:1), Run(1, 0:1)]))
0.0Regions.perimeter — Methodperimeter(r::Region) -> Float64Return the perimeter of a region measured as the number of 4-connected boundary edges (each edge has length 1.0).
julia> using Regions
julia> perimeter(Region([Run(0, 0:0)])) # single pixel: 4 exposed edges
4.0
julia> perimeter(Region([Run(0, 0:1), Run(1, 0:1)])) # 2×2 square: 8 edges
8.0Regions.roughness — Methodroughness(r::Region) -> Float64Return perimeter / convex_perimeter. Equals 1 for a convex region (whose 4-connected boundary length equals its convex-hull perimeter) and exceeds 1 when the actual boundary stair-steps or has concavities, making it longer than the convex hull.
julia> using Regions
julia> roughness(region_from_box(0, 0, 4, 4)) ≈ 1.0 # rectangle is convex
true
julia> roughness(region_from_circle(0, 0, 50)) > 1.0 # stair-stepped circle
trueRegions.roundness — Methodroundness(r::Region) -> Float64Return area / (π·r²) where r is the radius of the minimum bounding circle. Equals 1 for an ideal disk and decreases for shapes that do not fill their bounding circle.
julia> using Regions
julia> 0.0 < roundness(region_from_circle(0, 0, 50)) ≤ 1.0
trueRegions.scale_invariant_moments — Methodscale_invariant_moments(r::Region) -> NamedTupleAlias for normalized_moments. Returns the scale-invariant central moments (η20, η11, η02, η30, η21, η12, η03).
Regions.sphericity — Methodsphericity(r::Region) -> Float64Return 2·√(area/π) / max_feret. Equals 1 for an ideal disk and decreases towards 0 for elongated shapes — the ratio of the equivalent-area-disk diameter to the longest caliper width.
julia> using Regions
julia> sphericity(region_from_circle(0, 0, 50)) > 0.95
trueRegions.to_point_list — Methodto_point_list(r::Region) -> PointListFlatten all boundary polygons of r into a single PointList. Equivalent to concatenating all polygons returned by vectorized_boundaries.
julia> using Regions
julia> length(to_point_list(Region([Run(0, 0:0)])))
4Regions.vectorized_boundaries — Methodvectorized_boundaries(r::Region) -> Vector{PointList}Convert a region to a list of closed boundary polygons. Outer boundaries wind clockwise; hole boundaries wind counter-clockwise (interior always to the right of travel direction). Coordinates use pixel-corner convention: a pixel at (col, row) has its corners at (col±0.5, row±0.5).
The result may contain collinear midpoints on straight edges; call remove_collinear(poly) on each polygon to simplify.
julia> using Regions
julia> polys = vectorized_boundaries(Region([Run(0, 0:0)]));
julia> length(polys)
1
julia> length(polys[1])
4Regions.width — Methodwidth(r::Region) -> IntReturn the width (column extent) of the bounding box of a non-empty, non-complement region.
julia> using Regions
julia> width(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
3Point List
Regions.PointList — TypePointListAlias for Vector{Tuple{Float64,Float64}}. Each element is (column, row).
Regions.area — Methodarea(pts::PointList) -> Float64Return the signed area of a polygon using the shoelace formula. Positive for counter-clockwise winding, negative for clockwise.
julia> using Regions
julia> area([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)])
1.0Regions.bounding_box — Methodbounding_box(pts::PointList) -> NamedTupleReturn the axis-aligned bounding box of a point list as (xmin, xmax, ymin, ymax).
julia> using Regions
julia> bb = bounding_box([(1.0,2.0),(3.0,4.0),(2.0,1.0)]);
julia> bb.xmin, bb.xmax, bb.ymin, bb.ymax
(1.0, 3.0, 1.0, 4.0)Regions.centroid — Methodcentroid(pts::PointList) -> Tuple{Float64,Float64}Return the centroid (arithmetic mean) of the points as (column, row).
julia> using Regions
julia> centroid([(0.0,0.0),(2.0,0.0),(2.0,2.0),(0.0,2.0)])
(1.0, 1.0)Regions.convex_hull — Methodconvex_hull(pts::PointList) -> PointListReturn the convex hull of a point list as CCW-ordered vertices, using Andrew's monotone chain algorithm.
julia> using Regions
julia> pts = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0),(0.5,0.5)];
julia> h = convex_hull(pts);
julia> length(h)
4Regions.convex_hull_cw — Methodconvex_hull_cw(pts::PointList) -> PointListReturn the convex hull of a point list in clockwise order.
julia> using Regions
julia> pts = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)];
julia> convex_hull_cw(pts) == reverse(convex_hull(pts))
trueRegions.feret_diameters — Methodferet_diameters(pts::PointList) -> Tuple{Float64,Float64}Return (min_feret, max_feret) — minimum and maximum caliper widths — computed via rotating calipers on the convex hull of pts.
julia> using Regions
julia> mn, mx = feret_diameters([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]);
julia> mn ≈ 1.0 && mx ≈ sqrt(2)
trueRegions.maximum_width — Methodmaximum_width(pts::PointList) -> Float64Return the maximum caliper width (maximum Feret diameter) of a point list.
julia> using Regions
julia> maximum_width([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]) ≈ sqrt(2)
trueRegions.minimum_area_bounding_rectangle — Methodminimum_area_bounding_rectangle(pts::PointList) -> NamedTupleReturn the minimum-area bounding rectangle via rotating calipers on the convex hull. Returns (corners, width, height, angle, area) where corners is a PointList of 4 CCW vertices and angle is the long-axis rotation in radians.
julia> using Regions
julia> r = minimum_area_bounding_rectangle([(0.0,0.0),(2.0,0.0),(2.0,1.0),(0.0,1.0)]);
julia> r.area ≈ 2.0
trueRegions.minimum_bounding_circle — Methodminimum_bounding_circle(pts::PointList) -> NamedTupleReturn the minimum enclosing circle of a point list as (center, radius). Uses Welzl's incremental algorithm.
julia> using Regions
julia> c = minimum_bounding_circle([(0.0,0.0),(2.0,0.0),(2.0,2.0),(0.0,2.0)]);
julia> c.center[1] ≈ 1.0 && c.center[2] ≈ 1.0 && c.radius ≈ sqrt(2)
trueRegions.minimum_perimeter_bounding_rectangle — Methodminimum_perimeter_bounding_rectangle(pts::PointList) -> NamedTupleReturn the minimum-perimeter bounding rectangle via rotating calipers on the convex hull. Returns (corners, width, height, angle, perimeter) where corners is a PointList of 4 CCW vertices and angle is the long-axis rotation in radians.
julia> using Regions
julia> r = minimum_perimeter_bounding_rectangle([(0.0,0.0),(2.0,0.0),(2.0,1.0),(0.0,1.0)]);
julia> r.perimeter ≈ 6.0
trueRegions.minimum_width — Methodminimum_width(pts::PointList) -> Float64Return the minimum caliper width (minimum Feret diameter) of a point list.
julia> using Regions
julia> minimum_width([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]) ≈ 1.0
trueRegions.perimeter — Methodperimeter(pts::PointList) -> Float64Return the perimeter of a closed polygon. Points are first simplified using simplify_radial_distance with tolerance 3 to reduce the effect of digitisation jaggies on oblique edges.
julia> using Regions
julia> pts = [(0.0,0.0),(100.0,0.0),(100.0,100.0),(0.0,100.0)];
julia> perimeter(pts) ≈ 400.0
trueRegions.point_at — Methodpoint_at(pts::PointList, distance::Real) -> Tuple{Float64,Float64}Return the point at arc-length distance along the polyline.
distance = 0: first point.0 < distance < total_length: interpolated point on the appropriate segment.distance < 0ordistance > total_length: extrapolated beyond the respective end in the direction of the first or last edge.
julia> using Regions
julia> pts = [(0.0,0.0),(1.0,0.0),(1.0,1.0)];
julia> point_at(pts, 1.5)
(1.0, 0.5)Regions.remove_collinear — Methodremove_collinear(pts::PointList) -> PointListRemove collinear consecutive vertices from a polygon. A vertex is removed if it lies exactly on the line through its two neighbours.
julia> using Regions
julia> pts = [(0.0,0.0),(0.5,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)];
julia> remove_collinear(pts)
4-element Vector{Tuple{Float64, Float64}}:
(0.0, 0.0)
(1.0, 0.0)
(1.0, 1.0)
(0.0, 1.0)Regions.simplified_perimeter — Methodsimplified_perimeter(pts::PointList, tolerance::Real) -> Float64Return the perimeter of a closed polygon after simplifying with the given tolerance (see simplify_radial_distance).
julia> using Regions
julia> pts = [(0.0,0.0),(100.0,0.0),(100.0,100.0),(0.0,100.0)];
julia> simplified_perimeter(pts, 3.0) ≈ 400.0
trueRegions.simplify_radial_distance — Functionsimplify_radial_distance(pts::PointList, tolerance::Real, closed::Bool=false) -> PointListSimplify a polyline by removing points that lie within tolerance of the previous kept point (radial distance algorithm).
If closed is true the polyline is treated as a closed polygon: the first point is appended at the end of the result so that the closing edge is included when summing edge lengths.
julia> using Regions
julia> pts = [(0.0,0.0),(0.5,0.0),(100.0,0.0),(100.0,100.0)];
julia> length(simplify_radial_distance(pts, 1.0))
3Regions.translate — Methodtranslate(pts::PointList, dx::Real, dy::Real) -> PointList
translate(pts::PointList, d::Vector) -> PointListTranslate a point list by (dx, dy).
julia> using Regions
julia> translate([(0.0,0.0),(1.0,0.0)], 2.0, 3.0)
2-element Vector{Tuple{Float64, Float64}}:
(2.0, 3.0)
(3.0, 3.0)Region Profiles
Regions.profile_horizontal — Methodprofile_horizontal(r::Region, image::AbstractMatrix) -> VectorReturn the row-wise sum of image pixels that fall inside the non-empty, non-complement region r. The returned vector has length height(r); index i corresponds to image row top(r) + i - 1.
Pixels outside the region are not counted, so this is equivalent to masking the image with the region and then summing along the column axis.
image is indexed as image[row, column] (the package's standard convention) and must contain every pixel of r's bounding box.
julia> using Regions
julia> img = Float64[1 2 3; 4 5 6; 7 8 9];
julia> r = region_from_box(1, 1, 3, 3);
julia> profile_horizontal(r, img)
3-element Vector{Float64}:
6.0
15.0
24.0Regions.profile_vertical — Methodprofile_vertical(r::Region, image::AbstractMatrix) -> VectorReturn the column-wise sum of image pixels that fall inside the non-empty, non-complement region r. The returned vector has length width(r); index i corresponds to image column left(r) + i - 1.
Pixels outside the region are not counted, so this is equivalent to masking the image with the region and then summing along the row axis.
image is indexed as image[row, column] (the package's standard convention) and must contain every pixel of r's bounding box.
julia> using Regions
julia> img = Float64[1 2 3; 4 5 6; 7 8 9];
julia> r = region_from_box(1, 1, 3, 3);
julia> profile_vertical(r, img)
3-element Vector{Float64}:
12.0
15.0
18.0Region Distance Transform
Regions.distance_transform — Methoddistance_transform(r::Region) -> Matrix{Int}Return the city-block (4-connected, Manhattan) distance transform of r as a Matrix{Int} sized (height(r), width(r)). Index [1, 1] corresponds to image pixel (column = left(r), row = top(r)) — matching the indexing convention used by region_to_image.
For every pixel inside the region the matrix holds its shortest 4-connected distance to the nearest pixel outside the region; boundary pixels are therefore 1 and pixels outside the bounding box implicitly behave as 0. Locations inside the bounding box but outside the region (e.g. holes) are 0.
The implementation is the classic Rosenfeld–Pfaltz two-pass algorithm: a forward pass sets each region pixel to 1 + min(west, north) and a backward pass takes the minimum with 1 + min(east, south). Neighbours outside the region count as 0, so the first interior pixel from any side always lands at distance 1.
Requires a non-empty, non-complement region.
julia> using Regions
julia> distance_transform(Region([Run(0, 0:0)]))
1×1 Matrix{Int64}:
1
julia> distance_transform(region_from_box(0, 0, 2, 2))
3×3 Matrix{Int64}:
1 1 1
1 2 1
1 1 1Index
Regions.RegionsRegions.DownsampleRoundingModeRegions.PointListRegions.RegionRegions.RunBase.:(==)Base.containsBase.containsBase.containsBase.copyBase.hashBase.isemptyBase.isemptyBase.islessBase.showBase.showBase.unionImageBinarization.BinarizationAPI.binarizeRegions._differenceRegions._intersect!Regions._intersectionRegions._mergeRegions._minkowski_additionRegions._minkowski_additionRegions._minkowski_additionRegions._minkowski_subtractionRegions._minkowski_subtractionRegions._minkowski_subtractionRegions._pack!Regions._raw_momentsRegions._unionRegions.areaRegions.areaRegions.area_of_holesRegions.aspect_ratioRegions.bottomRegions.bottomRegions.bounding_boxRegions.boundsRegions.boundsRegions.bounds_centerRegions.center_regionRegions.central_momentsRegions.centroidRegions.centroidRegions.circularityRegions.closingRegions.closingRegions.compactnessRegions.complementRegions.componentsRegions.contourRegions.convex_areaRegions.convex_hullRegions.convex_hullRegions.convex_hull_cwRegions.convex_perimeterRegions.convexityRegions.differenceRegions.dilationRegions.dilationRegions.distance_transformRegions.downsampleRegions.equivalent_ellipseRegions.erosionRegions.erosionRegions.feret_diametersRegions.feret_diametersRegions.fiber_lengthRegions.fiber_widthRegions.fill_holesRegions.fill_holesRegions.filter_areaRegions.flusser_momentsRegions.heightRegions.holesRegions.hu_momentsRegions.inner_boundaryRegions.inner_boundaryRegions.intersectionRegions.invertRegions.invertRegions.invertRegions.is_centeredRegions.iscloseRegions.iscloseRegions.isoverlappingRegions.isoverlappingRegions.istouchingRegions.istouchingRegions.leftRegions.leftRegions.maximum_widthRegions.minimum_area_bounding_rectangleRegions.minimum_area_bounding_rectangleRegions.minimum_bounding_circleRegions.minimum_bounding_circleRegions.minimum_perimeter_bounding_rectangleRegions.minimum_perimeter_bounding_rectangleRegions.minimum_widthRegions.momentsRegions.morphological_gradientRegions.morphological_gradientRegions.normalized_momentsRegions.number_of_holesRegions.openingRegions.openingRegions.outer_boundaryRegions.outer_boundaryRegions.perforationRegions.perimeterRegions.perimeterRegions.point_atRegions.profile_horizontalRegions.profile_verticalRegions.region_from_boxRegions.region_from_circleRegions.region_from_ellipseRegions.region_from_line_segmentRegions.region_from_pointRegions.region_from_point_listRegions.region_from_polygonRegions.region_from_ringRegions.region_to_imageRegions.regions_to_imageRegions.remove_collinearRegions.rightRegions.rightRegions.roughnessRegions.roundnessRegions.scale_invariant_momentsRegions.segment_multi_thresholdRegions.set_intersectionRegions.set_unionRegions.simplified_perimeterRegions.simplify_radial_distanceRegions.sphericityRegions.to_point_listRegions.topRegions.topRegions.translateRegions.translateRegions.translateRegions.translateRegions.upscaleRegions.vectorized_boundariesRegions.width