Détection de régions d'objets dans l'image opencv
nous essayons actuellement de détecter les régions d'objets dans les images d'instruments médicaux en utilisant les méthodes disponibles dans OpenCV, version C++. Un exemple d'image est montré ci-dessous:
Voici les étapes que nous suivons:
- Conversion de l'image en échelle de gris
- application du filtre médian
- Trouver les bords à l'aide de filtre de sobel
- convertissez le résultat en image binaire en utilisant un seuil de 25
- Skeletonize l'image pour vous assurer que nous avons soigné les bords
- Trouver X plus de composants connectés
Cette approche fonctionne parfaitement pour l'image 1
et voici le résultat:
- les bordures jaunes sont les composants connectés détectés.
- les rectangles sont juste pour mettre en évidence la présence d'un composant connecté.
- pour obtenir des résultats compréhensibles, nous venons de supprimer les composants connectés qui sont complètement à l'intérieur de n'importe quel autre, donc le résultat final est quelque chose comme ceci:
jusqu'à présent, tout allait bien mais un autre échantillon d'image a compliqué notre travail montré ci-dessous.
avoir une petite serviette verte sous les objets produit cette image:
après avoir filtré les régions comme nous l'avons fait plus tôt, nous avons obtenu ceci:
Évidemment, ce n'est pas ce dont nous avons besoin..nous sommes à l'exception de quelque chose comme ceci:
je pense à regrouper les composants connectés les plus proches trouvés(d'une façon ou d'une autre!!), afin de minimiser l'impact de la présence de la serviette, mais ne sais pas encore si c'est quelque chose de faisable ou quelqu'un a essayé quelque chose comme ça avant? Aussi, quelqu'un a une meilleure idée de surmonter ce genre de les problèmes?
Merci d'avance.
3 réponses
Voici ce que j'ai essayé.
dans les images, l'arrière-plan est principalement verdâtre et la zone de l'arrière-plan est considérablement plus grande que celle du premier plan. Donc, si vous prenez un histogramme couleur de l'image, les bacs verdâtres auront des valeurs plus élevées. Limitez cet histogramme de façon à ce que les bacs ayant des valeurs plus faibles soient mis à zéro. De cette façon, nous allons probablement conserver les boîtes verdâtres (de valeur plus élevée) et jeter d'autres couleurs. Puis backproject cet histogramme. Le backprojection mettra en évidence ces régions verdâtres dans l'image.
rétroprojection:
- alors déclenchez cette rétro-injection. Cela nous donne l'arrière-plan.
arrière-plan (d'après certains signes de filtrage):
- Inverser l'arrière-plan pour obtenir de premier plan.
au premier plan (après quelques filtrage):
- puis trouver les contours du premier plan.
je pense que cela donne une segmentation raisonnable, et en utilisant cela comme masque vous pouvez être en mesure d'utiliser une segmentation comme GrabCut pour affiner les limites (je n'ai pas encore essayé cela).
EDIT: J'ai essayé le GrabCut approche et il en effet précise les limites. J'ai ajouté le code pour le GrabCut segmentation.
Contours:
segmentation du GrabCut en utilisant le premier plan comme masque:
j'utilise L'API OpenCV C pour le traitement de l'histogramme.
// 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 ...
mise à jour: nous pouvons faire ce qui précède en utilisant L'interface C++ comme montré ci-dessous.
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;
j'envisagerais quelques options. Je suppose que la caméra ne bouge pas. Je n'ai pas utilisé les images ou écrit de code, donc c'est surtout de l'expérience.
plutôt que de simplement chercher les contours, essayez de séparer le fond à l'aide d'un algorithme de segmentation. Mélange de gaussien peut aider avec cela. Étant donné un ensemble d'images sur la même région (i.e. vidéo), vous pouvez annuler les régions qui sont persistantes. Puis, de nouveaux éléments tels que les instruments vont apparaître hors. Connecté composants peuvent ensuite être utilisés sur les blobs.
- je regarde des algorithmes de segmentation pour voir si vous pouvez optimiser les conditions pour faire ce travail pour vous. Un élément important est de s'assurer que votre appareil photo est stable ou vous stabilisez les images vous-même pré-traitement.
je voudrais envisager d'utiliser des points d'intérêt pour identifier les régions dans l'image avec beaucoup de nouveau matériel. Étant donné que le fond est relativement simple, petit les objets tels que les aiguilles vont créer un tas de points d'intérêt. La serviette devrait être beaucoup plus clairsemée. Peut-être que superposer les points d'intérêt détectés au-dessus de l'empreinte du composant connecté vous donnera une métrique de "densité" que vous pouvez ensuite seuil. Si le composant connecté a un grand ratio de points d'intérêt pour la zone de l'élément, alors c'est un objet intéressant.
- sur cette note, vous pouvez même nettoyer l'empreinte du composant connecté en utilisant une coque convexe tailler les objets que vous avez détecté. Cela peut aider des situations telles qu'un instrument médical jetant une ombre sur la serviette qui étire la région du composant. C'est une supposition, mais les points d'intérêt peut certainement vous donner plus d'informations que celles bords.
enfin, étant donné que vous avez un arrière-plan stable avec des objets clairs en vue, je vais jeter un oeil à Bag-of-Features pour voir si vous pouvez juste détecter chaque objet individuel dans l'image. Cela peut être utile puisqu'il semble être un modèle cohérent pour les objets dans les images. Vous pouvez construire une grande base de données d'images telles que des aiguilles, gaze, ciseaux, etc. Alors BoF, qui est à OpenCV va trouver ces candidats pour vous. Vous pouvez également le mélanger avec d'autres opérations que vous faites pour comparer les résultats.
Sac de caractéristiques en utilisant OpenCV
-
je suggérerais aussi une idée à votre version initiale. Vous pouvez également sauter les contours, dont les régions ont une largeur et une hauteur supérieures à la moitié de la largeur et de la hauteur de l'image.
//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.