algorithm - computational - ¿Cómo calcular el OBB de múltiples curvas?
cgal (1)
Dadas varias curvas, incluidos segmentos de línea y arcos circulares, ¿cómo calcular el OBB total de todas las curvas?
Parece que la unión de cada OBB de las curvas individuales no es correcta, no es la cobertura mínima.
Mira esta imagen, ¿cómo calcular el cuadro rojo?
también debe agregar la entrada en forma de vector para que podamos probar sus datos ... Me acercaría así:
-
encontrar el centro del eje alineado bbox
O(n)
-
calcular la distancia máxima en cada ángulo
O(n)
solo cree una tabla para ángulos
m
suficientes (como un paso de 5 grados, entoncesm = 360/5
) donde para cada sección de ángulo recuerde solo la distancia máxima del punto distante. -
calcular la distancia perpendicular máxima para cada rotación
O(m^2)
así que para cada valor de cálculo de la sección de ángulo que es:
value[actual_section] = max(distance[i]*cos(section_angle[i]-section_angle[actual_section]))
donde cubro
+/- 90 deg
alrededor del ángulo de sección real, así que ahora tienes distancias perpendiculares máximas para cada ángulo ... -
escoja la mejor solución
O(m)
así que mire todas las rotaciones de 0 a 90 grados y recuerde la que tiene un área mínima de OBB . Solo para asegurarse de que el OBB esté alineado con el ángulo de sección y el tamaño de los ejes es el
value
de ese ángulo y todos los incrementos de 90 grados ... alrededor del centro
Esto no dará como resultado una solución óptima, sino muy cercana. Para mejorar la precisión, puede usar más secciones de ángulo o incluso buscar de forma recursiva la solución encontrada con un ángulo de ángulo cada vez más pequeño (no es necesario calcular las otras áreas de ángulo después de la primera ejecución)
[Editar1]
Traté de codificar esto en C ++ como prueba de concepto y usar su imagen (manejada como un conjunto de puntos) como entrada, así que aquí el resultado para que tenga algo con lo que comparar (para fines de depuración)
gris
son puntos detectados de su imagen, rectángulo
verde
está alineado a los ejes
BBox
el rectángulo
rojo
se encuentra
OBBox
.
Los puntos
aqua
se encuentran a una distancia máxima por intervalo de ángulo y
los
puntos
verdes
son una distancia perpendicular máxima para intervalos de ángulo
+/-90deg
grados.
Utilicé
400
ángulos y, como pueden ver, el resultado está bastante cerca ...
360/400 deg
precisión, por lo que este enfoque funciona bien ...
Aquí la fuente de C ++:
//---------------------------------------------------------------------------
struct _pnt2D
{
double x,y;
// inline
_pnt2D() {}
_pnt2D(_pnt2D& a) { *this=a; }
~_pnt2D() {}
_pnt2D* operator = (const _pnt2D *a) { *this=*a; return this; }
//_pnt2D* operator = (const _pnt2D &a) { ...copy... return this; }
};
struct _ang
{
double ang; // center angle of section
double dis; // max distance of ang section
double pdis; // max perpendicular distance of +/-90deg section
// inline
_ang() {}
_ang(_ang& a) { *this=a; }
~_ang() {}
_ang* operator = (const _ang *a) { *this=*a; return this; }
//_ang* operator = (const _ang &a) { ...copy... return this; }
};
const int angs=400; // must be divisible by 4
const int angs4=angs>>2;
const double dang=2.0*M_PI/double(angs);
const double dang2=0.5*dang;
_ang ang[angs];
List<_pnt2D> pnt;
_pnt2D bbox[2],obb[4],center;
//---------------------------------------------------------------------------
void compute_OBB()
{
_pnt2D ppp[4];
int i,j; double a,b,dx,dy;
_ang *aa,*bb;
_pnt2D p,*pp; DWORD *q;
// convert bmp -> pnt[]
pnt.num=0;
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->LoadFromFile("in.bmp");
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
for (p.y=0;p.y<bmp->Height;p.y++)
for (q=(DWORD*)bmp->ScanLine[int(p.y)],p.x=0;p.x<bmp->Width;p.x++)
if ((q[int(p.x)]&255)<20)
pnt.add(p);
delete bmp;
// axis aligned bbox
bbox[0]=pnt[0];
bbox[1]=pnt[0];
for (pp=pnt.dat,i=0;i<pnt.num;i++,pp++)
{
if (bbox[0].x>pp->x) bbox[0].x=pp->x;
if (bbox[0].y>pp->y) bbox[0].y=pp->y;
if (bbox[1].x<pp->x) bbox[1].x=pp->x;
if (bbox[1].y<pp->y) bbox[1].y=pp->y;
}
center.x=(bbox[0].x+bbox[1].x)*0.5;
center.y=(bbox[0].y+bbox[1].y)*0.5;
// ang[] table init
for (aa=ang,a=0.0,i=0;i<angs;i++,aa++,a+=dang)
{
aa->ang=a;
aa-> dis=0.0;
aa->pdis=0.0;
}
// ang[].dis
for (pp=pnt.dat,i=0;i<pnt.num;i++,pp++)
{
dx=pp->x-center.x;
dy=pp->y-center.y;
a=atan2(dy,dx);
j=floor((a/dang)+0.5); if (j<0) j+=angs; j%=angs;
a=(dx*dx)+(dy*dy);
if (ang[j].dis<a) ang[j].dis=a;
}
for (aa=ang,i=0;i<angs;i++,aa++) aa->dis=sqrt(aa->dis);
// ang[].adis
for (aa=ang,i=0;i<angs;i++,aa++)
for (bb=ang,j=0;j<angs;j++,bb++)
{
a=fabs(aa->ang-bb->ang);
if (a>M_PI) a=(2.0*M_PI)-a;
if (a<=0.5*M_PI)
{
a=bb->dis*cos(a);
if (aa->pdis<a) aa->pdis=a;
}
}
// find best oriented bbox (the best angle is ang[j].ang)
for (b=0,j=0,i=0;i<angs;i++)
{
dx =ang[i].pdis; i+=angs4; i%=angs;
dy =ang[i].pdis; i+=angs4; i%=angs;
dx+=ang[i].pdis; i+=angs4; i%=angs;
dy+=ang[i].pdis; i+=angs4; i%=angs;
a=dx*dy; if ((b>a)||(i==0)) { b=a; j=i; }
}
// compute endpoints for OBB
i=j;
ppp[0].x=ang[i].pdis*cos(ang[i].ang);
ppp[0].y=ang[i].pdis*sin(ang[i].ang); i+=angs4; i%=angs;
ppp[1].x=ang[i].pdis*cos(ang[i].ang);
ppp[1].y=ang[i].pdis*sin(ang[i].ang); i+=angs4; i%=angs;
ppp[2].x=ang[i].pdis*cos(ang[i].ang);
ppp[2].y=ang[i].pdis*sin(ang[i].ang); i+=angs4; i%=angs;
ppp[3].x=ang[i].pdis*cos(ang[i].ang);
ppp[3].y=ang[i].pdis*sin(ang[i].ang); i+=angs4; i%=angs;
obb[0].x=center.x+ppp[0].x+ppp[3].x;
obb[0].y=center.y+ppp[0].y+ppp[3].y;
obb[1].x=center.x+ppp[1].x+ppp[0].x;
obb[1].y=center.y+ppp[1].y+ppp[0].y;
obb[2].x=center.x+ppp[2].x+ppp[1].x;
obb[2].y=center.y+ppp[2].y+ppp[1].y;
obb[3].x=center.x+ppp[3].x+ppp[2].x;
obb[3].y=center.y+ppp[3].y+ppp[2].y;
}
//---------------------------------------------------------------------------
Usé la plantilla de lista dinámica mía, así que:
List<double> xxx;
es lo mismo que
double xxx[];
xxx.add(5);
agrega
5
al final de la lista
Elemento de matriz de acceso
xxx[7]
(seguro)
xxx.dat[7]
matriz de acceso
xxx.dat[7]
(acceso directo inseguro pero rápido)
xxx.num
es el tamaño real utilizado de la matriz
xxx.reset()
borra la matriz y establece
xxx.num=0
xxx.allocate(100)
preasigna espacio para
100
elementos
Puede ignorar la parte
// convert bmp -> pnt[]
VCL ya que ya tiene sus datos.