平滑二值图像的边缘

问题描述 投票:0回答:7

如何平滑阈值处理后获得的血管二值图像的边缘。

我尝试了一种与此方法有点相似的方法,但没有完全得到我预期的结果。

这是代码:

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)  

可以采取哪些措施来改善这些粗糙边缘的平滑度?

编辑

我想平滑边缘,例如http://pscs5.tumblr.com/post/60284570543。如何在 OpenCV 中做到这一点?

python opencv image-processing blur smoothing
7个回答
27
投票

这是我用您的图像获得的结果:

我的方法主要基于应用于放大图像的几个

cv::medianBlur

这是代码:

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);

锯齿状边缘是由于阈值处理造成的。如果您对非二值输出图像(即具有 256 种灰色阴影)感到满意,您可以将其删除,然后得到此图像:


6
投票

您可以先膨胀然后腐蚀区域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)  

在内容之前编辑比例系数为 4 的内容


5
投票

我对@dhanushka 的另一个问题的答案做了一些修改并获得了这些图像。

抱歉,这是 C++ 代码,但也许您会将其转换为 Python。

您可以更改以下参数以获得不同的结果。

// 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);
}

3
投票

您可以做的是提高图像的分辨率(例如使用

resize
将其提高两倍或三倍)。之后,如上面其他答案中所述的腐蚀和膨胀将产生更好的结果。


2
投票

您很可能首先获得血管的灰度图像,然后进行阈值处理。它看起来仍然不平滑,因为原始灰度图像内部有噪声。现在要求平滑边缘将导致分辨率降低。例如,另一个答案中提出的稀释和侵蚀可能会在稀释步骤中融合相邻的容器,然后在侵蚀步骤中无法再次分离。

最好先去除灰度图像中的噪声(也称为平滑),然后将阈值处理作为最后一步。

因为您没有提供灰度图像,所以我对二值图像进行了温和的平滑(大约一个像素宽度),并再次进行了阈值处理。

我进行了平滑(使用固定大小的高斯核)和阈值处理(使用阈值参数)。我建议您在灰度图像数据上执行此操作并调整两个参数,直到您喜欢结果为止。

Matlab 代码(如果感兴趣):

% 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');

0
投票

这是上面 sturkmen 的帖子中转换为 Python 的算法

import numpy as np
import cv2 as cv

def smooth_raster_lines(im, filterRadius, filterSize, sigma):
    smoothed = np.zeros_like(im)
    contours, hierarchy = cv.findContours(im, cv.RETR_CCOMP, cv.CHAIN_APPROX_NONE)
    hierarchy = hierarchy[0]
    for countur_idx, contour in enumerate(contours):
        len_ = len(contour) + 2 * filterRadius
        idx = len(contour) - filterRadius

        x = []
        y = []    
        for i in range(len_):
            x.append(contour[(idx + i) % len(contour)][0][0])
            y.append(contour[(idx + i) % len(contour)][0][1])

        x = np.asarray(x, dtype=np.float32)
        y = np.asarray(y, dtype=np.float32)

        xFilt = cv.GaussianBlur(x, (filterSize, filterSize), sigma, sigma)
        xFilt = [q[0] for q in xFilt]
        yFilt = cv.GaussianBlur(y, (filterSize, filterSize), sigma, sigma)
        yFilt = [q[0] for q in yFilt]


        smoothContours = []
        smooth = []
        for i in range(filterRadius, len(contour) + filterRadius):
            smooth.append([xFilt[i], yFilt[i]])

        smoothContours = np.asarray([smooth], dtype=np.int32)


        color = (0,0,0) if hierarchy[countur_idx][3] > 0 else (255,255,255)
        cv.drawContours(smoothed, smoothContours, 0, color, -1)
    
    return(smoothed)

0
投票

这篇文章现在有点旧了,但是我想对阅读这个问题的其他人提出警告。

我是一名神经放射科医生,在我的日常工作中解释这些血管造影照片。我不确定OP所呈现的平滑这些图像的用例是什么,但是在平滑这些图像时你需要小心 - 一些不规则性实际上是由于病理学造成的,你可能(取决于用例)不想删除这个。

根据使用案例,最好与您的临床放射学部门合作,就适当的平滑程度达成一致。

© www.soinside.com 2019 - 2024. All rights reserved.