sábado, 14 de abril de 2007

Intentando aprender manejo de bitmap III - Pixels por mm (en delphi)

(Actualizado)

Después de mucho andar finalmente resolví el problema como lo hacía originalmente, ya que aparentemente la información contenida en un archivo de tipo bitmap, no tiene los datos necesarios. Conclusión: hay que crear como sea una escala para calcular la relación pixels por mm.


  1. marcar 3 puntos en la imagen digitalizada p1, p2, p3 de modo que formen un ángulo recto. p1p2 es un segmento paralelo al eje Ox y p2p3 al eje Oy (así obtengo la escala en X y en Y, esto es porque la densidad de pixeles no tiene porque coincidir para ambos ejes).

  2. Ingreso la distancia real en mm entre p1p2 y p2p3 (p2 vértice).

  3. Calculo la distancia de esos segmentos en pixels, y como la distancia equivalente en mm es conocida por mi (fue ingresada) obtengo un factor en X y factor en Y, que son las escalas para cada eje.

  4. A cada coordenada del punto (X,Y) las multiplico por el factor en X y en Y para obtener el equivalente en mm de las mismas. Luego todos los cálculos arrojan como resultados valores en mm.


Asi funciona correctamente, medidas de ángulos, rectas, distancias, proyecciones, etc.
Pero tenía una duda, ya que para cada imagen debo guardar esas escalas, temía que éstas cambiaran al cambiar la resolución, y por lo tanto no bastaran por si sólo eso datos, sino que ademas tendría que guardar la resolución a la que se calcularon los factores, y no es así.

Mi imagen es un bitmap que está dentro de un objeto de TImage, y tanto el bitmap, y el objeto imagen (de TImage), quedan con sus propiedades Width y Height constantes independientemente de la resolución de pantalla. Por lo tanto los factores en X e Y tampoco cambian.
Esto se debe a tres cosas:


  1. El bitmap contiene información sobre sus dimensiones, y son las que serán usadas para representarlo o mostrarlo. Lo que si sucede es que al adaptar el bitmap a las diferentes resoluciones, este se distorcione levemente. Si la propiedad Align := alNone para el bitmap, entonces no modifica sus dimensiones originales para adaptarlo a algun contenedor. En caso contrario, las dimensiones se alteran, pero como las guarda se respetan independientemente de la resolución.

  2. La información en tiempo de diseño del form y los componentes contenidos en el están en el archivo .dfm (y luego se respetan, cualquiera sea la resolución). Esto último lo verifiqué abriendo el archivo .dfm correspondiente con el Notepad. (Válido si Align := alNone)

  3. Si el TImage y el TBitmap tienen su propiedad Align := alClient tampoco cambia la escala, ya que tanto el Height como el Width de la imagen como del bitmap, se modificaran en un factor constante en X y en Y, lo que hace que las diferencias entre dos puntos en X y en Y no se vean alteradas.(Aunque en ese caso tanto el TImage, como el TBitmap, sí intentarán acomodarse al objeto contenedor. TImage a un TForm y TBitmap al TImage).

En resumen, con la propiedad Align := alClient de los componentes, sí se modifican sus respectivas propiedades Width y el Height, aunque no se modifica la distancia en pixeles entre 2 puntos sobre los ejes (x e Y) porque ambos fueron multiplicados por el mismo factor. Cuando Align := alNone, las mismas propiedades mencionadas permanecen incambiables, ya se porque el bitmap conserva las originales y/o el TImage.

Observé la variable Screen.PixelsPerInch del form que contiene el TImage, y sin embargo su valor cambia al modificar la resolución, pero los componentes contenidos en el form no cambiarán sus dimensiones.

El código es muy sencillo, simplemente aplicar lo que dije:

Procedimiento de un controlador que calcula la escala.
Procedure TCalcHandler.calcEscala(P1:TPuntoVisual;P2:TPuntoVisual; P3:TPuntoVisual);
Begin
if ((P1 = nil) or (P2 = nil) or (P3 = nil)) then
ShowMessage('Faltan parámetros para calcular la distancia')
else
begin

// Puntos iguales

if ((P1 = P2) or (P1 = P3) or (P2 = P3)) then
ShowMessage('Hay puntos que coinciden,
la distancia es cero, no hay escala')
else
begin
imgHdl := fact.getInstanceImgHndl;

// setea imgHdl.FactorX y imgHdl.FactorY
imgHdl.setEscala(P1,P2,P3);
end;
end;
End;


p2 es el vértice del ángulo recto.
distXmm, distYmm, DistPixels son todas propiedades del controlador TImgHandler.
distXmm: distancia en mm segun OX conocida en la imagen sin digitalizar.
distYmm: idem segun Oy.

Inicializa los valores de la escala.
Procedure TImgHandler.setEscala(p1,p2,p3:TPuntoVisual);
Begin
// p1,p2 forman sentido del eje Ox
// p2,p3 forman sentido del eje Oy

distEnPixels(p1,p2);

// relacion entre mm/pixel

FactorX := self.distXmm/DistPixels;
distEnPixels(p2,p3);

FactorY := self.distYmm/DistPixels;
End;




Function TImgHandler.distEnPixels(p1,p2:DTPunto):double;
Var
distP1P2:TDistanciaP1P2;
Begin
distP1P2 := TDistanciaP1P2V.Create(p1,p2);
distP1P2.Calcular;

DistPixels := distP1P2.Distancia;

distEnPixels := DistPixels
End;



Function TDistanciaP1P2V.Calcular:Integer;
Var
deltaX,deltaY:Double;
Begin

//P1, P2 ya fueron pasados al crear el objeto

deltaX := P2.X - P1.X;
deltaY := P2.Y - P1.Y;

Calcular := Sqrt(sqr(deltaX) + sqr(deltaY));
End;

No hay comentarios.: