simpleblobdetector - Detectando regiones de objetos en la imagen opencv
typeerror: incorrect type of self(must be ''feature2d'' or its derivative) (3)
Actualmente estamos tratando de detectar las regiones objeto en imágenes de instrumentos médicos utilizando los métodos disponibles en OpenCV, versión C ++. Una imagen de ejemplo se muestra a continuación:
Estos son los pasos que estamos siguiendo:
- Convertir la imagen a escala de grises
- Aplicando filtro mediano
- Encuentra bordes usando filtro sobel
- Convierta el resultado en imagen binaria usando un umbral de 25
- Esqueleto de la imagen para asegurarnos de que tengamos bordes limpios
- Encontrar los componentes conectados más grandes de X
Este enfoque funciona perfectamente para la imagen 1
y aquí está el resultado:
- Los bordes amarillos son los componentes conectados detectados.
- Los rectángulos son solo para resaltar la presencia de un componente conectado.
- Para obtener resultados comprensibles, simplemente eliminamos los componentes conectados que están completamente dentro de cualquier otro, por lo que el resultado final es algo como esto:
Hasta ahora, todo estaba bien, pero otra muestra de imagen complicaba nuestro trabajo que se muestra a continuación.
Tener una pequeña toalla verde clara debajo de los objetos da como resultado esta imagen:
Después de filtrar las regiones como lo hicimos antes, obtuvimos esto:
Obviamente, no es lo que necesitamos ... estamos exceptuando algo como esto:
Estoy pensando en agrupar los componentes conectados más cercanos encontrados (¡de alguna manera!) Para poder minimizar el impacto de la presencia de la toalla, pero aún no sabemos si es algo factible o alguien ha intentado algo como esto antes. Además, ¿alguien tiene una mejor idea para superar este tipo de problemas?
Gracias por adelantado.
También sugeriría una idea para su versión inicial. También puede omitir los contornos, cuyas regiones tienen ancho y alto mayor que la mitad del ancho y alto de la imagen.
//take the rect of the contours
Rect rect = Imgproc.boundingRect(contours.get(i));
if (rect.width < inputImageWidth / 2 && rect.height < inputImageHeight / 2)
//then continue to draw or use for next purposes.
Esto es lo que intenté.
En las imágenes, el fondo es principalmente verdoso y el área del fondo es considerablemente más grande que la del primer plano. Por lo tanto, si toma un histograma de color de la imagen, los contenedores verdosos tendrán valores más altos. Umbral este histograma para que los contenedores que tienen valores más pequeños se pongan a cero. De esta forma, probablemente retengamos los contenedores verdosos (de mayor valor) y descartemos otros colores. Luego haz un backproject de este histograma. La retroproyección resaltará estas regiones verdosas en la imagen.
Retroproyección:
- Luego, limite esta retroproyección. Esto nos da el fondo.
Antecedentes (después de algunos filtros morfológicos):
- Invierta el fondo para obtener el primer plano.
Primer plano (después de algunos filtros morfológicos):
- Luego encuentra los contornos del primer plano.
Creo que esto proporciona una segmentación razonable, y al usar esto como máscara, puede usar una segmentación como GrabCut para refinar los límites (aún no lo he intentado).
EDITAR: Probé el enfoque GrabCut y, de hecho, refina los límites. He agregado el código para la segmentación de GrabCut.
Contornos:
Segmentación de GrabCut usando el primer plano como máscara:
Estoy usando la API C de OpenCV para la parte de procesamiento del histograma.
// load the color image
IplImage* im = cvLoadImage("bFly6.jpg");
// get the color histogram
IplImage* im32f = cvCreateImage(cvGetSize(im), IPL_DEPTH_32F, 3);
cvConvertScale(im, im32f);
int channels[] = {0, 1, 2};
int histSize[] = {32, 32, 32};
float rgbRange[] = {0, 256};
float* ranges[] = {rgbRange, rgbRange, rgbRange};
CvHistogram* hist = cvCreateHist(3, histSize, CV_HIST_ARRAY, ranges);
IplImage* b = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* g = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* r = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* backproject32f = cvCreateImage(cvGetSize(im), IPL_DEPTH_32F, 1);
IplImage* backproject8u = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
IplImage* bw = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
IplConvKernel* kernel = cvCreateStructuringElementEx(3, 3, 1, 1, MORPH_ELLIPSE);
cvSplit(im32f, b, g, r, NULL);
IplImage* planes[] = {b, g, r};
cvCalcHist(planes, hist);
// find min and max values of histogram bins
float minval, maxval;
cvGetMinMaxHistValue(hist, &minval, &maxval);
// threshold the histogram. this sets the bin values that are below the threshold to zero
cvThreshHist(hist, maxval/32);
// backproject the thresholded histogram. backprojection should contain higher values for the
// background and lower values for the foreground
cvCalcBackProject(planes, backproject32f, hist);
// convert to 8u type
double min, max;
cvMinMaxLoc(backproject32f, &min, &max);
cvConvertScale(backproject32f, backproject8u, 255.0 / max);
// threshold backprojected image. this gives us the background
cvThreshold(backproject8u, bw, 10, 255, CV_THRESH_BINARY);
// some morphology on background
cvDilate(bw, bw, kernel, 1);
cvMorphologyEx(bw, bw, NULL, kernel, MORPH_CLOSE, 2);
// get the foreground
cvSubRS(bw, cvScalar(255, 255, 255), bw);
cvMorphologyEx(bw, bw, NULL, kernel, MORPH_OPEN, 2);
cvErode(bw, bw, kernel, 1);
// find contours of the foreground
//CvMemStorage* storage = cvCreateMemStorage(0);
//CvSeq* contours = 0;
//cvFindContours(bw, storage, &contours);
//cvDrawContours(im, contours, CV_RGB(255, 0, 0), CV_RGB(0, 0, 255), 1, 2);
// grabcut
Mat color(im);
Mat fg(bw);
Mat mask(bw->height, bw->width, CV_8U);
mask.setTo(GC_PR_BGD);
mask.setTo(GC_PR_FGD, fg);
Mat bgdModel, fgdModel;
grabCut(color, mask, Rect(), bgdModel, fgdModel, GC_INIT_WITH_MASK);
Mat gcfg = mask == GC_PR_FGD;
vector<vector<cv::Point>> contours;
vector<Vec4i> hierarchy;
findContours(gcfg, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
for(int idx = 0; idx < contours.size(); idx++)
{
drawContours(color, contours, idx, Scalar(0, 0, 255), 2);
}
// cleanup ...
ACTUALIZACIÓN: podemos hacer lo anterior utilizando la interfaz C ++ como se muestra a continuación.
const int channels[] = {0, 1, 2};
const int histSize[] = {32, 32, 32};
const float rgbRange[] = {0, 256};
const float* ranges[] = {rgbRange, rgbRange, rgbRange};
Mat hist;
Mat im32fc3, backpr32f, backpr8u, backprBw, kernel;
Mat im = imread("bFly6.jpg");
im.convertTo(im32fc3, CV_32FC3);
calcHist(&im32fc3, 1, channels, Mat(), hist, 3, histSize, ranges, true, false);
calcBackProject(&im32fc3, 1, channels, hist, backpr32f, ranges);
double minval, maxval;
minMaxIdx(backpr32f, &minval, &maxval);
threshold(backpr32f, backpr32f, maxval/32, 255, THRESH_TOZERO);
backpr32f.convertTo(backpr8u, CV_8U, 255.0/maxval);
threshold(backpr8u, backprBw, 10, 255, THRESH_BINARY);
kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
dilate(backprBw, backprBw, kernel);
morphologyEx(backprBw, backprBw, MORPH_CLOSE, kernel, Point(-1, -1), 2);
backprBw = 255 - backprBw;
morphologyEx(backprBw, backprBw, MORPH_OPEN, kernel, Point(-1, -1), 2);
erode(backprBw, backprBw, kernel);
Mat mask(backpr8u.rows, backpr8u.cols, CV_8U);
mask.setTo(GC_PR_BGD);
mask.setTo(GC_PR_FGD, backprBw);
Mat bgdModel, fgdModel;
grabCut(im, mask, Rect(), bgdModel, fgdModel, GC_INIT_WITH_MASK);
Mat fg = mask == GC_PR_FGD;
Consideraría algunas opciones. Mi suposición es que la cámara no se mueve. No he usado las imágenes ni he escrito ningún código, así que esto es principalmente por experiencia.
En lugar de simplemente buscar bordes, intente separar el fondo con un algoritmo de segmentación. La mezcla de Gaussian puede ayudar con esto. Dado un conjunto de imágenes sobre la misma región (es decir, video), puede cancelar regiones que son persistentes. Luego, aparecerán nuevos elementos como instrumentos. Los componentes conectados se pueden usar en los blobs.
- Me gustaría ver los algoritmos de segmentación para ver si puede optimizar las condiciones para que esto funcione para usted. Un elemento principal es asegurarse de que su cámara sea estable o estabilizar las imágenes usted mismo antes del procesamiento.
Consideraría usar puntos de interés para identificar regiones en la imagen con mucho material nuevo. Dado que el fondo es relativamente simple, los objetos pequeños como las agujas crearán un montón de puntos de interés. La toalla debe ser mucho más escasa. Quizás al superponer los puntos de interés detectados sobre la huella del componente conectado, obtendrá una métrica de "densidad" que luego podrá establecer como umbral. Si el componente conectado tiene una gran proporción de puntos de interés para el área del artículo, entonces es un objeto interesante.
- En esta nota, puede incluso limpiar la huella del componente conectado utilizando un casco convexo para podar los objetos que ha detectado. Esto puede ayudar a situaciones como un instrumento médico que proyecta una sombra sobre la toalla que estira la región del componente. Esto es una suposición, pero los puntos de interés definitivamente pueden darle más información que solo los bordes.
Finalmente, dado que tienes un fondo estable con objetos despejados a la vista, echaría un vistazo a Bag-of-Features para ver si puedes detectar cada objeto individual en la imagen. Esto puede ser útil ya que parece haber un patrón consistente para los objetos en estas imágenes. Puedes construir una gran base de datos con imágenes como agujas, gasas, tijeras, etc. Luego BoF, que está en OpenCV, encontrará esos candidatos para ti. También puede mezclarlo con otras operaciones que está haciendo para comparar resultados.
Bolsa de características usando OpenCV
-