How to use GeoPandas or Fiona to find the inner part of the polygon through subtraction? - polygon

enter image description here
enter image description here
I am trying to find a way how we can get the upper part of the polygon (image 2) from the bigger polygon (image 1) using GeoPandas/Fiona functions. The other way round is quite easy by using the overlay set operation of "difference" but the way I want it, the functions/tools in GeoPandas does not work.

simply you need https://geopandas.org/en/stable/docs/user_guide/set_operations.html#
you have not provided any geometry. Have used Ukraine as larger geometry and a number of box geometries that overlap Ukraine
full example below
import shapely
import geopandas as gpd
import geopy.distance
import numpy as np
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
dims = (10, 10)
a, b, c, d = world.loc[world["name"].eq("Ukraine")].total_bounds
g = .1
# generate some polygons and a line that cuts through some of the polygons
gdf_grid = gpd.GeoDataFrame(
geometry=[
shapely.geometry.box(minx + g, miny + g, maxx - g, maxy - g)
for minx, maxx in zip(
np.linspace(a, c, dims[0]), np.linspace(a, c, dims[0])[1:]
)
for miny, maxy in zip(
np.linspace(b, d, dims[1]), np.linspace(b, d, dims[1])[1:]
)
],
crs="epsg:4326",
).sample(8, random_state=1)
big_without_small = world.loc[world["name"].eq("Ukraine")].overlay(gdf_grid, how="difference")
big_without_small.plot()

Related

Distance between contours in Julia

Hi I'm using contour detection in Julia to detect contours. UPDATE: I need to find closest distance between two contours (made from broken lines).Is there function that finds the closest distance ? I'm not sure whats the best way of joining those two.
The case I'm looking at is
so I've used the code given at link example link
[https://juliaimages.org/v0.21/democards/examples/contours/contour_detection/] I've contours with n-element Vector{CartesianIndex} datatype. How do I join the line / two contours?
UPDATE: I've tried removing close contour points using the following
sorted_contours = sort!(contours, by=x -> x[1])
for i in eachindex(sorted_contours)
if check_adjacent(sorted_contours[i], sorted_contours) >= 1
sorted_contours[i] = CartesianIndex.(0, 0)
end
end
deleteat!(sorted_contours,findall(sorted_contours.==CartesianIndex.(0, 0))) but from this [![enter image description here][3]][3] it improved to this only [![enter image description here][3]][3]
I want to remove these points as these are incorrect contour points. Or how could I remove the spur which causes it?
You are vague in your question (as of 27 November) about the packages you are using. If you are using Contour, here is a nonoptimized way to find the minimum distance between two contours. You would need to select the two contours using contour() for the different x, y, z sets of values in each of your different contours.
using Contour
using Distances
const x = -3:0.01:3
const y = -4:0.02:5
const z = [Float64((xi^2 + yi^2)) for xi in x, yi in y]
x5, y5 = contour(x, y, z, 5) |> lines |> first |> coordinates
points5 = [[x5[i], y5[i]] for i in eachindex(x5)]
x10, y10 = contour(x, y, z, 10) |> lines |> first |> coordinates
points10 = [[x10[i], y10[i]] for i in eachindex(x10)]
distances = [(euclidean(p1, p2), p1, p2) for p1 in points5, p2 in points10]
d, idx = findmin(t -> t[1], distances)
#show d, idx #(d, idx) = (0.9261944913478685, CartesianIndex(918, 126))
#show distances[idx] # distances[idx] = (0.9261944913478685, [1.64, 1.52], [2.32, 2.1488372093023256])
#show points5[idx[1]] points10[idx[2]]
# points5[idx[1]] = [1.64, 1.52]
# points10[idx[2]] = [2.32, 2.1488372093023256]
It's the best approach to show your effort and explain where you have the problem, and you'll get the best answers to solve it. Since this topic interested me and made me curious, I found it worthwhile, although your question doesn't meet the requirements of a good question.
To better detect the contours, First, I perform thinning on the image to locate the contours easily. I use the following image as the input to see the results in a bigger size:
using Images, FileIO, ImageBinarization
img = load("img.png")
gimg = Gray.(img)
bin = binarize(gimg, UnimodalRosin()) .> 0.5
thinned = thinning(bin, algo=GuoAlgo())
The thinned variable represents the following picture:
julia> Gray.(thinned)
Now, I define my functions to find the contours:
"""
check_adjacent(loc::CartesianIndex{2}, all_locs::Vector{CartesianIndex{2}})
Return the number of adjacent locations that are in `all_locs` to `loc`.
This function is used to determine whether a location is a corner (contour) or not.
# Arguments
- `loc::CartesianIndex{2}`: the location to check.
- `all_locs::Vector{CartesianIndex{2}}`: all the locations that aren't black.
# Returns
- `Int`: the number of adjacent locations that are in `all_locs` to `loc`.
"""
function check_adjacent(loc::CartesianIndex{2}, all_locs::Vector{CartesianIndex{2}})
conditions = [
loc - CartesianIndex(0,1) ∈ all_locs,
loc + CartesianIndex(0,1) ∈ all_locs,
loc - CartesianIndex(1,0) ∈ all_locs,
loc + CartesianIndex(1,0) ∈ all_locs,
loc - CartesianIndex(1,1) ∈ all_locs,
loc + CartesianIndex(1,1) ∈ all_locs,
loc - CartesianIndex(1,-1) ∈ all_locs,
loc + CartesianIndex(1,-1) ∈ all_locs
]
return sum(conditions)
end
"""
find_the_contour(img::BitMatrix)
Return the contours of the image `img`.
# Arguments
- `img::BitMatrix`: the image to get the contours of.
# Returns
- `Vector{CartesianIndex{2}}`: the contours of the image `img`.
"""
function find_the_contour(img::BitMatrix)
img_matrix = convert(Array{Float64}, img)
not_black = findall(!=(0.0), img_matrix)
contours = Vector{CartesianIndex{2}}()
for nb∈not_black
t = check_adjacent(nb, not_black)
t==1 && push!(contours, nb)
end
return contours
end
Now I use the find_the_contour function:
julia> contours = find_the_contour(thinned)
4-element Vector{CartesianIndex{2}}:
CartesianIndex(161, 334)
CartesianIndex(256, 452)
CartesianIndex(69, 1213)
CartesianIndex(210, 1243)
How should I know this function locates the contours correctly? I define another function to highlight the contours:
"""
highlight_contours(img, contours)
Return the image `img` with the contours `contours` highlighted with red color.
# Arguments
- `img::BitMatrix`: the image to highlight the contours on.
- `contours::Vector{CartesianIndex{2}}`: the contours to highlight.
- `resize`::Bool=false: whether to resize the image to 512x512 or not.
- `scale`::Union{Int64, Float64}=1: the scale to resize the image to.
# Returns
- `img_matrix`: the image `img` with the contours `contours` highlighted with red color.
"""
function highlight_contours(img, contours; resize::Bool=false, scale::Union{Int64, Float64}=1)
img_matrix = convert(Array{RGB, 2}, img)
for c∈contours
img_matrix[c] = RGB(1,0,0)
end
if resize
img_matrix = typeof(scale)==Int64 ? imresize(img_matrix, size(img).*scale) : imresize(img_matrix, ratio=scale)
end
return img_matrix
end
Then I pass the thinned Matrix and contours to it:
julia> highlited = highlight_contours(thinned, contours)
If you zoom in a little bit, you find the contours with red color:
Now that I made sure the contours are located successfully, I go for calculating the distance between contours. I used the Euclidean distance between the CartesianIndexes of each contour! You can adjust it to match your taste:
"""
distance_between_contours(V::Vector{T}, v::T) where T<:Tuple{Int64, Int64}
Return the euclidean distance between the contour `v` and the contours in `V`.
# Arguments
- `V::Vector{T}`: the contours to check the distance to.
- `v::T`: the contour to check the distance from.
# Returns
- `Float64`: the euclidean distance between the contour `v` and the contours in `V`.
"""
function distance_between_contours(V::Vector{T}, v::T) where T<:Tuple{Int64, Int64}
return broadcast((x, y)->sqrt(sum((x.-y).^2)), V, Ref(v))
end
"""
distance_between_contours(all_contours::Vector{CartesianIndex{2}})
Return the euclidean distance between each pair of contours in `all_contours`.
# Arguments
- `all_contours::Vector{CartesianIndex{2}}`: the contours to calculate the distance between.
# Returns
- `Matrix{Float64}`: the euclidean distance between each pair of contours in `all_contours`.
"""
function distance_between_contours(all_contours::Vector{CartesianIndex{2}})
n = length(all_contours)
distances = Matrix{Float64}(undef, n, n)
for j in eachindex(all_contours)
distances[:, j] .= distance_between_contours(
Tuple.(all_contours),
Tuple(all_contours[j])
)
end
return distances
end
Now I pass it the contours and calculate the pairwise Euclidean distance between each:
julia> distance_between_contours(contours)
4×4 Matrix{Float64}:
0.0 151.489 883.801 910.32
151.489 0.0 783.639 792.336
883.801 783.639 0.0 144.156
910.32 792.336 144.156 0.0
In this matrix, for example, the 151.489 value shows the distance between the first contour and the second. Remember that the first and second contours are the followings:
CartesianIndex(161, 334)
CartesianIndex(256, 452)
I followed the same procedure for your image, and this the followings are the distances and the contours:
julia> distance_between_contours(contours)
6×6 Matrix{Float64}:
0.0 27.7308 88.6397 168.6 166.676 170.188
27.7308 0.0 60.9262 140.872 139.284 142.622
88.6397 60.9262 0.0 80.1311 79.2465 82.0061
168.6 140.872 80.1311 0.0 26.2488 19.2354
166.676 139.284 79.2465 26.2488 0.0 8.06226
170.188 142.622 82.0061 19.2354 8.06226 0.0
highlited = highlight_contours(thinned, contours, resize=true, scale=2)
TL;DR
Code:
using Images, FileIO, ImageBinarization
function check_adjacent(loc::CartesianIndex{2}, all_locs::Vector{CartesianIndex{2}})
conditions = [
loc - CartesianIndex(0,1) ∈ all_locs,
loc + CartesianIndex(0,1) ∈ all_locs,
loc - CartesianIndex(1,0) ∈ all_locs,
loc + CartesianIndex(1,0) ∈ all_locs,
loc - CartesianIndex(1,1) ∈ all_locs,
loc + CartesianIndex(1,1) ∈ all_locs,
loc - CartesianIndex(1,-1) ∈ all_locs,
loc + CartesianIndex(1,-1) ∈ all_locs
]
return sum(conditions)
end
function find_the_contour(img::BitMatrix)
img_matrix = convert(Array{Float64}, img)
not_black = findall(!=(0.0), img_matrix)
contours = Vector{CartesianIndex{2}}()
for nb∈not_black
t = check_adjacent(nb, not_black)
t==1 && push!(contours, nb)
end
return contours
end
function distance_between_contours(V::Vector{T}, v::T) where T<:Tuple{Int64, Int64}
return broadcast((x, y)->sqrt(sum((x.-y).^2)), V, Ref(v))
end
function distance_between_contours(all_contours::Vector{CartesianIndex{2}})
n = length(all_contours)
distances = Matrix{Float64}(undef, n, n)
for j in eachindex(all_contours)
distances[:, j] .= distance_between_contours(
Tuple.(all_contours),
Tuple(all_contours[j])
)
end
return distances
end
img = load("img.png")
gimg = Gray.(img)
bin = binarize(gimg, UnimodalRosin()) .> 0.5
thinned = thinning(bin, algo=GuoAlgo())
contours = find_the_contour(thinned)
distance_between_contours(contours)

Positioning objects parallel with a mesh

I'm trying to align multiple line objects along a human body circumference depending on the orientation of the triangles from the mesh. I would like to put the lines parallel to the mesh. I correctly assign the position for the lines along the circumference, but I also need to add the rotation of the lines such that to be parallel with the body.
The body is a mesh formed by multiple triangles and every line is "linked" with a triangle.
All I have is:
3 points for the closest triangle from the mesh for every line
The normal of the triangle
The positions for the instantiated lines (2 points, start and end)
I need to calculate the angle for every X, Y, Z axes for the line such that the normal of the triangle is perpendicular with the line mesh. I don't know how to get the desired angle. I really appreciate if someone would like to help me.
input:
FVector TrianglePoints[3];
FVector Triangle_Normal; //Calculated as (B-A)^(C-A), where A,B,C are the points of the triangle
FVector linePosition; //I also have the start line and the endLine position if that helps
ouput:
//FRotator rotation(x,y,z), such that the triangle normal and the line object to be perpendicular.
An overview of the circumference line construction. Now the rotation is calculated using the Start position and End position for each line. When we cross some irregular parts of the mesh we want to rotate the lines correctly. Now the rotation is fixed, depending just on the line start and end position.
If I have understood correctly your goal, here is some related vector geometry:
A,B,C are the vertices of the triangle:
A = [xA, yA, zA],
B = [xB, yB, zB]
C = [xC, yC, zC]
K,L are the endpoints of the line-segment:
K = [xK, yK, zK]
L = [xL, yL, zL]
vectors are interpreted as row-vectors
by . I denote matrix multiplication
by x I denote cross product of 3D vectors
by t() I denote the transpose of a matrix
by | | I denote the norm (magnitude) of a vector
Goal: find the rotation matrix and rotation transformation of segment KL
around its midpoint, so that after rotation KL is parallel to the plane ABC
also, the rotation is the "minimal" angle rotation by witch we need to
rotate KL in order to make it parallel to ABC
AB = B - A
AC = C - A
KL = L - K
n = AB x AC
n = n / |n|
u = KL x n
u = u / |u|
v = n x u
cos = ( KL . t(v) ) / |KL|
sin = ( KL . t(n) ) / |KL|
U = [[ u[0], u[1], u[2] ],
[ v[0], v[1], v[2] ],
[ n[0], n[1], n[2] ],
R = [[1, 0, 0],
[0, cos, sin],
[0, -sin, cos]]
ROT = t(U).R.U
then, one can rotate the segment KL around its midpoint
M = (K + L)/2
Y = M + ROT (X - M)
Here is a python script version
A = np.array([0,0,0])
B = np.array([3,0,0])
C = np.array([2,3,0])
K = np.array([ -1,0,1])
L = np.array([ 2,2,2])
KL = L-K
U = np.empty((3,3), dtype=float)
U[2,:] = np.cross(B-A, C-A)
U[2,:] = U[2,:] / np.linalg.norm(U[2,:])
U[0,:] = np.cross(KL, U[2,:])
U[0,:] = U[0,:] / np.linalg.norm(U[0,:])
U[1,:] = np.cross(U[2,:], U[0,:])
norm_KL = np.linalg.norm(KL)
cos_ = KL.dot(U[1,:]) / norm_KL
sin_ = KL.dot(U[2,:]) / norm_KL
R = np.array([[1, 0, 0],
[0, cos_, sin_],
[0,-sin_, cos_]])
ROT = (U.T).dot(R.dot(U))
M = (K+L) / 2
K_rot = M + ROT.dot( K - M )
L_rot = M + ROT.dot( L - M )
print(L_rot)
print(K_rot)
print(L_rot-K_rot)
print((L_rot-K_rot).dot(U[2,:]))
A more inspired solution was to use a procedural mesh, generated at runtime, that have all the requirements that I need:
Continuously along multiple vertices
Easy to apply a UV map for texture tiling
Can be updated at runtime
Isn't hard to compute/work with it

Finding if a circle is fully contained within multiple triangles?

In a game, an area is defined by triangles that never overlap, and characters are defined by circles.
How can I know whether the full character's collision circle is contained within these triangles?
Example image:
Here, the red parts are outside triangles, so the circle isn't contained within them. Is there an algorithm that can detect this?
I've only came up with "non-perfect" solutions, like sampling points at the border of the circle, then testing if each is inside a triangle.
So basically, the triangles form a domain with polygonal boundary and you want to check if a disk, defined by a center point and a radius is contained inside the domain. So if you start with the triangles, you have to find a way to extract the polygonal boundary of your domain and represent it as a 2D array (matrix) of shape n rows and two columns so that every row is the two coordinates of a vertex point of the polygonal boundary line and the points are ordered so that they are consecutive order along the boundary in a counterclockwise position, i.e. when you walk in a direction from point of index i to the next point i+1 the domain stays on your left. For example, here is the representation of a polygonal boundary of a domain like yours:
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
Observe that the first and the last points are the same.
In such a scenario, maybe you can try the following algorithm:
import numpy as np
import math
def angle_and_dist(p1, p2, o):
p12 = p2 - p1
op1 = p1 - o
op2 = p2 - o
norm_p12 = math.sqrt(p12[0]**2 + p12[1]**2)
norm_op1 = math.sqrt(op1[0]**2 + op1[1]**2)
norm_op2 = math.sqrt(op2[0]**2 + op2[1]**2)
p12_perp = np.array([ - p12[1], p12[0] ])
h = - op1.dot(p12_perp)
theta12 = op1.dot(op2) / (norm_op1*norm_op2)
theta12 = math.acos( theta12 )
if h < 0:
theta12 = - theta12
if op1.dot(p12) > 0:
return theta12, norm_op1
elif op2.dot(p12) < 0:
return theta12, norm_op2
else:
return theta12, h/norm_p12
def is_in_polygon(p, disk):
o, r = disk
n_p = len(p)-1
index_o = 0
h_min = 400
for i in range(n_p):
theta, h = angle_and_dist(p[i,:], p[i+1,:], o)
index_o = index_o + theta
if 0 <= h and h < h_min:
h_min = h
if theta <= math.pi/100:
return 'center of disc is not inside polygon'
elif theta > math.pi/100:
if h_min > r:
return 'disc is inside polygon'
else:
return 'center of disc is inside polygon but disc is not'
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
# A test example:
#disc = (np.array([3*a/4, 2]), a/4-0.001)
disc = (np.array([3*a/4, 2]), math.sqrt(3)*a/8 - 0.0001)
print(is_in_polygon(Pgon, disc))

How to detect and calculate an angle in image? (spray)

I'm trying write a code to find a nozzle spray angle (image) after binary process. I read something about hough transform to do this but I can't apply correctly. (Calculating the angle between two lines in an image in Python and How to find angle of hough lines detected?)
For a while I have this:
import cv2
import numpy as np
import math
import matplotlib.pyplot as plt
img_grey = cv2.imread('sprayang.jpg', cv2.IMREAD_GRAYSCALE)
img = cv2.GaussianBlur (img_grey, (5,5), 0)
th = 0
max_val = 255
ret, o1 = cv2.threshold(img, th, max_val, cv2.THRESH_BINARY + cv2.THRESH_OTSU )
And the generated image is that:
Using the angle measurement tool in ImageJ I measured about 90°.
cast 2 horizontal rays (from each direction)
find first hit with your spray
let call the 2D points p0,p1 from left and p2,p3
compute angle
smaller unsigned angle between 2 vectors in 2D is defined by dot product like this:
ang = acos( dot( p1-p0 , p2-p1 ) / (|p1-p0|*|p2-p1|) )
(x1-x0)*(x2-x1) + (y1-y0)*(y2-y1)
ang = acos( ------------------------------------------------------------- )
sqrt( (x1-x0)^2 + (y1-y0)^2 ) * sqrt( (x2-x1)^2 + (y2-y1)^2 )

Recursive Surfaces in Pygame

Suppose you have an app with a big gaping hole of negative space, so you want to fill it up with something... so why not fill the void with a copy of the app itself?
I have an app. It will have nested apps. Some apps are normal (i.e., rectangular), but (most) others are elliptical, or even triangular. There are connector layers for nesting the various combinations of app geometries.
While still in progress, there is a notion of somehow managing the ratio of positive space vs. negative space caused by such nesting. One idea I'm working on is subclassing those connectors to create more areas where there will be positive space, and dynamically selecting a subclass.
This will be able to give me a number of extra positive spaces-to-be, which need to be filled with something.
Based on:
Pygame. How do I resize a surface and keep all objects within proportionate to the new window size?
At a high level, we need to:
take a copy of whatever is going to be fractalized
compute where the fractalized image can be repeated
(the bounding rectangles, here called "recursion points")
create a temporary surface for drawing
scale said copy to the size of the temp surface
draw the scaled copy onto the temp surface
In other words:
def draw_background (self, temp):
App.draw_background (self, temp) # default background for apps is to show a default image
self.child.draw_scene (temp) # draw whatever is going to be fractalized
def draw_foreground (self, temp): # fractalization function
TR = temp.get_rect () # bounding rect for parent
X, Y, W, H = TR
ts = pygame.Surface ((W, H)) # get a fresh surface for working
pic = temp.copy () # could be anything
for rp in self.recursion_points (temp):
x, y, w, h = rp
w, h = tr ((w, h)) # round coordinates
trans = pygame.transform.scale (pic, (w, h)) # scale fake screen to bounding rect
ts.blit (trans, (x, y)) # blit fake screen onto working surface
temp.blit (ts, (X, Y)) # blit working-surface onto real surface
Client code:
d = None # default behavior for no app is to not display a foreground
# these adapters handle differing window geometries
c = CircledSquare (d, rotation=STRAIGHT) # middle circle, inner square
b = SquaredCircle (c, background=SECONDARY_BACKGROUND) # outer square, middle circle
a = RecursiveComposite (b) # without this layer, the inner square is not visible... boring!
Geometry functions:
def recursive_affine (rect, dx, dy, rw, rh, n):
x, y, w, h = rect
for k in range (1, n + 1):
dx, dy = dx * rw, dy * rh
x, y = x + dx, y + dy
w, h = w * rw, h * rh
yield x, y, w, h
def recurse_point (rect, rp, minsz):
X, Y, W, H = rect
x, y, w, h = rp
# get scale and offset for recursion point
dx, dy = x - X, y - Y
rw, rh = w / W, h / H
# get number of recursions until < minsz
f = lambda a, b, c: (log (a) - log (b)) / log (c)
xmin, ymin = minsz
xn, yn = f (xmin, w, rw), f (ymin, h, rh)
n = min (xn, yn)
n = ceil (n)
# recursively apply scale and offset
tail = recursive_affine (rp, dx, dy, rw, rh, n)
return rp, *tail

Resources