Spencer Karofsky

Spencer Karofsky

Home
Skills
Projects
Experience
This project is not yet complete; updates are being provided on a frequent basis.

Computer Vision Traffic Light

July 2023-Present

We've all experienced that frustrating moment: waiting at a red light with no traffic from the other direction to justify the delay.

It doesn't have to be this way.

Using computer vision, I'm building a new class of traffic light that adjusts the lights according to the amount of traffic it detects from each street. Throughout this project, I have encountered numerous probelms. Through preserverance, I was able to ultimately resolve these issues and significantly increased my understanding of computer vision.

I am building this system on a Raspberry Pi 4, which has presented a large problem: the Raspberry Pi only supports one camera, while I need four (one for each street side). As a solution, I decided to put the camera on a stepper motor, where the motor rotates 90 degrees to capture each street side.

The first step of this project was to select a traffic dataset. I searched all over Kaggle and Google Search, but I couldn't find anything that fit my needs. Building a dataset like I did with my traffic sign classification project was impractical because unlike traffic signs, vehicles vary in size, shape, color, and features, which increases the complexity of assembling a good dataset. I ultimately found COCO, a large 120,000 image dataset with images for 80 different classes - ranging from different types of vehicles, to people, to toothbrushes.

Next, I needed to choose an object detection algorithm. I was tempted to use a Convolutional Neural Network again for its high accuracy; but because training models is computationally expensive, I opted for an algorithm called YOLO (You Only Look Once) which is a much faster and computationally efficent algorithm, albeit slightly less accurate than a CNN. The next big problem came to training a model. Downloading the entire COCO dataset was over 30 GB, and the dataset would then need to be put in a special format to be passed to YOLO. I spent 2 months trying to figure out how to create this model. I tried numerous libraries, searched all over the internet, but none of the guides made any sense. I then found Ultralytics, an API with a pretrained YOLOv8 (newest/fastest version of YOLO) on the COCO dataset.

The Ultralytics API was a lifesaver; it detected multiple different classes with high accuracy. Here's a photo of the model on an Adobe stock video:

At first, I had all my code in one large file. This method made the program very hard to read and view, so I split the code into classes: a Detector class, which detects and classifies objects; a TrafficLight class, which controls the traffic light and contains the algorithm for controlling lights; and a Motor class, which controlls the stepper motor.

Next, I created a algorithm for controlling the traffic light. The algorithm is as follows:

Here is the algorithm I coded for my TrafficLight class:

python

    
    def set_traffic_lights(self):
        # Case 1: All sides have less than 2 cars; intersection will act as a 4-way stop.
        if self.traffic_dataframe.iloc[-1, 1] <= 2 and self.traffic_dataframe.iloc[-1, 2] <= 2 and self.traffic_dataframe.iloc[-1, 3] <= 2 and self.traffic_dataframe.iloc[-1, 4] <= 2:
            # turn all greens and yellows off; all reds  blink to signal 4-way stop
            print("Less than 2 cars for each side; intersection functioning as a 4 way stop.")
            self.set_flash_red('Street 1')
            self.set_flash_red('Street 2')
        # Case 2: some sides have more than 2 cars, so the ratio between the 2 streets will be calculated using the calculate_traffic_ratio function.
        # That ratio will then be used to determine the timing of the lights.
        else:
            ratio = self.calculate_traffic_ratio(self.traffic_dataframe.iloc[-1])
            # Case A: Street 2 has more traffic than Street 1; Street 2 gets green light priority, unless Street 2 has had a green light for more than 3 minutes.
            if ratio < 1:
                if not(self.last_street_change_time()):
                    self.set_red_light('Street 1')
                    self.set_green_light('Street 2')
                    print("Street 2 has more traffic than Street 1; Street 2 gets green light priority")
                else:
                    self.set_red_light('Street 2')
                    self.set_green_light('Street 1')
                    print("Street 2 has had green light for more than 3 minutes, so Street 1 gets red light.")


            # Case B: Street 1 has more traffic than Street 2; Street 1 gets green light priority, unless Street 1 has had a green light for more than 3 minutes.
            elif ratio > 1:
                if not (self.last_street_change_time()):
                    # Set street 1 lights (green[0] and green[2]) to green and street 2 lights to red.
                    self.set_red_light('Street 2')
                    self.set_green_light('Street 1')
                    print("Street 1 has more traffic than Street 2; Street 1 gets green light priority.")
                else:
                    # Set street 2 lights (green[1] and green[3]) to green and street 1 lights to red.
                    self.set_red_light('Street 1')
                    self.set_green_light('Street 2')
                    print("Street 1 has had green light for more than 3 minutes, so Street 2 gets red light.")
            else:
                # The streets have equal traffic amounts.
                print("The streets have equal traffic amounts.")
                # If traffic_dataframe row is first row, randomly select street to get green light priority
                current_row_index = self.traffic_dataframe.index.get_loc(self.traffic_dataframe.index[-1])
                if current_row_index == 0:
                    random_street = random.randint(1, 2)
                    if random_street == 1:
                        print("No existing data; street 1 randomly selected to receive green light priority")
                        # Street 1 gets green light priority
                        green_light_time = self.get_green_light_time('Side 1')
                        # Set green and red lights
                        self.set_red_light('Street 2')
                        self.set_green_light('Street 1')
                    else:
                        print("No existing data; street 2 randomly selected to receive green light priority")
                        # Street 2 gets green light priority
                        green_light_time = self.get_green_light_time('Side 2')
                        # Set green and red lights
                        self.set_red_light('Street 1')
                        self.set_green_light('Street 2')
                # Else, give green light priority to the last street to have red light.
                else:
                    print("The last street to get the green gets a red light.")
                    street_green = self.get_last_green()
                    self.set_red_light(street_green)
                    if street_green == 'Street 1':
                        self.set_green_light('Street 2')
                        print("Street 2 gets a green light.")
                    else:
                        self.set_green_light('Street 1')
                        print("Street 1 gets a green light.")
        self.traffic_dataframe.to_csv('Traffic_Data',index=False,mode='w')
    
    

The last line saves the Pandas dataframe to a csv. I plan to eventually replace the algorithm above with a deep learning model that can better make predictions, but I first need to collect a lot of training data from this light.