Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Gráficos (https://www.clubdelphi.com/foros/forumdisplay.php?f=8)
-   -   Pixles por mm en una imágen (https://www.clubdelphi.com/foros/showthread.php?t=41621)

rochi 21-03-2007 00:59:36

Pixles por mm en una imágen
 
Hola tengo el siguiente problema, quiero calcular la relación pixel por mm de un bmp que está en un TImage. La idea es que el usuario pueda trazar rectas, calcular ángulos, distancias entre puntos, entre puntos y rectas, etc.
Antes de googlear, obtenía esa relación marcando 2 ptos de la imágen e indicando a cuántos mm correspondía. Mediante regla de 3, sacaba la relación, y aplicaba ese factor escala a todas las medidas obtenidas y asi daba el resultado esperado en todos los cálculos.

Quise hacerlo mas automático, sin que el usuario tenga que conocer la distancia, que marque 2 puntos y obtener la información automáticamente a cuantos mm corresponde. Encontré varios links, y opté por aplicar lo leido, pero no me anda, me da una diferencia de unos 2mm, y no se si lo aplico
bien.
Resumo la idea de lo que hago, dados 2 puntos, calculo la distancia en X y en Y que los separa en pixels P1(x1,y1) y P2(x2,y2), deltaX = x2 - x1, idem para y. Esas distancias corresponden a los catetos de un triángulo rectángulo, luego convierto las mismas a mm, obteniendo los 'catetos en mm'. Aplico Pitágoras para obtener la distancia en pixels y mm, ahí tengo la relación mm/pixels.

Código Delphi [-]
img:TImage // contiene al bitmap

// Relación horizontal entre pixels y mm
function TCmdCalcEscala.PixelHorizToMM(deltaX: Double): Double;
var
  hdcDesk:HDC;
Begin   
 
  hdcDesk:= img.Bitmap.Canvas.Handle; // Antes hdcDesk:= img.Canvas.Handle;

  try
     result:= deltaX * (GetDeviceCaps(hdcDesk, HORZSIZE) / GetDeviceCaps(hdcDesk, HORZRES));
  finally
     ReleaseDC(hdcDesk, hdcDesk);
  end;
End;

// Relación vertical entre pixels y mm
function TCmdCalcEscala.PixelVertiToMM(deltaY: Double): Double;
var
  hdcDesk:HDC;
Begin
 
  hdcDesk:= img.Bitmap.Canvas.Handle;

  try
     result:= deltaY * (GetDeviceCaps(hdcDesk, VERTSIZE) / GetDeviceCaps(hdcDesk, VERTRES));
  finally
     ReleaseDC(hdcDesk, hdcDesk);
  end;
End;


// X distancia horizontal, Y distancia vertical
function TCmdCalcEscala.distancia(X,Y: Double): Double;
Begin  
  result:=Abs(Sqrt(Sqr(x) + Sqr(y)));
End;


function TCmdCalcEscala.deltaPixels(X,Y: Double): Double;
Begin
  result := Abs(X - Y);
End;


//Calcula la relación entre pixels y mm.
procedure TCmdCalcEscala.ejecutar(Pto1:TPuntoVisual;Pto2:TPuntoVisual);
Var 
  distXpixel,distYpixel:Double;
  distXmm,distYmm:Double;
Begin
   
   distXpixel := deltaPixels(pto1.x,pto2.x); // Distancia horizontal
   distYpixel := deltaPixels(pto1.y,pto2.y); // Distancia vertical

   //Paso a mm esas distancias
   distXmm := PixelHorizToMM(distXpixel);
   distYmm := PixelVertiToMM(distYpixel);
   
   //DistPixel, Distmm, FactorEscala son propiedades de TCmdCalcEscala   
   DistPixels := distancia(distXpixel,distYpixel);
   Distmm := distancia(distXmm,distYmm);
   FactorEscala := Distmm/DistPixels;
End;

A DistPixels, le corresponden Distmm, obvio para comprobar yo se cuanto vale Distmm, bueno, con esto no anda. Aclaro que antes al GetDeviceCaps le pasaba el handle de la pantalla, tampoco andaba asi.

Les agradezco cualquier sugerencia, componente, o link.
Saludos, rochi

seoane 21-03-2007 01:41:56

:confused: Aclarame una duda que tengo, ¿donde mides la distancia "real"? ¿imprimes la imagen y la mides con una regla? o lo haces sobre la propia pantalla

rochi 21-03-2007 02:36:44

Hola, la 'medida real' la mido sobre la imágen real, a priori de escanear la imágen. Luego marco los puntos correspondiente al segmento medido, y asi calculo la escala (x distancia en pixels -> distenMM). Esa es la forma rústica de hacerlo, pero quiero sacar esta responsabilidad del usuario, y obtenerla desde el bmp propiamente dicho. Buscando encontré GetDeviceCaps una función de las APIs que puede dar la cantidad de pixels o mm vertical y horizontal del dispositivo en cuestión, en mi caso, la imágen.

Si alguien sabe como...

Saludossss

seoane 21-03-2007 02:54:50

Bien, escaneas la imagen. Cuando la escaneas la pasas directamente al programa o la guardas primero en un bmp. Si es lo segundo puedes usar este código:
Código Delphi [-]
procedure GetBitmapInfo(Filename: string; var Info: BITMAPINFOHEADER);
begin
  FillChar(Info,Sizeof(Info),0);
  with TFileStream.Create(Filename,fmOpenRead,fmShareDenyWrite) do
  try
    Seek(Sizeof(BITMAPFILEHEADER),soFromBeginning);
    ReadBuffer(Info,Sizeof(Info));
  finally
    Free;
  end;
end;

// Por ejemplo
var
  Info: BITMAPINFOHEADER;
  Str: string;
begin
  GetBitmapInfo('c:\1.bmp',Info);
  Str:=
    'Ancho = ' + IntToStr(Info.biWidth) + #13 +
    'Alto = ' + IntToStr(Info.biHeight) + #13 +
    'Profundidad en bits = ' + IntToStr(Info.biBitCount) + #13 +
    'Resolucion horizontal = ' + IntToStr(Info.biXPelsPerMeter) + ' Pixeles por metro' + #13 +
    'Resolucion vertical = ' + IntToStr(Info.biYPelsPerMeter) + ' Pixeles por metro' + #13;
  case Info.biCompression of
    BI_RGB: Str:= Str + 'Compresion = Sin comprimir';
    BI_RLE8: Str:= Str + 'Compresion = RL8';
    BI_RLE4: Str:= Str + 'Compresion = RLE4';
    BI_BITFIELDS: Str:= Str + 'Compresion = BITFIELDS';
  end;
  ShowMessage(Str);
end;
si te fijas uno de los parámetros que podemos obtener del bitmap son los "Píxeles por metro", que creo que es lo que estas buscando.

:confused: ¿Te sirve o buscamos por otro lado?

rochi 21-03-2007 04:06:23

Muchas gracias seoane, voy a estudiar ese código (desconozco BITMAPINFOHEADER).

Si estoy trabajando con archivos .bmp.

Creo que ya se donde estaba mi error, mi escaso conocimiento de las funciones API tiene mucho que ver. GetDeviceCaps transforma pixels a mm, pero mm de pantalla, que no necesariamente se corresponden con los mm medidos en la imágen sin digitalizar.

O sea que lo que estaba haciendo no tiene sentido si no tengo medidas 'reales'. Ya miro el código para ver si por ahi sale el asunto.

saludos, rochi

rochi 22-03-2007 00:37:57

Hola, no solucionó mi problema, es mas me da mas lejos de lo real, en la versión anterior, al menos obtenía la dist. en mm sobre la pantalla y era correcta. Ahora, ni eso.
Estoy usando una función que retorna elBITMAPINFOHEADER, supuestamente tengo los pixels por metro, ¿metro medido sobre qué?.

Aqui dejo el código:
Código Delphi [-]
Var
  pto1,pto2:TPuntoVisual;

  pixelXpormetro,pixelYpormetro:Integer;

  distXpixel,distYpixel: Double;
  distXmm,distYmm: Double;  
  pixelXpormm,pixelYpormm: Double;
  
Begin

   //función que obtiene dato de tipo BITMAPINFOHEADER    
   getResolucionBitMap(pixelXpormetro,pixelYpormetro);

   // Convierto a cantidad de Pixels por mm    
   pixelXpormm := 0.001*pixelXpormetro;
   pixelYpormm := 0.001*pixelYpormetro;

   imgHdl := fact.getInstanceImgHndl;
   pto1 := imgHdl.getPuntoVisual(p1);
   pto2 := imgHdl.getPuntoVisual(p2);

   // deltaX = Abs(pto1.x - pto2.x)
   distXpixel := deltaPixels(pto1.x,pto2.x);
   distYpixel := deltaPixels(pto1.y,pto2.y);

       
   // Paso la distancia en pixels a mm
   distXmm := distXpixel/pixelXpormm;
   distYmm := distYpixel/pixelYpormm;

  
   // A mm milímetros le corresponden distPixels, distancia = formula Pitágoras

   imgHdl.DistPixels := distancia(distXpixel,distYpixel);
   imgHdl.Distmm := distancia(distXmm,distYmm);
   imgHdl.FactorEscala := (imgHdl.Distmm/imgHdl.DistPixels)*(1.0);
End;
Si alguien sabe que puede pasar, gracias...

rochi 15-04-2007 03:07:12

Hola, que tal? bueno, al final resolví el problema como lo hacía originalmente,
  1. marco 3 puntos en la imagen digitalizada p1,p2,p3 , de modo que formen un angulo recto, p1p2 paralelo al eje Ox y p2p3 al eje Oy.
  2. Ingreso la distancia real en mm entre p1p2 y p2p3.
  3. Calculo la distancia de esos segmentos en pixels, y como la equivalente en mm es conocida por mi (ingresada) obtengo un factor en X y factor en Y.
  4. A cada distancia punto, le multiplico sus cooords. por el factor en X y en Y para obtener el equivalente en mm.
Asi funciona correctamente, medidas de angulos, rectas, proyecciones, etc.
Duda que tengo, mi imagen es un Bitmap, que está dentro de un TImage.
Cambié la resolución de pantalla, y noté que el ancho y alto del objeto TImage y del TBitmap permanecen constantes => los factores de conversión en X y en Y tampoco cambian, aun cuando cambia el valor de Screen.PixelsPerInch. ¿A que se debe eso?
¿El dfm tiene esa información y la usa independientemente de la resolución de la pantalla adaptando (distorcionando) la imagen?
¿Debo controlar la resolución para que no cambie la escala? (aunque no parece haber riesgo).
Entro otras cosas me interesa porque no se si ademas de guardar los factores en X y en Y por imagen, deba guardar la resolución a la que fue hallada.


Saludos, gracias

rochi

rochi 15-04-2007 22:52:51

Me autorespondo, por si le sirve a alguien. Efectivamente la cantidad de pixeles del .bmp no cambia al cambiar la resolución ya que está guardada en el propio bitmap (y en el .dfm si la imagen está en el form).
Asi como el .dfm también guarda información del objeto TImage, que tampoco altera sus dimensiones al cambiar la resolución.

saludos, ro


La franja horaria es GMT +2. Ahora son las 16:09:55.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi