PDA

Ver la Versión Completa : Pixles por mm en una imágen


rochi
21-03-2007, 00:59:36
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.

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:

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 el BITMAPINFOHEADER, supuestamente tengo los pixels por metro, ¿metro medido sobre qué?.

Aqui dejo el código:
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,

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.
Ingreso la distancia real en mm entre p1p2 y p2p3.
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.
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