Как получить площадь контуров?

#opencv #computer-vision

#opencv #компьютерное зрение

Вопрос:

У меня есть такая картинка:
исходное изображение

А затем я преобразую это в двоичное изображение и использую canny для определения края изображения:

 gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)
edge = Image.fromarray(edges)
  

И тогда я получаю результат в виде:
изображение края
Я хочу получить площадь 2 вот так:
введите описание изображения здесь


Мое решение состоит в том, чтобы использовать HoughLines для поиска линий на рисунке и вычисления площади треугольника, образованного линиями. Однако этот способ не является точным, поскольку замкнутая область не является стандартным треугольником. Как получить площадь региона 2?

Комментарии:

1. Нижний контур не завершен, он открыт с других 3-х сторон, вам нужно вручную нарисовать белые линии вокруг границы, а затем найти область контура.

Ответ №1:

Простым подходом с использованием floodFill и countNonZero мог бы быть следующий фрагмент кода. Моя стандартная цитата по contourArea из справки:

Функция вычисляет площадь контура. Аналогично moments , площадь вычисляется с использованием формулы Грина. Таким образом, возвращаемая площадь и количество ненулевых пикселей, если вы рисуете контур с помощью drawContours или fillPoly , могут отличаться. Кроме того, функция, скорее всего, выдаст неправильные результаты для контуров с самопересечениями.

Код:

 import cv2
import numpy as np

# Input image
img = cv2.imread('images/YMMEE.jpg', cv2.IMREAD_GRAYSCALE)

# Needed due to JPG artifacts
_, temp = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)

# Dilate to better detect contours
temp = cv2.dilate(temp, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))

# Find largest contour
cnts, _ = cv2.findContours(temp, cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_NONE)
largestCnt = []
for cnt in cnts:
    if (len(cnt) > len(largestCnt)):
        largestCnt = cnt

# Determine center of area of largest contour
M = cv2.moments(largestCnt)
x = int(M["m10"] / M["m00"])
y = int(M["m01"] / M["m00"])

# Initiale mask for flood filling
width, height = temp.shape
mask = img2 = np.ones((width   2, height   2), np.uint8) * 255
mask[1:width, 1:height] = 0

# Generate intermediate image, draw largest contour, flood filled
temp = np.zeros(temp.shape, np.uint8)
temp = cv2.drawContours(temp, largestCnt, -1, 255, cv2.FILLED)
_, temp, mask, _ = cv2.floodFill(temp, mask, (x, y), 255)
temp = cv2.morphologyEx(temp, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))

# Count pixels in desired region
area = cv2.countNonZero(temp)

# Put result on original image
img = cv2.putText(img, str(area), (x, y), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, 255)

cv2.imshow('Input', img)
cv2.imshow('Temp image', temp)

cv2.waitKey(0)
  

Временное изображение:

Временное изображение

Результирующее изображение:

Результирующее изображение

Предостережение: findContours есть некоторые проблемы с правой стороны, где линия находится очень близко к нижней границе изображения, что, возможно, приводит к пропуску некоторых пикселей.

Отказ от ответственности: Я новичок в Python в целом, и особенно в Python API OpenCV (C для win). Комментарии, улучшения, выделение недостатков Python настоятельно приветствуются!

Ответ №2:

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

  1. Область, которую нужно найти, сверху ограничена линией
  2. Любые дополнительные линии на изображении находятся над интересующей линией
  3. В линии нет разрывов

В этом случае площадь интересующей области определяется суммой длин от нижней части изображения до первого установленного пикселя. Мы можем вычислить это с помощью:

 import numpy as np
import matplotlib.pyplot as pp

img = pp.imread('/home/cris/tmp/YMMEE.jpg')
img = np.flip(img, axis=0)
pos = np.argmax(img, axis=0)
area = np.sum(pos)
print('Area = %dn'%area)
  

Это печатается Area = 22040 .

np.argmax находит первый установленный пиксель в каждом столбце изображения, возвращая индекс. При первом использовании np.flip мы переворачиваем эту ось так, чтобы первый пиксель был фактически тем, что внизу. Индекс соответствует количеству пикселей между нижней частью изображения и линией (не включая установленный пиксель).

Таким образом, мы вычисляем площадь под линией. Если вам нужно включить саму линию в область, добавьте pos.shape[0] к области (т.е. количество столбцов).