/* * Filter for L-shaped contours */ void PlaygroundDetector::filterContours(const Contours &contours, Contours &filteredContours) const { filteredContours.clear(); for(unsigned i=0; i<contours.size(); i++) { if(contours[i].size() > 5) { double area = cv::contourArea(contours[i]); if(area > 100 && area < 3000) { // smooth contour std::vector<cv::Point> approxCurve; cv::approxPolyDP(contours[i], approxCurve, double ( contours[i].size() ) *0.025 , true ); if(approxCurve.size() != 6) continue; std::vector<cv::Point> hull; cv::convexHull(approxCurve, hull); float convexity = area / cv::contourArea(hull); if(convexity > 0.3f && convexity < 0.7f && hull.size() == 5) { filteredContours.push_back(approxCurve); //std::cout << "Area:" << area << " Convexity:" << convexity << std::endl; } } } } }
cv::Mat connectedComponentsFilter(cv::Mat& curFrame, cv::Mat& img) { if (!img.isContinuous()) { throwError("Parammeter 'img' in 'connectedComponentsFilter' must be continuous"); } //morphology_(img); maskContours.clear(); // Отрисовать найденные области обратно в маску cv::Mat result(img.size(), img.type()); result.setTo(0); cv::findContours(img, maskContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); size_t i = 0; while (i < maskContours.size()) { Contour& contour = maskContours[i]; cv::Mat contourMat(contour, false); double len = cv::arcLength(contourMat, true); if (len * PERIM_SCALE < img.size().height + img.size().width) { // Отбрасываем контуры со слишком маленьким периметром. maskContours.erase(maskContours.begin() + i); } else { // Достаточно большие контуры аппроксимируем указанным методом. Contour newContour; // Методы аппроксимации. //cv::approxPolyDP(contourMat, newContour, CHAIN_APPROX_SIMPLE, true); cv::convexHull(contourMat, newContour, true); cv::Mat newContourMat(newContour, false); Rect boundingRect = cv::boundingRect(newContourMat); cv::rectangle(curFrame, boundingRect, cv::Scalar(255)); maskContours[i] = newContour; i++; //points.push_back(CvPoint(boundingRect.x + boundingRect.width / 2, boundingRect.y + boundingRect.height / 2)); } } if (!maskContours.empty()) { // Обходим баг OpenCV 2.1.0; в 2.3.1 он уже исправлен. cv::drawContours(result, maskContours, -1, cv::Scalar(255), FILLED); } return result; }
/* * Find Playground in image and calc its Extrinsics */ bool PlaygroundDetector::detect(const cv::Mat &image, Playground &playground, Contours &candidateContours, aruco::CameraParameters &cameraParameters) { // pg not valid playground.id = -1; // get all contours from color thres image Contours contours; findContours(image, contours); // search for L shaped contours filterContours(contours, candidateContours); if(candidateContours.size() < 4) return false; // combine exatly 4 L-contours to one rectangle with 4 corners std::vector<cv::Point2f> corners; // max length: 4 extractPlayGroundCorners(candidateContours, corners); if(corners.size() != 4) return false; playground.resize(4); std::copy(corners.begin(), corners.end(), playground.begin()); // playground valid playground.id = 0; // calc translation and rotation playground.calculateExtrinsics(cameraParameters); return true; }
Matrix Matrix::mask(Contours contours) { Matrix masked = clone(); if (contours.size() > 0) { // Create mask cv::Mat mask = cv::Mat::zeros(size(), CV_8UC1); // Draw contours onto the mask so they are not removed for (int i = 0; i < contours.size(); ++i) { cv::drawContours(mask, contours, i, cv::Scalar(255), CV_FILLED); } masked = cv::Mat(cv::Mat::zeros(size(), type())); copyTo(masked, mask); } return masked; }
Matrix Matrix::crop(Contours contours) { Matrix cropped = cv::Mat(cv::Mat::zeros(1, 1, type())); if (contours.size() > 0) { // Determine ROI int minX = INT_MAX, minY = INT_MAX, maxX = INT_MIN, maxY = INT_MIN; for (int i = 0; i < contours.size(); ++i) { cv::Rect rect = cv::boundingRect(cv::Mat(contours[i])); if (rect.x < minX) minX = rect.x; if (rect.y < minY) minY = rect.y; if (rect.x + rect.width > maxX) maxX = rect.x + rect.width; if (rect.y + rect.height > maxY) maxY = rect.y + rect.height; } cropped = (*this)(cv::Rect(minX, minY, maxX - minX, maxY - minY)).clone(); } return cropped; }
/* * Draw playground, color according to lock / newly detected */ void HUD::drawPlayground(const Playground &playground, const Contours &candidateContours, bool highlight, bool pglock) { // show detected single edges if(candidateContours.size() > 0) { cv::drawContours(image, candidateContours, -1, cv::Scalar(0,0,255), 1); } cv::Scalar color = (pglock) ? cv::Scalar(0,255,0) : cv::Scalar(0,134,209); int lineSize = (highlight) ? 4 : 1; playground.draw(image,color,lineSize); }
bool closeSurface( fwVertexPosition &_vertex, fwVertexIndex &_vertexIndex ) { typedef std::pair< int, int > Edge; typedef std::vector< Edge > Contour; // at Border typedef std::vector< Contour> Contours; Contours contours; findBorderEdges( _vertexIndex , contours); bool closurePerformed = !contours.empty() ; // close each hole for ( Contours::iterator contour=contours.begin(); contour != contours.end(); ++contour ) { int newVertexIndex = _vertex.size() ; // create gravity point & insert new triangle std::vector< float > massCenter(3,0); for ( Contour::iterator edge =contour->begin(); edge != contour->end(); ++edge ) { for (int i=0; i<3; ++i ) { massCenter[i] += _vertex[edge->first][i]; massCenter[i] += _vertex[edge->second][i]; } // create new Triangle std::vector< int > triangleIndex(3); triangleIndex[0] = edge->first; triangleIndex[1] = edge->second; triangleIndex[2] = newVertexIndex; _vertexIndex.push_back( triangleIndex ); // TEST } for (int i=0; i<3; ++i ) { massCenter[i] /= contour->size()*2; } _vertex.push_back( massCenter ); // normalize barycenter } return closurePerformed; }
void Calibration::Start() { Utility::Logger* logger = Utility::Logger::GetInstance(); logger->SetFileLoggingState(false); logger->Log(Utility::Logger::LOG_MESSAGE, "Calibration: Welcome to the camera calibration! Please hold an odd checkerboard or KB-06 techboard in front of the camera. Press G to start the calibration!"); // Return empty if this isn't open if (!m_settings->GetIsOpenedAndGood()) { return; } const char KEY_ESC = 27; const char KEY_U = 'u'; const char KEY_G = 'g'; State mode = m_settings->GetInputType() == CalibrationSettings::InputType::IMAGE_LIST ? State::CAPTURING : State::DETECTION; cv::Size imageSize; cv::Mat cameraMatrix; cv::Mat distortionCoefficients; Contours contours; char key; bool isRunning = true; clock_t timestamp = 0; while (isRunning) { cv::Mat image; FetchNextImage(image); if (!image.empty()) { bool blinkOutput = false; imageSize = image.size(); if (m_settings->GetInputFlipHorizontal()) { cv::flip(image, image, 0); } if (mode == State::CAPTURING && contours.size() >= static_cast<unsigned int>(m_settings->GetNumberOfFrames())) { if (RunAndSaveCalibration(imageSize, cameraMatrix, distortionCoefficients, contours)) { mode = State::CALIBRATED; } else { mode = State::DETECTION; } } bool found = false; Corners corners; switch (m_settings->GetCalibrationPattern()) { case CalibrationSettings::Pattern::CHESSBOARD: found = cv::findChessboardCorners(image, m_settings->GetBoardSize(), corners, (CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE)); break; case CalibrationSettings::Pattern::CIRCLES_GRID: found = cv::findCirclesGrid(image, m_settings->GetBoardSize(), corners); break; case CalibrationSettings::Pattern::ASYMMETRIC_CIRCLES_GRID: found = cv::findCirclesGrid(image, m_settings->GetBoardSize(), corners, CALIB_CB_ASYMMETRIC_GRID); break; } if (found) { // improve the found corners' coordinate accuracy for chessboard if (m_settings->GetCalibrationPattern() == CalibrationSettings::Pattern::CHESSBOARD) { cv::Mat gray; cv::cvtColor(image, gray, CV_BGR2GRAY); cv::cornerSubPix(gray, corners, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); } // For camera only take new samples after delay time if (mode == State::CAPTURING && ((clock() - timestamp) > (m_settings->GetInputDelay() * 1e-3 * CLOCKS_PER_SEC))) { contours.push_back(corners); blinkOutput = m_capture.isOpened(); // Up the current frame m_currentFrame++; } // Draw the corners. cv::drawChessboardCorners(image, m_settings->GetBoardSize(), cv::Mat(corners), found); } // Check if we need to blink output if (blinkOutput) { cv::bitwise_not(image, image); } // Check if we can undistort output if (mode == State::CALIBRATED && m_settings->GetShowUndistortedImage()) { cv::undistort(image.clone(), image, cameraMatrix, distortionCoefficients); } // Show image cv::imshow("TEST", image); // Delay + capture key key = static_cast<char>(cv::waitKey(m_capture.isOpened() ? 50 : m_settings->GetInputDelay())); // Check for esc key if (key == KEY_ESC) { isRunning = false; } // Check for u key else if (key == KEY_U) { m_settings->SetShowUndistortedImage(!m_settings->GetShowUndistortedImage()); } // Check for k key else if (key == KEY_G && m_capture.isOpened()) { mode = State::CAPTURING; contours.clear(); } } else { if (contours.size() > 0) { RunAndSaveCalibration(imageSize, cameraMatrix, distortionCoefficients, contours); isRunning = false; } } } }
Mat ScreenDetector::getTransformationMatrix(Error& error) { bool approxFound = false; // convert image to HSV cvtColor(img, hsv, CV_BGR2HSV); // threshold the image inRange(hsv, hsvMin, hsvMax, thresholded); // Optimize threshold by reducing noise erode(thresholded, thresholded, getStructuringElement(MORPH_ELLIPSE, Size(erodeDilateSize, erodeDilateSize)) ); dilate( thresholded, thresholded, getStructuringElement(MORPH_ELLIPSE, Size(erodeDilateSize, erodeDilateSize)) ); dilate( thresholded, thresholded, getStructuringElement(MORPH_ELLIPSE, Size(erodeDilateSize, erodeDilateSize)) ); erode(thresholded, thresholded, getStructuringElement(MORPH_ELLIPSE, Size(erodeDilateSize, erodeDilateSize)) ); GaussianBlur(thresholded, thresholded, Size(3,3), 0); Mat forContours; thresholded.copyTo(forContours); // find all contours Contours contours; Contour approximatedScreen; findContours(forContours, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); int nbContours = contours.size(); cout << nbContours << " contours found, debug: " << DEBUG << endl; if(nbContours == 0) { error.setError("Unable to find the screen", "The camera doesn't detect any screen or green element." "Please check if your screen is turned on and directed toward the screen"); return img; } sort(contours.begin(), contours.end(), contour_compare_area); // find the contour with the biggest area that have 4 points when approximated for(int i=0; i < nbContours; ++i) { approxPolyDP(contours.at(i), approximatedScreen, approximateEpsilon * arcLength(contours.at(i), true), true); // our screen has 4 point when approximated if(approximatedScreen.size() == 4) { approxFound = true; break; } } if(!approxFound) { error.setError("Unable to find the screen properly", "It seems that the screen is not fully detectable by the camera. Try to reduce light in your room"); return img; } if(DEBUG) { namedWindow("debug", WINDOW_KEEPRATIO); namedWindow("thresholded_calibration", WINDOW_KEEPRATIO); Mat debug = Mat::zeros(img.rows, img.cols, CV_8UC3); polylines(debug, approximatedScreen, true, Scalar(0,0,255), 3); imshow("debug", debug); imshow("thresholded_calibration", thresholded); } return transformImage(approximatedScreen); }
void StripIdsFromContours(const ContoursWithIds &contoursWithIds, Contours &contoursNoIds) { contoursNoIds.resize(contoursWithIds.size()); for(unsigned int j=0; j< contoursWithIds.size(); j++) StripIdsFromContour(contoursWithIds[j], contoursNoIds[j]); }