Basado en la tesis doctoral de una profesora de la universidad mi salón y yo llegamos a conjuntos de algoritmos desarrollados en C# para la detección de tumores en mamografías
Todo el proyecto se dividio en cinco, lo que me toco a mi equipo es binarzación, etiquetación, deteccion de área mayor y corte de la imagen, todos los métodos se explicaran conforme vaya apareciendo. Cabe mencionar que el código esta muy desordenado debido a la falta de tiempo, y hay algunos métodos que no utilizamos, y otros que no funcionan con todas las mamografías (falta de tiempo, uds saben como buen mexicano todo lo dejamos hasta el final), por lo que las sugerencias son aceptadas:
Esta es la clase Form1 que consta de varios botones como se podrán dar cuenta en los métodos
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Proyecto_Etapa1
{
public partial class Form1 : Form
{
private int[,]arreglo;
private Metodos m;//variable del tipo métodos clase que se pondra después
private Bitmap binarizada;
private Bitmap imagen;
private Bitmap cortada;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)//Cual mamografía vamos a estudiar
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.InitialDirectory = Application.StartupPath+"\\Imagenes\\";
ofd.Title = "Seleccionar una imagen";
if (ofd.ShowDialog() == DialogResult.OK)
{
string cadena = ofd.FileName;
pictureBox1.Image = Image.FromFile(cadena);
imagen = new Bitmap(pictureBox1.Image);
m = new Metodos(imagen);
binarizada = m.Binarizar();
pictureBox1.Image = binarizada;//muestra la imagen binarizada
}
}
private void btnOriginal_Click(object sender, EventArgs e)
{//este método regresa de la binarizada a la original
int cont_x = -1, cont_y = -1;
Bitmap imagen1 = imagen;
Bitmap imagen2 = cortada;
for (int i = 1; i < imagen1.Height-1; i++)
{
cont_x++;
cont_y = -1;
for (int j = 1; j < imagen1.Width-1; j++)
{
cont_y++;
int componentes = (int)(imagen2.GetPixel(j,i).B + imagen2.GetPixel(j,i).G + imagen2.GetPixel(j, i).R)/3;
if (componentes==255)
{
imagen2.SetPixel(j,i, imagen1.GetPixel(j,i));
}
}
}
pictureBox1.Image = imagen2;
}
private void QuitaNegros_Click(object sender, EventArgs e)
{
bool lado = m.Ubicacion();
Bitmap imagen2 = binarizada;
int etiqueta = 1;
int bandera = 0;
int cont = 0;
int[,] arreglo = new int[imagen2.Height,imagen2.Width ];
for (int i = 1; i < imagen2.Height - 1; i++)//Primera barrida de derecha a izq
{
for (int j = 1; j < imagen2.Width - 1; j++)
{
}
}
}
public void Colindantes(int y, int x,bool lado)//se buscan colindantes segun el lado de la mama
{
if (lado)
{
if (arreglo[x, y - 1] != 0)
{
arreglo[x, y] = arreglo[x, y - 1];
return;
}
if (arreglo[x - 1, y] != 0)
{
arreglo[x, y] = arreglo[x - 1, y];
return;
}
if (arreglo[x - 1, y - 1] != 0)
{
arreglo[x, y] = arreglo[x - 1, y - 1];
return;
}
if (arreglo[x - 1, y + 1] != 0)
{
arreglo[x, y] = arreglo[x - 1, y + 1];
return;
}
}
else
{
if (arreglo[x, y - 1] != 0)
{
arreglo[x, y] = arreglo[x, y - 1];
return;
}
if (arreglo[x + 1, y-1] != 0)
{
arreglo[x, y] = arreglo[x + 1, y-1];
return;
}
if (arreglo[x + 1 , y] != 0)
{
arreglo[x, y] = arreglo[x - 1, y - 1];
return;
}
if (arreglo[x + 1, y + 1] != 0)
{
arreglo[x, y] = arreglo[x + 1, y + 1];
return;
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{//esta método corta a la imagen para trabajar con una imagen más chica
//y así hacer el programa más eficiente
cortada = m.Corte(binarizada);
pictureBox1.Image = cortada;
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.InitialDirectory = Application.StartupPath.ToString();
saveFileDialog1.Filter = "Bitmap Files (*.bmp)|*.bmp|Jpeg files (*jpg)|*.jpg|All valid files (*.bmp/*.jpg)|*.bmp/*.jpg";
saveFileDialog1.FilterIndex = 1;
saveFileDialog1.RestoreDirectory = true;
if (DialogResult.OK == saveFileDialog1.ShowDialog())
{
cortada.Save(saveFileDialog1.FileName);
}
}
private void button2_Click(object sender, EventArgs e)
{
}
private void button4_Click(object sender, EventArgs e)
{
pictureBox1.Image = m.Eliminacion_pectoral(cortada);
}
private void button2_Click_1(object sender, EventArgs e)
{
pictureBox1.Image = m.etiquetar(imagen);
}
}
}
Clase Métodos:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Proyecto_Etapa1
{
public class Metodos
{
private Bitmap imagen; //Variable donde se guarda la imágen a ser procesada (origen)
private Bitmap resultado; //variable donde se guarda la imagen procesada (resultado)
private int ancho, alto, max;
private int[,] matriz; //Matriz de valores de grises para la imagen a ser proces(origen)
private int[] histo = new int[256]; //Matriz del histograma
private int aux = 0;
private float a = 0, b = 0;
private int[,] matriz_binarizada_cortada;
private int largo_imagen_cortada,alto_imagen_cortada;
//Propiedad para leer la imagen
public Bitmap Imagen
{
get
{
return imagen;
}
}
public Metodos(Bitmap IMAGEN)
{
int gris; //Variable que contiene el valor de gris de cada pixel
Color pixel; //Variable que contiene el color de cada pixel
imagen = IMAGEN; //La imágen que ingreso en el método, se guarda en la variable global
ancho = imagen.Width; //Guardo el ancho de la imagen
alto = imagen.Height; //Guardo el Alto de la imagen
matriz = new int[ancho, alto]; //Dimensiono la matriz
//For anidado que recorre todos los pixeles de la imagen
for (int y = 0; y < alto; y++)
{
for (int x = 0; x < ancho; x++)
{
pixel = imagen.GetPixel(x, y); //Guardo el color del pixel
gris = (int)(pixel.R + pixel.B + pixel.G) / 3; //Saco el valor de gris haciendo promedio entre la componente Rojo, verde y Azul
matriz[x, y] = gris; //Guardo los valores en la matriz
histo[gris] += 1; //Guardo valores de la matriz histograma
if (histo[gris] > max) max = histo[gris]; //Separo en la variable MAX el valor más alto de l histograma
}
}
}
public Bitmap Binarizar()
{
resultado = new Bitmap(ancho, alto); //Asigno la misma altura y ancho de la imagen origen
{
int cont=0;//Sacamos promedio de histograma sin incluir negros para el umbral de binarizacion
for (int i = 0; i < ancho; i++)
{
for (int j = 0; j < alto; j++)
{
if (matriz[i, j] != 0)
{
aux += matriz[i, j];
cont++;
}
}
}
aux = aux / cont;
MessageBox.Show("Promedio Histograma: " + aux.ToString());
}
//For anidado para binarizacion
for (int y = 1; y < imagen.Height - 1; y++)
{
for (int x = 1; x < imagen.Width - 1; x++)
{
int componentes = (int)(imagen.GetPixel(x, y).B + imagen.GetPixel(x, y).G + imagen.GetPixel(x, y).R) / 3;
if (componentes < aux)
{
resultado.SetPixel(x, y, Color.Black);
}
else
resultado.SetPixel(x, y, Color.White);
//Imprimo pixel por pixel la nueva imagen
}
}
return(resultado);
}
public Bitmap etiquetar(Bitmap enviada)
{//este método ayuda para detectar el área mayor (mama) para quitar el ruido de la imagen
//tenemos algunos problemas en este método no siempre etiqueta bien sugerencias aceptadas
Bitmap original = new Bitmap(enviada);
int[,] array = new int[original.Width, original.Height];
int etiqueta = 1;
for (int y = 1; y < original.Width - 1; y++)
{
for (int x = 1; x < original.Height - 1; x++)
{
float componentes = (original.GetPixel(y, x).B + original.GetPixel(y, x).G + original.GetPixel(y, x).R) / 3;
//primer if verifica que los colindantes sean puros pixeles sin etiquetar en este caso el pixel estaria rodeado de puros pixeles negros, de ser asi se incrementa el valor de la etiqueta y se etiqueta el pixel
if (componentes != 0 && array[y, x + 1] != etiqueta && array[y, x + 1] == 0 && array[y + 1, x + 1] != etiqueta && array[y + 1, x + 1] == 0 && array[y + 1, x - 1] != etiqueta && array[y + 1, x - 1] == 0 && array[y + 1, x] != etiqueta && array[y + 1, x] == 0 && array[y - 1, x] != etiqueta && array[y, x - 1] != etiqueta && array[y - 1, x - 1] != etiqueta && array[y - 1, x + 1] != etiqueta && array[y - 1, x - 1] == 0 && array[y, x - 1] == 0 && array[y - 1, x + 1] == 0 && array[y, x - 1] == 0)
{
etiqueta++;
array[y, x] = etiqueta;
original.SetPixel(y, x, Color.DeepSkyBlue);
}
//segundo if verifica que al menos uno de los pixeles colindantes al pixel que estamos leyendo tenga etiqueta de ser asi le asigna la misma etiqueta al pixel que estamos leyendo
if (componentes != 0 && array[y - 1, x] != 0 || componentes != 0 && array[y - 1, x - 1] != 0 || componentes != 0 && array[y, x - 1] != 0 || componentes != 0 && array[y - 1, x + 1] != 0)
{
array[y, x] = etiqueta;
original.SetPixel(y, x, Color.Blue);
}
}
}
for (int y = original.Width - 1; y > 1; y--)
{
for (int x = original.Height - 1; x > 1; x--)
{
float componentes = (original.GetPixel(y, x).B + original.GetPixel(y, x).G + original.GetPixel(y, x).R) / 3;
if (componentes != 0 && array[y, x] != etiqueta)
{
array[y, x] = array[y - 1, x];
original.SetPixel(y, x, Color.Aquamarine);
}
}
}
return (original);
}
public bool Ubicacion()
{//detecta que mama es (izquierda o derecha) funciona para otros métodos
bool resultado1=false;
long contizq=0, contder=0;
for (int i = 1; i <= (int) (ancho/2)-1; i++)//cortamos la imagen a la mitad y contamos los blancos
{
for (int j = 1; j < alto-1; j++)
{
int componentes = (int)(resultado.GetPixel(i,j).B + resultado.GetPixel(i,j).G + resultado.GetPixel(i,j).R)/3;
if (componentes == 255) contizq++;
}
}
for (int i = ancho / 2; i < ancho; i++)
{
for (int j = 0; j < alto; j++)
{
int componentes=(int)(resultado.GetPixel(i,j).B + resultado.GetPixel(i,j).G + resultado.GetPixel(i,j).R)/3;
if (componentes == 255) contder++;
}
}
if (contder < contizq) resultado1 = true;//La mama estará del lado izq
return (resultado1);
}
public Bitmap Eliminacion(int[,] arr)//Etiquetado y Eliminacion de blancos fuera del area mayor
{
//arr es el arreglo de etiquetas asignadas a cada pixel
int aux=0;
int etiqueta=-1;
int[] cantidad_etiquetas = new int[300];//La casilla indica la etiqueta, la info nos dice cuantas etiquetas
for (int i = 0; i < ancho; i++)
{
for (int j = 0; j < alto; j++)
{
aux = arr[i, j];
cantidad_etiquetas[aux] = cantidad_etiquetas[aux] + 1;//Sumamos cuantas etiquetas tenemos
}
}
aux = 0;
for (int i = 0; i < cantidad_etiquetas.Length; i++)
{
if (cantidad_etiquetas[i] > aux && cantidad_etiquetas[i]!=0)
{
aux = cantidad_etiquetas[i];//Cual es el cuerpo mayor
etiqueta = i;//recordar que la casilla es la etiqueta
}
}
for (int i = 1; i < ancho-1; i++)
{
for (int j = 1; j < alto - 1; j++)
{
if (arr[i, j] != 0 && arr[i, j] != etiqueta)
resultado.SetPixel(i, j, Color.Black);//Afuera de area mayor los ponemos negros
}
}
return (resultado);
}
public Bitmap Corte(Bitmap binarizada)//hacemos una imagen mucho más pequeña
{
Color[,] colores_original;
int max_x=0, max_y = 0;//Maximos y minimos del tamaño de la imagen
int min_x=1025, min_y = 1025;
int largo1,alto1=0, cont_x=-1,cont_y=-1;
int[,] arr = new int[ancho, alto];
Bitmap imagen_cortada;
for (int i = 1; i < ancho - 1; i++)//Recorremos la imagen binarizada
{ //Guardamos los pixeles binarizados en un arreglo para un mejor manejo
for (int j = 1; j < alto - 1; j++)
{
arr[i,j] = (int)(binarizada.GetPixel(i, j).B + binarizada.GetPixel(i, j).G + binarizada.GetPixel(i, j).R) / 3;
}
}
{//Corte
for (int i = 0; i < ancho; i++)
{
for (int j = 0; j < alto; j++)
{
if (arr[i, j] == 255)//sacamos max y min de las filas/columnas en blancos
{
if (i > max_x) max_x = i;
if (i < min_x) min_x = i;
if (j > max_y) max_y = j;
if (j < min_y) min_y = j;
}
}
}
largo1=(max_x - min_x);
largo_imagen_cortada = largo1;
alto1=(max_y - min_y);
alto_imagen_cortada = alto1;
imagen_cortada = new Bitmap(largo1,alto1);
colores_original = new Color[largo1, alto1];
matriz_binarizada_cortada = new int[largo1, alto1];
for (int x = min_x; x < max_x; x++)
{
cont_x++;
cont_y = -1;
for (int y =min_y; y < max_y; y++)
{
cont_y++;
colores_original[cont_x, cont_y] = imagen.GetPixel(x, y);
//la imagen es mas grande por eso recorremos de los minimos a los maximos y se los asig-
//namos al arreglo de colores del tamaño de la imagen cortada
}
}
for (int i = 0; i < largo1; i++)
{
for (int j = 0; j < alto1; j++)
{
imagen_cortada.SetPixel(i, j, colores_original[i, j]);
//asignamos los pixeles originales a la imagen cortada
matriz_binarizada_cortada[i, j] = (int)(imagen_cortada.GetPixel(i, j).B +
imagen_cortada.GetPixel(i, j).R +
imagen_cortada.GetPixel(i, j).G) / 3;
}
}
}
return (imagen_cortada);
}
public void Minimos_Cuadrados()
{//se intenta quitar el pectoral mayor por medio de reducción por medio de mínimos cuadrados
//este no funciona nada aunque la idea es buena
int cont=0, x=0, y=50;
int[] puntos_x = new int[10];
int[] puntos_y = new int[10];
bool lado = Ubicacion();
if (lado)
{
do
{
if (matriz_binarizada_cortada[x + 1, y] == 0)
{
puntos_x[cont] = x;
puntos_y[cont] = y;
x = 0;
y += 5;
cont++;
}
else x++;
}
while (cont < 10);
}
else
{
x = largo_imagen_cortada;//es igual al largo de la imagen cortada
do
{
if (matriz_binarizada_cortada[x - 1, y] == 0)
{
puntos_x[cont] = x;
puntos_y[cont] = y;
x = largo_imagen_cortada;
y += 5;
cont++;
}
else x--;
}
while (cont < 10);
}
int sum_x=0,sum_y=0,sum_x_cuadrada=0,sum_xy=0,sum_x2=0;
for (int i = 0; i < 10; i++)
{
sum_x += puntos_x[i];
sum_y -= -puntos_y[i];
sum_x_cuadrada += puntos_x[i]*puntos_x[i];
sum_xy += puntos_x[i] * -puntos_y[i];
}
sum_x2 = sum_x * sum_x;
a=((10*sum_xy)-(sum_x*sum_y))/((10*sum_x_cuadrada)-sum_x2);
b = ((sum_x_cuadrada * sum_y) - (sum_x * sum_y)) / ((10 * sum_x_cuadrada) - (sum_x2));
MessageBox.Show("a vale = " + a.ToString() + " b vale= " + b.ToString());
}
public Bitmap Eliminacion_pectoral(Bitmap cortada)
{//se eliminan los pixeles del pectoral mayor
Minimos_Cuadrados();
float operacion = 0;
for (int x = 0; x < largo_imagen_cortada; x++)
{
for (int y = 0; y < alto_imagen_cortada; y++)
{
a = a * -1;
operacion = y + (a * x);
if (operacion > b) cortada.SetPixel(x, y, Color.Black);
}
}
return (cortada);
}
}
}
Estos algoritmos fueron diseñaos enteramente por nosostros.
Aclaraciones:
1) no hay pasos atras por lo que recomiendo hacer copias de las imagenes anteriormente, y trabajar con dos variables dentro del código (una donde se guarda la original y otra donde se trabaja con la imagen)
2) en form1 necesitaran dos images box
3) el resto del codigo lo tienen los otros equipos, esta es la primera parte por lo que solo preparamos la mamografia para futura revisión hecha por los otros equipos
4) la tesis doctoral en la que nos basamos, esta hecha en matlab, exelente lenguaje para el manejo de imagenes.
5) los minimos cuadrados necesitan una muy buena revisión. Se trata de generar un recta para borrar el pectoral mayor con una fórmula que ya tenemos establecida en el código, el problema es q la fórmula trabaja con puntos sobre la recta y nosotros trabajamos en posiciones de pixeles dentro de una matriz.
6) les recuerdo que es como un "borrador" de programa, necesita los detalles para que funcione al 100 y se vea "bonito"
lunes, 31 de diciembre de 2007
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario