Как вы считаете автомобили в OpenCV с Python?

Я пытаюсь автоматически подсчитать количество автомобилей на изображении с помощью OpenCV и Python.

введите описание изображения здесь

Сначала я думал, что смогу сделать это с некоторой сегментацией, но у меня не было большого успеха. Затем я подумал, что преобразование Хафа может помочь вычислить границу вокруг автомобилей, но только на самом деле выбрали парковочные места. Единственное, о чем я могу думать, это начать обучение некоторых матчей по шаблонам автомобилей и не-автомобилей, но я надеюсь, что есть что-то более простое, что будет хорошо здесь. Я также пробовал обнаружение наложения, которое выглядит позитивно, но не совсем уверен, как продолжить:

введите описание изображения здесь

Ответ 1

Хорошо, так... Я, наверное, слишком много работал над этим, но это казалось достаточно простым.

Для моей реализации я решил, что было бы лучше найти пустые парковочные места и предположить, что все остальные пространства заняты. Чтобы определить, пусто ли место, я просто сравнил его с участком дороги на парковке. Это означает, что этот же алгоритм должен работать, будь то яркий или темный, и т.д., Потому что шаблон извлекается непосредственно из изображения.

В этот момент я просто делаю сопоставление шаблонов (я пробовал различные методы, но cv2.TM_CCORR_NORMED работал лучше всего). Это дает достойный результат, и теперь нам нужно его обработать.

введите описание изображения здесь

Я создаю ROI (области интереса) вокруг рядов парковочных мест. Затем я разбиваю их на один вектор, беря статистику за столбец. Я смотрю на середину. введите описание изображения здесь

Его довольно хороший индикатор, вы уже можете четко видеть, где находятся пустые пространства. Но темные цветные автомобили все еще представляют некоторые проблемы, поэтому теперь мы решили посмотреть на другую статистику, как насчет дисперсии? Парковка во всех регионах довольно постоянна. С другой стороны, автомобиль имеет несколько изменений, окна, зеркала на крыше, делают некоторые отклонения. Поэтому я рисую "инвертированную" дисперсию. Поэтому вместо изменения, имеющего дисперсию 0, оно имеет дисперсию 1. Это выглядит примерно так: введите описание изображения здесь

Это выглядит довольно многообещающе! Но вы знаете, что еще лучше? Сочетание двух! поэтому давайте просто умножим их вместе, я назвал этот результат "вероятностью", так как он должен находиться в диапазоне от 0 до 1 введите описание изображения здесь

Теперь вы можете увидеть разницу между пробелом и темным автомобилем. Так что давайте сделаем простой порог. Это здорово, но это не дает вам NUMBER транспортных средств/пустое пространство. На этом этапе мы переходим через столбец "вероятность" по столбцу, и мы ищем определенное количество последовательных пикселей над порогом. Сколько пикселей? примерно столько же пикселей, сколько автомобиль широкий. Эта модель типа "гистерезис" должна подавлять любые пики или ложные точки данных.

И теперь все это объединяется, мы предполагаем, что количество пространств является постоянным (разумное предположение, я думаю), и мы просто говорим number of cars = number of spaces - number of empty spaces и отмечаем изображение

введите описание изображения здесь

и напечатать некоторые результаты

found 24 cars and 1 empty space(s) in row 1
found 23 cars and 0 empty space(s) in row 2
found 20 cars and 3 empty space(s) in row 3
found 22 cars and 0 empty space(s) in row 4
found 13 cars and 9 empty space(s) in row 5

и, конечно, код. Это может быть не самый эффективный, но я, как правило, человек из Matlab, мой первый проект openCV/Python

import cv2
import numpy as np
from matplotlib import pyplot as plt

# this just keeps things neat
class ParkingLotRow(object):
    top_left=None
    bot_right=None
    roi=None
    col_mean=None
    inverted_variance=None
    empty_col_probability=None
    empty_spaces=0
    total_spaces=None

    def __init__(self,top_left,bot_right,num_spaces):
        self.top_left = top_left
        self.bot_right = bot_right
        self.total_spaces = num_spaces

############################ BEGIN: TWEAKING PARAMETERS ###########################################
car_width = 8       #in pixels
thresh = 0.975      #used to determine if a spot is empty
############################### END: TWEAKING PARAMETERS ###########################################
parking_rows = []

# defines regions of interest, row 1 is on top, row 5 is on bottom, values determined empirically
parking_rows.append(ParkingLotRow((  1, 20),(496, 41),25))     #row 1
parking_rows.append(ParkingLotRow((  1, 87),(462,105),23))     #row 2
parking_rows.append(ParkingLotRow((  1,140),(462,158),23))     #row 3
parking_rows.append(ParkingLotRow((  1,222),(462,240),22))     #row 4
parking_rows.append(ParkingLotRow((  1,286),(462,304),22))     #row 5

#read image
img = cv2.imread('parking_lot.jpg')
img2 = img.copy()

#creates a template, its jsut a car sized patch of pavement
template = img[138:165,484:495]
m, n, chan = img.shape

#blurs the template a bit
template = cv2.GaussianBlur(template,(3,3),2)
h, w, chan = template.shape

# Apply template Matching 
res = cv2.matchTemplate(img,template,cv2.TM_CCORR_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
#adds bounding box around template
cv2.rectangle(img,top_left, bottom_right, 255, 5)

#adds bounding box on ROIs
for curr_parking_lot_row in parking_rows:
    tl = curr_parking_lot_row.top_left
    br = curr_parking_lot_row.bot_right

    cv2.rectangle(res,tl, br, 1, 5)

#displays some intermediate results
plt.subplot(121),plt.imshow(res,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.title('Original, template in blue'), plt.xticks([]), plt.yticks([])

plt.show()

curr_idx = int(0)

#overlay on original picture
f0 = plt.figure(4)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)),plt.title('Original')


for curr_parking_lot_row in parking_rows:
    #creates the region of interest
    tl = curr_parking_lot_row.top_left
    br = curr_parking_lot_row.bot_right

    my_roi = res[tl[1]:br[1],tl[0]:br[0]]

    #extracts statistics by column
    curr_parking_lot_row.col_mean = np.mean(my_roi, 0)
    curr_parking_lot_row.inverted_variance = 1 - np.var(my_roi,0)
    curr_parking_lot_row.empty_col_probability = curr_parking_lot_row.col_mean * curr_parking_lot_row.inverted_variance

    #creates some plots
    f1 = plt.figure(1)
    plt.subplot('51%d' % (curr_idx + 1)),plt.plot(curr_parking_lot_row.col_mean),plt.title('Row %d correlation' %(curr_idx + 1))

    f2 = plt.figure(2)
    plt.subplot('51%d' % (curr_idx + 1)),plt.plot(curr_parking_lot_row.inverted_variance),plt.title('Row %d variance' %(curr_idx + 1))

    f3 = plt.figure(3)
    plt.subplot('51%d' % (curr_idx + 1))
    plt.plot(curr_parking_lot_row.empty_col_probability),plt.title('Row %d empty probability ' %(curr_idx + 1))
    plt.plot((1,n),(thresh,thresh),c='r')

    #counts empty spaces
    num_consec_pixels_over_thresh = 0
    curr_col = 0

    for prob_val in curr_parking_lot_row.empty_col_probability:
        curr_col += 1

        if(prob_val > thresh):
            num_consec_pixels_over_thresh += 1
        else:
            num_consec_pixels_over_thresh = 0

        if (num_consec_pixels_over_thresh >= car_width):
            curr_parking_lot_row.empty_spaces += 1

            #adds mark to plt
            plt.figure(3)   # the probability graph
            plt.scatter(curr_col,1,c='g')

            plt.figure(4)   #parking lot image
            plt.scatter(curr_col,curr_parking_lot_row.top_left[1] + 7, c='g')

            #to prevent doubel counting cars, just reset the counter
            num_consec_pixels_over_thresh = 0

    #sets axis range, apparantlly they mess up when adding the scatters
    plt.figure(3)
    plt.xlim([0,n])

    #print out some stats
    print('found {0} cars and {1} empty space(s) in row {2}'.format(
        curr_parking_lot_row.total_spaces - curr_parking_lot_row.empty_spaces,
        curr_parking_lot_row.empty_spaces,
        curr_idx +1))

    curr_idx += 1

#plot some figures
plt.show()

Ответ 2

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

Я думаю, что ваш метод обнаружения края может работать, если вы можете ограничить область интереса. Чтобы получить лучшее преимущество и устранить парковочные линии, используйте фильтр 2x2 на цветном изображении перед запуском обнаружения края. Тогда я бы рекомендовал использовать более сильный метод обнаружения края (например, sobel или prewitt). Вы должны получить что-то вроде этого (мой запускается на всех трехцветных каналах):

3-х канальное определение края привязки после эрозии

Затем вы можете преобразовать в двоичный файл, если хотите (не обязательно), или просто подсчитать пиксели в соответствующих ROI.

ROIs

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

Ответ 3

Быстрый код MATLAB, который можно легко преобразовать в python, чтобы вы начали...

b = rgb2hsv(I);
se = strel('rectangle', [2,4]); %# structuring element
BW = b(:,:,3)>0.5; 
BW2 = bwareaopen(BW,30);
BW3 = imerode(BW2, se); 
BW4 = imdilate(BW3, se);
CC = bwconncomp(BW4);

Отсюда вам нужно взглянуть на результаты передачи CC и вычислять ширину по высоте, так как вы знаете, насколько велики автомобили. Вот мой результат из BW4.

введите описание изображения здесь

Ответ 4

Автомобили - это объекты с высокой плотностью углов, поэтому вы получите дополнительную информацию, чтобы определить, может ли область изображения быть автомобилем. Я просто использовал

goodFeaturesToTrack

с maxCorners = 10000, qualityLevel = 0.01 и minDistance = 1 и получил этот результат

обнаруженные углы на изображении

соответственно

обнаруженные углы

Это само по себе очень шумно, но, может быть, вы можете объединить его с другими подходами сегментации. Например, вы можете рассчитать "ожидаемый прямоугольник размера автомобиля" на основе сегментации цвета и поиска таких прямоугольников с высокой плотностью углов. Когда у вас очень структурированная ситуация, например, в образце, вы "знаете", где автомобиль должен быть таким, чтобы вы могли проверять эти области.