| 
5 | 5 | #-------------------------------------------------------------------------------  | 
6 | 6 | # ex05_detect_sfe_logo.py  | 
7 | 7 | #   | 
8 |  | -# This example demonstrates a basic vision processing pipeline. It reads frames  | 
9 |  | -# from the camera, finds contours in the image, and compares them to a reference  | 
10 |  | -# contour to detect the SparkFun flame logo. Below is some (bad) ASCII art of  | 
11 |  | -# the logo for reference. The example draws the actual contour that it's looking  | 
12 |  | -# for in the top left corner of the display.  | 
13 |  | -#   | 
 | 8 | +# This example demonstrates a basic vision processing pipeline. A pipeline is  | 
 | 9 | +# just a sequence of steps used to extract meaningful data from an image. The  | 
 | 10 | +# pipeline in this example attempts to detect the SparkFun flame logo using  | 
 | 11 | +# contour matching. If it's detected, it will be outlined on the display for  | 
 | 12 | +# visualization. The bounding box and center of the logo will also be drawn,  | 
 | 13 | +# demonstrating how to acquire useful numerical data from an image (eg. the  | 
 | 14 | +# position and size of an object).  | 
 | 15 | +#  | 
 | 16 | +# Note that this pipeline is very simple and does not include many of the steps  | 
 | 17 | +# that would typically be included in more robust pipelines. This was done for  | 
 | 18 | +# simplicity and performance, so it may produce false positives or miss the logo  | 
 | 19 | +# entirely sometimes.  | 
 | 20 | +#-------------------------------------------------------------------------------  | 
 | 21 | + | 
 | 22 | +# Import OpenCV and hardware initialization module  | 
 | 23 | +import cv2 as cv  | 
 | 24 | +from cv2_hardware_init import *  | 
 | 25 | + | 
 | 26 | +# Import NumPy  | 
 | 27 | +from ulab import numpy as np  | 
 | 28 | + | 
 | 29 | +# Import time for frame rate calculation  | 
 | 30 | +import time  | 
 | 31 | + | 
 | 32 | +# Here we define a reference contour for the SparkFun flame logo. This was  | 
 | 33 | +# created manually by picking points on the boundary of a small image of the  | 
 | 34 | +# logo in an image editor. Below is also ASCII art of the logo for reference,  | 
 | 35 | +# but the actual contour is drawn in the top left corner of the display.  | 
14 | 36 | #     ___  | 
15 | 37 | #    /  _\  | 
16 | 38 | #    \  \  | 
 | 
21 | 43 | # |  _____/  | 
22 | 44 | # | /  | 
23 | 45 | # |/  | 
24 |  | -#   | 
25 |  | -# If the logo is detected, it will be highlighted in red on the display. Note  | 
26 |  | -# that this vision pipeline is very simple and does not include many of the  | 
27 |  | -# steps that would typically be included in more robust pipelines for the sake  | 
28 |  | -# of simplicity and performance. So it may produce false positives or miss the  | 
29 |  | -# logo entirely in some cases.  | 
30 |  | -#-------------------------------------------------------------------------------  | 
31 |  | - | 
32 |  | -# Import OpenCV  | 
33 |  | -import cv2 as cv  | 
34 |  | -from cv2_hardware_init import *  | 
35 |  | -from ulab import numpy as np  | 
36 |  | -import time  | 
37 |  | - | 
38 |  | -# Here we define a reference contour for the SparkFun flame logo. This was  | 
39 |  | -# created manually by picking points on the boundary of a small image of the  | 
40 |  | -# logo in an image editor. This gets drawn in the top left corner of the  | 
41 |  | -# display for reference  | 
42 | 46 | logo_contour = np.array(  | 
43 | 47 |     [[[0,48]],  | 
44 | 48 |      [[0,22]],  | 
 | 
65 | 69 |      [[20,36]],  | 
66 | 70 |      [[12,36]]], dtype=np.float)  | 
67 | 71 | 
 
  | 
68 |  | -# Initialize a loop timer to calculate processing speed in FPS  | 
69 |  | -loop_time = time.ticks_us()  | 
70 |  | - | 
71 |  | -# Open the camera  | 
72 |  | -camera.open()  | 
73 |  | - | 
74 |  | -# Prompt the user to press a key to continue  | 
75 |  | -print("Press any key to continue")  | 
76 |  | - | 
77 |  | -# Loop to continuously read frames from the camera and display them  | 
78 |  | -while True:  | 
79 |  | -    # Read a frame from the camera  | 
80 |  | -    success, frame = camera.read()  | 
81 |  | - | 
 | 72 | +# This is the pipeline implementation. This gets called for each frame captured  | 
 | 73 | +# by the camera in the main loop  | 
 | 74 | +def my_pipeline(frame):  | 
82 | 75 |     # Here we binarize the image. There are many ways to do this, but here we  | 
83 | 76 |     # simply convert the image to grayscale and then apply Otsu's thresholding  | 
84 | 77 |     # method to create a binary image. This means it will only detect a dark  | 
 | 
87 | 80 |     gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)  | 
88 | 81 |     ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)  | 
89 | 82 | 
 
  | 
90 |  | -    # Find contours in the binary image, which represent the boundaries of  | 
91 |  | -    # shapes. Contours are a powerful tool in OpenCV for shape analysis and  | 
92 |  | -    # object detection  | 
 | 83 | +    # Find contours in the binary image, which are simply lists of points around  | 
 | 84 | +    # the boundaries of shapes. Contours are a powerful tool in OpenCV for shape  | 
 | 85 | +    # analysis and object detection  | 
93 | 86 |     contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)  | 
94 | 87 | 
 
  | 
95 | 88 |     # It's possible that no contours were found, so first check if any were  | 
 | 
130 | 123 |         # that good matches are usually around 0.5, so we'll use a slightly  | 
131 | 124 |         # higher threshold of 1.0  | 
132 | 125 |         if best_similarity < 1.0:  | 
133 |  | -            # Now we'll draw the best contour found on the original image  | 
 | 126 | +            # The best contour found is a good match, so we'll draw it on the  | 
 | 127 | +            # frame to outline the detected logo for visualization  | 
134 | 128 |             frame = cv.drawContours(frame, [best_contour], -1, (0, 0, 255), 2)  | 
135 | 129 | 
 
  | 
 | 130 | +            # Visualization is great, but the purpose of most real pipelines is  | 
 | 131 | +            # to extract useful data from the image. For example, suppose we  | 
 | 132 | +            # want to know where the logo is located in the image and how large  | 
 | 133 | +            # it is. We can use the bounding rectangle of the contour to get the  | 
 | 134 | +            # position and size of the logo  | 
 | 135 | +            left, top, width, height = cv.boundingRect(best_contour)  | 
 | 136 | +            center_x = left + width // 2  | 
 | 137 | +            center_y = top + height // 2  | 
 | 138 | + | 
 | 139 | +            # Now we could use this data for some task! For example, if we had  | 
 | 140 | +            # a robot that needed to drive up to the logo, we could turn to face  | 
 | 141 | +            # the logo with the center point, then drive towards it until the  | 
 | 142 | +            # size is big enough.  | 
 | 143 | +            #   | 
 | 144 | +            # This example doesn't actually make use of the data, so we'll just  | 
 | 145 | +            # draw the bounding box and center of the logo for visualization  | 
 | 146 | +            frame = cv.rectangle(frame, (left, top), (left + width, top + height), (255, 0, 0), 2)  | 
 | 147 | +            frame = cv.circle(frame, (center_x, center_y), 5, (0, 255, 0), -1)  | 
 | 148 | + | 
 | 149 | +# Initialize a loop timer to calculate processing speed in FPS  | 
 | 150 | +loop_time = time.ticks_us()  | 
 | 151 | + | 
 | 152 | +# Open the camera  | 
 | 153 | +camera.open()  | 
 | 154 | + | 
 | 155 | +# Prompt the user to press a key to continue  | 
 | 156 | +print("Press any key to continue")  | 
 | 157 | + | 
 | 158 | +# Loop to continuously read frames from the camera and display them  | 
 | 159 | +while True:  | 
 | 160 | +    # Read a frame from the camera  | 
 | 161 | +    success, frame = camera.read()  | 
 | 162 | +    if not success:  | 
 | 163 | +        print("Failed to read frame from camera")  | 
 | 164 | +        break  | 
 | 165 | +      | 
 | 166 | +    # Call the pipeline function to process the frame  | 
 | 167 | +    my_pipeline(frame)  | 
 | 168 | + | 
136 | 169 |     # All processing is done! Calculate the frame rate and display it  | 
137 | 170 |     current_time = time.ticks_us()  | 
138 | 171 |     fps = 1000000 / (current_time - loop_time)  | 
 | 
0 commit comments