CLR: .Net / Mono / Boo / Otros CLR > Otros CLR
Aplicar Una Máscara A Una Imagen
KAYSS:
Alguno sabéis de una forma rápida de aplicar una máscara a una imagen en mapa de bits.
Yo primero creo la máscara (matriz nxn), recorro pixel a pixel la imagen, sitúo la máscara y recorro la máscara posición por posición para dicho pixel, y así con todos los demás pixels de la imagen. Pero esto es muy lento.
Os muestro el código que he desarrollado por si es causa de que no está bien planteado:
--- Código: Text ---Bitmap^ AplicarMascara(Bitmap ^Imagen, Array^ Mascara, int Tamanio){ Bitmap ^ImagenFiltrada=gcnew Bitmap (Imagen->Width, Imagen->Height); int IncX, IncY, X, Y; int TamanioNxN=Tamanio*Tamanio; double Sumatorio; Color color; // Aplicamos la máscara para cada uno de los pixels de la imagen for(int i=0; i<Imagen->Width; i++){ for (int j=0; j<Imagen->Height; j++){ IncY=Tamanio/2; Sumatorio=0; // Recorremos los valores de la máscara para aplicarlos a la imagen for (int k=0; k<Tamanio; k++){ Y=j+IncY; IncX=Tamanio/2; for (int q=0; q<Tamanio; q++){ X=i+IncX; if ((X>=0&&X<Imagen->Width) && (Y>=0&&Y<Imagen->Height)) Sumatorio=Sumatorio + Mascara->GetValue(Tamanio/2+IncX, Tamanio/2+IncY)) * Imagen->GetPixel(X,Y).R; IncX--; } IncY--; } // El valor del pixel será el valor del sumatorio obtenido color=Color::FromArgb(Sumatorio, Sumatorio, Sumatorio); ImagenFiltrada->SetPixel(i, j, color); } } return ImagenFiltrada;}
Gracias, un saludo
JuanK:
No es muy rápido, lo mejor es que uses trabajo a nivel de bytes. El objeto bitmap te permite acceder directamente a todos los bytes de la imagen usando los metodos de lockbitmap, y una vez allli puedes trabajar todo de una sola vez con operaciones binarias y/o con arreglos. Eso si es muy rápido.
si necesitas ayuda me cuentas... aunque no deberias...
KAYSS:
Muchas gracias por la ayuda, Juan Carlos.
He aplicado lo que me propones y he conseguido reducir de 2 minutos y medios a 1 minuto y medio el tiempo empleado en aplicar una máscara 3x3 a una imagen, que aunque sigue siendo mucho tiempo pero mejora el anterior.
Te muestro el código resultante:
--- Código: Text ---Bitmap^ AplicarMascara2(System::Drawing::Bitmap ^Imagen, cli::array<int>^ Mascara, int Tamanio){ this->ImagenFiltrada=gcnew System::Drawing::Bitmap (Imagen); int i, j, k, q, IncX, IncY, X, Y, ValorPixel, PosVectorRGB; int TamanioNxN=Tamanio*Tamanio; double Sumatorio; Color color; int Cte1=Tamanio/2; // formato de pixel PixelFormat pxf = PixelFormat::Format24bppRgb; // Bloqueamos los pixels de la imagen original y la filtrada Rectangle rect = System::Drawing::Rectangle(0, 0, Imagen->Width, Imagen->Height); BitmapData ^bmpDatosOriginal = Imagen->LockBits(rect, ImageLockMode::ReadWrite, pxf); BitmapData ^bmpDatosFiltrada = ImagenFiltrada->LockBits(rect, ImageLockMode::ReadWrite, pxf); // Obtenemos la direcciones de comienzo IntPtr ptrOriginal = bmpDatosOriginal->Scan0; IntPtr ptrFiltrada = bmpDatosFiltrada->Scan0; // Declaramos los arrays para almacenar los bytes de los bitmaps int numBytes = Imagen->Width * Imagen->Height * 3; array<Byte> ^valoresRGBOriginal = gcnew array<Byte>(numBytes); array<Byte> ^valoresRGBFiltrada = gcnew array<Byte>(numBytes); // Copiamos los valores RGB a los arrays Marshal::Copy(ptrOriginal, valoresRGBOriginal, 0, numBytes); Marshal::Copy(ptrFiltrada, valoresRGBFiltrada, 0, numBytes); // Recorremos la imagen for(i=0; i<Imagen->Width; i++){ for (j=0; j<Imagen->Height; j++){ IncY=Cte1; Sumatorio=0; // Recorremos la máscara for (k=0; k<Tamanio; k++){ Y=j+IncY; IncX=Cte1; for (q=0; q<Tamanio; q++){ X=i+IncX; if ((X>=0&&X<Imagen->Width)&&(Y>=0&&Y<Imagen->Height)){ Sumatorio=Sumatorio+ Convert::ToDouble(Mascara[(Cte1+IncX)*3 + (Cte1+IncY)])* Convert::ToDouble(valoresRGBOriginal[(Y*Imagen->Width+X)*3]); } IncX--; } IncY--; } // El valor del pixel será el valor del sumatorio obtenido if (Sumatorio>0)ValorPixel=Convert::ToInt32(Sumatorio)%255; else ValorPixel=0; PosVectorRGB=(j*Imagen->Width+i)*3; valoresRGBFiltrada[PosVectorRGB]=ValorPixel; valoresRGBFiltrada[PosVectorRGB+1]=ValorPixel; valoresRGBFiltrada[PosVectorRGB+2]=ValorPixel; } } // Copiamos de nuevo los valores del array al bitmap Marshal::Copy(valoresRGBFiltrada, 0, ptrFiltrada, numBytes); // Desbloqueamos los bytes ImagenFiltrada->UnlockBits(bmpDatosFiltrada); Imagen->UnlockBits(bmpDatosOriginal); return ImagenFiltrada;}
Gracias, un saludo
JuanK:
Bueno no ando con mucho tiempo para revisar la logica completa del programa y hacer una optimizacion agresiva, pero si por encima estos son los tips:
1- Trata de deshacerte de todo lo que implique conversion de tipos de dato, ejemplo
--- Código: Text ---Convert::ToDouble
Seguramente deberas replantear la mayoria de las operaciones aritmeticas que tienes para trabajar sin convertir tipos de datos por lo menos mientras estes dentro de los bucles.
2- Optimiza de acuerdo a la plataforma, si vas a trabajar para plataforma de 32 bot utiliza tipos de dato de 32 bit o si es plataforma de 64 utiliza tipos de dato de 64 bit, el punto es que si usas plataforma de 32 bit maneja tipos de dato Int32 en contadores ciclos etc, y utiliza todas las operaciones sobre unidades de 32 bit, inclusive las operaciones de corrimiento.
Seguramente la logica de tu programa sera un poco mas elaborada pero creeme que justificara.
3- Minimiza la mayor cantidad de operaciones posibles a operaciones a nivel binario.
En todo caso de todo lo que te mencione lo mas importante es lo de las conversiones de tipo de dato.
Ahh si, trata de reducirlos bucles, entre menos vueltas mejor.
saludos,
KAYSS:
Muchas gracias de nuevo, todos tus consejos me han sido de gran ayuda.
Aplicaré todo lo que me dices
Un saludo
Navegación
[#] Página Siguiente
Ir a la versión completa