Considering the mesh is close to a plane,
that can be done by taking boundary vertices, then try to see which 4 of them are closest to a 90° degrees angle.
This script does that:
import bpy
import bmesh
from mathutils import Vector
import heapq
bpy.ops.object.mode_set(mode = 'OBJECT')
# Get active object
obj = bpy.context.active_object
# Deselect all vertices
bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.mesh.select_mode(type="VERT")
bpy.ops.mesh.select_all(action = 'DESELECT')
# Back to object mode so that we can select vertices
bpy.ops.object.mode_set(mode = 'OBJECT')
# Gets bmesh from obj data
bm = bmesh.new()
bm.from_mesh( obj.data )
# Allow indexed access for vertices and edges
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
# Vector from an edge
def vect_from_edge( e ):
return (e.verts[0].co - e.verts[1].co).normalized()
# Absolute of the dot product: the smaller the closer to 90°
# => "corner intensity"
def abs_dot( e1, e2 ):
return abs( vect_from_edge( e1 ).dot( vect_from_edge( e2 ) ) )
boundary_vertices = []
# Collect boundary vertices
for vert in (v for v in bm.verts if v.is_boundary):
# Corresponding boundary edges
edges = [e for e in vert.link_edges if e.is_boundary]
# But consider only 2 (we dont want vertices at the jonction of 2 parts)
if len( edges ) == 2:
# Associates the vertex to its "corner intensity"
boundary_vertices.append( (vert, abs_dot( edges[0], edges[1] ) ) )
# Get the 4 smallest "corner intensity" which should correspond to the wanted result
for v in [vi[0] for vi in heapq.nsmallest( 4, boundary_vertices, key = lambda vi:vi[1] )]:
obj.data.vertices[v.index].select = True
# Release the bmesh
bm.free()
Note: not sure this approach solves all the situations, though.