Skip to content

Commit ba60958

Browse files
committed
Improve logo detection example
Re-organize and improve comments Move pipeline to function
1 parent 3285909 commit ba60958

File tree

1 file changed

+75
-42
lines changed

1 file changed

+75
-42
lines changed

examples/ex05_detect_sfe_logo.py

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,34 @@
55
#-------------------------------------------------------------------------------
66
# ex05_detect_sfe_logo.py
77
#
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.
1436
# ___
1537
# / _\
1638
# \ \
@@ -21,24 +43,6 @@
2143
# | _____/
2244
# | /
2345
# |/
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
4246
logo_contour = np.array(
4347
[[[0,48]],
4448
[[0,22]],
@@ -65,20 +69,9 @@
6569
[[20,36]],
6670
[[12,36]]], dtype=np.float)
6771

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):
8275
# Here we binarize the image. There are many ways to do this, but here we
8376
# simply convert the image to grayscale and then apply Otsu's thresholding
8477
# method to create a binary image. This means it will only detect a dark
@@ -87,9 +80,9 @@
8780
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
8881
ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
8982

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
9386
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
9487

9588
# It's possible that no contours were found, so first check if any were
@@ -130,9 +123,49 @@
130123
# that good matches are usually around 0.5, so we'll use a slightly
131124
# higher threshold of 1.0
132125
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
134128
frame = cv.drawContours(frame, [best_contour], -1, (0, 0, 255), 2)
135129

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+
136169
# All processing is done! Calculate the frame rate and display it
137170
current_time = time.ticks_us()
138171
fps = 1000000 / (current_time - loop_time)

0 commit comments

Comments
 (0)