Download da Minha Dissertação de Mestrado

Prezados, bom dia!

É com muito prazer (e alívio) que disponibilizo o link de download da minha dissertação, diretamente da biblioteca da Unicamp.

Veja também como foi a parte prática do meu mestrado, todos os código- fonte estão disponibilizados no githubTambém disponibilizo aqui a apresentação que utilizei na banca de defesa.

Resumo

O exame experimental de sudorese iodo-amido tem como objetivo auxiliar o diagnóstico de problemas no sistema nervoso, revelando áreas de anidrose e de hipoidrose por meio da reação química entre as secreções aquosas do paciente e um composto de iodo-amido. A reação acontece quando o paciente é induzido a transpirar dentro de uma câmara aquecida por um determinado período de tempo. Na sequência, um profissional da área de saúde avalia de forma visual as regiões de interesse, completando um processo que pode levar tempo e desconforto ao paciente. Dessa forma as soluções computacionais podem contribuir para a melhoria da eficácia e reduzir o tempo total de realização do exame. Neste trabalho realizou-se um estudo comparativo de técnicas de segmentação de imagens 3D de pacientes obtidas pelo dispositivo Microsoft Kinect® após a realização do exame. Foram comparadas as imagens segmentadas via K-Means e uma técnica de Crescimento de Regiões, nos modelos de cor RGB e CIELab, com imagens de referência (golden standard) produzidas por um especialista que definem a melhor segmentação possível para as regiões de anidrose e hipoidróticas. Para determinar a melhor estratégia de segmentação foram elaborados mais de cem cenários de experimentos variando os parâmetros de entrada das técnicas e modelos de cor. Ao término dos experimentos foram calculadas as métricas precisão, revocação, acurácia e medida-F para cada um dos cenários. Assim, concluiu-se que a técnica K-Means (acurácia de 93,18% para anidrose e 86,98% para anidrose e hipoidrose) e Crescimento de Regiões (com acurácia de 92,94% para anidrose e 85,12% para anidrose e hipoidrose), ambas no modelo de cor CIELab, são praticamente equivalentes. Entretanto pode-se perceber uma vantagem na segmentação via Crescimento de Regiões devido sua execução ser feita de forma não supervisionada.


Palavras-chave: Exame de Sudorese, Segmentação 3D, Crescimento de Regiões, K-Means

 

Abstract

The experimental exam of sweating iodine-starch aims to aid the diagnosis of problems in the nervous system, revealing areas of anhidrosis and hypohidrosis through the chemical reaction between the patient’s aqueous secretions and an iodine-starch compound. The reaction happens when the patient is induced to perspire inside a heated chamber for a certain period of time. Following this, a healthcare professional visually evaluates the regions of interest, and completes a process that can take time and it may be uncomfortable to the patient. In this way, the computational solutions may contribute to the improvement of the effectiveness. It also may reduce the total time of accomplishment of the examination. In this work, a comparative study of 3D image segmentation techniques of patients was made. The 3D was obtained by the Microsoft Kinect® device after the exams were made. K-Means segmented images and a Region Growing technique were compared, in the RGB and CIELab color models, to reference images (golden standard) produced by a specialist that defined the very best segmentation for the anhydrous and hypohidrotic regions. To determine the best segmentation strategy, more than one hundred experiment scenarios were elaborated by varying the input parameters of techniques and color models. At the end of the experiments, the accuracy, recall, accuracy and F-measure metrics were calculated for each of the scenarios. Thus, the K-Means technique (accuracy of 93.18% for anhidrosis and 86.98% for anhidrosis and hypohidrosis) and Region Growing (with an accuracy of 92.94% for anhidrosis and 85.12% for anhidrosis and hypohidrosis), both in the CIELab color model, are practically equivalent. However, it is possible to note an advantage in the segmentation via Region Growing, because its performance is gave in an unsupervised way

 

Obrigado,

Tiago.

A Parte Prática do meu Mestrado

Olá, neste post faço um compilado das técnicas, algoritmos e códigos-fonte que utilizei no meu mestrado. Aqui está um guia da parte prática das etapas que tive que realizar. No fim deste post há links para os repositórios onde as imagens 3D estão e dos código-fonte.

Detalhes de cada Parte

  1. Instalação de Pacotes C++ no Windows com VCPKG: Point Cloud Library (PCL) e OpenCV
  2. Usando a PCL e OpenCV no Microsoft Windows com Visual Studio 2017
  3. Segmentação Manual de Nuvens de Pontos no CloudCompare
  4. Usando Crescimento de Regiões em RGB da PCL
  5. Conversão de Nuvens de Pontos de RGB para CIELab
  6. Cálculo do Precision and Recall (Precisão e Revocação) em Nuvens de Pontos

 

Repositórios

Como produto dos estudos da parte prática, foram gerados código-fonte que implementam parte da teoria estudada. Eles estão nos repositórios abaixo:

Obrigado!

Calculando Precisão, Revocação, Acurácia e Medida F com PCL e C++

Olá!

Neste post descrevo a implementação do cálculo das métricas vistas no post sobre Precisão, Revocação, Acurácia e Medida F.

Como trabalhei com processamento de imagens 3D no meu mestrado, neste post o foco é apresentar a implementação realizada utilizando a biblioteca PCL e a OpenCV.

O objetivo deste post é explicar o código-fonte. Caso você queria fazer o download, vá no repositório no github. É um projeto do Visual Studio 2017, caso você esteja por fora, veja aqui como instalar a PCL no Windows e como criar um projeto com a PCL no Visual Sutio 2017.

Abaixo estão os trechos de código explicados. Veja aqui o código completo.

 

Declarando as Nuvens de Pontos em RGB

Aqui vamos declarar as nuvens de pontos que serão lidas dos arquivos PLY. Note que as nuvens são coloridas:

// Declarando a nuvem do cenário.
pcl::PointCloud ::Ptr cloud_org(new pcl::PointCloud );

// Declarando o padrao ouro.
pcl::PointCloud ::Ptr cloud_pb(new pcl::PointCloud );


// Declarando o padrao ouro com regioes hipoidroticas.
pcl::PointCloud ::Ptr cloud_pb_hipoidroticas(new pcl::PointCloud );

 

Declarando as Nuvens  de Pontos com Intensidade

 

// Declarando o Padrão Ouro (somente anidrose) em Escala de Cinza.
pcl::PointCloud <pcl::PointXYZI>::Ptr cloud_ref;

// Declarando o Padrão Ouro com Hipoidrose em Escala de Cinza.
pcl::PointCloud <pcl::PointXYZI>::Ptr cloud_ref_hipoidrose;

// Declarando a nuvem de pontos do cenario corrente.
pcl::PointCloud <pcl::PointXYZI>::Ptr cloud_pred;


// Declarando a variável que contém os dados do precision and recall das duas comparações.
std::stringstream valores_calculados;

 

 

Leitura das Nuvens de Ponto do Padrão Ouro

Aqui estou fazendo a leitura das nuvens de pontos do pardão ouro. Na minha pesquisa eu usei duas versões do padrão ouro. Dê uma olhada neste post onde utilizo o CloudCompare para segmentar manualmente as imagens 3D.

/**
* Le a nuvem de pontos que simboliza o padrao ouro: Entra em PLY.
*/
void read_cloud_padrao_ouro()
{
  std::cout << "___________________________________________________________" << std::endl;
  std::cout << "                LENDO AS NUVENS DO PADRAO OURO             " << std::endl;

  std::cout << "  - Tentando ler a nuvem do padrao ouro somente das areas de anidrose... ";

  std::string padrao_ouro_anidrose = "PLY_PadraoOuro/padrao_ouro_anidrose.ply";

  pcl::PLYReader reader;

  // Lendo a nuvem do Padrão Ouro considerando apenas as áreas de Anidrose.
  if (reader.read(padrao_ouro_anidrose, *cloud_pb) == -1) { //lucy_pb.ply
    std::cout << std::endl << "  - Erro ao ler a nuvem de pontos do padrao ouro das areas de anidrose." << std::endl;
  }

  std::cout << "OK " << std::endl;


  std::cout << "  - Calculando a intensidade da nuvem de pontos das areas de anidrose... ";
  cloud_ref = cloudRGB2GRAY(cloud_pb);
  std::cout << "OK " << std::endl << std::endl;


  
  std::cout << "  - Tentando ler a nuvem do padrao ouro areas de anidrose com hipoidroticas... ";

  std::string padrao_ouro_com_hipoidrotica = "PLY_PadraoOuro/padrao_ouro_anidrose_com_hipoidrotica.ply";

  // Lendo a nuvem do Padrão Ouro considerando apenas as áreas de Anidrose com Hipoidrose.
  if (reader.read(padrao_ouro_com_hipoidrotica, *cloud_pb_hipoidroticas) == -1) { //lucy_pb.ply
    std::cout << std::endl << "Erro ao ler a nuvem de pontos do padrao ouro das areas de anidrose com hipoidroticas." << std::endl;
  }

  std::cout << "OK " << std::endl;


  std::cout << "  - Calculando a intensidade da nuvem de pontos das areas de anidrose com hipoidroticas... ";
  cloud_ref_hipoidrose = cloudRGB2GRAY(cloud_pb_hipoidroticas);
  std::cout << "OK " << std::endl << std::endl;
}

 

 

Convertendo Tons de Cinza

Nesta função vamos converter as nuvens que estão em RGB para tons de cinza. Vamos utilizar a estrutura específica da PCL para isso, que contém o campo adicional intensity, onde teremos uma coleção XYZI. Repare que utilizei a biblioteca OpenCV para me ajudar na conversão de cada m dos pontos da nuvem.

/**
* Converte as nuvens para tons de cinza, adicionando a intensidade que é usada como critério de
* cálculo entre do TP, TN, FP e FN.
*/
pcl::PointCloud<pcl::PointXYZI>::Ptr cloudRGB2GRAY(pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud) {
  pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_gray(new pcl::PointCloud<pcl::PointXYZI>);
  cloud_gray->height = cloud->height;
  cloud_gray->width = cloud->width;

  for (pcl::PointCloud<pcl::PointXYZRGB>::iterator it = cloud->begin(); it != cloud->end(); it++) {
    // Color conversion
    cv::Mat pixel(1, 1, CV_8UC3, cv::Scalar(it->r, it->g, it->b));
    cv::Mat temp;
    cv::cvtColor(pixel, temp, CV_RGB2GRAY);

    pcl::PointXYZI pointI;
    pointI.x = it->x;
    pointI.y = it->y;
    pointI.z = it->z;
    pointI.intensity = temp.at<uchar>(0, 0);

    cloud_gray->push_back(pointI);

  }
  return cloud_gray;
}

 

 

Gravando o Resultado num Arquivo de Texto

Como pretendia processar várias nuvens de pontos em sequencia decidi gravar os resultados obtidos em arquivos de texto, por exemplo: Dados_Precision_and_Recall_Cenario_22.txt. Veja como fiz:

/**
 * Função que calcula e grava o arquivo txt com o nro do cenario e os dados do Precision and Recall.
 */
void save_txt(int cenario) {

  std::stringstream nome_arquivo;
  nome_arquivo << "Dados_Precision_and_Recall_Cenario_" << cenario << ".txt";

   

  std::stringstream ss;
  ss << "            Dados Precision and Recall do Cenario " << cenario << std::endl << std::endl;
  

  ss << valores_calculados.str();



  std::cout << "  - Gravando os valores calculados no aquivo... ";

  FILE * pFile;
  pFile = fopen(nome_arquivo.str().c_str(), "w");
  if (pFile != NULL)
  {
    fputs(ss.str().c_str(), pFile);
    fclose(pFile);
  }

  std::cout << "OK " << std::endl << std::endl;
  std::cout << ss.str();
}

 

Mais Importante: Comparando as Duas Nuvens!

Nesta função calculamos as métricas

/**
 * Função que computa dos dados de TP, TN, FP, FN entre as duas nuvens.
 */
void cloud_compare(int scenario, pcl::PointCloud <pcl::PointXYZI>::Ptr cloud_ref, pcl::PointCloud <pcl::PointXYZI>::Ptr cloud_pred) {

  
  float tn = 0.f, tp = 0.f, fn = 0.f, fp = 0.f;
  int hidrotica_pontos = 0;

  pcl::KdTreeFLANN<pcl::PointXYZI> tree_ref;
  tree_ref.setInputCloud(cloud_ref);

  std::vector<int> nn_indices(1);
  std::vector<float> nn_dists(1);



  // Comparando as duas nuvens de pontos.
  for (pcl::PointCloud<pcl::PointXYZI>::iterator it = cloud_pred->begin(); it != cloud_pred->end(); it++) {
    tree_ref.nearestKSearch(*it, 1, nn_indices, nn_dists);

    float i_ref = cloud_ref->points[nn_indices[0]].intensity;
    float i_pred = it->intensity;

    if (i_ref == 255 & i_pred == 255) tp++;
    else if (i_ref == 0 & i_pred == 0) tn++;
    else if (i_ref == 0 & i_pred == 255) fp++;
    else if (i_ref == 255 & i_pred == 0) fn++;


    if (i_ref == 255 & i_pred == 255) hidrotica_pontos++;
  }



  // Calculando Precision and Recall.
  float precision = tp / (tp + fp);
  float recall = tp / (tp + fn);
  float accu = (tp + tn) / (tp + tn + fp + fn);
  float tpr = tp / (tp + fn);
  float tnr = tn / (tn + fp);
  float f_m = 2 * ((precision*recall) / (precision + recall));


  // Calculando % das áreas de anidrose e hipoidrose juntas.
  int total_pontos = cloud_org->size();

  float porcentagem_hidrotica = (hidrotica_pontos * 100) / total_pontos;
  float porcentagem_anidrose_hipoidrotica = 100 - porcentagem_hidrotica;


  valores_calculados << "   - Precision: " << precision << std::endl;
  valores_calculados << "   - Recall: " << recall << std::endl;
  valores_calculados << "   - True Positive Rate: " << tpr << std::endl;
  valores_calculados << "   - True Negative Rate: " << tnr << std::endl;
  valores_calculados << "   - Accuracy: " << accu << std::endl;
  valores_calculados << "   - F measure: " << f_m << std::endl << std::endl;

  valores_calculados << "   - Areas Hidroticas: " << porcentagem_hidrotica << "%" << std::endl;
  valores_calculados << "   - Areas Anidrose e Hipoidroticas: " << porcentagem_anidrose_hipoidrotica << "%" << std::endl;
}

 

 

 

Chamando a função de Acordo com o Cenário

aqui..

No Método Prinicipal

Chamando no Método Principal…

int main()
{
  std::cout << "PROGRAMA DE COMPARACAO DE PRECISION AND RECALL INICIADO! " << std::endl << std::endl;


  // Lendo a núvem do padrao ouro.
  read_cloud_padrao_ouro();



  std::cout << "___________________________________________________________" << std::endl;
  std::cout << "           INICIANDO O CALCULO PARA OS CENARIOS            ";



  /**
  * Avaliando a segmentação do Region Growing em LAB com a variação do MinClusterSize.
  */
  make_scenario(10, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_10_pb.ply");
  make_scenario(11, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_11_pb.ply");
  make_scenario(12, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_12_pb.ply");
  make_scenario(13, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_13_pb.ply");
  make_scenario(14, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_14_pb.ply");
  make_scenario(15, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_15_pb.ply");
  make_scenario(16, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_16_pb.ply");
  make_scenario(17, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_17_pb.ply");
  make_scenario(18, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_18_pb.ply");
  make_scenario(19, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_19_pb.ply");
  make_scenario(20, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_20_pb.ply");
  make_scenario(21, "PLY_Comparacao/LAB/minClusterSize/paciente_segmentado_cenario_21_pb.ply");
}

 

 

Conclusão

por fim…