Comment lisser les bords de cette image binaire des vaisseaux sanguins obtenue après seuillage.
J'ai essayé une méthode quelque peu similaire à cette méthode mais je n'ai pas vraiment obtenu le résultat attendu.
Voici le code:
import cv2
import numpy as np
INPUT = cv2.imread('so-br-in.png',0)
MASK = np.array(INPUT/255.0, dtype='float32')
MASK = cv2.GaussianBlur(MASK, (5,5), 11)
BG = np.ones([INPUT.shape[0], INPUT.shape[1], 1], dtype='uint8')*255
OUT_F = np.ones([INPUT.shape[0], INPUT.shape[1], 1],dtype='uint8')
for r in range(INPUT.shape[0]):
for c in range(INPUT.shape[1]):
OUT_F[r][c] = int(BG[r][c]*(MASK[r][c]) + INPUT[r][c]*(1-MASK[r][c]))
cv2.imwrite('brain-out.png', OUT_F)
Que peut-on faire pour améliorer le lissage de ces bords durs?
[~ # ~] modifier [~ # ~]
Je voudrais lisser les bords quelque chose comme http://pscs5.tumblr.com/post/6028457054 . Comment faire cela dans OpenCV?
Voici le résultat que j'ai obtenu avec votre image:
Ma méthode est principalement basée sur plusieurs cv::medianBlur
appliqué sur une image agrandie.
Voici le code:
cv::Mat vesselImage = cv::imread(filename); //the original image
cv::threshold(vesselImage, vesselImage, 125, 255, THRESH_BINARY);
cv::Mat blurredImage; //output of the algorithm
cv::pyrUp(vesselImage, blurredImage);
for (int i = 0; i < 15; i++)
cv::medianBlur(blurredImage, blurredImage, 7);
cv::pyrDown(blurredImage, blurredImage);
cv::threshold(blurredImage, blurredImage, 200, 255, THRESH_BINARY);
Les bords dentelés sont dus au seuillage. Si vous êtes à l'aise avec une image de sortie non binaire (c'est-à-dire avec 256 nuances de gris), vous pouvez simplement la supprimer et vous obtenez cette image:
Vous pouvez dilater puis éroder les zones http://docs.opencv.org/2.4/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html .
import cv2
import numpy as np
blur=((3,3),1)
erode_=(5,5)
dilate_=(3, 3)
cv2.imwrite('imgBool_erode_dilated_blured.png',cv2.dilate(cv2.erode(cv2.GaussianBlur(cv2.imread('so-br-in.png',0)/255, blur[0], blur[1]), np.ones(erode_)), np.ones(dilate_))*255)
j'ai fait quelques modifications sur @dhanushka's réponse à une autre question et obtenir ces images.
Désolé, c'est du code C++ mais peut-être que vous le convertirez en Python.
Vous pouvez modifier les paramètres ci-dessous pour obtenir des résultats différents.
// contour smoothing parameters for gaussian filter
int filterRadius = 10; // you can try to change this value
int filterSize = 2 * filterRadius + 1;
double sigma = 20; // you can try to change this value
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main( int argc, const char** argv )
{
Mat im = imread(argv[1], 0);
Mat cont = ~im;
Mat original = Mat::zeros(im.rows, im.cols, CV_8UC3);
Mat smoothed = Mat(im.rows, im.cols, CV_8UC3, Scalar(255,255,255));
// contour smoothing parameters for gaussian filter
int filterRadius = 5;
int filterSize = 2 * filterRadius + 1;
double sigma = 10;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
// find contours and store all contour points
findContours(cont, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0, 0));
for(size_t j = 0; j < contours.size(); j++)
{
// extract x and y coordinates of points. we'll consider these as 1-D signals
// add circular padding to 1-D signals
size_t len = contours[j].size() + 2 * filterRadius;
size_t idx = (contours[j].size() - filterRadius);
vector<float> x, y;
for (size_t i = 0; i < len; i++)
{
x.Push_back(contours[j][(idx + i) % contours[j].size()].x);
y.Push_back(contours[j][(idx + i) % contours[j].size()].y);
}
// filter 1-D signals
vector<float> xFilt, yFilt;
GaussianBlur(x, xFilt, Size(filterSize, filterSize), sigma, sigma);
GaussianBlur(y, yFilt, Size(filterSize, filterSize), sigma, sigma);
// build smoothed contour
vector<vector<Point> > smoothContours;
vector<Point> smooth;
for (size_t i = filterRadius; i < contours[j].size() + filterRadius; i++)
{
smooth.Push_back(Point(xFilt[i], yFilt[i]));
}
smoothContours.Push_back(smooth);
Scalar color;
if(hierarchy[j][3] < 0 )
{
color = Scalar(0,0,0);
}
else
{
color = Scalar(255,255,255);
}
drawContours(smoothed, smoothContours, 0, color, -1);
}
imshow( "result", smoothed );
waitKey(0);
}
Ce que vous pouvez faire, c'est augmenter la résolution de votre image (par exemple, la doubler ou la tripler en utilisant resize
). Après cela, l'érosion et la dilatation décrites dans l'autre réponse ci-dessus conduiront à des résultats plus fins.
Vous avez très probablement d'abord obtenu une image en échelle de gris des vaisseaux sanguins, puis seuillée. Il semble toujours non lisse, car l'image d'origine de l'échelle de gris avait du bruit à l'intérieur. Demander maintenant un lissage des bords entraînera une résolution inférieure. Par exemple, la dilution et l'érosion proposées dans ne autre réponse pourraient fusionner les récipients voisins dans l'étape de dilution qui ne peuvent alors plus être séparés dans l'étape d'érosion.
Il peut être préférable de supprimer d'abord le bruit dans l'image en niveaux de gris (c'est-à-dire d'y faire un lissage) et de faire le seuillage comme dernière étape.
Parce que vous n'avez pas fourni l'image d'échelle de gris, j'ai effectué un lissage doux (environ une largeur de pixel) ici sur l'image binaire et effectué à nouveau un seuillage.
J'ai fait un lissage (avec un noyau gaussien de taille fixe) et un seuillage (avec un paramètre de seuillage). Je vous suggère de le faire sur les données d'image en niveaux de gris et d'ajuster les deux paramètres jusqu'à ce que le résultat vous plaise.
Code Matlab en cas d'intérêt:
% read
img = imread('YyNQV.png');
img = double(img(:, :, 1) ~= 255); % png is RGB -> binary
% smooth
kernel = fspecial('gaussian', 10, 1.5);
kernel = kernel / sum(kernel(:)); % normalize to 1
img_smooth = conv2(img, kernel, 'same');
% binarize again
threshold = 0.4; % experiment with values between 0 and 1
img_smooth_threshold = img_smooth > threshold;
% save (exchange black and white)
imwrite(~img_smooth_threshold, 'YyNQV_smooth.png');