Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you count cars in OpenCV with Python?

I'm trying to count the number of cars in the image automatically using OpenCV and Python.

enter image description here

Initially I thought I could do this with some segmentation, but I didn't have much success. I then thought that a Hough Transform might help compute the border around the cars but it only really picked out the parking space lines. The only thing I can think of would be to start training some matches on templates of cars and non-cars, but I'm hoping there is something simpler that will do well here. I also tried edge detection which looks positive but not really sure how to continue:

enter image description here

like image 885
nickponline Avatar asked Feb 17 '16 03:02

nickponline


People also ask

How do you count the number of cars on the road?

A simple but accurate method of traffic counting comprises people manually counting vehicles. A person either uses an electronic hand held counter or records data using a tally sheet. They may stand at the side of the road, or, more commonly watch a video of the road and count from that.

How does Python calculate car speed?

tim2-tim1 gives the total time taken for car to pass polygon region and we already know that distance between those horizontal lines is 3 meters as we defined earlier it calculates speed in (m/s) using simple mathematical formula speed=distance/time and prints it to the console.


1 Answers

Ok, so.... I probably did too much work on this, but it seemed simple enough.

For my implementation I decided it would be better to find empty parking spaces, and assume that all other spaces are occupied. To determine if a spot is empty I simply compared it to a parking space sized portion of the road. This means that this same algorithm should work whether its bright or dark, etc. because the template is extracted directly from the image.

At this point I just do template matching (I tried various methods, but cv2.TM_CCORR_NORMED worked best). This gives a decent result, and now we need to process it.

enter image description here

I create the ROIs (regions of Interest) around the parking lot rows. I then collapse them to a single vector by taking statistics per column. I look at the mean. enter image description here

Its a pretty good indicator, you can already clearly see where the empty spaces are. But dark colored cars still present some issues, so now we decide to look at another statistic, how about variance? The parking lot is pretty constant throughout the entire region. A car on the other hand has a few changes, the windows, roofrack mirrors make for some variance. So I plot "inverted" variance. So rather than no change having a variance of 0, it has a variance of 1. It looks something like this enter image description here

This looks pretty promising! But you know whats even better? Combining the two! so lets just multiply them together, i called this result the "probability" since it should range between 0 and 1 enter image description here

Now you can really see the difference between a blank space, and a dark car. So lets do some simple threshold. This is great, but it doesn't give you the NUMBER of vehicles/empty spaces. At this point we go through the "probability" column by column, and we look for a certain number of consecutive pixels over the threshold. How many pixels? about as many pixels as a car is wide. This "hysteresis" type model should suppress any peaks or spurious data points.

And now it all comes together, we assume the number of spaces is constant(reasonable assumption I think) and we just say the number of cars = number of spaces - number of empty spaces and mark the image

enter image description here

and print some results

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

and of course, the code. It might not be the most efficient, but I'm typically a matlab person, its my first openCV/Python project

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()
like image 171
andrew Avatar answered Sep 22 '22 22:09

andrew