Радиус диска в двоичном изображении - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть бинаризованные изображения, подобные этому:

enter image description here

Мне нужно определить центр и радиус внутреннего твердого диска.Как видите, он окружен текстурированной областью, которая касается его, поэтому простое обнаружение подключенных компонентов не работает.В любом случае, на большей части периметра есть пустое поле.

Возможное излечение может состоять в разрушении, пока вся текстура не исчезнет или не отсоединится от диска, но это может занять много времени и количество итераций.не уверен.(Кроме того, в некоторых неудачных случаях в диске есть крошечные отверстия, которые будут расти с эрозией.)

Есть ли лучшее предложение для надежного и быстрого решения этой проблемы?(Я пометил OpenCV, но это не обязательно, важен подход.)

Ответы [ 6 ]

0 голосов
/ 29 ноября 2018

Вот простой подход:

  1. Сотрите изображение (используя большой круговой SE), затем найдите центр тяжести результата.Это должно быть очень близко к центроиду центрального диска.
  2. Вычислить среднее значение как функцию радиуса исходного изображения, используя вычисленный центроид в качестве центра.

Вывод выглядит следующим образом:

Radial mean of the picture in the question

Отсюда определение радиуса довольно просто.

Вот код, I 'm используя PyDIP (у нас еще нет бинарного дистрибутива, вам нужно скачать и собрать источники форм):

import matplotlib.pyplot as pp
import PyDIP as dip
import numpy as np

img = dip.Image(pp.imread('/home/cris/tmp/FDvQm.png')[:,:,0])
b = dip.Erosion(img, 30)
c = dip.CenterOfMass(b)
rmean = dip.RadialMean(img, center=c)
pp.plot(rmean)
r = np.argmax(rmean < 0.5)

Здесь r равно 102, арадиус в целое число пикселей, я уверен, что это можно интерполировать для повышения точности.c - это [184.02, 170.45].

0 голосов
/ 29 ноября 2018

Моя вторая попытка по этому делу.На этот раз я использую морфологическую операцию закрытия, чтобы ослабить шум и поддержать сигнал.За этим следует простой порог и анализ связанных компонентов.Я надеюсь, что этот код может работать быстрее.

enter image description here

Используя этот метод, я могу найти центроид с точностью до субпикселя

('center : ', (184.12244328746746, 170.59771290442544))

Радиус определяется по площади круга.

('radius : ', 101.34704439389715)

Вот полный код

import cv2
import numpy as np

# load image in grayscale
image = cv2.imread('radius.png',0)
r,c = image.shape
# remove noise
blured = cv2.blur(image,(5,5))

# Morphological closing
morph = cv2.erode(blured,None,iterations = 3)
morph = cv2.dilate(morph,None,iterations = 3)
cv2.imshow("morph",morph)
cv2.waitKey(0)

# Get the strong signal
th, th_img = cv2.threshold(morph,200,255,cv2.THRESH_BINARY)

cv2.imshow("th_img",th_img)
cv2.waitKey(0)

# Get connected components
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(th_img)
print(num_labels)
print(stats)

# displat labels
labels_disp = np.uint8(255*labels/np.max(labels))
cv2.imshow("labels",labels_disp)
cv2.waitKey(0)

# Find center label
cnt_label = labels[r/2,c/2]

# Find circle center and radius
# Radius calculated by averaging the height and width of bounding box
area = stats[cnt_label][4]
radius = np.sqrt(area / np.pi)#stats[cnt_label][2]/2 + stats[cnt_label][3]/2)/2
cnt_pt = ((centroids[cnt_label][0]),(centroids[cnt_label][1]))
print('center : ',cnt_pt)
print('radius : ',radius)

# Display final result
edges_color = cv2.cvtColor(image,cv2.COLOR_GRAY2BGR)
cv2.circle(edges_color,(int(cnt_pt[0]),int(cnt_pt[1])),int(radius),(0,0,255),1)
cv2.circle(edges_color,(int(cnt_pt[0]),int(cnt_pt[1])),5,(0,0,255),-1)

x1 = stats[cnt_label][0]
y1 = stats[cnt_label][1]
w1 = stats[cnt_label][2]
h1 = stats[cnt_label][3]
cv2.rectangle(edges_color,(x1,y1),(x1+w1,y1+h1),(0,255,0))

cv2.imshow("edges_color",edges_color)
cv2.waitKey(0)
0 голосов
/ 29 ноября 2018

Вот пример использования крутого круга.Это может работать, если вы установите минимальный и максимальный радиус на правильный диапазон.

import cv2
import numpy as np

# load image in grayscale
image = cv2.imread('radius.png',0)
r , c = image.shape

# remove noise
dst = cv2.blur(image,(5,5))

# Morphological closing
dst = cv2.erode(dst,None,iterations = 3)
dst = cv2.dilate(dst,None,iterations = 3)

# Find Hough Circle
circles = cv2.HoughCircles(dst
    ,cv2.HOUGH_GRADIENT
    ,2
    ,minDist = 0.5* r
    ,param2 = 150
    ,minRadius = int(0.5 * r / 2.0) 
    ,maxRadius = int(0.75 * r / 2.0)
    )

# Display
edges_color = cv2.cvtColor(image,cv2.COLOR_GRAY2BGR)
for i in circles[0]:
    print(i)
    cv2.circle(edges_color,(i[0],i[1]),i[2],(0,0,255),1)



cv2.imshow("edges_color",edges_color)
cv2.waitKey(0)

Вот результат [185.167. 103,6]

enter image description here

0 голосов
/ 29 ноября 2018

Мой метод заключается в использовании morph-open, findcontours и minEnclosingCircle следующим образом:

enter image description here

#!/usr/bin/python3
# 2018/11/29 20:03 
import cv2

fname = "test.png"
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

th, threshed = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_OPEN, kernel, iterations = 3)

cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]

cnt = max(cnts, key=cv2.contourArea)
pt, r = cv2.minEnclosingCircle(cnt)

pt = (int(pt[0]), int(pt[1]))
r = int(r)

print("center: {}\nradius: {}".format(pt, r))

Окончательный результат:

enter image description here

center: (184, 170)
radius: 103
0 голосов
/ 29 ноября 2018

Вы можете:

  1. Инвертировать изображение
  2. Найти самый большой выровненный по оси прямоугольник, содержащий только нули , (я использовал свой код C ++ из этот ответ ).Алгоритм довольно быстрый.
  3. Получить центр и радиус круга из прямоугольника

enter image description here

Код:

#include <opencv2\opencv.hpp>

using namespace std;
using namespace cv;

// https://stackoverflow.com/a/30418912/5008845
cv::Rect findMaxRect(const cv::Mat1b& src)
{
    cv::Mat1f W(src.rows, src.cols, float(0));
    cv::Mat1f H(src.rows, src.cols, float(0));

    cv::Rect maxRect(0,0,0,0);
    float maxArea = 0.f;

    for (int r = 0; r < src.rows; ++r)
    {
        for (int c = 0; c < src.cols; ++c)
        {
            if (src(r, c) == 0)
            {
                H(r, c) = 1.f + ((r>0) ? H(r-1, c) : 0);
                W(r, c) = 1.f + ((c>0) ? W(r, c-1) : 0);
            }

            float minw = W(r,c);
            for (int h = 0; h < H(r, c); ++h)
            {
                minw = std::min(minw, W(r-h, c));
                float area = (h+1) * minw;
                if (area > maxArea)
                {
                    maxArea = area;
                    maxRect = cv::Rect(cv::Point(c - minw + 1, r - h), cv::Point(c+1, r+1));
                }
            }
        }
    }

    return maxRect;
}


int main()
{
    cv::Mat1b img = cv::imread("path/to/img", cv::IMREAD_GRAYSCALE);

    // Correct image
    img = img > 127;

    cv::Rect r = findMaxRect(~img);

    cv::Point center ( std::round(r.x + r.width / 2.f), std::round(r.y + r.height / 2.f));
    int radius = std::sqrt(r.width*r.width + r.height*r.height) / 2;

    cv::Mat3b out;
    cv::cvtColor(img, out, cv::COLOR_GRAY2BGR);
    cv::rectangle(out, r, cv::Scalar(0, 255, 0));
    cv::circle(out, center, radius, cv::Scalar(0, 0, 255));

    return 0;
}
0 голосов
/ 29 ноября 2018

Вы пробовали что-то похожее на Circle Hough Transform ?Я вижу, что OpenCv имеет свою собственную реализацию .Однако здесь может потребоваться некоторая предварительная обработка (медианная фильтрация?).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...