Как определить дыры, связанные линиями в двоичном графе - PullRequest
4 голосов
/ 29 февраля 2020

Я выбрал сегмент кода из ответа автора на этот вопрос:

https://answers.opencv.org/question/9863/fill-holes-of-a-binary-image/

Переформатировал его как:

cv::Mat image = cv::imread("image.jpg", 0);

cv::Mat image_thresh;
cv::threshold(image, image_thresh, 125, 255, cv::THRESH_BINARY);

// Loop through the border pixels and if they're black, floodFill from there
cv::Mat mask;
image_thresh.copyTo(mask);
for (int i = 0; i < mask.cols; i++) {
 if (mask.at<char>(0, i) == 0) {
     cv::floodFill(mask, cv::Point(i, 0), 255, 0, 10, 10);
 }   
  if (mask.at<char>(mask.rows-1, i) == 0) {

     cv::floodFill(mask, cv::Point(i, mask.rows-1), 255, 0, 10, 10);
   }
 }

 for (int i = 0; i < mask.rows; i++) {

  if (mask.at<char>(i, 0) == 0) {
    cv::floodFill(mask, cv::Point(0, i), 255, 0, 10, 10);
  }

  if (mask.at<char>(i, mask.cols-1) == 0) {
     cv::floodFill(mask, cv::Point(mask.cols-1, i), 255, 0, 10, 10);
  } 
}


 // Compare mask with original.
cv::Mat newImage;
image.copyTo(newImage);
for (int row = 0; row < mask.rows; ++row) {
 for (int col = 0; col < mask.cols; ++col) {
    if (mask.at<char>(row, col) == 0) {
        newImage.at<char>(row, col) = 255;
    }           
 }
}

cv::imshow("filled image", mask);
cv::imshow("Final image", newImage);
cv::imwrite("final.jpg", newImage);
cv::waitKey(0);

return 0;

Я понимаю, что он использовал алгоритм заливки, чтобы попытаться заполнить отверстия, и я проверил на другом образце изображения:

enter image description here

, и он работает очень хорошо, обнаружив все 9 лунок.

Однако я попробовал другое немного сложное изображение:

enter image description here

На этот раз он выиграл ' Это сработает, и он заполнит весь график белым, а количество обнаруженных дыр равно 1700.

Я думаю, что мне, возможно, не хватает значительного количества морфологических знаний здесь, но я предполагаю, что мне следует сделать «Уклоняться» от неудачного изображения, прежде чем вставить его в код автора?

Не могли бы эксперты поделиться со мной некоторыми мыслями, потому что я не смог найти в Google очень похожие графики для обнаружения дырок. Так что же такого особенного в отверстиях, когда в двоичном изображении два отверстия связаны с белым путем? Заранее спасибо!

Ответы [ 2 ]

4 голосов
/ 29 февраля 2020

Проблема в вашем изображении, у него тонкая белая полоса, окружающая 3 стороны вашего изображения. Эта полоса также связана с 4 белыми прямоугольниками слева, что создает дополнительный замкнутый контур / уровень, который запутывает «заливку», я думаю.

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

Отверстия, которые вы ищете, имеют два свойства:

  1. Это дочерний контур ( отверстие)
  2. У них нет другого контура внутри (не родитель)

Код для поиска этих отверстий:

auto image = cv::imread(in_img_path, cv::ImreadModes::IMREAD_GRAYSCALE);
cv::threshold(image, image, 128, 255, cv::THRESH_OTSU);
std::vector<std::vector<cv::Point>> contours, selected_contours;
std::vector<cv::Vec4i> hierarchy;   
cv::findContours(image, contours, hierarchy, cv::RetrievalModes::RETR_TREE, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++) {
    if (hierarchy[i][2] == -1 && hierarchy[i][3] != -1) //the contour has no children but has a parent
        selected_contours.emplace_back(std::move(contours[i]));
}
cv::Mat drawing_image(image.size(), image.type(), cv::Scalar::all(0));

for (int i = 0; i < selected_contours.size(); i++) {
    cv::drawContours(drawing_image, selected_contours, i, cv::Scalar(255), 1);
}

Редактировать: Я попробовал, кажется, что первая проверка в этом случае избыточна. Следующее условие является достаточным:

if (hierarchy[i][3] != -1) // the contour has a parent

Количество отверстий (размер selected_contours): 71

И 'Drawing_image' будет выглядеть так: Image showing found holes

0 голосов
/ 03 марта 2020

Другой простой подход - использовать фильтрацию области контура. Идея состоит в том, чтобы найти контуры , а затем выполнить фильтрацию области контура с помощью cv2.contourArea и минимальной пороговой области. Если контур проходит этот пороговый фильтр, мы считаем его действительным контуром. Вот результаты с пороговым значением для 500. Возможно, вам придется изменить его в зависимости от вашего изображения.


image image

image image

Я реализовал это в Python но вы можете легко адаптировать тот же подход в C ++

import cv2
import numpy as np

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
mask = np.zeros(image.shape[:2], dtype=np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Find contours and filter using contour area
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < 500:
        cv2.drawContours(image,[c],0,(36,255,12),-1)
        cv2.drawContours(mask,[c],0,255,1)

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