Как получить площадь контуров? - PullRequest
1 голос
/ 02 апреля 2019

У меня есть такая картинка:
origin picture

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

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

И тогда я получаю результат как:
edge pic
Я хочу получить площадь 2 так:
enter image description here


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

Ответы [ 2 ]

3 голосов
/ 02 апреля 2019

Простой подход с использованием 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)

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

Temporary image

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

Result image

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

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

2 голосов
/ 02 апреля 2019

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

  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 = %d\n'%area)

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

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

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

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