| Ver tema anterior :: Ver tema siguiente |
| Autor |
Mensaje |
Isa1976 Lt Cmdr


Registrado: 23 Nov 2005Mensajes: 104
|
Publicado: Sab 29 May, 2010 10:53 pm Asunto: Método más rápido de busca un pixel en una imagen |
|
|
Hola a todos!
Tengo un TImage con una imagen cargada.
En esa imagen hay un solo pixel que es de color verde (clGreen).
¿Cual creeis que es la forma más rapida de buscar ese pixel?
Se que puedo buscarlo recorriendo con un "for" cada uno de los pixels de la imagen, pero no va del todo "rápido".
Muchas gracias y un saludo. |
|
| Volver arriba |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Dom 30 May, 2010 1:13 am Asunto: |
|
|
Supongo que estas usando la propiedad "Pixel", prueba con "ScanLine" es muchísimo más rápido. Un ejemplo:
| Código: | Image1->Picture->Bitmap->PixelFormat = pf32bit;
for (int y = 0; y < Image1->Picture->Bitmap->Height; y++)
{
TColor* p = static_cast<TColor*>(Image1->Picture->Bitmap->ScanLine[y]);
for (int x = 0; x < Image1->Picture->Bitmap->Width; x++)
{
if (p[x] == clGreen)
{
ShowMessage(Format("X=%d, Y=%d", ARRAYOFCONST((x, y)) ));
y = Image1->Picture->Bitmap->Height;
break;
}
}
} |
Otro ejemplo, aunque menos claro, es más rápido que el anterior:
| Código: | Graphics::TBitmap* bmp = Image1->Picture->Bitmap;
bmp->PixelFormat = pf32bit;
TColor* p = static_cast<TColor*>(bmp->ScanLine[bmp->Height-1]);
int i = bmp->Height * bmp->Width;
while (i--)
{
if (*p++ == clGreen)
{
int y = i / bmp->Width;
int x = bmp->Width - (++i - (y * bmp->Width));
ShowMessage(Format("X=%d, Y=%d", ARRAYOFCONST((x, y)) ));
break;
}
} |
|
|
| Volver arriba |
|
 |
escafandra Master

Registrado: 29 Nov 2007Mensajes: 625Ubicación: Madrid - España
|
Publicado: Dom 30 May, 2010 4:55 pm Asunto: |
|
|
También podríamos hacerlo así:
| Código: | bool BuscaColor(Graphics::TBitmap* bmp, TColor TrColor)
{
if(bmp->Empty) return false;
bmp->PixelFormat = pf32bit;
PDWORD pixel = (PDWORD)bmp->ScanLine[bmp->Height-1];
DWORD tc = (TrColor & 0xFF00FF00) | (GetRValue(TrColor) << 16) | GetBValue(TrColor);
for (int i = 0; i < bmp->Height * bmp->Width; i++)
if(pixel[i] == tc) return true;
return false;
}
|
Saludos. |
|
| Volver arriba |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Dom 30 May, 2010 5:30 pm Asunto: |
|
|
En las pruebas que he hecho, el segundo método que he puesto es el más rápido con mucha diferencia, en los otros en el "for" se pierde mucho tiempo.
La prueba que he hecho es ejecutarlo 1000 veces sobre un bitmap de 720x540 (con el pixel verde justo en el centro de la imagen), y me da estos tiempos:
| Código: | Método1 Método2 Escafandra
-------------------------------------
BCB6 2,786 0,204 2,105
CB2010 2,859 0,207 2,103
-------------------------------------
(Tiempos en segundos) |
(Esto desde fuera del IDE y en modo release) |
|
| Volver arriba |
|
 |
escafandra Master

Registrado: 29 Nov 2007Mensajes: 625Ubicación: Madrid - España
|
Publicado: Dom 30 May, 2010 9:52 pm Asunto: |
|
|
Bueno, realmente entre el método 2 de _Leo y mi método no hay mucha diferencia salvo el uso de índices para el puntero y el uso de aritmética de punteros en el método de _Leo. Está claro que la aritmética de punteros es mas rápida que los índices.
Quizás es mas fácil de entender los índices para luego optimizarlo con la aritmética de punteros.
Saludos. |
|
| Volver arriba |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Dom 30 May, 2010 10:20 pm Asunto: |
|
|
for (int i = 0; i < bmp->Height * bmp->Width; i++)
La parte marcada en negrita es lo que más ralentiza el código (lo vuelve a calcular cada vez), haciendo este cálculo solo una vez antes de comenzar el bucle baja muchísimo el tiempo.. el índice ralentiza también un poco, pero no tanto como ese cálculo.. bueno, no realmente haciendo el calculo, sino leyendo las propiedades para hacer el calculo..
Con el siguiente pequeño cambio sobre el segundo método se consigue mejorar un poco más el tiempo (en las mismas condiciones 0,150 seg.):
| Código: | Graphics::TBitmap* bmp = Image1->Picture->Bitmap;
bmp->PixelFormat = pf32bit;
TColor* p = static_cast<TColor*>(bmp->ScanLine[bmp->Height-1]);
for (int i = bmp->Height * bmp->Width-1; i >= 0; i--)
{
if (*p++ == clGreen)
{
int y = i / bmp->Width;
int x = bmp->Width - (++i - (y * bmp->Width));
ShowMessage(Format("X=%d, Y=%d", ARRAYOFCONST((x, y)) ));
break;
}
} |
|
|
| Volver arriba |
|
 |
escafandra Master

Registrado: 29 Nov 2007Mensajes: 625Ubicación: Madrid - España
|
Publicado: Dom 30 May, 2010 11:22 pm Asunto: |
|
|
Si _leo, tienes razón, ese producto repetido es innecesario...
Siempre pensé que el bucle for o while eran indiferentes a la hora de la velocidad. Yo prefiero casi siempre el for por la sintaxis mas compacta y su mayor plasticidad, pero no creí que hubiesen diferencias de velocidad también.
Una pregunta, _Leo. He observado que cuando accedes a la memoria del bitmap32 como DWORD, tenemos permutados el rojo y el azul, por eso hago la conversión del color con esto:
| Código: | DWORD tc = (TrColor & 0xFF00FF00) | (GetRValue(TrColor) << 16) | GetBValue(TrColor); |
Sin embargo tu haces directamente el casting entre el color y el valor en memoria, que no deje de ser un DWORD con esos componentes cambiados, o es que ¿el static_cast<TColor*> modifica esto?, yo creo que no.
Saludos. |
|
| Volver arriba |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Lun 31 May, 2010 12:53 am Asunto: |
|
|
| escafandra escribió: | | Sin embargo tu haces directamente el casting entre el color y el valor en memoria, que no deje de ser un DWORD con esos componentes cambiados, o es que ¿el static_cast<TColor*> modifica esto?, yo creo que no. |
Cierto, estaba mas concentrado en la velocidad que en este detalle que casualmente el verde es igual en RGB y BGR.
Quedaría así (usando como base la que menos tarda):
| Código: | bool BuscaColor(Graphics::TBitmap* bmp, TColor color, TPoint& pt)
{
bool ret = false;
bmp->PixelFormat = pf32bit;
DWORD bgr = GetRValue(color) << 16 | GetGValue(color) << 8 | GetBValue(color);
DWORD* p = static_cast<DWORD*>(bmp->ScanLine[bmp->Height-1]);
for (int i = bmp->Height * bmp->Width-1; i >= 0; i--)
{
if (*p++ == bgr)
{
pt.y = i / bmp->Width;
pt.x = bmp->Width - (++i - (pt.y * bmp->Width));
ret = true;
break;
}
}
return ret;
} |
Y un ejemplo de uso:
| Código: | void __fastcall TForm1::Button5Click(TObject *Sender)
{
TPoint pt;
if ( BuscaColor(Image1->Picture->Bitmap, clSkyBlue, pt) )
ShowMessage(Format("X=%d, Y=%d", ARRAYOFCONST((pt.x, pt.y)) ));
else
ShowMessage("No encontrado.."); |
(Yo estoy presuponiendo que necesitas saber la posición del pixel en concreto) |
|
| Volver arriba |
|
 |
Isa1976 Lt Cmdr


Registrado: 23 Nov 2005Mensajes: 104
|
Publicado: Mie 02 Jun, 2010 9:17 pm Asunto: |
|
|
perfecto!
Muchas gracias
PD: gracias por la posición del pixel, en realidad me hacia falta... |
|
| Volver arriba |
|
 |
|