/* When we're looking for balls it is helpful if they are surrounded by green. * The best place to look is underneath. So let's do that. * @param b the potential ball * @return did we find some green? */ bool Ball::greenCheck(Blob b) { const int ERROR_TOLERANCE = 5; const int EXTRA_LINES = 10; const int MAX_BAD_PIXELS = 4; if (b.getRightBottomY() >= IMAGE_HEIGHT - 1 || b.getLeftBottomY() >= IMAGE_HEIGHT-1) return true; if (b.width() > IMAGE_WIDTH / 2) return true; int w = b.width(); int y = 0; int x = b.getLeftBottomX(); stop scan; for (int i = 0; i < w; i+= 2) { y = b.getLeftBottomY(); vertScan(x + i, y, 1, ERROR_TOLERANCE, GREEN, GREEN, scan); if (scan.good > 1) return true; } // try one more in case its a white line int bad = 0; for (int i = 0; i < EXTRA_LINES && bad < MAX_BAD_PIXELS; i++) { int pix = thresh->thresholded[min(IMAGE_HEIGHT - 1, b.getLeftBottomY() + i)][x]; if (pix == GREEN) return true; if (pix != WHITE) bad++; } return false; }
/* Draws the outline of a blob in the specified color. * @param b the blob * @param c the color to paint */ void Ball::drawBlob(Blob b, int c) { #ifdef OFFLINE thresh->drawLine(b.getLeftTopX(), b.getLeftTopY(), b.getRightTopX(), b.getRightTopY(), c); thresh->drawLine(b.getLeftTopX(), b.getLeftTopY(), b.getLeftBottomX(), b.getLeftBottomY(), c); thresh->drawLine(b.getLeftBottomX(), b.getLeftBottomY(), b.getRightBottomX(), b.getRightBottomY(), c); thresh->drawLine(b.getRightTopX(), b.getRightTopY(), b.getRightBottomX(), b.getRightBottomY(), c); #endif }
/* Print debugging information for a blob. * @param b the blob */ void Robots::printBlob(Blob b) { #if defined OFFLINE cout << "Outputting blob" << endl; cout << b.getLeftTopX() << " " << b.getLeftTopY() << " " << b.getRightTopX() << " " << b.getRightTopY() << endl; cout << b.getLeftBottomX() << " " << b.getLeftBottomY() << " " << b.getRightBottomX() << " " << b.getRightBottomY() << endl; #endif }
/** * Update the robot values from the blob * * @param b The blob to update our object from. */ void VisualRobot::updateRobot(Blob b) { setLeftTopX(b.getLeftTopX()); setLeftTopY(b.getLeftTopY()); setLeftBottomX(b.getLeftBottomX()); setLeftBottomY(b.getLeftBottomY()); setRightTopX(b.getRightTopX()); setRightTopY(b.getRightTopY()); setRightBottomX(b.getRightBottomX()); setRightBottomY(b.getRightBottomY()); setX(b.getLeftTopX()); setY(b.getLeftTopY()); setWidth(dist(b.getRightTopX(), b.getRightTopY(), b.getLeftTopX(), b.getLeftTopY())); setHeight(dist(b.getLeftTopX(), b.getLeftTopY(), b.getLeftBottomX(), b.getLeftBottomY())); setCenterX(getLeftTopX() + ROUND2(getWidth() / 2)); setCenterY(getRightTopY() + ROUND2(getHeight() / 2)); setDistance(1); }
/* When we're looking for balls it is helpful if they are surrounded by green. * The best place to look is underneath, but that doesn't always work. So let's * try the other sides. * @param b the potential ball * @return did we find some green? */ bool Ball::greenSide(Blob b) { const int ERROR_TOLERANCE = 5; const int X_EXTRA = 8; int x = b.getRightBottomX(); int y = b.getRightBottomY(); stop scan; for (int i = y; i > (b.height()) / 2; i = i - 2) { horizontalScan(x, i, 1, ERROR_TOLERANCE, GREEN, GREEN, x - 1, x + X_EXTRA, scan); if (scan.good > 0) return true; } x = b.getLeftBottomX(); y = b.getLeftBottomY(); for (int i = y; i > (b.height()) / 2; i = i - 2) { horizontalScan(x, i, -1, ERROR_TOLERANCE, GREEN, GREEN, x - X_EXTRA, x + 1, scan); if (scan.good == 0) return true; } return false; }
bool Ball::badSurround(Blob b) { // basically check around the blob and see if it is ok - ideally we'd have // some green, worrisome would be lots of RED static const int SURROUND = 12; const float GREEN_PERCENT = 0.1f; int x = b.getLeftTopX(); int y = b.getLeftTopY(); int w = b.width(); int h = b.height(); int surround = min(SURROUND, w/2); int greens = 0, orange = 0, red = 0, borange = 0, pix, realred = 0, yellows = 0; // now collect information on the area surrounding the ball and the ball x = max(0, x - surround); y = max(0, y - surround); w = w + surround * 2; h = h + surround * 2; for (int i = 0; i < w && x + i < IMAGE_WIDTH; i++) { for (int j = 0; j < h && y + j < IMAGE_HEIGHT; j++) { pix = thresh->getThresholded(y + j,x + i); if (pix == ORANGE || pix == ORANGEYELLOW) { orange++; if (x + i >= b.getLeft() && x + i <= b.getRight() && y + j >= b.getTop() && y + j <= b.getBottom()) { borange++; } } else if (pix == RED) { realred++; } else if (pix == ORANGERED) { red++; } else if (pix == GREEN) { greens++; } else if (pix == YELLOW && j < surround) { yellows++; } } } if (BALLDEBUG) { cout << "Surround information " << red << " " << realred << " " << orange << " " << borange << " " << greens << " " << yellows << endl; } if (realred > borange) { if (BALLDEBUG) { cout << "Too much real red" << endl; } return true; } if (realred > greens && w * h < 2000) { if (BALLDEBUG) { cout << "Too much real red versus green" << endl; } return true; } if (realred > borange && realred > orange) { if (BALLDEBUG) { cout << "Too much real red vs borange" << endl; } return true; } if (orange - borange > borange * 0.3 && orange - borange > 10) { if (BALLDEBUG) { cout << "Too much orange outside of the ball" << endl; } // We can run into this problem with reflections - let's see if // we're up against a post or something if (yellows > w * 3) { if (BALLDEBUG) { cout << "But lots of yellow, doing nothing " << endl; } return false; } else { return true; } } if ((red > orange) && (static_cast<float>(greens) < (static_cast<float>(w * h) * GREEN_PERCENT))) { if (BALLDEBUG) { cout << "Too much real orangered without enough green" << endl; } return true; } if (red > orange || (realred > greens && realred > 2 * w && realred > borange * 0.1)) { if (BALLDEBUG) { cout << "Too much real red - doing more checking" << endl; } x = b.getLeftTopX(); y = b.getLeftBottomY(); if (nearImageEdgeX(x, 2) || nearImageEdgeX(x+b.width(), 2) || nearImageEdgeY(y, 2)) { if (BALLDEBUG) { cout << "Dangerous corner location detected " << x << " " << y << " " << w << endl; } return true; } return roundness(b) == BAD_VALUE; } return false; }
/* Is the ball at the boundary of the screen? * @param b the ball * @return whether or not it borders a boundary */ bool Ball::atBoundary(Blob b) { return b.getLeftTopX() == 0 || b.getRightTopX() >= IMAGE_WIDTH -1 || b.getLeftTopY() == 0 || b.getLeftBottomY() >= IMAGE_HEIGHT - 1; }
int Ball::roundness(Blob b) { const int IMAGE_EDGE = 3; const int BIG_ENOUGH = 4; const int TOO_BIG_TO_CHECK = 20; const int WIDTH_AT_SCREEN_BOTTOM = 15; const float RATIO_TO_INT = 10.0f; const float CORNER_CHUNK_DIV = 6.0f; int w = b.width(); int h = b.height(); int x = b.getLeftTopX(); int y = b.getLeftTopY(); float ratio = static_cast<float>(w) / static_cast<float>(h); int r = 10; if ((h < SMALLBALLDIM && w < SMALLBALLDIM && ratio > BALLTOOTHIN && ratio < BALLTOOFAT)) { } else if (ratio > THINBALL && ratio < FATBALL) { } else if (y + h > IMAGE_HEIGHT - IMAGE_EDGE || x == 0 || (x + w) > IMAGE_WIDTH - 2 || y == 0) { if (BALLDEBUG) cout << "Checking ratio on occluded ball: " << ratio << endl; // we're on an edge so allow for streching if (h > BIG_ENOUGH && w > BIG_ENOUGH && (y + h > IMAGE_HEIGHT - 2 || y == 0) && ratio < MIDFAT && ratio > 1) { // then sides } else if (h > BIG_ENOUGH && w > BIG_ENOUGH && (x == 0 || x + w > IMAGE_WIDTH - 2) && ratio > MIDTHIN && ratio < 1) { } else if ((h > TOO_BIG_TO_CHECK || w > TOO_BIG_TO_CHECK) && (ratio > OCCLUDEDTHIN && ratio < OCCLUDEDFAT) ) { // when we have big slivers then allow for extra } else if (b.getLeftBottomY() > IMAGE_HEIGHT - IMAGE_EDGE && w > WIDTH_AT_SCREEN_BOTTOM) { // the bottom is a really special case } else { if (BALLDEBUG) //cout << "Screening for ratios" << endl; return BAD_VALUE; } } else { if (BALLDEBUG) { drawBlob(b, BLACK); printBlob(b); cout << "Screening for ratios " << ratio << endl; } return BAD_VALUE; } if (ratio < 1.0) { int offRat = ROUND2((1.0f - ratio) * RATIO_TO_INT); r -= offRat; } else { int offRat = ROUND2((1.0f - 1.0f/ratio) * RATIO_TO_INT); r -= offRat; } if (w * h > SMALLBALL) { // now make some scans through the blob - horizontal, vertical, diagonal int pix; int goodPix = 0, badPix = 0; if (y + h > IMAGE_HEIGHT - IMAGE_EDGE || x == 0 || (x + w) > IMAGE_WIDTH - 2 || y == 0) { } else { // we're in the screen int d = ROUND2(static_cast<float>(std::max(w, h)) / CORNER_CHUNK_DIV); int d3 = min(w, h); for (int i = 0; i < d3; i++) { pix = thresh->thresholded[y+i][x+i]; if (i < d || (i > d3 - d)) { if (pix == ORANGE || pix == ORANGERED) { //drawPoint(x+i, y+i, BLACK); badPix++; } else goodPix++; } else { if (pix == ORANGE || pix == ORANGERED || pix == ORANGEYELLOW) goodPix++; else if (pix != GREY) { badPix++; //drawPoint(x+i, y+i, PINK); } } pix = thresh->thresholded[y+i][x+w-i]; if (i < d || (i > d3 - d)) { if (pix == ORANGE || pix == ORANGERED) { //drawPoint(x+w-i, y+i, BLACK); badPix++; } else goodPix++; } else if (pix == ORANGE || pix == ORANGERED || pix == ORANGEYELLOW) goodPix++; else if (pix != GREY) { badPix++; //drawPoint(x+w-i, y+i, BLACK); } } //cout << "here" << endl; for (int i = 0; i < h; i++) { pix = thresh->thresholded[y+i][x + w/2]; //drawPoint(x + w/2, y+i, BLACK); if (pix == ORANGE || pix == ORANGERED || pix == ORANGEYELLOW) { goodPix++; } else if (pix != GREY) badPix++; } } for (int i = 0; i < w; i++) { pix = thresh->thresholded[y+h/2][x + i]; //drawPoint(x+i, y+h/2, BLACK); if (pix == ORANGE || pix == ORANGERED || pix == ORANGEYELLOW) { goodPix++; } else if (pix != GREY) badPix++; } if (BALLDEBUG) cout << "Roundness: Good " << goodPix << " " << badPix << endl; // if more than 20% or so of our pixels tested are bad, then we toss it out if (goodPix < badPix * 5) { if (BALLDEBUG) cout << "Screening for bad roundness" << endl; return BAD_VALUE; } } return 0; }