Как получить контур металлов c, блестящих объектов с помощью OpenCV - PullRequest
2 голосов
/ 13 февраля 2020

Я пытаюсь найти контур металлических c, блестящих объектов, таких как изображение ниже:

enter image description here

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

enter image description here

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

Ответы [ 3 ]

2 голосов
/ 13 февраля 2020

В Python / OpenCV этого можно достичь:

  • Чтение ввода
  • Преобразование в цветовое пространство HSV и извлечение канала насыщенности (поскольку серый не имеет насыщенности, а зеленый - нет) )
  • Размытие изображения для уменьшения шума
  • Порог
  • Применение морфологии близко к заполнению внутренних отверстий в блестящем объекте
  • Поиск контуров и фильтрация по наибольшему ( хотя должен быть только один)
  • Нарисуйте контур на входном изображении
  • Сохраните результаты

Ввод:

enter image description here

import cv2
import numpy as np

# read input
img = cv2.imread('shiny.jpg')

# convert to hsv and get saturation channel
sat = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)[:,:,1]

# do a little Gaussian filtering
blur = cv2.GaussianBlur(sat, (3,3), 0)


# threshold and invert to create initial mask
mask = 255 - cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY)[1]

# apply morphology close to fill interior regions in mask
kernel = np.ones((15,15), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)


# get outer contours from inverted mask and get the largest (presumably only one due to morphology filtering)
cntrs = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
result = img.copy()
area_thresh = 0
for c in cntrs:
    area = cv2.contourArea(c)
    if area > area_thresh:
        area = area_thresh
        big_contour = c

# draw largest contour
cv2.drawContours(result, [big_contour], -1, (0,0,255), 2)


# write result to disk
cv2.imwrite("shiny_mask.png", mask)
cv2.imwrite("shiny_outline.png", result)

# display it
cv2.imshow("IMAGE", img)
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)


Порог и фильтрованная маска:

enter image description here

Результат:

enter image description here

Альтернативным подходом будет пороговое значение с использованием cv2.inRange () для зеленого цвета.

1 голос
/ 13 февраля 2020

Вот еще одно возможное решение, реализованное на C ++ и использующее k-означает в качестве основного метода сегментации. Идея этой сегментации заключается в том, что k-means (метод кластеризации) будет группировать цвета одинакового значения. Здесь я устанавливаю k-means, чтобы найти кластеры 2 цветов: цвет фона и цвет переднего плана.

Давайте посмотрим на код:

std::string imageName = "C://opencvImages/LSl42.jpg";
cv::Mat testImage =  cv::imread( imageName );
//apply Gaussian Blur to smooth out the input:
cv::GaussianBlur( testImage, testImage, cv::Size(3,3), 0, 0 );

Ваше изображение имеет шумный (высокочастотный) фон. Вы можете немного размыть его, чтобы получить более плавный градиент и улучшить сегментацию. Я применил Gaussian Blur со стандартным размером ядра 3 x 3. Проверьте разницу между входным и сглаженным изображением:

enter image description here

Очень круто. Теперь я могу передать это изображение K-means. imageQuantization является функцией, взятой из здесь , которая реализует сегментацию на основе K-средних. Как я уже упоминал, он может группировать цвета одинакового значения в кластеры. Это очень удобно! Давайте сгруппируем цвета в 2 группы: передний план объект и фон .

int segmentationClusters = 2; //total number of clusters in which the input will be segmented...
int iterations = 5; // k-means iterations
cv::Mat segmentedImage = imageQuantization( testImage, segmentationClusters, iterations );

Результат:

enter image description here

Довольно хорошо, а?

Вы можете применить обнаружение края непосредственно к этому изображению, но я хочу улучшить его, используя немного морфология . Сначала я преобразовываю изображение в градации серого, применяю пороговое значение Outsu, а затем выполняю морфологическое закрытие:

//compute grayscale image of the segmented output:
cv::Mat grayImage;
cv::cvtColor( segmentedImage, grayImage, cv::COLOR_RGB2GRAY );

//get binary image via Otsu:
cv::Mat binImage;
cv::threshold( grayImage, binImage, 0, 255, cv::THRESH_OTSU );

//Perform a morphological closing to lose up holes in the target blob:
cv::Mat SE = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3) );
cv::morphologyEx( binImage, binImage, cv::MORPH_CLOSE, SE, cv::Point(-1,-1), 10 );

Я использую прямоугольный angular структурирующий элемент размером 3x3 и 10 итераций операции закрытия, это результат:

enter image description here

Затем определите края, используя детектор контуров Кэнни:

cv::Mat testEdges;
//setup lower and upper thresholds for Canny’s edge detection:
float lowerThreshold = 30;
float upperThreshold = 3 * lowerThreshold;
cv::Canny( binImage, testEdges, lowerThreshold, upperThreshold );

Наконец, получите контур капли:

std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;

cv::findContours( testEdges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );

for( int i = 0; i< contours.size(); i++ )
{
 cv::Scalar color = cv::Scalar( 0,255,0 );
 cv::drawContours( resizedImage, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
}

Это конечный результат, который я получаю:

enter image description here

Хотите улучшить результат, расширив контур? Попробуйте расширить двоичное изображение за несколько итераций, прежде чем передать его в обнаружение края Кэнни. Это тест, расширение изображения в 5 раз:

enter image description here

1 голос
/ 13 февраля 2020

Простой подход заключается в применении большого размытия по Гауссу для сглаживания изображения, а затем адаптивного порога . Предполагая, что объект является самой большой вещью на изображении, мы можем найти контуры и затем отсортировать их по наибольшему контуру, используя фильтрацию области контура.

Двоичное изображение

enter image description here

Результат

enter image description here

Код

import cv2
import numpy as np

# Load image, convert to grayscale, Gaussian Blur, adaptive threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (13,13), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,51,7)

# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=1)

# Find contours, sort for largest contour, draw contour
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    cv2.drawContours(image, [c], -1, (36,255,12), 2)
    break

cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...