DESARROLLO: |
C.1.DESARROLLO
C.1.1.Conceptos Aplicados Para El Desarrollo
Las DIMENSIONES DE PÍXEL son las medidas horizontales y verticales de una imagen, expresadas en píxeles. Las dimensiones de píxel se pueden determinar multiplicando tanto el ancho como la altura por el dpi.
Este es el motivo por el cual dots-per-inch (puntos por pulgada) (dpi) o pixels-per-inch (píxeles por pulgada) (ppi) son términos comunes y sinónimos utilizados para expresar la resolución de imágenes digitales.
La PROFUNDIDAD DE BITS es determinada por la cantidad de bits utilizados para definir cada píxel. Cuanto mayor sea la profundidad de bits, tanto mayor será la cantidad de tonos (escala de grises o color) que puedan ser representados. Las imágenes digitales se pueden producir en blanco y negro (en forma bitonal), a escala de grises o a color.
Una imagen bitonal está representada por píxeles que constan de 1 bit cada uno, que pueden representar dos tonos, utilizando los valores 0 para el negro y 1 para el blanco.
Una imagen a escala de grises está compuesta por píxeles representados por múltiples bits de información, que típicamente varían entre 2 a 8 bits o más.
Ejemplo: En una imagen de 2 bits, existen cuatro combinaciones posibles: 00, 01, 10 y 11. Si "00" representa el negro, y "11" representa el blanco, entonces "01" es igual a gris oscuro y "10" es igual a gris claro. La profundidad de bits es dos, pero la cantidad de tonos que pueden representarse es 22 ó 4. A 8 bits, pueden asignarse 256 (28) tonos diferentes a cada píxel. |
Una imagen a color está típicamente representada por una profundidad de bits entre 8 y 24 o superior a ésta. En una imagen de 24 bits, los bits por lo general están divididos en tres grupos: 8 para el rojo, 8 para el verde, y 8 para el azul. Para representar otros colores se utilizan combinaciones de esos bits. Una imagen de 24 bits ofrece 16,7 millones (224) de valores de color. Cada vez más, los scáners están capturando 10 bits o más por canal de color y por lo general imprimen a 8 bits para compensar el "ruido" del escáner y para presentar una imagen que se acerque en el mayor grado posible a la percepción humana.
Profundidad de bits: De izquierda a derecha - imagen bitonal de 1 bit, a escala de grises de 8 bits, y a color de 24 bits.
Cálculos binarios para la cantidad de tonos representados por profundidades de bits comunes:
La luz tiene tres colores primarios: rojo, verde y azul. Combinando esos colores primarios en diversas intensidades podemos conseguir todos los colores visibles.
Si representamos la intensidad de cada color primario con un número, entonces podemos representar los colores con tres números (uno para el rojo, uno para el verde y uno para el azul). Esta forma de representar los colores con números para la intensidad de los componentes rojo, verde y azul de la luz se conoce como el sistema de color RGB (del inglés Red, Green y Blue, que en castellano suele referirse como RVA: Rojo, Verde y Azul).
Si se utilizan los números enteros en el rango 0..255 (un byte), es decir 256 valores posibles para cada color primario, entonces con tres bytes se puede representar 256 x 256 x 256 combinaciones diferentes de los colores primarios, es decir una gama de más de 16 millones de colores (frecuentemente referida como "color verdadero"). Este sistema de color RGB se conoce como RGB-256.
Algunos programas utilizan un sistema conocido como RGB-100, que utiliza números en el rango 0..100 para indicar un porcentaje de intensidad (0%=apagado; 100%=máximo). En los ejemplos se utiliza RGB-256 porque es el más popular.
El color negro es la ausencia de color (o la ausencia de luz) y se representa como RGB(0,0,0) (Rojo=0, Verde=0, Azul=0). El blanco es la presencia de todos los colores (en su intensidad máxima) y se representa entonces como RGB(255,255,255) (Rojo=255, Verde=255, Azul=255). Todos los tonos de grises del negro al blanco se representan con tres valores iguales para los componentes rojo, verde y azul (ningún color predomina), es decir que tienen la forma RGB(x, x, x). Por ejemplo, el color definido en la jerga como "gris claro" se representa como RGB(192,192,192), y el "gris oscuro" es RGB(128,128,128).
Tabla C.1 :Tonos de Grises
|
RGB(0,0,0) = Negro |
|
RGB(128,128,128) = Gris oscuro |
|
RGB(192,192,192) = Gris claro |
|
RGB(255,255,255) = Blanco |
Para comenzar, los colores primarios se representan de esta manera:
Tabla C.2 :Colores Primarios
|
RGB(255,0,0) = Rojo claro (o rojo brillante, o rojo intenso) |
|
RGB(0,255,0) = Verde claro (o verde brillante, o verde intenso) |
|
RGB(0,0,255) = Azul claro (o azul brillante, o azul intenso) |
Sus versiones oscuras serían:
Tabla C.3 :Colores Primarios Oscuros
|
RGB(128,0,0) = Rojo oscuro |
|
RGB(0,128,0) = Verde oscuro |
|
RGB(0,0,128) = Azul oscuro |
Las diferentes intensidades de rojo tienen la forma RGB(x,0,0), las de verde tienen la forma RGB(0,x,0) y las de azul son de la forma RGB(0,0,x).
Los colores secundarios de la luz son cian, magenta y amarillo, y resultan de la combinación de diferentes pares de los colores primarios en iguales intensidades. Por ejemplo, sus versiones brillantes serían:
Tabla C.4 :Colores Secundarios
|
RGB(0,255,255) = Cian claro = verde claro + azul claro |
|
RGB(255,0,255) = Magenta claro = rojo claro + azul claro |
|
RGB(255,255,0) = Amarillo claro = rojo claro + verde claro |
La versiones oscuras de los colores secundarios serían:
Tabla C.5 :Colores Secundarios Oscuros
|
RGB(0,128,128) = Cian oscuro = verde oscuro + azul oscuro |
|
RGB(128,0,128) = Magenta oscuro = rojo oscuro + azul oscuro |
|
RGB(128,128,0) = Amarillo oscuro = rojo oscuro + verde oscuro |
Como puede ver, las diferentes intensidades de cian son de la forma RGB(0,x,x), las de magenta tienen la forma RGB(x,0,x) y las de azul son de la forma RGB(x,x,0).
Los colores puros o saturados combinan sólo dos colores primarios y tienen la forma RGB(x,y,0), RGB(y,x,0), RGB(0,x,y), RGB(0,y,x), RGB(x,y,0) o RGB(y,x,0) donde x <> 0 y x >= y:
Los colores de la misma forma que tienen la misma relación x/y se dice que tienen el mismo "matiz". Por ejemplo, RGB(255,128,0) y RGB(192,96,0) son de la misma forma (RGB(x,y,0)) y tienen la misma relación x/y (255/128 = 192/96), así que tienen el mismo matiz. El "matiz" es lo que incorrectamente con frecuencia se refieren como "color". Por ejemplo, en este caso el matiz de ambos colores es "naranja", pero es naranja brillante y el otro es naranja oscuro, es decir, comparten el mismo "matiz", pero difieren en la "luminosidad". La luminosidad mide cuán cercano un color está del blanco, y se representa usualmente como un porcentaje. Por ejemplo, RGB(192,96,0) es un naranja con una luminosidad de 75%, y RGB(128,64,0) es un naranja con una luminosidad de 50%.
Tabla C.6 :Colores Puros
|
RGB(255,128,0) = Naranja 100% luminosidad |
|
RGB(192,96,0) = Naranja 75% luminosidad |
|
RGB(128,64,0) = Naranja 50% luminosidad |
El matiz de RGB(0,0,128) y de RGB(0,0,64) es el azul, pero el segundo color tiene un 50% de la luminosidad del primero (tienen una luminosidad de 50% y 25% respectivamente).
Aparte del matiz y la luminosidad, los colores se definen por un tercer parámetro conocido como "saturación", que mide la pureza de un color. Hasta aquí hemos tratado con colores 100% puros. Los colores impuros son colores puros mezclados con gris. Mientras mas mezclado con gris, más saturado es un color. Por ejemplo, RGB(192,128,64) tiene el mismo matiz y luminosidad que el naranja brillante RGB(255,128,0), pero un 50% de saturación, y resulta de mezclar el naranja brillante con el gris medio RGB(128,128,128):
naranja gris
Rojo = ( 255 + 128 ) / 2 = 192
Verde = ( 128 + 128 ) / 2 = 128
Azul = ( 0 + 128 ) / 2 = 64
Si nuevamente mezclamos el resultado con gris medio, obtendríamos un naranja con 25% de saturación RGB(160,128,96), aún más cercano al gris medio. Si se mezcla gris otra vez, obtiene un naranja con una saturación de sólo un 12,5% RGB(144,128,112), casi un gris medio.
Nótese que un color es más saturado cuando la diferencia entre los valores RGB es más grande. Cuando los valores RGB son más cercanos unos de otros, el color es menos saturado (es decir, es más grisáceo, o se puede decir que es menos "saturado", "vivo", "vívido" o "puro").
La saturación se puede calcular con la siguiente fórmula:
Saturación = ((máximo-medio) + (medio-mínimo)) / 255 * 100%
Por ejemplo:
Saturación(160,128,96) = ((160-128) + (128-96)) / 255 * 100%
= (32 + 32) / 255 * 100%
= 64 / 255 * 100%
Saturación(160,128,96) = 25%
Los tres bytes que representan un color se pueden combinar en una constante entera de 32-bit, representada normalmente en notación hexadecimal. Por ejemplo, RGB(160,128,96) es 6080A0:
Rojo (RR) = 160 decimal = A0 hexadecimal
Verde (GG) = 128 decimal = 80 hexadecimal
Azul (BB) = 96 decimal = 60 hexadecimal
Nótese que las constantes de color tienen la forma BBGGRR, donde BB es el byte (dos dígitos hexadecimales) para el azul (blue), GG es el byte para el verde (green) y RR es el byte para el rojo (red). La razón es que los enteros se almacenan con el byte menos significativo primero, así que BBGGRR se almacena como RRGGBB, el orden en que se esperan los valores RGB (por ejemplo 6080A0 se almacena internamente como A08060).
C.1.1.2.10.RGB(red-green-blue)
RGB en el sistema de color más utilizado por la mayoría de los formatos actuales. Es un sistema aditivo que variando la cantidad de color rojo, verde y azul agregados al negro, se produce nuevos colores. En los archivos gráficos el sistema RGB se usa para representar cada pixel con un tripleta de la forma (R,G,B).
Representando el modelo en un cubo unitario (Fig.C.1), cada componente de la tripleta toma valores en el rango de 0 a 1. Así un color Cl se expresa:
Cl = RR + GG + BB
(Fig.C.1:Modelo de un Cubo Unitario)
Para los valores de 24-bits la tripleta (0, 0, 0) representa al negro, mientras que (255, 255, 255) representa al blanco. Cuando los tres valores RGB tienen un mismo valor - (63, 63, 63) o (127, 127, 127) o (191, 191, 191) por ejemplo - el color resultante en un tono de gris (Fig.C.2).
(Fig.C.2:Modelo de un Cubo RGB)
Con base en la teoría de tres estímulos de la visión, nuestros ojos perciben los colores mediante el estímulo de tres pigmentos visuales en los conos de la retina. Estos pigmentos visuales tienen la sensibilidad pico en longitudes de onda de alrededor de 630 nm (rojo), 530 nm (verde) y 450 nm (azul).
C.1.1.3.Digitalización de la imagen
Toda imagen encierra una gran cantidad de información acerca del objeto representado. La imagen obtenida con una fotografía o un aparato de rayos X, de una estructura, sin tratamiento informático alguno, se llama imagen analógica, porque es una representación análoga a esa estructura, y contiene una distribución continua de brillos, cuyos límites están dentro de los márgenes de dicha imagen. Con un sistema informático, los diferentes brillos o densidades continuas tienen una representación de sus valores máximo y mínimo, con unos límites concretos, en una escala de tonos o en una escala de grises. Así, en una distribución espacial, esos valores de grises pueden tener una posición definida, con un valor de gris concreto. A cada una de estas posiciones o elementos de la imagen se les denomina pixels, y a este tipo de representación es a lo que se llama imagen digital. Cuando se digitaliza una imagen analógica, se pierde algo de la información, sobre todo en los detalles finos, pero en cambio, se obtiene la posibilidad de actuar sobre ella electrónicamente: se puede cuantificar la información, y modificarla en algunos aspectos para mejorar su visualización. Es una imagen electrónica, y el ordenador la trata, utilizando el sistema binario: el ordenador sólo maneja 2 dígitos (0 y 1), aislados, o en combinaciones que permiten representar cualquier cantidad de que uno desee expresar. La unidad mínima funcional del ordenador es el Bit (binary digit point punt). Los bits se agrupan en ocho posiciones, las cuales constituyen el Byte.
C.1.1.4.Adquisicion de Datos
La adquisición de datos del campo, donde se esta aplicando un sistema de control de imagen, se realiza con una cámara de vídeo, la cual puede transformar las ondas de luz en señales electrónicas que sean reflejo fiel de las imágenes.
Un sistema de lentes de la cámara enfoca la imagen a un traductor que efectúa la conversión. Un tipo de traductor es el CCD (Charge-Coupled Device, Dispositivo acoplado a carga), que es una memoria electrónica que puede ser cargada mediante luz. Son analógicos y están construidos con un tipo especial de transistor CMOS.
Para aprovechar su funcionalidad, se utilizan en combinación con convertidores de señal (ADC) analógica a digital, para cuantificar la carga variable dentro de un número discreto de colores.
Una vez que la señal es digitalizada es enviada a la CPU por un puerto USB.
C.1.2.Desarrollo del Software
Este código va a conformar un archivo dll (Dynamic Link Library)
C.1.2.1.Código de Visual C (Comentado paso por paso):
// DetectCambio.cpp : Defines the initialization routines for the DLL. // #include "stdafx.h" #include "DetectCambio.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // // Note! // // If this DLL is dynamically linked against the MFC // DLLs, any functions exported from this DLL which // call into MFC must have the AFX_MANAGE_STATE macro // added at the very beginning of the function. // // For example: // // extern "C" BOOL PASCAL EXPORT ExportedFunction() // { // AFX_MANAGE_STATE(AfxGetStaticModuleState()); // // normal function body here // } // // It is very important that this macro appear in each // function, prior to any calls into MFC. This means that // it must appear as the first statement within the // function, even before any object variable declarations // as their constructors may generate calls into the MFC // DLL. // // Please see MFC Technical Notes 33 and 58 for additional // details. // ///////////////////////////////////////////////////////////////////////////// // CDetectCambioApp BEGIN_MESSAGE_MAP(CDetectCambioApp, CWinApp) //{{AFX_MSG_MAP(CDetectCambioApp) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDetectCambioApp construction CDetectCambioApp::CDetectCambioApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance m_nTamX=0; m_nTamY=0; m_nUmbral=0; } ///////////////////////////////////////////////////////////////////////////// // The one and only CDetectCambioApp object bool LeerMat(); void GuardarMat(); bool bInicio=false; bool bFirst=true; CDetectCambioApp dllApp; CMatrixRGB g_Mat; bool CDetectCambioApp::GetBMPInfo(CString pszFile, BYTE ** pByte,DWORD &dwWidth,DWORD &dwHeight,DWORD &dwSizeImage) { bool bRet=false; HANDLE hf; // handle a archivo BITMAPFILEHEADER hdr; // bitmap file-header BITMAPINFO bi; PBITMAPINFOHEADER pbih; //bitmap info-header DWORD dwTmp=0; *pByte=NULL; hf = CreateFile((LPCTSTR)pszFile, //levanto archivo GENERIC_READ, (DWORD) 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); try { if (hf != INVALID_HANDLE_VALUE) //si el handle no es invalido por algún error al tomar el archivo { if(ReadFile(hf,(LPVOID) &hdr, sizeof(BITMAPFILEHEADER), //lee la cabezera del BITMAP FILE HEADER (LPDWORD) &dwTmp, NULL)) { dwSizeImage=hdr.bfSize - hdr.bfOffBits; //tamaño en bytes de la imagen if(!dwSizeImage) AfxMessageBox("SizeImage es cero",MB_OK | MB_ICONSTOP,NULL); if(ReadFile(hf,(LPVOID) &bi,(hdr.bfSize - (dwSizeImage + sizeof(BITMAPFILEHEADER))), //lee cabecera de BMP (LPDWORD) &dwTmp, NULL)) { pbih = (PBITMAPINFOHEADER) &bi; if(pbih->biCompression==BI_RGB && pbih->biBitCount==24) //si el formato es BMP de 24 bit { dwWidth=pbih->biWidth; dwHeight=pbih->biHeight; *pByte=new BYTE[dwSizeImage]; if(!ReadFile(hf,(LPVOID) *pByte,dwSizeImage,&dwTmp,NULL)) //lee información de la imagen en un array { AfxMessageBox("Error al leer la imagen | MB_ICONSTOP",MB_OK,NULL); if(*pByte) { delete *pByte; *pByte=NULL; } } else bRet=true; } else AfxMessageBox("Formato invalido en archivo bmp",MB_OK | MB_ICONSTOP,NULL); } else AfxMessageBox("Error al leer la info de bmp",MB_OK | MB_ICONSTOP,NULL); } else AfxMessageBox("Error al leer encabezado de bmp",MB_OK | MB_ICONSTOP,NULL); CloseHandle(hf); } else AfxMessageBox("Error en Handle de archivo",MB_OK | MB_ICONSTOP,NULL); } catch(CException * Ex) { AfxMessageBox("Error Inesperado en GetBMPInfo",MB_OK | MB_ICONSTOP,NULL); Ex->Delete(); } return bRet; }
bool CDetectCambioApp::MakeMatrixRGB(LPBYTE pInfo, DWORD dwWidth, DWORD dwHeigth, DWORD dwSizeImage, CMatrixRGB &Mat) { bool bNeg=true,bRet=true; int x,y,aux; DWORD i,j,PixX,ResX,LongX,OffSetX,PixY,ResY,LongY,OffSetY,OffSet; sRGB rgb,zero; zero.nBlue=0; zero.nGreen=0; zero.nRed=0; if(dwHeigth<0) // recorrida de la imagen desde arriba hacia abajo dwHeigth=abs(dwHeigth); //lo hace positivo else // recorrida de la imagen desde abajo hacia arriba bNeg=false; PixX=dwWidth/m_nTamX; ResX=dwWidth%m_nTamX; PixY=dwHeigth/m_nTamY; ResY=dwHeigth%m_nTamY; try { for(y=0;y<m_nTamY;y++) //recorrido vertical de los cuadros { for(x=0;x<m_nTamX;x++) //recorrido horizontal de los cuadros { OffSet=dwWidth * PixY * y + PixX * x; rgb=zero; if(y==(m_nTamY-1)) LongY=PixY+ResY; else LongY=PixY; for(j=0;j<LongY;j++) //recorrido vertical de pixeles del cuadro elegido { OffSetY=OffSet + (dwWidth * j); if(x==(m_nTamX-1)) LongX=PixX+ResX; else LongX=PixX; for(i=0;i<LongX;i++) //recorrido horizontal de los pixeles del cuadro elegido { OffSetX=i; // un Pixel equivale a 3 BYTES(24 bits) del RGB if(dwSizeImage>(sizeof(RGBTRIPLE)*(OffSetY + OffSetX)+2)) { rgb.nRed+=(int) pInfo[sizeof(RGBTRIPLE)*(OffSetY + OffSetX)]; //acumula RED rgb.nGreen+=(int) pInfo[sizeof(RGBTRIPLE)*(OffSetY + OffSetX)+1]; //acumula GREEN rgb.nBlue+=(int) pInfo[sizeof(RGBTRIPLE)*(OffSetY + OffSetX)+2]; //acumula BLUE } } } if(LongX*LongY) //si la cantidad de pixeles no es cero { rgb.nRed=rgb.nRed/(int)(LongX*LongY); //saca promedio por cada color rgb.nGreen=rgb.nGreen/(int)(LongX*LongY); rgb.nBlue=rgb.nBlue/(int)(LongX*LongY); } else return false; if(bNeg) //imagen top-down Mat.SetPos(x,y); //posiciona la matriz en el cuadro analizado else { //imagen bottom-up Mat.SetPos(x,(m_nTamY-y)-1); //posiciona la matriz en el cuadro analizado aux=rgb.nRed; //intercambia RED por BLUE porque la información esta al revés rgb.nRed=rgb.nBlue; rgb.nBlue=aux; } Mat.SetRGB(rgb); //coloca promedio del cuadro RGB en la matriz } } } catch(CMemoryException * MEx) { bRet=false; AfxMessageBox("Error de Memoria en MakeMatrixRGB",MB_OK | MB_ICONSTOP,NULL); MEx->Delete(); } catch(CException * Ex) { bRet=false; AfxMessageBox("Error Inesperado en MakeMatrixRGB",MB_OK | MB_ICONSTOP,NULL); Ex->Delete(); } return bRet; } bool CDetectCambioApp::DetectarMovimiento(CMatrixRGB &MatOld, CMatrixRGB &MatNew) { bool bRet=false; sRGB rgbA,rgbB; CString str; int Red=0,Green=0,Blue=0; int x,y; for(x=0;x<m_nTamX;x++) //recorrida de la matrix horizontal { for(y=0;y<m_nTamY;y++) //recorrida de la matriz vertical { MatOld.SetPos(x,y); //posiciona rgbA=MatOld.GetRGB(); //obtiene RGB MatNew.SetPos(x,y); rgbB=MatNew.GetRGB(); Red=abs(rgbA.nRed-rgbB.nRed); //valor absoluto de la diferencia de color entre ambas matrices Green=abs(rgbA.nGreen-rgbB.nGreen); Blue=abs(rgbA.nBlue-rgbB.nBlue); if(Red>=m_nUmbral || Green>=m_nUmbral || Blue>=m_nUmbral) //si algún valor absoluto de cada bRet=true; //color es mayor igual al umbral detecta cambio } } MatOld=MatNew; //asigna como matriz vieja a la nueva matriz ingresada return bRet; }
__declspec(dllexport) short int __stdcall Iniciar(short TamX,short TamY, short Umbral) //Inicia Tamaño de matriz RGB { dllApp.m_nTamX=TamX; //Asignación de valores globales dllApp.m_nTamY=TamY; dllApp.m_nUmbral=Umbral; g_Mat.Create(dllApp.m_nTamX,dllApp.m_nTamY); //Crea objeto Matriz RGB bInicio=true; //Matrix iniciada bFirst=true; //Primera imagen return 0; }
__declspec(dllexport) short int __stdcall Detectar(VARIANT vtFile) //Detecta cambio de la imagen nueva con respecto a la anterior { short int ret=0; //retorno por defecto BYTE * pByte=NULL; DWORD dwWidth,dwHeight,dwSizeImage; CString strFile; CMatrixRGB Mat; strFile=vtFile.bstrVal; if(!bInicio) //Si no esta iniciada la matriz retorna error return -1; Mat.Create(dllApp.m_nTamX,dllApp.m_nTamY); //Crea matriz nueva. if(dllApp.GetBMPInfo(strFile,&pByte,dwWidth,dwHeight,dwSizeImage)) //Obtiene los datos de la imagen BMP de 24 bits de color { if((DWORD)dllApp.m_nTamX<=dwWidth && (DWORD)dllApp.m_nTamY<=dwHeight) //si el tamaño de la imagen en pixeles es menor al { //de la cantidad de cuadros a analizar da error if(dllApp.MakeMatrixRGB((LPBYTE) pByte,dwWidth,dwHeight,dwSizeImage,Mat)) //crea matriz según la imagen de entrada { if(dllApp.DetectarMovimiento(g_Mat,Mat)) //Detecta cambios entre la matriz nueva y la anterior y la guarda ret=1; //valor si detecto cambios. if(bFirst) //si es la primera imagen a partir de la inicialización { ret=1; //Fuerza el valor de retorno como si hubiera cambios bFirst=false; //la primera ya pasó } } } else ret=-3; //retorna error en tamaño de imagen con respecto a la de cuadros } if(pByte) delete pByte; else ret=-2; //Error al tomar la imagen (Error de archivo o de formato) return ret; //Respuesta a la detección. }
// MatrixRGB.cpp : implementation file // #include "StdAfx.h" #include "MatrixRGB.h" CMatrixRGB::CMatrixRGB() { m_bInit=false; m_nX=0; m_nY=0; m_nMaxX=0; m_nMaxY=0; m_Mat=NULL; } CMatrixRGB::CMatrixRGB(int x,int y) { int i; m_nX=0; m_nY=0; m_nMaxX=x; m_nMaxY=y; m_Mat=NULL; if(x>0 && y>0) { m_bInit=true; try { m_Mat=new sRGB * [y]; if(m_Mat) { for(i=0;i<y;i++) { m_Mat[i]=NULL; m_Mat[i]=new sRGB[x]; if(!m_Mat[i]) m_bInit=false; } } else m_bInit=false; } catch(CMemoryException * MEx) { m_bInit=false; MEx->Delete(); } if(m_bInit) InitAll(); else Destroy(); } else m_bInit=false; } CMatrixRGB::~CMatrixRGB() { Destroy(); } void CMatrixRGB::Create(int x, int y) { int i; if(m_bInit) Destroy(); m_nX=0; m_nY=0; m_nMaxX=x; m_nMaxY=y; if(x>0 && y>0) { m_bInit=true; try { m_Mat=new sRGB * [y]; if(m_Mat) { for(i=0;i<y;i++) { m_Mat[i]=NULL; m_Mat[i]=new sRGB[x]; if(!m_Mat[i]) m_bInit=false; } } else m_bInit=false; } catch(CMemoryException * MEx) { m_bInit=false; MEx->Delete(); } if(m_bInit) InitAll(); } else m_bInit=false; }
bool CMatrixRGB::SetPos(int x, int y) { bool bRet=false; if(m_bInit) { if(x<m_nMaxX && y<m_nMaxY) { m_nX=x; m_nY=y; bRet=true; } } return bRet; } void CMatrixRGB::GetPos(int &x,int &y) { x=m_nX; y=m_nY; } void CMatrixRGB::SetRGB(sRGB rgb) { if(m_bInit) m_Mat[m_nY][m_nX]=rgb; } sRGB CMatrixRGB::GetRGB(void) { sRGB rgb; if(m_bInit) rgb=m_Mat[m_nY][m_nX]; else { rgb.nBlue=0; rgb.nGreen=0; rgb.nRed=0; } return rgb; } void CMatrixRGB::InitAll() { int i,j; sRGB rgb; rgb.nBlue=0; rgb.nGreen=0; rgb.nRed=0; if(m_bInit) { for(i=0;i<m_nMaxY;i++) for(j=0;j<m_nMaxX;j++) m_Mat[i][j]=rgb; } } void CMatrixRGB::operator=(CMatrixRGB &Mat) { int x,y; if(m_nMaxX!=Mat.m_nMaxX || m_nMaxY!=Mat.m_nMaxY) Create(Mat.m_nMaxX,Mat.m_nMaxY); if(m_bInit) { for(x=0;x<m_nMaxX;x++) { for(y=0;y<m_nMaxY;y++) { if(SetPos(x,y) && Mat.SetPos(x,y)) SetRGB(Mat.GetRGB()); } } } } void CMatrixRGB::Destroy() { int i; try { if(m_Mat) { for(i=0;i<m_nMaxY;i++) { if(m_Mat[i]) delete []m_Mat[i]; } delete []m_Mat; } m_bInit=false; m_nX=0; m_nMaxX=0; m_nY=0; m_nMaxY=0; m_Mat=NULL; } catch(CMemoryException * MEx) { MEx->Delete(); } }
void CMatrixRGB::Serialize(CArchive &ar) { int i,j,aux; sRGB rgb; if(ar.IsStoring()) { ar<<m_nX; ar<<m_nY; ar<<m_nMaxX; ar<<m_nMaxY; aux=(int) m_bInit; ar<<aux; if(m_bInit) { for(i=0;i<m_nMaxX;i++) { for(j=0;j<m_nMaxY;j++) { if(SetPos(i,j)) { rgb=GetRGB(); ar<<rgb.nRed; ar<<rgb.nGreen; ar<<rgb.nBlue; } } } } } else { if(m_bInit) Destroy(); ar>>m_nX; ar>>m_nY; ar>>m_nMaxX; ar>>m_nMaxY; ar>>aux; m_bInit=(bool) aux; if(m_bInit) { try { m_Mat=new sRGB * [m_nMaxY]; if(m_Mat) { for(i=0;i<m_nMaxY;i++) { m_Mat[i]=NULL; m_Mat[i]=new sRGB[m_nMaxX]; if(!m_Mat[i]) m_bInit=false; } } else m_bInit=false; } catch(CMemoryException * MEx) { m_bInit=false; MEx->Delete(); } if(m_bInit) { for(i=0;i<m_nMaxX;i++) { for(j=0;j<m_nMaxY;j++) { ar>>rgb.nRed; ar>>rgb.nGreen; ar>>rgb.nBlue; if(SetPos(i,j)) SetRGB(rgb); } } } else Destroy(); } } }
// stdafx.cpp : source file that includes just the standard includes // DetectCambio.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" |
C.1.2.2.Código de Visual Basic (Comentado paso por paso):
FrmPictDiff(FrmPictDiff.frm):
Option Explicit Dim filephat As String Private Sub cmdComp_Click() Dim cont As Integer Dim vrFile As String Dim intRet As Integer Dim intResp As Integer '--------------------------------------------------------------- 'Verificación de la existencia de imágenes en la lista de comparación '--------------------------------------------------------------- If List2.ListCount = 0 Then If List1.ListCount <> 0 Then If List1.ListCount > 1 Then ' ------------------------------------------------------------ '|Llamada a la función de inicialización de la DLL ("Iniciar")| ' ------------------------------------------------------------ intRet = Iniciar(TamX, TamY, Umbral) For cont = 0 To List1.ListCount - 1 vrFile = filephat & List1.List(cont) '------------------------------------------------------------------------------ '|Llamada a la función de control de cambios de imágenes de la DLL ("Detectar")| '------------------------------------------------------------------------------ intResp = Detectar(vrFile) If intResp < 0 Then MsgBox "Error al querer comparar imagenes", vbCritical Exit For End If ' ------------------------------------------------------------------ '|Realiza carga de archivos .BMP distintos en la lista de resultados ' ------------------------------------------------------------------ If intResp = 1 Then List2.AddItem List1.List(cont) End If DoEvents Next lblResumen = "Imagenes Analizadas: " & List1.ListCount & Space(6) & "Imagenes Distintas: " & List2.ListCount Else MsgBox "Se necesitan 2 imágenes como mínimo para realizar la comparación", vbExclamation, "Imposible Comparar" End If End If End If End Sub Private Sub cmdEnd_Click() Dim decision As Byte
decision = MsgBox("Está seguro de finalizar con la aplicación", vbYesNo + vbDefaultButton2 + vbQuestion, "Cierre del Programa") If decision = vbYes Then End End If End Sub Private Sub cmdSettings_Click() Me.Enabled = False frmSetings.Show End Sub Private Sub cmdVer_Click() Text1 = Get_Path(Text1.Text, CommonDialog1, True) End Sub Private Sub Command1_Click() Dim fdd As New FileSystemObject Dim fil As file Dim clnn Dim fold As Folder
If Right(Text1, 1) <> "\" And Text1 <> "" Then Text1 = Text1 & "\" End If List1.Clear List2.Clear Image1.Picture = Nothing Image2.Picture = Nothing lblResumen = "" On Error GoTo errHandle ' --------------------------------------------------------- '|Realiza carga de archivos .BMP en la lista de comparación ' --------------------------------------------------------- Set fold = fdd.GetFolder(Text1) Set clnn = fold.Files For Each fil In clnn If UCase(ExtractExt(fil.Name)) = "BMP" Then List1.AddItem fil.Name End If Next ' -------------------------------- '|Asigna la ruta de trabajo actual ' -------------------------------- filephat = Text1 Set fold = Nothing Set clnn = Nothing List1.Selected(0) = True errHandle: Exit Sub End Sub Private Sub Form_Load() Dim fdd As New FileSystemObject Dim fil As file Dim clnn Dim fold As Folder
If Mid(App.Path, Len(App.Path), 1) = "\" Then Text1.Text = App.Path Else Text1.Text = App.Path & "\" End If ' ------------------------------------------------------------------------ '| Inicialización de los parámetros por defecto de configuración de la DLL ' ------------------------------------------------------------------------ TamX = 10 TamY = 10 Umbral = 75 ' ------------------------------------- '|Asigna la ruta de trabajo por defecto ' ------------------------------------- filephat = Text1 Set fold = fdd.GetFolder(Text1) Set clnn = fold.Files ' ----------------------------------------------------------------- '|Realiza carga inicial de archivos .BMP en la lista de comparación ' ----------------------------------------------------------------- For Each fil In clnn If UCase(ExtractExt(fil.Name)) = "BMP" Then List1.AddItem fil.Name End If Next Set fold = Nothing Set clnn = Nothing End Sub Private Sub List1_Click() Image1.Picture = LoadPicture(filephat & List1.List(List1.ListIndex))
End Sub Private Sub List2_Click() Image2.Picture = LoadPicture(filephat & List2.List(List2.ListIndex)) End Sub
FrmSetings(FrmSetings.frm): Private Sub cmdAceptar_Click() ' ------------------------------------------------------- 'Asignación de los parámetros de configuración de la DLL| ' ------------------------------------------------------- Dim aux As Integer aux = Val(txt1.Text) If aux > 0 Then TamX = aux aux = Val(txt2.Text) If aux > 0 Then TamY = aux aux = Val(txt3.Text) If aux > 0 Then If aux > 255 Then aux = 255 Umbral = aux End If Me.Hide frmPictDiff.Show frmPictDiff.Enabled = True End Sub Private Sub cmdCancelar_Click() frmPictDiff.Enabled = True frmPictDiff.Show Me.Hide
End Sub Private Sub Form_Load() txt1.Text = CStr(TamX) txt2.Text = CStr(TamY) txt3.Text = CStr(Umbral) End Sub Private Sub Form_Unload(Cancel As Integer) frmPictDiff.Enabled = True End Sub
Módulo: MdlMAin(MdlMain.bas): Option Explicit ' -------------------------------------------------------- 'Declaración de los parámetros de configuración de la DLL| ' -------------------------------------------------------- Public TamX As Integer Public TamY As Integer Public Umbral As Integer ' ----------------------------------------------------------------------- 'Declaración de las funciones de entrada a la DLL ("Iniciar","Detectar")| ' ----------------------------------------------------------------------- Declare Function Iniciar Lib "DetectCambio.dll" (ByVal TamX As Integer, ByVal TamY As Integer, ByVal Umbral As Integer) As Integer Declare Function Detectar Lib "DetectCambio.dll" (ByVal varFile As Variant) As Integer Public Function Get_Path(strDefPath As String, CmmDialog As Object, OnlyFolder As Boolean) ' --------------------------------------------------------------- '|Obtiene el path de un cuadro de diálogo de apertura de archivos ' --------------------------------------------------------------- On Error GoTo errHandle If Right(strDefPath, 1) <> "\" And strDefPath <> "" Then strDefPath = strDefPath & "\" End If CmmDialog.FileName = strDefPath & "*.bmp" CmmDialog.Filter = "Archivos (*.bmp)|*.*" CmmDialog.CancelError = True CmmDialog.ShowOpen
If OnlyFolder Then Get_Path = ExtractFolder(CmmDialog.FileName) Else Get_Path = CmmDialog.FileName End If Exit Function errHandle: Get_Path = strDefPath End Function Function ExtractFolder(ByVal file As String) As String ' ------------------------------------------------- '|Trunca el nombre de archivo del phat seleccionado ' ------------------------------------------------- Dim i As Integer ExtractFolder = "" For i = Len(file) To 1 Step -1 If Mid(file, i, 1) = "\" Then ExtractFolder = Trim(Mid(file, 1, i)) Exit For End If Next End Function
Function ExtractExt(ByVal file As String) As String ' ----------------------------------- '|Devuelve la extensión de un archivo ' ----------------------------------- Dim i As Integer ExtractExt = "" For i = Len(file) To 1 Step -1 If Mid(file, i, 1) = "." Then ExtractExt = Trim(Mid(file, i + 1)) Exit For End If Next End Function
|
C.1.2.3.EXPLICACIÓN DE LOS ELEMENTOS DE INTERACCIÓN DEL USUARIO EN LA APLICACIÓN VISUAL BASIC
C.1.2.3.1.Carpeta de Imágenes:
Especifica la ruta de ubicación donde se encuentran almacenados los archivos bitmap, que serán cargados posteriormente para ser procesados. Esta operación puede hacerse por medio de un cuadro de diálogo o simplemente introduciendo el path manualmente en un cuadro de texto.
C.1.2.3.2.Cargar:
Realiza la carga de archivos .bmp en la lista de comparación, de acuerdo a la ubicación de la carpeta de imágenes anteriormente especificada, pudiendo desplazarse y visualizar las imágenes forma de lista .
C.1.2.3.3.Configurar:
Esta opción permite el seteo de los parámetros de configuración de la DLL por medio de un cuadro de diálogo de configuración:
C.1.2.3.4.Parámetros de Configuración
C.1.2.3.4.1.Definición Horizontal: Establece el número de filas que tendrá la matriz de la imagen.
(10 por default).
C.1.2.3.4.2.Definición Vertical: Establece el número de columnas que tendrá la matriz de la imagen.
(10 por default).
C.1.2.3.4.3.Umbral de cambio: Establece la sensibilidad de cambio del RGB de las filas de la matriz de la imagen. (75 por default).
C.1.2.3.5.Comparar:
En esta opción si hay imágenes cargadas en la lista de comparación, procede a ejecutar las funciones de inicialización y detección de la DLL, con las cuales se podrá establecer las imágenes que no presentan similitud alguna supeditadas al umbral de cambio establecido.
Posteriormente si hay imágenes distintas serán almacenadas en la lista de resultados, pudiendo desplazarse y visualizar las imágenes en la lista.
Si con solo alguna de las imágenes a ser procesadas de la lista de comparación, no es válida por no cumplir con el requisito de ser de 24 bits, la aplicación finalizará de inmediato.
C.1.2.3.6.Salir:
Realiza el cierre de la aplicación.