it-swarm.com.ru

Доступ к значениям пикселей в пределах границы контура с использованием OpenCV в Python

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

Один способ, которым я думал, что мог сделать это, это преобразовать изображение в двоичное, получить контуры капель (в данном случае отслеживаемый объект) и получить координаты границы объекта. Затем я могу перейти к этим координатам границы в изображении в градациях серого, получить интенсивности пикселей, окруженные этой границей, и отследить этот цветовой градиент/интенсивности пикселей в других кадрах. Таким образом, я мог бы держать два объекта отдельно друг от друга, чтобы они не рассматривались как новые объекты в следующем кадре.

У меня есть координаты границы контура, но я не знаю, как получить интенсивность пикселей в пределах этой границы. Может ли кто-нибудь помочь мне с этим?

Спасибо!

9
Kaya311

Следуя нашим комментариям, вы можете создать список массивов numpy, где каждый элемент - это интенсивности, которые описывают внутреннюю часть контура каждого объекта. В частности, для каждого контура создайте двоичную маску, которая заполняет внутреннюю часть контура, найдите координаты (x,y) заполненного объекта, затем внесите в указатель свое изображение и захватите интенсивности.

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

import cv2
import numpy as np

#... Put your other code here....
#....

# Call if necessary
#img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Call cv2.findContours
contours,_ = cv2.findContours(img, cv2.RETR_LIST, cv2.cv.CV_CHAIN_APPROX_NONE)

contours теперь представляет собой список трехмерных массивов numpy, каждый из которых имеет размер N x 1 x 2, где N - общее количество точек контура для каждого объекта.

Таким образом, вы можете создать наш список следующим образом:

# Initialize empty list
lst_intensities = []

# For each list of contour points...
for i in range(len(contours)):
    # Create a mask image that contains the contour filled in
    cimg = np.zeros_like(img)
    cv2.drawContours(cimg, contours, i, color=255, thickness=-1)

    # Access the image pixels and create a 1D numpy array then add to list
    pts = np.where(cimg == 255)
    lst_intensities.append(img[pts[0], pts[1]])

Для каждого контура мы создаем пустое изображение, а затем рисуем контур заливки на этом пустом изображении. Вы можете заполнить область, которую занимает контур, указав для параметра thickness значение -1. Я установил внутреннюю часть контура на 255. После этого мы используем numpy.where , чтобы найти все строки и столбцы в массиве, которые соответствуют определенному условию. В нашем случае мы хотим найти значения, равные 255. После этого мы используем эти точки для индексации нашего изображения, чтобы получить интенсивность пикселей, внутреннюю по отношению к контуру.

lst_intensities содержит этот список массивов 1D numpy, где каждый элемент дает вам интенсивности, которые принадлежат внутренней части контура каждого объекта. Чтобы получить доступ к каждому массиву, просто выполните lst_intensities[i], где i - контур, к которому вы хотите получить доступ.

15
rayryeng

Ответ от @rayryeng отличный!

Одна небольшая вещь из моей реализации: Функция np.where() возвращает Tuple, который содержит массив индексов строк и массив индексов столбцов. Итак, pts[0] включает в себя список row indices, которые соответствуют высоте изображения, pts[1] включает в себя список column indices, которые соответствуют ширине изображения. img.shape возвращает (rows, cols, channels). Поэтому я думаю, что это должен быть img[pts[0], pts[1]], чтобы нарезать ndarray позади img.

1
Jundong