void Cross::checkForCrosses() { // TODO: These were thrown together in an hour. They should // be more rigorously determined const int maxWidth = 75; const int maxHeight = 75; const int minWidth = 5; const int minHeight = 5; const int maxRatio = 5; // loop through all the blobs and test the ones that are the right // basic size for (int i = 0; i < blobs->number(); i++) { Blob candidate = blobs->get(i); if (CROSSDEBUG) { cout << "Blob " << candidate.width() << " " << candidate.height() << endl; cout << "Coords " << candidate.getLeft() << " " << candidate.getTop() << endl; vision->drawRect(candidate.getLeft(), candidate.getTop(), candidate.width(), candidate.height(), BLACK); } if (candidate.width() < maxWidth && candidate.height() < maxHeight && candidate.width() > minWidth && candidate.height() > minHeight && candidate.width() < maxRatio * candidate.height() && candidate.height() < maxRatio * candidate.width()) { checkForX(candidate); } } }
/* Checks out how much of the blob is of the right color. * If it is enough returns true, if not false. * @param tempobj the cross we're checking * @param minpercent how good it needs to be * @return was it good enough? */ bool Cross::rightBlobColor(Blob tempobj, float minpercent) { int x = tempobj.getLeft(); int y = tempobj.getTop(); int spanX = tempobj.width(); int spanY = tempobj.height(); if (spanX < 1 || spanY < 1) return false; int ny, nx, starty, startx; int good = 0, total = 0; for (int i = 0; i < spanY; i++) { starty = y + i; startx = x; for (int j = 0; j < spanX; j++) { nx = startx + j; ny = starty; if (ny > -1 && nx > -1 && ny < IMAGE_HEIGHT && nx < IMAGE_WIDTH) { total++; if (thresh->thresholded[ny][nx] == WHITE) { good++; } } } } float percent = (float)good / (float) (total); if (percent > minpercent) { return true; } return false; }
/* Are two blobs close enough that they might actually be the same? Note: if the area between is bigger than one of the blobs then can it? */ bool Robots::closeEnough(int i, int j) { Blob a = blobs->get(i); Blob b = blobs->get(j); int width1 = a.width(); int width2 = b.width(); int height1 = a.height(); int height2 = b.height(); int bigWidth = max(width1, width2); int bigHeight = max(height1, height2); int dist1 = distance(a.getLeft(), a.getRight(), b.getLeft(), b.getRight()); int dist2 = distance(a.getTop(), a.getBottom(), b.getTop(), b.getBottom()); if (a.getRight() <= 0 || b.getRight() <= 0) { return false; } if (dist1 > 5 && dist2 > 5) { return false; } if (dist1 < bigHeight && dist2 < bigWidth) { return true; } return false; }
//attempts to find robots where no uniform is visible //NOTE: currently dangerous in that it gives false positives. needs substantial work void Robots::findRobotParts(){ //merge overlapping white blobs into a single blob for (int i = 0; i < whiteBlobs->number(); i++){ for (int j = 0; j < whiteBlobs->number(); j++){ if (i == j) continue; if (whiteBlobs->blobsOverlap(i, j)){ whiteBlobs->mergeBlobs(i, j); } } } //sort the white blobs in descending order by size whiteBlobs->sort(); for (int i = 0; i < whiteBlobs->number(); i++){ bool aligned = false; bool side = false; //true if the blob is at the edge of the screen. For now we are only looking //at these blobs, since looking at all blobs leads to too many false positives if (whiteBlobs->get(i).getLeft() <= 10 || whiteBlobs->get(i).getRight() >= IMAGE_WIDTH-10 || whiteBlobs->get(i).getTop() <= 10 || whiteBlobs->get(i).getBottom() >= IMAGE_HEIGHT-10) { side = true; } if (side){ //checks if the white blob is aligned with a robot of known team. //if it is, we have already indentified this blob for (int j = 0; j < blobs->number(); j++){ if (whiteBlobs->get(i).isAligned(blobs->get(j))) { aligned = true; break; } } } //true if we have found a potential robot part if (!aligned && side) { Blob part = correctBlob(whiteBlobs->get(i)); //for now just outlines the region we found. Once this method is functional, //it should also call updateRobots in some manner vision->drawRect(part.getLeft(), part.getTop(), part.width(), part.height(), WHITE); } } }
/* Given a viable uniform blob, add all vertically alligned white blobs to create an "area of interest" for where to more rigorously determine where a robot is @param robotBlob potential uniform blob @return blob indicating an area of interest for robot detection */ Blob Robots::createAreaOfInterest(Blob robotBlob) { int left = robotBlob.getLeft(); int right = robotBlob.getRight(); int top = robotBlob.getTop(); int bottom = robotBlob.getBottom(); int height = robotBlob.height(); int width = right-left; for (int i = 0; i < whiteBlobs->number(); i++){ if (whiteBlobs->get(i).isAligned(robotBlob)) { robotBlob.merge(whiteBlobs->get(i)); } } //places constraints the on the area of interest with knowledge of the relative //ratio of the dimensions of the robot to the dimensions of its uniform //NOTE: this ratio could be more accurately defined if (robotBlob.getLeft() < left-width) robotBlob.setLeft(left-width); if (robotBlob.getRight() > right+width) robotBlob.setRight(right+width); if (robotBlob.getTop() < top-5*height) robotBlob.setLeft(top-5*height); if (robotBlob.getBottom() > bottom+6*height) robotBlob.setBottom(bottom+6*height); return robotBlob; }
/* When we are looking down, the shadowed carpet often has lots of Navy. Make sure we aren't just looking at carpet */ bool Robots::notGreen(Blob candidate) { int bottom = candidate.getBottom(); int top = candidate.getTop(); int left = candidate.getLeft(); int right = candidate.getRight(); int area = candidate.width() * candidate.height() / 5; int greens = 0; for (int i = left; i < right; i++) { for (int j = top; j < bottom; j++) { if (Utility::isGreen(thresh->getThresholded(j, i))) { greens++; if (greens > area) { return true; } } } } return false; }
/* We have a swatch of color. Let's make sure that there is some white around somewhere as befits our robots. */ bool Robots::noWhite(Blob b) { const int MINWHITE = 5; int left = b.getLeft(), right = b.getRight(); int top = b.getTop(), bottom = b.getBottom(); int width = b.width(); int tops, bottoms; for (int i = 1; i < b.height(); i++) { tops = 0; bottoms = 0; for (int x = left; x <= right; x++) { if (top - i >= 0 && Utility::isWhite(thresh->getThresholded(top - i,x))) tops++; if (bottom + i < IMAGE_HEIGHT && Utility::isWhite(thresh->getThresholded(bottom+i,x))) bottoms++; if (tops > width / 2 || tops == width) return false; if (bottoms > width / 2 || tops == width) return false; } } return true; }
/* Since the white blob check catches a lot of extra stuff like lines, we need to check a bit more carefully. */ bool Robots::whiteAbove(Blob candidate) { int top = candidate.getTop(); int height = candidate.height(); int scanline = top - height; for (int y = scanline; y > 0 && y > scanline - height; y -= 3) { int white = 0; int green = 0; for (int x = candidate.getLeft(); x < candidate.getRight(); x++) { if (Utility::isWhite(thresh->getThresholded(y, x))) { white++; } else if (Utility::isGreen(thresh->getThresholded(y, x))) { green++; } } if (green > candidate.width() / 2 && white == 0) { return false; } if (white > candidate.width() / 4) { return true; } } return false; }
/* Since the white blob check catches a lot of extra stuff like lines, we need to check a bit more carefully. */ bool Robots::whiteBelow(Blob candidate) { int bottom = candidate.getBottom(); int height = candidate.height(); int scanline = bottom + height; for (int y = scanline; y < IMAGE_HEIGHT && y < scanline + height; y += 3) { int white = 0; int green = 0; for (int x = candidate.getLeft(); x < candidate.getRight(); x++) { if (Utility::isWhite(thresh->getThresholded(y, x))) { white++; } else if (Utility::isGreen(thresh->getThresholded(y, x))) { green++; } } if (green > candidate.width() / 2 && white == 0) { return false; } if (white > candidate.width() / 4) { return true; } } return false; }
void Cross::createObject() { // TODO: These were thrown together in an hour. They should // be more rigorously determined const int maxWidth = 75; const int maxHeight = 75; const int minWidth = 5; const int minHeight = 5; const int maxRatio = 5; // do basic run-length encoding if (numberOfRuns > 1) { for (int i = 0; i < numberOfRuns; i++) { // search for contiguous blocks int nextX = runs[i].x; int nextY = runs[i].y; int nextH = runs[i].h; blobs->blobIt(nextX, nextY, nextH); } } if (CROSSDEBUG) cout << blobs->number() << " white blobs" << endl; // loop through all the blobs and test the ones that are the right // basic size for (int i = 0; i < blobs->number(); i++) { Blob candidate = blobs->get(i); if (CROSSDEBUG) { cout << "Blob " << candidate.width() << " " << candidate.height() << endl; cout << "Coords " << candidate.getLeft() << " " << candidate.getTop() << endl; } if (candidate.width() < maxWidth && candidate.height() < maxHeight && candidate.width() > minWidth && candidate.height() > minHeight && candidate.width() < maxRatio * candidate.height() && candidate.height() < maxRatio * candidate.width()) { checkForX(candidate); } } }
/* We have two blobs that are reasonably close. See if they should be merged. */ void Robots::checkMerge(int i, int j) { Blob a = blobs->get(i); Blob b = blobs->get(j); if (debugRobots) { cout << endl << endl << "Checking merge" << endl; printBlob(a); printBlob(b); } int dist1 = distance(a.getLeft(), a.getRight(), b.getLeft(), b.getRight()); int dist2 = distance(a.getTop(), a.getBottom(), b.getTop(), b.getBottom()); int green = 0; int col = 0; int miss = 0; // top to bottom defines the y region between the two int top = max(a.getTop(), b.getTop()); int bottom = min(a.getBottom(), b.getBottom()); int midx = (a.getLeft() + a.getRight()) / 2; // left to right defines the x region between the two int left = a.getRight(); int right = b.getLeft(); int midy = (a.getTop() + a.getBottom()) / 2; int ii; // check if they overlap in either dimension if (left > right) { // they overlap in x so we'll swap left and right int temp = left; left = right; right = temp; } if (top > bottom) { int temp = top; top = bottom; bottom = temp; } int width = right - left + 1; int height = bottom - top + 1; int area = max(10, width * height / 27); int stripe = max(width, height); for (int x = left; x < right; x+=3) { for (int y = top; y < bottom; y+=3) { if (debugRobots) { vision->drawPoint(x, y, MAROON); } if (Utility::colorsEqual(thresh->getThresholded(y, x), color)) { col++; if (col > area || col > stripe) { blobs->mergeBlobs(i, j); if (debugRobots) { cout << "Merge" << endl; } return; } } else if (Utility::isGreen(thresh->getThresholded(y, x))) { green++; if (green > 5) { return; } } else if (Utility::isWhite(thresh->getThresholded(y, x))) { miss++; if (miss > area / 9 || miss > stripe) { return; } } } } if (col > min(width, height) && green < 3) { blobs->mergeBlobs(i, j); if (debugRobots) { cout << "Merge" << endl; } } }
//correct the area of interest to a reasonable estimate of the robot Blob Robots::correctBlob(Blob area){ int left = max(0, area.getLeft()); int right = min(area.getRight(), IMAGE_WIDTH); int top = max(0, area.getTop()); int bottom = min(area.getBottom(), IMAGE_HEIGHT); int width = right - left; int height = bottom - top; int nonRobot = 0; //uncomment for debugging of corrections. Drawn rect is blob before correction //vision->drawRect(left,top, width, height, WHITE); //correct left side for (int x = left; x < left + (width/2); x++) { for (int y = top; y < bottom; y++) { if (Utility::isGreen(thresh->getThresholded(y,x)) || Utility::isOrange(thresh->getThresholded(y,x))) { nonRobot++; } if (nonRobot > .4*height) { area.setLeft(x+1); nonRobot = -1; break; } } if (nonRobot != -1) break; nonRobot = 0; } //correct right side for (int x = right; x > left + (width/2); x--) { for (int y = top; y < bottom; y++) { if (Utility::isGreen(thresh->getThresholded(y,x)) || Utility::isOrange(thresh->getThresholded(y,x))) { nonRobot++; } if (nonRobot > .4*height) { area.setRight(x-1); nonRobot = -1; break; } } if (nonRobot != -1) break; nonRobot = 0; } //correct top for (int y = top; y < top + (height/2); y++) { for (int x = left; x < right; x++) { if (Utility::isGreen(thresh->getThresholded(y,x)) || Utility::isOrange(thresh->getThresholded(y,x))) { nonRobot++; }; if (nonRobot > .4*width) { area.setTop(y+1); nonRobot = -1; break; } } if (nonRobot != -1) break; nonRobot = 0; } //correct bottom for (int y = bottom; y > top + (height/2); y--) { for (int x = left; x < right; x++) { if (Utility::isGreen(thresh->getThresholded(y,x)) || Utility::isOrange(thresh->getThresholded(y,x))) { nonRobot++; } if (nonRobot > .4*width) { area.setBottom(y-1); nonRobot = -1; break; } } if (nonRobot != -1) break; nonRobot = 0; } return area; }
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; }
/* Returns true is the blob abuts any image edge @param b the blob to check @return true when the blob is near an edge */ bool Ball::nearEdge(Blob b) { return nearImageEdgeX(b.getLeft(), 1) || nearImageEdgeX(b.getRight(), 1) || nearImageEdgeY(b.getTop(), 1) || nearImageEdgeY(b.getBottom(), 2); }