Banning the Pixycam and using OpenCV won't make the work more challenging. In the narrow domain of finding a red ball, both OpenCV and the PixyCam perform the SAME FUNCTION for the programmer.
You see... the PixyCam is a "smart camera" but with a very "simple library"... while OpenCV uses a "simple camera" but with a very VERY "smart library".
From the programmer's perspective, both PixyCam and OpenCV end up doing the same thing. They both simply return an array of blobs (called Blocks for Pixycam... contours for OpenCV) that match the color red.
The programmer only has four things to do in both cases:
- get an array of blobs that match the color red (this is what PixyCam and OpenCV do)
- take the array of red blobs and determine (based on size) which one is the red ball.
- figure out if the ball is to the left, right or center of the camera image.
- run your motors in that direction
To prove my point let's look at some very simple sample code of a Raspberry Pi using OpenCV and an Arduino using the PixyCam to find a red ball. Even though they are in different languages (C++ and Python) you can see they are almost exactly the same from a logical standpoint... especially in the loop() function where all the work takes place.
import numpy as np
import cv2
redLowerBound = (0, 0, 140);
redUpperBound = (70, 70, 255);
def driveTowardsBall(centerX,ballX):
# do something cheap here - not a real good algorithm
if ballX < centerX/2:
print ('Veer Left')
elif ballX > centerX + (centerX/2):
print ('Veer Right')
else:
print ('Go Straight')
def setup():
global cap, centerX
cap = cv2.VideoCapture(0)
if cap.isOpened():
#let's calc center(X,Y) based on the image dimension
ret,image = cap.read()
(centerX, centerY) = (image.shape[1] // 2, image.shape[0] // 2)
def loop():
global cap, centerX
while(cap.isOpened()):
#the next three lines are similar to calling getBlocks
ret,image = cap.read()
ball = cv2.inRange(image, redLowerBound, redUpperBound)
#find contours (blocks)
_, contours, _ = cv2.findContours(ball, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) > 0:
# find the block that is max in area and assume it is red ball
sortedContours = sorted(contours, key = cv2.contourArea, reverse = True)
biggestRedObject = sortedContours[0]
(ballX, ballY), radius = cv2.minEnclosingCircle(biggestRedObject)
# for debugging only we need to see ball
cv2.circle(image, (int(ballX), int(ballY)), int(radius), (255, 0, 0), 3)
driveTowardsBall(centerX,int(ballX))
#for debugging only we need to see ball
cv2.imshow("Ball", image)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
def main():
setup()
loop()
if __name__ == "__main__":
main()
Here is the same code for Arduino:
#include <SPI.h>
#include <Pixy.h>
Pixy pixy;
const int centerX=320/2;
inline int block_area(const Block& b){
return b.width * b.height;
}
void driveTowardsBall(int centerX,int ballX){
// do something cheap here - not a real good algorithm
if (ballX < centerX/2){
Serial.println ("Veer Left");
}
else if (ballX > centerX + (centerX/2)) {
Serial.println ("Veer Right");
}
else{
Serial.println ("Go Straight");
}
}
void setup(){
Serial.begin(115200);
Serial.print("Starting...\n");
pixy.init();
}
void loop(){
static int i = 0;
int j;
uint16_t blocks;
char buf[32];
blocks = pixy.getBlocks();
if(blocks){
int max_block = 0;
int max_block_area = 0;
for (j=0; j<blocks; j++){
//find the largest Block - assume it is the ball
if (block_area(pixy.blocks[j]) > max_block_area){
max_block = j;
max_block_area = block_area(pixy.blocks[j]);
}
pixy.blocks[j].print();
}
int ballX = pixy.blocks[max_block].x + pixy.blocks[max_block].width/2;
driveTowardsBall(centerX,ballX);
}
}
All the real work is done in the loop() functions and both sets of code essentially do the same thing. The programmer makes some calls to get back an array of blobs that match the color red. Then the programmer determines the largest blob assuming it is the red ball. Then determine the center of the ball (centerX) and determine if the center is to the left, right or in the middle of the camera's field of view. They simply drive turn towards the direction of the ball (note: this code assumes the ball is actually in front of us).
The only real difference is that the PixyCam must be "trained" to find red while in OpenCV the upper and lower bound for red must be passed to the library. A very subtle difference that is more logistical than programmatic.
So I strongly feel that banning the use of the PixyCam would be an arbitrary decision. It buys nothing... it doesn't make the programming more challenging and it simply restricts what processing platforms students can use.