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:

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');
Nota: 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

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

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

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

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

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)

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

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

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