#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:
Существует очень простой способ найти эту область, если принять некоторые допущения, которые соблюдены в примере изображения:
- Область, которую нужно найти, сверху ограничена линией
- Любые дополнительные линии на изображении находятся над интересующей линией
- В линии нет разрывов
В этом случае площадь интересующей области определяется суммой длин от нижней части изображения до первого установленного пикселя. Мы можем вычислить это с помощью:
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]
к области (т.е. количество столбцов).