Ring Stack Detection
UGRectRingPipeline
UGRectRingPipeline
The heart of the Ultimate Goal detector is the pipeline. A pipeline is just a fancy way describing the sequence of instructions given to continuously manipulate the image(in this case, what the camera sees). Ignoring the fancy code, the pipeline boils down to these following instructions:
Receiving the Input
In this line, what the camera sees is being passed in and represented as a matrix.
Manipulating the Input
The first line will convert the input matrix color space from RGB to YCrCb. Because the way YCrCb represents color by luminance(Y), chroma of red(CR), chroma of blue(Cb), it keeps values consistent under different lighting. Next we draw 2 rectangles on screen with predetermined position. The top rectangle should be where the 4th ring will be, and the bottom one should be where the first one will be. Then we extract the Cb value of each predetermined area of the ring to compare.
Finding CB Values
Here we crop the mat to just everything inside the two rectangles. Then we find the average of the values and store them in bottomAverage
and topAverage
.
Creating An Instance of UGRectDetector
UGRectDetector
The UGRectDetector
is a class that will show how you would use the pipeline. For more in-depth explanation of what everything does or more functionalities, please visit here. To start, create an instance of UGRectDetector
. The detector's constructor is overloaded. You can choose between:
hMap
: An instance of the hardware mapwebcamName
: The webcam name
If you use the first constructor, the detector will set the camera to the phone's camera. If the second is used, the webcam will be used.
Manipulating Detector Settings
You can change the orientation, width, and height of the camera for all instances (since the camera doesn't change between runs). You can update the settings by manipulating the static variables.
Setting Rectangle Positions
topRectHeightPercentage
: the percentage of the height of the user's input and should be a decimal under 1. It is used to calculate the first y value for the top rectangle.topRectWidthPercentage
: the percentage of the width of the user's input and should be a decimal under 1. It is used to calculate the first x value for the top rectangle.
bottomRectHeightPercentage
: the percentage of the height of the user's input and should be a decimal under 1. It is used to calculate the first y value for the bottom rectangle.bottomRectWidthPercentage
: the percentage of the width of the user's input and should be a decimal under 1. It is used to calculate the first x value for the bottom rectangle.
rectangleWidth
: the width of the rectangles in terms of pixelsrectangleHeight
: the height of the rectangles in terms of pixels
After creating an instance of the detector and setting the rectangle positions, continuously run DetectorInstance.getStack()
to get the number in the stack.
UGContourRingPipeline
UGContourRingPipeline
Vision Pipelines, the heart of any Ultimate Goal Detector. A pipeline is just a fancy way of saying a certain set of instructions that are applied to every inputted frame we see from the camera.
This is a Vision Pipeline utilizing Contours and an Aspect Ratio to determine the number of rings currently in the ring stack.
Using the Detector
The contour pipeline comes with a premade detector that can be used out of the box in your opmode. The UGContourRingDetector
is an object that runs the pipeline. You can manipulate the pipeline settings separately, but some of the settings of the detector get carried over to the pipeline.
To create an instance of the detector, there are different constructors, some with optional parameters.
You can, like with the rectangle detector, manipulate the settings of the contour detector.
To initialize the detector, simply call init
.
This initializes the pipeline with your configured settings. To retrieve the height determined by the pipeline, simply call DetectorInstance.getHeight()
.
Tuning
There are many values that the pipeline uses that can be changed/tuned to increase or decrease accuracy.
All configuration values are stored in a companion object
called Config (see here). In this, companion object
there are six variables, two of which are constants and cannot be changed.
lowerOrange
: the value of the lower orange used in finding the maskupperOrange
: the value of the upper orange used in finding the maskCAMERA_WIDTH
: the width of the resolution of the cameraHORIZON
: the value representing the horizon, on the y-axis. used in the horizon checkMIN_WIDTH
: the algorithmically generated value used in the minimum width checkBOUND_RATIO
: the value that the aspect ratio checks to determine whether the ring stack is one and four.
Config is stored in a companion object, this means that every instance of the UGContourRingPipeline
will share the same configuration.
These values and variables may change with future releases.
Receiving the Input
What the camera sees is being passed into the pipeline stored as an OpenCV Mat
type (short for matrix).
Manipulating the Input
We first take this Mat and convert it to YCrCb to help with thresholding.
We then perform an inRange operation on the input Mat and store the result in a temporary variable called mask. This mask is a black and white image where all white pixels on the mask were pixels in input that are in the orange range threshold. All black pixels on the mask were pixels in the input that were not in the orange range threshold.
Next, using a GaussianBlur, we eliminate any noise between the rings on the stack. Due to how we are thresholding in the mask calculation, there may be some gaps in the stack, due to shadows or unwanted light sources.
Finding the Contours
After the GaussianBlur, this noise is eliminated as the picture becomes "blurrier". We then find all contours on the image.
After finding the contours on the black and white mask, we then perform a linear search algorithm on the resulting list of contours (stored in as MatOfPoint). We first find the bounding rectangle of each contour and use this bounding rectangle (not rotated) to find the rectangle with the biggest width. We do this in order to not confuse the ring stack with other objects that may have been thought to be orange by the mask. Since the ring stack will most likely be the largest blob of orange in the view of the camera. When then do a check on the width of the widest contour. To see if it is actually a ring stack since zero is a valid option we must account for it. This check also makes sure that we don't mistake other smaller objects on the field as the ring stack, even if they are rings.
We also implemented a horizon check. Anything above the horizon is disregarded and not looked at even if it has the greatest width from all the other contours. This is to ensure that the algorithm down not detect the red goal as YCrCb color space is very unreliable when detecting the difference between red and orange.
Calculating the Aspect Ratio
After finding the widest contour, which is to be assumed the stack of rings, we perform an aspect ratio of the height over the width of the largest bounding rectangle.
Last updated
Was this helpful?