En el presente tutorial se apliacarán los conceptos de Procesamiento Digital de Imágenes vistos hasta el momento para realizar un conteo de objetos y su posterior identificación por su color.

Durante el proceso de construcción, se van a ir recordando conceptos y especificando, que conceptos en particular se están aplicando en cada uno de los pasos.

¡Vamos a comenzar!

Para la elaboración del presente tutorial vamos a necesitar:

Estructura

En este momento se abre el software de Matlab con la cámara previamente instalada. Se abre un editor de archivos y digitamos:

clear 
clc 
imaqhwinfo

El comando imaqhwinfo se utilza para identificar cuáles son los controladores de video que se encuentran instalados.

Ahora se va a abrir la cámara, se va a capturar una imagen, se guarda localmente y luego se cierra la cámara, así:

vid = videoinput('winvideo',2);
start(vid); 
preview(vid); 
pause 
closepreview(vid); 
Im =getsnapshot(vid); 
stop(vid); 
delete(vid); 
imshow(Im); 
imwrite(Im,'dulces.jpg');

OriginalNota: En algunos casos se pueden presentar errores por limitación de memoria. Si esto les llega a pasar, se recomienda colocar las siguientes líneas de código al inicio del programa:

imaqreset 
imaqmex('feature','-limitPhysicalMemoryUsage',false);

La imagen capturada, se va a visualizar (imshow), y se va a umbralizar a partir de cada uno de los canales RGB de la imagen. Para la segmentación se usará una umbralización automática usando el comando graythresh para escoger el valor de umbral óptimo.

Cada una de las capas de color umbralizadas se concatenan, se hace el complemento (la idea es que los objetos sean blancos en fondos negros) y se despliega.

% Umbralizar la imagen en cada plano de color
Im=imread("dulces.jpg"); 
Im = im2double(Im); 
[r, c, p]=size(Im); 
imshow(Im) 
title('imagen Original') 
pause 
% Extraer los planos individuales RGB de la imagen 
ImR = Im(:,:,1); 
ImG = Im(:,:,2); 
ImB = Im(:,:,3); 
% Umbralizando los planos individuales
imBinaryR= imbinarize(ImR, graythresh(ImR)); imBinaryG= imbinarize(ImG, graythresh(ImG)); imBinaryB= imbinarize(ImB, graythresh(ImB)); imBinary=imcomplement(imBinaryR&imBinaryG&imBinaryB); 
imshow(imBinary) 
title('Imagen binarizada') 
pause

Binarizada

Se realizarán operaciones morfológicas con el fin de cumplir con diferentes objetivos, como: llenar pequeños huecos en la imagen, eliminar pequeñas uniones y protuberancias, y eliminar bordes.

% Apertura morfológica 
se = strel("disk", 7); 
imClean = imopen(imBinary, se); 
imshow(imClean) 
title('Se realiza una apertura ') 
pause 

Apertura

% Llenar los huecos y limpiar los bordes 
imClean = imfill(imClean,"holes"); 
imshow(imClean) 
title('Se rellenan los huecos') 
pause 

Huecos

imClean = imclearborder(imClean); 
imshow(imClean); 
title('Se rellenan los bordes') 
pause

Bordes

Una vez todos los objetos han sido segmentados se procede a su etiquetado y conteo. En este contexto, etiquetar se refiere a un procedimiento se asigna un valor numérico a un conjunto de pixeles asociados a un solo elemento. El número de etiquetas creadas corresponde al número de objetos de la imagen.

% Segmentar imagen en escala de grises
[labels, numlabels]=bwlabel(imClean); 
objects =['Número de objetos detectados: ', num2str(numlabels)]; 
disp(objects); 
imshow(mat2gray(labels)) 
title(objects) 
pause

Detectados

En este paso lo que hacemos es pintar cada uno de los pixeles pertenecientes a un mismo objeto del mismo color. Para ello dentro de cada uno de los objetos detectados se calcula el valor promedio en cada una de sus capas de color.

% Inicializar matrices
rLabel= zeros(r,c); 
gLabel= zeros(r,c); 
bLabel= zeros(r,c); 
% Obtener vector de colores promedio por cada región etiquetada 
for i=1:numlabels 
rLabel(labels==i)=median(ImR(labels==i));   gLabel(labels==i)=median(ImG(labels==i)); bLabel(labels==i)=median(ImB(labels==i))
end imLabel = cat(3,rLabel,gLabel,bLabel); imshow(imLabel) 
title('Objetos etiquetados') 
impixelinfo(gcf)

Etiquetados

Como paso final se va a segmentar los objetos que tienen el mismo color (por ejemplo, si quisiéramos separar frutas maduros y sin madurar). Para ello se va a escoger el color de los objetos que se quiere extraer e identificar dentro de los objetos disponibles que color es más cercano al valor escogido.

Sin embargo, en este proceso se presentan ciertas complejidades. Particularmente, el formato de color RGB no es muy adecuado para realizar comparaciones de color, es decir, colores que para nosotros como humanos son similares no lo son así en el formato RGB. Por lo tanto, en este tipo de aplicaciones de color resulta más adecuado usar un esquema de imagen que se corresponda mejor con la manera como percibimos el mundo los humanos. En este caso se va a usar el esquema $L^*a^b^$.

Este espacio de color es ampliamente usado porque correlaciona los valores numéricos de color consistentemente con la percepción visual humana.

En el siguiente segmento de código se va a escoger un color y se convertirá la imagen segementada a formato $L^*a^b^$.

% Obtener color deseado para segmentar
[x,y]=ginput(1); 
selColor = imLabel(floor(y), floor(x),:);
C =makecform('srgb2lab'); 
imLAB=applycform(imLabel, C); 
imSelLAB= applycform(selColor, C); 
% Extraer los valores a y b 
imA=imLAB(:,:,2); 
imB=imLAB(:,:,3); 
imSelA= imSelLAB(2); 
imSelB= imSelLAB(3);

Como paso final, en el espacio $L^*a^b^$ se va a medir la distancia euclidiana que hay entre el color escogido y cada uno de los objetos. Menor dístancia significa objetos del mismo color.

% Calcular la distancia al color seleccionado
disTresh =4; 
imMask = zeros(r,c); 
imDist = hypot(imA-imSelA, imB-imSelB); 
imshow(mat2gray(imDist)) 
title('Distancia de color respecto a la referencia') 
pause 

DistToRef

unique(imDist) 
imMask(imDist<disTresh)=4; 
imshow(imMask) 
title('Objetos del mismo color detectados') 

mismocolor

pause 
imSeg=repmat(selColor, [r,c,1]).*repmat(imMask,[1,1,3]); 
imshow(imSeg); 
pause 
close

Final