- Ya tengo el algoritmo para hacer la extracción de Bits.
- Ya tengo el código para codificar la imagen binaria en RLE (gracias JMG, ya lo tenía arrinconado)
- Ya tengo el código para decodificar la imagen codificada con lo anterior
Intento de blog para hablar sobre temas de Procesamiento Digital de Imágenes, Internet de las Cosas (IoT), Sistemas Embebidos, Diseño de Software, etc
27 julio 2009
Update del Compresor que estoy haciendo
16 julio 2009
Idea para un compresor de imágenes con pérdidas usando extracción de bits y codificación RLE a una imagen PGM
Tengo una idea para hacer un compresor de imágenes PGM (por si se preguntan, no, no tengo fijación alguna con este tipo de imágenes solo que es sencillo, ¡gulp!, trabajar con ellas) con pérdidas usando la extracción de bits (medio explicada aquí) y codificación RLE. La idea principal viene de el hecho de que al aplicar un algoritmo extracción de bits a una imagen tenemos un arreglo binario como resultado, tendremos 1 arreglo 2D de ceros y unos por cada bit extraído, es decir que para una imagen de 8-bits, tendremos 8 arreglos de binarios. De esto podemos observar que cada imagen resultante contiene información de la imagen, y se puede observar una degradación empezando del bit más significativo hacia abajo. Se pueden emplear un par de estas imágenes para codificarlas usando un algoritmo de RLE aprovechando que tendremos secuencias de unos y ceros; luego al recuperarlas y aplicarles algún tipo de técnica de Diethering, para mejorar la calidad de la imagen resultante, claro, con pérdida de información con respecto a la original.
A continuación listo los pasos que creo que son necesarios para este algoritmo:
- Aplicar la extracción de bits a la imagen. Resultado: 8 imágenes (1 por bit en este caso)
- Seleccionar las imágenes que serán empleadas para la codificación RLE. Resultado: conjunto de imágenes a comprimir.
- Aplicar RLE a un cada una de las imágenes seleccionadas en el paso anterior. Resultado: n número de conjuntos de datos (1 por imagen). El total de datos obtenidos aquí nos dará como resultado una compresión.
- Para descomprimir la imagen, aplicamos RLE inversa a los conjuntos de datos. Resultado: n número de imágenes binarias.
- Se combinan las imágenes obtenidas en el paso anterior (esto es posible asignando cada bit y reconstruyendo la imagen de 8bits). Resultado: Imagen de 8bits con pérdida de información.
- A la imagen resultante le aplicamos algún tipo de Diethering para mejorar la calidad. Resultado imagen codificada y decodificada con pérdidas.
No se hasta que punto sea posible obtener una buena imagen como resultado, o que tan buena compresión se pueda lograr para mantener un balance entre calidad de imagen y compresión.
Ya pondré los resultados en un post en cuanto tenga algo que valga la pena. :)
24 junio 2009
Visión por Computadora
¿Qué Visión por Computadora? Emular la visión humana por medio de una computadora. (¿Uhhh?)
El propósito de la visión por computadora es hacer lo que la visión humana hace, pero, por medios artificiales, para poder entender esto, primero hay que entender un poco como funciona la visión humana ¿no? Pues si, es ahí en donde empiezan los problemas. Nosotros tenemos los sentidos para percibir e interpretar nuestro medio ambiente, las cosas que nos rodean, es ahí donde comienza nuestra realidad. Físicamente, nuestros ojos nos sirven como una especie de sensores que captan las ondas luminosas que se reflejan en los objetos y los captura, los pasa al cerebro como impulsos eléctricos y el cerebro se encarga de hacer todo lo demás. (Que fácil se escucha todo esto, en realidad es mucho más complicado, pero a grandes rasgos así es).
El sensor que se encuentra en una cámara fotográfica la podemos comparar (en este caso) con los ojos, es la que nos permite captar las información de las ondas luminosas del ambiente y la convierte a impulsos eléctricos, aquí en este caso, la cámara también funge un poco como cerebro, puesto que procesa las señales eléctricas obtenidas del sensor y las acomoda en una forma que puedan ser transferidas a otro medio. Aquí, no se puede limitar a las cámaras fotográficas como único ejemplo, puesto a que también lo es un aparato de resonancia magnética, o bien, un ultrasonido.
Una parte muy importante en los sistemas de visión por computadora es la iluminación, ya sea la del ambiente o la propia del sistema. ¿Por qué es necesario un sistema de iluminación? Pues porque las condiciones de iluminación del objeto que vamos a capturar pueden afectar el objeto. Nuestro sistema de visión humano no necesita de tan preciso sistema de iluminación ya que puede distinguir los objetos ya que sus algoritmos son demasiado complejos y perfectos que no los podemos comprender. (Si, todavía soy de los que piensan que la computadoras no van a poder igualar el cerebro humano :P) La técnica a utilizar en el sistema va a depender puramente de la aplicación de la misma, esto es, no existe una técnica única para los sistemas de visión, y por lo consiguiente se tiene que analizar cual es la más apropiada para utilizar.
Bueno, si ya tenemos el sensor y la iluminación, ¿qué es lo que nos hace falta? pues la computadora, una vez que el sensor nos capturó la información y la convertimos de una manera que la computadora la pueda leer, pues lo que nos hace falta, son los algoritmos para procesar la imagen. ¡PUM!
Es ahí donde entra el procesamiento digital de imágenes, es emular lo que el cerebro hace, no los ojos, eso ya lo hicieron los sensores, ahora es el turno de emular al cerebro. (good grief!) Existen un gran número de algoritmos y técnicas de procesamiento de imágenes, y al pasar del tiempo se vuelven más complejos y pueden atacar problemas especificos que antes no era posible. Pero hay muchas cosas que el cerebro humano puede hacer y un sistema de visión no (¿por ahorita?)
Por ejemplo, si vemos la imágen del gran Vicente, una persona puede decir muchas cosas, desde la combinación de colores, hasta temas filosóficos acerca del porque del sí. Pero, si esta imagen es alimentada a un sistema de visión por computadora, pues no serían más que una fila de ceros y unos, haría falta de aplicar ciertos algoritmos para obtener cierta información, pero solamente será la información para la cual esta diseñado el algoritmo. Una computadora no puede decir nada de la pintura, no puede saber que se quiso transmitir cuando el pintor pintó la obra, ni siquiera se deprime cuando ve la imagen, ni mucho menos se siente identificada con el hastío que le transmite, ni se siente de ese mismo modo.
Technorati : visión computacional, visión por computadora
Del.icio.us : visión computacional, visión por computadora
Zooomr : visión computacional, visión por computadora
Flickr : visión computacional, visión por computadora
12 junio 2009
Ejemplo de extracción de Bits a una imagen PGM en LabVIEW 8.6
El ejemplo a continuación hace exactamente lo mismo que este post pasado, pero esta ocasión esta implementado en LabVIEW, utiliza el código mostrado en el post anterior donde se carga una imagen PGM y se aplica la extracción de bits a la imagen leída y se muestra en pantalla.
El codigo siguiente muestra como se abre la imagen y se carga en pantalla, luego se obtiene el arreglo 1D de los datos y a ese buffer se le aplica la extracción de bits y luego se muestra en pantalla.
A continuación se muestra el código para realizar la extracción de bits al arreglo de 1D de bytes.
Y así es como queda la imagen de los chiles al extraer el bit mas significativo de la imagen.
Este ejemplo me ha servido para aplicar la teoría en la práctica. Pues estoy haciendo la implementación de los algoritmos a mano, es divertido. (uy si, definitivamente necesito una vida)
11 junio 2009
Ejemplo para cargar una imagen PGM en LabVIEW sin IMAQ
Je je, antes de que alguien del cuerpo técnico y ventas de National Instruments lea esto, mejor rectifico.
Dado a la naturaleza de LabVIEW también se pueden implementar un par de algoritmos (ja ja, de acuerdo, el universo de posibilidades es infinito, “the sky is the limit”) de la manera en que se haría con MATLAB (según un ingeniero de campo de NI me platicó que al principio eran una sola compañía lo que ahora es Mathworks y NI, pero en algún punto en el tiempo se separaron, así que se disputan el origen del engine de matrices ambos, claro que el inge de NI juraba que era de ellos, jeje, me imagino que dirían los de Mathworks) , ya saben el paradigma orientado a datos que manejan estos de National Instruments, claro ellos tienen un Toolkit dedicado al procesamiento de imágenes, pero este post se enfocará en implementarlo sin el uso del tan famoso Vision Development Module.
Como ya he puesto en otros posts vamos a leer imágenes PGM ya que LabVIEW no cuenta con una librería para leer este tipo de imágenes. Las imágenes PGM son ampliamente usadas para probar algoritmos de procesamiento digital de imágenes, son fotos en escala de grises cada byte representa un pixel, es decir 8 bits, 256 posibles escalas de grises.
El ejemplo que propongo consta de un VI (instrumento virtual) principal y 3 subVIs. (Los VIs se pueden ver como la contraparte de las funciones en los programas por texto). La primera parte consta de leer un archivo del disco duro, se lee como archivo binario, como resultado de este se obtiene un arreglo de bytes que contiene los valores leídos en el archivo. En seguida pasa por un parser para el encabezado del archivo PGM, que básicamente comienza con dos bytes en ascii representando “P5”, en seguida viene el ancho, seguido del alto de la imagen, y para finalizar el nivel de grises en que está codificada la imagen. En medio de estos debe de estar como separador un espacio, ya sea un espacio en blanco (0x20), un carrier return (0x0D), un line feed (0x0A) o un tab (0x09). También hay que notar que después del “P5” se pueden tener comentarios y se identifican con el símbolo (#) al principio de la línea. Para más información del formato PGM, favor de ir a la página de la especificación.
A continuación se muestra el VI principal que realiza el leer el archivo, procesar la información y mostrarla en pantalla.
Una de las funciones del VI “ParsePGMHeader.vi” es determinar si es un archivo válido, es por eso que la salida del VI se alambra a un case para determinar si es válido y continuar o de lo contrario terminar el programa, como se observa en las figuras anteriores. Tengo que agradecer a un miembro del foro de NI (JB) de donde tome el VI para mostrar un msgbox en LabVIEW si lo necesitan pueden checar el foro. (n.d.r: el post es algo viejo y es para una versión de LabVIEW algo vieja por ende, y no se si la versión 8.6 ya tenga algo embebido). A continuación se muestra el diagrama a bloques de “ParsePGMHeader.vi”
Este parser lo que hace es que lee el bufer de bytes que se leen de el archivo y busca el encabezado, de ahí saca el ancho y el alto de la imagen, (tengo que confesar que este algoritmo no cumple completamente con la especificación, ya que asume que la imagen es de 256 niveles de grises, pero eso no es necesariamente cierto según la especificación; y entonces los pixeles ya no serían necesariamente de un byte, sino de dos. :S) Así como también, determina si es un archivo válido. En seguida lee el valor isPGM? y se mete a un case, para determinar si se continúa con la ejecución o se detiene le programa.
En caso de ser verdadero, se agarra el buffer de datos y se obtiene el buffer de la imagen, como datos de entrada se tiene la salida del VI anterior, el cual le pasa el buffer, en donde empieza la imagen y el ancho y alto de la misma. La salida de este VI nos da un arreglo en 2D conteniendo la imagen. A continuacion se muestra el diagrama del VI “GetPGMImageData.vi”.
La salida de este VI se usa para dibujar la imagen PGM en un objeto picture. También da como salida el ancho y alto de la imagen. En seguida se muestra el “DrawPGMImage.vi”. A la cual se le pasa como entrada el arreglo en 2D conteniendo la imagen y a la salida se le conecta un objeto picture. El diagrama se muestra a continuación.
Al final en el VI se agrega un nodo de propiedades para poder modificar el ancho y alto de la imagen a mostrar, dependiendo del tamaño de la imagen original.
Como decía al principio, es que se trata de cargar la imagen sin necesidad de tener el Toolkit de procesamiento de imágenes, ya que trae integradas funciones para leer archivos png, jpg y bmp, pero no los pgm. Igual, si quieren los archivos para hacer pruebas solo mándenme un email y se los envío con gusto. Todavía ando viendo como hacerle para poder subir archivos y linkearlos al blog, creo que no me va a quedar de otra que usar un disco virtual.
Simple, ¿no?
10 junio 2009
Cómo mostrar una imagen transformada con fft2 en GUIDE usando MATLAB7
Este ejemplo es para complementar el ejemplo anterior de “Un ejemplo simple de una interfaz gráfica usando GUIDE en MATLAB para Procesamiento Digital de Imágenes”. En esa ocasión el énfasis que puse fue en mostrar como se crea la interfaz gráfica y como se muestra una imagen en pantalla. Ahora se muestra como agregar el código para mostrar la transformada de Fourier usando la función fft2 que viene incluida en MATLAB.
Si se sigue el ejemplo anterior solamente tendremos que agregar el siguiente código al callback del botón relacionado al procesamiento de la imagen.
% --- Executes on button press in ProcesarImagen.
function ProcesarImagen_Callback(hObject, eventdata, handles)
% hObject handle to ProcesarImagen (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
guidata(hObject, handles);
% Carga la imagen de la variable global a una local.
mImage = handles.myImage;
% Calcula la transformada de fourier
mImage2 = fft2(double(mImage));
% Acondiciona la imagen para mostrar las magnitudes centradas
s = fftshift(log(abs(mImage2)));
limites = [(min(s(:))) (max(s(:)))];
set(handles.axes2, 'CLim', limites);
image( s, 'Parent', handles.axes2, 'CDataMapping', 'scaled');
axis(handles.axes2, 'off');
guidata(hObject, handles);
No tengo manera de subir los archivos todavia, y no quiero ponerlos en un servidor externo así que si alguien le interesa alguno de los ejemplos que aqui se ponen, simplemente manden un email y se los mando
30 mayo 2009
29 mayo 2009
Otro Software para editar posts Offline "El cuervo" Zoundry Raven
Dado a que no se por que motivo no he podido publicar desde Windows Live Writer, tuve que darme a la tarea de buscar un editor de reemplazo para WLW, ya estaba empezando a agarrarle la movida cuando de repente, ya no pude poner ni un post más y solo me daba un error como de "object not found" o algo por el estilo. La verdad no quise buscar más y no perder tiempo en arreglar algo que de la nada salió.
Bueno haciendo uso de San Google, me encontré con un par de aplicaciones que me parecieron interesantes. La primera, BlogDesk. Leí un par de criticas favorables acerca de BlogDesk, pero el único pero que le encontré es que no funciona para el vulgo, o sea, Blogger. Y como es por ahorita mi sistema predilecto de blogeo, pues a pesar de instalarlo, tuve que descartarlo de la lista. Después me encontré con este Zoundry Raven, el cual hasta ahorita, me parece bueno. Al menos mejor que el editor on-line de blogger, que tenía un post listo para publicar en WLW y después tuve que volver a poner en blogger y pues fue un desastre el volver a formatear el texto y subir las imágenes, pero bueno, al final quedó publicado, no como yo quisiera y sin antes aventarme un clavado dentro del HTML.
Por ahorita tiene todo lo que podría ocupar para publicar posts en mi blog, no se si se le pueden agregar "plug-ins" como al WLW, pero haber que sale.
28 mayo 2009
Ejemplo simple de una interfaz gráfica usando GUIDE en MATLAB para procesamiento digital de imágenes
Hace poco que aprendí lo básico de crear interfaces gráficas en Matlab. En realidad es hasta cierto punto simple, pero también creo que no es del todo amigable para crear GUIs. Si, estoy de acuerdo que tiene un estilo de editor para hacernos más fácil la existencia. Pero no es del todo intuitivo, o al menos es lo que pienso en el par de programitas que he tenido oportunidad de hacer. Bueno dejando las quejas a un lado, voy a empezar con el ejemplo. Voy a crear una ventana en la que se cargue una imagen por medio de un botón y desplegarla en la ventana, y hacer un procesamiento sencillo y también mostrarlo en pantalla, se que es un ejemplo muy básico, pero creo que es perfecto para empezar. (o al menos es lo que andaba buscando yo cuando quise hacer esto)
Lo primero que tenemos que hacer es iniciar el editor GUIDE que se puede hacer de dos maneras, la primera es desde el menú de inicio: File –> New –> GUI como se muestra en la siguiente imagen.
También se puede iniciar desde la línea de comandos o “command window” de Matlab poniendo “guide”. Aparece una ventana de inicio del GUIDE.
La ventana anterior es (como su nombre lo indica) el lanzador rápido para el GUIDE con varias opciones, la que vamos a seleccionar es la que viene por default y presionamos “OK” y nos aparece el editor de interfaces gráficas en blanco, como se muestra a continuación:
Como primer paso vamos a insertar el objeto que nos va a servir para desplegar la imagen en la ventana. En este caso Matlab lo maneja con “axes”, así que presionamos el botón que esta en la barra de herramientas de la izquierda y ponemos dos “axes” que es donde vamos a cargar la imagen original y la procesada, como se muestra en la siguiente imagen.
Luego presionando el botón “Push Button” (1) ponemos dos botones (2) que el primero nos va a servir para seleccionar el archivo de la imagen y el segundo para procesarla, también vamos a mostrar donde se seleccionan y se cambian las propiedades del objeto (3).
En la figura siguiente se muestra el inspector de propiedades “property inspector” en donde podemos modificar las propiedades del botón. En el (1) se modifica el texto del botón, y en (2), se modifica el nombre del botón.
Modificamos (1) y (2) para cada botón de manera que nos quede como en la imagen siguiente.
En donde dice String simple y sencillamente cambiamos el texto que tiene el botón y en donde dice tag se define el nombre del botón, esto es importante, porque aquí es donde definimos el nombre que va a tener la función “Callback” que es la que se manda llamar cuando se presiona el botón. En la imagen siguiente se muestra como queda la GUI con los cambios hechos. Tambien se muestra como tener acceso a la función donde vamos a poner el código que nos cargue la imagen en pantalla.
A continuación se abre la ventana del editor de códigos de MATLAB y nos selecciona el inicio de la función en la que debemos agregarle nuestro código que en este ejemplo nos va a seleccionar un archivo para ponerlo en axes1.
Una vez que tenemos el editor de MATLAB abierto, buscamos el siguiente código y le agregamos la línea de código que se muestra a continuación, lo que se esta haciendo es agregar una variable para almacenar en memoria la imagen que vamos a cargar .
% --- Executes just before GuiBasica is made visible.
function GuiBasica_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to GuiBasica (see VARARGIN)
% Choose default command line output for GuiBasica
handles.output = hObject;
%Add variable to hold images (Agregar variable para almacenar imágenes)
handles.myImage = [];
% Update handles structure
guidata(hObject, handles);
La línea que agregué en el código anterior es donde dice handles.myImage = []; que nos va a servir para almacenar la imagen temporalmente para procesarla cuando tengamos que hacerlo en el botón que definimos para procesar la imagen.
El código siguiente se agrega en el callback del botón CargarImagen.
% --- Executes on button press in CargarImagen.
function CargarImagen_Callback(hObject, eventdata, handles)
% hObject handle to CargarImagen (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
try
[filename,pathname] = uigetfile('*.pgm','Selecciona imagen para abrir');
if isequal(filename,0)
%Do nothing yet
else
handles.myImage = imread(fullfile(pathname, filename));
[X, myMap] = gray2ind(handles.myImage);
mImagen2 = ind2rgb(X,myMap);
image(mImagen2, 'Parent', handles.axes1);
colormap(gray);
end
guidata(hObject, handles);
catch
msgbox('Error')
end
Y agregando el código siguiente al callback del botón ProcesarImagen como se muestra a continuación hacemos nuestro ejemplo de procesamiento, aquí solamente voy a hacer el negativo de la imagen para ilustrar el ejemplo con algo simple, en este caso se pretende mostrar como hacer un GUI en MATLAB, no dar una clase de procesamiento de imágenes.
% --- Executes on button press in ProcesarImagen.
function ProcesarImagen_Callback(hObject, eventdata, handles)
% hObject handle to ProcesarImagen (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
guidata(hObject, handles);
mImage = handles.myImage;
mImage2 = 255-mImage;
[X, myMap] = gray2ind(mImage2);
mImagen2 = ind2rgb(X,myMap);
image(mImagen2, 'Parent', handles.axes2);
Y a continuación se muestra como queda el resultado final de nuestra GUI.
Si todo se ha hecho como se indica, debería de funcionar. jeje. En caso de que me falte un paso o que algo no se entienda solo manden un correo y trataré de corregirlo. gustavo.ramirez.v@gmail.com
23 abril 2009
Ejemplo de extracción de bits de una imagen en escala de grises de 8 bits en MATLAB
En este caso supongamos que tenemos una imagen en formato PGM, (escala de grises de 256 niveles, o sea 8 bits). Si queremos extraer la información de cada bit entonces tenemos lo siguiente:
- u, es el valor del pixel al que le vamos a extraer el bit
- B, es el número de bits usado para representar un pixel
- n, es el bit que queremos extraer de cada pixel, B es el menos significativo y 1 es el más significativo
- L, es en nivel máximo que puede tener nuestra imagen
Según Anil K. Jain en su libro Fundamentals of Digital Image Processing, en el capítulo dedicado al mejoramiento de imágenes (o image enhancement), explica la extracción de bits en una imagen y define al pixel u de la siguiente manera:
y queremos que nuestro pixel resultado v de la extracción de bit sea:
El autor define kn como se muestra a continuación:
En donde:
En este caso u puede tomar valores entre 0 y 255 (ya que es una imagen PGM de 8 bits). Por ejemplo si decimos que u = 237 y queremos extraer el bit más significativo entonces tenemos que n = 8 y B = 8. Representando u en su equivalente binario tendriamos que u = 11101101. Nos quedaría de la siguiente manera:
- in = i8 = int[237/(2^8-8)] = 237
- in-1 = i7 = int[237/(2^8-7)] = int[237/2^1] = int[237/2] = int[118.5] = 118
- kn = k8 = 237 – 2*118 = 237 – 236 = 1
Enseguida se muestra el codigo en MATLAB para llevar a cabo la extracción de bits de la imagen del bote:
clc;clear;
myImagen = imread('boat.pgm');
myImagenNueva = myImagen;
[rows cols] = size(myImagen);B = 8;
L = 255;for n = 1:B
for i = 1 : rows
for j = 1 : cols
u = double(myImagen(i,j));
iEne = floor(u/2^(B-n));
iEneMenosUno = floor(u/2^(B-(n-1)));
BitExtraido = iEne - (2*iEneMenosUno);
if BitExtraido == 1
BitExtraido = L;
else
BitExtraido = 0;
end
myImagenNueva(i,j) = BitExtraido;
end
end
subplot(2,4,n);
imshow(myImagenNueva);
title(['Bit', num2str(n)]);
end
08 marzo 2009
Mucho ruido y pocas nueces. Cuantización escalar con Dithering aplicando ruido pseudo-aleatorio en MATLAB
En este ejemplo voy a tomar una imagen PGM de 8 bits en escala de grises (256 tonos de grises o 2^8) y la voy a convertir a 5 bits (32 tonos de grises o 2^5). Ese es el objetivo de la cuantización, al menos la cuantización en imágenes, el poder representar una imagen con diferente número de bits que la original tratando de que la imagen cuantizada se vea lo más parecido a la original, que obviamente es prácticamente imposible.
clear;clc;
imagen = double(imread('boat.pgm'));figure(); imshow(uint8(imagen));
title('Imagen Original 8 bits');[cols rows] = size(imagen);
%--------------------------------------------------------
l = (2^3);
imQ = UniformQuantizationToImage(imagen,l);figure(); imshow(uint8(imQ));
title('Imagen Cuantizada 4 bits');for i=1:6
a = i*4;
imWNoise = imagen + generateNoiseImage(cols,rows,-a,a);
imR = UniformQuantizationToImage(imWNoise,l);
figure(); imshow(uint8(imR));
title(['Imagen con Ruido aleatorio[-a a] con a= ', num2str(a)]);
end
%--------------------------------------------------------
function imgQuantized=UniformQuantizationToImage(imgToQuantize,levels)
%Aplicar quantizacion uniforme a la imagen
imQ = floor(imgToQuantize.*((levels+1)/256))+1;
%Recuperar imagen en base a la cuantizada para mostrar
%la pérdida de información
imgQuantized = imQ.*((256/levels)-1);
end
27 febrero 2009
Quantization by Dummies
Every time it happens to me, I remember the episode in The Simpsons where Homer wants to get a raise when he was working at Bowlarama, he proposes to Al to triple the business, then you can see Homer reading “Advanced Marketing”, next scene the book is in trash bin and Homer is reading “Basic Marketing”, after that you can see a pile of books in the trash and Homer looking for the word “marketing” at the dictionary…
I had to read an technical article related to Quantization and present an explanation for the Digital Image Processing class in a workshop session. The teacher gave us a list of articles related to quantization and she asked us to choose one of them. The first problem we faced was the availability of the papers, although the university has some kind of subscription with IEEE and ScienceDirect, the number of articles available for us to download was very limited and the date of them was quite old.
While looking at the titles of the papers “the” question was arisen, which article should I choose? First of all looked at the articles that sounded good to me, then I realized that none of them were available from IEEE, and couldn’t connect to ScienceDirect from school. Then I started searching any article from the list available from IEEE was “Generalized Scalar Quantizer Design Using Dynamic Programming” (bear in mind that if you don’t have a subscription you can’t see the article). The article is three pages long, but I couldn’t get to understand it enough to make a presentation.
I gave up searching the articles from the list and started surfing with Saint Google, I don’t remember the keywords I used to search, but I came up with “Optimal Entropy-Constrained Scalar Quantization of a Uniform Source”. It’s funny because while writing this post I was looking for the link to this file and found that the one of the coauthors of the article, ANDRÁS GYÖRGY, has a bunch of his papers published listed in his publication's page, are available to download and some of them are about quantization.
After that, troubles has just begun, I started reading the article, and realized that most of the ideas were unclear to me, then I had to go to the books to search for the definition of the term I was stuck with, but while reading the definition I found also some things that for the moment I wasn’t able to understand, besides that I couldn’t found any ‘for dummies’ book about digital image processing, go figure, and it’s obvious there is no such thing. So I was reading in circles, finally, I put some kind of order in my ideas and made a presentation with the concepts I got. Still, it wasn’t enough to be clear the concept proposed in the paper, at least for me.
The best thing that I recall from this exercise is that it helped me to understand that research is not easy, but reading and sharing the little knowledge acquired, is the only way to start learning new things, it opens the mind to a different level, because we can see the work other people involved in the same field is doing, and also get fresh ideas from our peers, remember that feedback is always good and there is no such thing as “constructive nor destructive” critics.
25 febrero 2009
¿Qué tan gacha se ve la foto matemáticamente hablando? Parámetros para medir el desempeño de un algoritmo de compresión de datos con pérdidas
En la compresión de datos, lo que se busca presisamente es comprimir, valga la redundancia (y no es chiste), o sea, reducir en tamaño los datos originales, y esto es principalmente con dos fines: almacenamiento y transmisión. Existen dos tipos de compresión usados comúnmente:
- Con pérdidas (Loosy): Se refiere a los algoritmos que comprimen los datos, pero que al recuperarlos no se tiene la información original, sino un aproximado, todo se basa en la subjetividad.
- Sin pérdidas (Lossless): Como su nombre lo indica, se reduce el número de datos para representar la información, pero al recuperarse se reproduce tal como la original.
Usualmente, el primero ofrece una compresión mayor en comparación con el otro, y esto es de una manera obvio, debido a que al ser un algoritmo que permite pérdidas, entonces es menor la cantidad de información necesaria.
Una de las medidas más usadas en imágenes y audio es el promedio de errores cuadrados (MSE: mean square error):
Ahora, si queremos el error relativo a la señal se calcula el error de la señal reconstruida y el MSE, y a esta relación se le llama relación de señal a ruido (SNR: Signal to Noise Ratio) y esta dada por:
El SNR se mide comúnmente en escala logarítmica, en decibeles (dB) y se representa cómo:
Hay veces que es necesario conocer el error relativo al valor máximo, o valor pico de la señal, y a esto se le llama Razón de la Señal Pico al Ruido (PSNR: Peak Signal to Noise Ratio) y se calcula de la siguiente manera:
Existen otras medidas que se emplean para medir la distorción de manera frecuente, un ejemplo usado para evaluar algoritmos de compresion de imágenes es el del promedio de diferencias absolutas:
Cuando la distorción es imperceptible entonces se calcula el valor de la magnitud del error:
Los parámetros mencionados en este post, son como ya se mencionó, para medir el desempeño de los algoritmos de compresión, y una pregunta que me hice al empezar a estudiar esto, ¿y qué demonios me dice esto o para que me sirve? A, pues medio simple, son números que nos sirven para determinar que tan “fiel” a la original esta la señal y se usan estos números para tomar un tipo de decisión o llegar a una conclusión en cuanto al algoritmo utilizado.
¿Capish?
24 febrero 2009
¿Cómo demonios se supone que debo interpretar esto? Cuantización escalar uniforme de una imagen en MATLAB
Una parte fundamental del procesamiento digital de imágenes, es la representación de una imagen. De manera muy general, si vemos una imagen como parte de un sistema de adquisición de datos, el sensor es el que se encarga de capturar el brillo de la luz y convertirla a valores que puedan ser usados digitalmente, es nuestro convertidor análogo a digital. De acuerdo a lo que establece Sonka, Hlavac y Boyle en Image Processing, Analysis, and Machine Vision (Ed. Thompson, 2008, USA), la transición entre los valores continuos de la función de la imagen (brillo) y sus equivalentes en el mundo digital se le llama cuantización, y el número de niveles de cuantización debe ser lo suficientemente grande como para permitir que el ser humano perciba los detalles finos del sombreado en la imagen.
En este ejemplo vamos a trabajar con una imagen PGM, que al leerla en MATLAB nos regresa una matriz de tamaño NxM. Cada elemento representa un pixel en la imagen, con valores que van de 0 a 255, es decir 256 valores diferentes (8bits). A esta imagen también le podemos aplicar una cuantización ya que con menos bits es posible representar la imagen, con pérdidas debido a que son menos niveles de grises disponibles para mostrar la imagen.
- %Lee imagene PGM
- imagen = double(imread('peppers.pgm'));
- %Crea variables temporales
- imgQuantized = zeros(512,512);
- imgQuantizedToDisplay = zeros(512,512);
- n = 2; %Define bits para cuantización
- figure();
- imgQuantized = floor(imagen/(256/(2^n)-1)); %Cuantizar
- imgQuantizedToDisplay = ((imgQuantized)*(256/2^n));
- imshow(uint8(imgQuantizedToDisplay));
- title(['Cuantización a ', num2str(n) ,' bits']);
- n = 4; %Define bits para cuantización
- figure();
- imgQuantized = floor(imagen/(256/(2^n)-1)); %Cuantizar
- imgQuantizedToDisplay = ((imgQuantized)*(256/2^n));
- imshow(uint8(imgQuantizedToDisplay));
- title(['Cuantización a ', num2str(n) ,' bits']);
- n = 6; %Define bits para cuantización
- figure();
- imgQuantized = floor(imagen/(256/(2^n)-1)); %Cuantizar
- imgQuantizedToDisplay = ((imgQuantized)*(256/2^n));
- imshow(uint8(imgQuantizedToDisplay));
- title(['Cuantización a ', num2str(n) ,' bits']);
Con n=2 bits podemos representar 2^n=2^2=4 niveles distintos de grises. Así mismo, con n=4 bits podemos representar 2^n=2^4= 16 niveles y con n=6 bits se representan 2^n=2^6=64 niveles en escala de grises.
23 febrero 2009
El impacto de una revelación inesperada
¿A poco no cambian las cosas cuando en el momento menos esperado descubres de una u otra manera algo que por algún tiempo había vagado sin rumbo aparente por los pasillos del inconsciente? Siempre sucede de la misma manera. Al estar buscando respuestas a los grandes enigmas universales, uno se encuentra en el mundo de las tinieblas ya que la claridad nunca llega en el momento indicado y más aún, siempre surgen dudas adicionales en el camino. Pero, también todo es diferente cuando en un solo instante las cosas parecen aclararse, no me refiero a resolver los enigmas, sino que a raíz de dicha epifanía uno cree comprender el porqué de las cosas, tampoco significa que tienes que estar de acuerdo con ellas…, pero así sucede, desgraciadamente .
Si tenemos una imagen en formato PGM, que según sus autores “está designado para ser extremadamente fácil de aprender”, ya que cada uno de los valores de los datos representa un pixel en escala de grises de 8 bits, no representa en mayor problema hacer un programa para obtener la imagen del archivo. Afortunadamente no es este el caso, en MATLAB leemos un archivo pgm de la siguiente manera:
imagen = imread('peppers.pgm','pgm');
Ahora, imagen es una variable que contiene una matriz de MxN pixeles, donde N y M dependen del tamaño de la imagen. A esto lo vemos como nuestra señal, y le aplicamos la Transformada de Fourier Discreta, pero al ser una imagen y como lo mencionaba antes, es una matriz de 2 dimensiones, lo que quiere decir que tenemos que realizar la transformada a través de los renglones y después de las columnas. El código que se presenta a continuación fue sacado de aquí, aunque lo modifique ligeramente para solamente pasarle los datos y calcular automáticamente el tiempo y las frecuencias a partir de esto, lo que realiza esta función es calcular la DFT a los datos de entrada:
- function X=dft(x)
- [sx1 sx2] =size(x); %Obtener el tamaño de la matriz
- t = [0:sx2-1]; %Calcular la base de tiempo
- f = [0:(1/sx2):(1-(1/sx2))]; %Calcular las frecuencias
- t = t(:); % Formatear 't' en vector columna
- x = x(:); % Formatear 'x' en vector columna
- f = f(:); % Formatear 'f' en vector columna
- W = exp(-2*pi*j * f*t');
- X = W * double(x);
Código 1. Función que realiza la DFT en MATLAB
Las últimas dos líneas de código representan en esencia la transformada de fourier. Tengo que hacer referencia al post que escribí iniciando este tema, ya que no recuerdo de que fuente tomé la ecuación en el post anterior, pero noto que es diferente en cuanto X(k) y x(n) está representadas como X(k+1) y x(n+1), creo que la diferencia radica en donde se tomen los coeficientes.
Ok, otra vez, la ecuación siguiente nos muestra la transformada de fourier discreta (Tomada de: Practical Digital Signal Processing for Engineers and Technicians, pp 63)
Ahora, viendo el programa relacionamos la línea 8 con lo siguiente:
Podemos observar de la ecuación anterior que k es el equivalente a la líneas 3 y n/N es la línea 4. Esto es, k es el ‘tiempo’ o mas bien dicho cada muestra en la imagen y las frecuencias en las que contienen son n/N. (Si, esto me sigue causando problemas emocionales todavía). En la última línea del código anterior (la nueve) tenemos las muestras x(n) con los componentes de frecuencia (lo representado en la ecuación 2).
Código 2. Programa que realiza la DFT a una imagen de 8 bits escala de grises en MATLAB
- imagen = imread('peppers.pgm','pgm');
- imfinfo('peppers.pgm')
- figure(1); imshow(imagen);
- [s1 s2]= size(imagen);
- dftimagen = zeros(s1);
- for i=1:s2
- dftimagen(i,:) = dft(imagen(i,:));
- end
- dftimagen= dftimagen.';
- for i=1:s2
- dftimagen(i,:) = dft(dftimagen(i,:));
- end
- dftimagen = dftimagen.';
- figure(2);imshow(uint8(ifft2(dftimagen)));
De las líneas 1 a la 3 se lee la imagen y muestra en pantalla, en las líneas 4 y 5 se crea una nueva variable para almacenar el resultado de la transformada (este código tiene el inconveniente que da por hecho que es una matriz cuadrada). Las líneas 6 a 8 realizan la DFT a cada renglón de la imagen, posteriormente se realiza una traspuesta (en la línea 9) a la imagen para realizar la DFT a lo largo de las columnas en las lineas 10 a 12 y se regresa con la traspuesta de la línea 13.
Por último, se toma la variable que tiene almacenada la imagen transformada y usando la transformada inversa que viene incluida en MATLAB se recupera la imagen original y se muestra en pantalla (línea 14) para verificar que se halla realizado correctamente la DFT.
Solo para terminar esta imagen en la que hice pruebas es una imagen de 512 x 512 pixeles y es bastante lento, no tengo los datos de cuanto se tarda, pero no se compara con la función que trae integrada MATLAB para realizar la FFT (no de en vano se llama Fast Fourier Transform, claro)
Isn’t ironic? Don’t you think?
This is I
- Gustavo Ramírez
- Blog dedicado a escribir sobre Sistemas Embebidos y el Internet de las Cosas o IoT que le llaman.