|
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