Introdução
O histograma é uma ferramenta muito importante usada para avaliar o comportamento de uma imagem. Quando precisamos identificar quantas ocorrencias - de pixels - existem em cada nível (de tom de cinza ou de cor - RBG). Podemos ver na seguite figura, o exemplo de um histograma:

A partir do conhecimento do histograma de uma imagem, podemos analisar algumas características da imagem e, mais que isso, realizar alguns tipos de modificações que melhoram a qualidade da imagem. Dentre as modificações que podemos realizar, uma bastante comum é a equalização do histograma, que acarreta em efeitos que iremos analisar nessa atividade.
Desenvolvimento da atividade
A atividade propõe mostrar o processo de capturar imagens de uma webcam instalada no computador, calcular os histogramas em tons de cinza e desenhá-los no canto superior esquerdo da imagem capturada. Além disso, em outra janela de exibição, mostrar como cada imagem capturada ficaria com seu histograma equalizado.
Podemos ver o resultado esperado nas seguintes figuras, onde podemos observar as diferenças entre uma imagem e sua versão com o histograma equalizado. No canto superior esquerdo podemos ver o histograma de cada imagem.


Código no OpenCV
O programa implementado apresenta o comportamento indicado pelo seguinte fluxograma:

Para a realização de equalização, cálculo e normalização do histograma, foram utilizadas funções oferecidas pela biblioteca OpenCV. O código do programa pode ser visualizado a seguir:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv){
Mat image, saida;
int larg, alt;
//Variáveis necessárias para a construção do histograma
int histSize = 64; //Número de subdivisões horizontais
float range[] = { 0, 256 }; //Limites inferior e superior do histograma
const float* histRange = { range };
Mat histograma, histograma2; //Objeto onde o histograma será armazenado
//Variáveis para a 'plotagem' do histograma
int hist_w = histSize; //Tamanho do histograma será 1 pixel por subdivisão horizontal
int hist_h = 32; //Tamanho da altura do histograma (1 pixel por valor possível)
Mat histImage (hist_h, hist_w, CV_8UC1, Scalar(0)); //Inicializando as imagens que apresentarão os histogramas
Mat histImage2(hist_h, hist_w, CV_8UC1, Scalar(0));
//Abertura da webcam
VideoCapture cap;
cap.open(0);
if(!cap.isOpened()){
cout << "Erro ao abrir a câmera.\n";
return -1;
}
while(1){
cap >> image;
flip(image,image,1); //espelhar a tela pra deixar a visualização mais fiel da realidade
cvtColor(image,image,CV_BGR2GRAY); //Convertendo RGB para Escala de Cinza
equalizeHist(image, saida); //Função do opencv para equalizar o histograma
//Reinicializando as imagens dos histogramas
histImage.setTo( Scalar(0));
histImage2.setTo(Scalar(0));
//Calculando o histograma das imagens
calcHist(&image, 1, 0, Mat(), histograma , 1, &histSize, &histRange, true, false);
calcHist(&saida, 1, 0, Mat(), histograma2, 1, &histSize, &histRange, true, false);
//Normalizando o histograma das imagens
normalize(histograma , histograma , 0, histImage.rows , NORM_MINMAX, -1, Mat());
normalize(histograma2, histograma2, 0, histImage2.rows, NORM_MINMAX, -1, Mat());
//Plotagem do histograma
for(int i = 0; i < histSize; i++){
line( histImage, Point(i,hist_h) , Point(i,hist_h-cvRound(histograma.at<float>(i))) , Scalar( 255, 255, 255), 1, 8, 0);
line( histImage2,Point(i,hist_h) , Point(i,hist_h-cvRound(histograma2.at<float>(i))), Scalar( 255, 255, 255), 1, 8, 0);
}
//Copiando histograma para ser apresentado na saída
histImage.copyTo(image(Rect(0, 0, histSize, hist_h)));
histImage2.copyTo(saida(Rect(0, 0, histSize, hist_h)));
//Apresentação dos resultados
namedWindow("entrada", CV_WINDOW_KEEPRATIO);
namedWindow("saida", CV_WINDOW_KEEPRATIO);
imshow("entrada", image);
imshow("saida",saida);
if(waitKey(30) >= 0) break;
}
imwrite("saida.png",saida);
imwrite("entrada.png",image);
return 0;
}
Para a realização do processamento, primeiramente um quadro do vídeo é capturado, e após isso, a imagem é invertida, para que haja uma interação melhor com o usuário na apresentação do resultado. Nesse momento, a imagem é convertida para tons de cinza.
A equalização do histograma é realizada através da função equalizeHist(). Essa função recebe uma imagem e armazena o resultado da equalização em outra imagem. Quando o processo de equalização é realizado, os níveis do histograma tendem a se espalharem por todo os valores de tons de cinza. Com isso, há uma normalização no brilho da imagem, aumentando seu contraste.
Após a equalização da imagem, é então calculado o histograma da imagem original e da imagem equalizada. Esse cálculo é necessário para a apresentação do histograma resultante nas imagens. O cálculo é realizado pela função calcHist(). Nessa função, fornecemos alguns valores como a imagem a ser analisada, tamanho do histograma, etc, e o resultado do cáculo é armazendo em uma imagem. Para maior detalhamento, consultar a documentação da função fornecida no site do OpenCV. Os histogramas são então normalizados e copiados para suas respectivas imagens.
Resultados
Em desenvolvimento…
Com a execução do código, a imagem capturada pela webcam passa a ser apresentada em uma janela, e o resultado da equalização é apresentada em outra janela. Podemos um exemplo da imagem resultante a seguir:


Com isso, podemos perceber que os efeitos da equalização foram obtidos com sucesso, sendo possível observar claramente a diferença entre o histograma da imagem original e da imagem processada.
Dependendo da iluminação do local, a equalização pode ser pouco perceptível, pois o histograma da imagem original já está bem distribuída ao longo de todos os tons de cinza. Podemos ver isso a seguir:

