MatchingPointList OpenCVAlgorithms::fastMatchTemplate(const QList<cv::Mat> &sources, const cv::Mat &target, int matchPercentage, int maximumMatches, int downPyrs, int searchExpansion, AlgorithmMethod method) { MatchingPointList matchingPointList; int sourceIndex = 0; foreach(const cv::Mat &source, sources) { try { // create copies of the images to modify cv::Mat copyOfSource = source.clone(); cv::Mat copyOfTarget = target.clone(); cv::Size sourceSize = source.size(); cv::Size targetSize = target.size(); // down pyramid the images for(int ii = 0; ii < downPyrs; ii++) { // start with the source image sourceSize.width = (sourceSize.width + 1) / 2; sourceSize.height = (sourceSize.height + 1) / 2; cv::Mat smallSource(sourceSize, source.type()); cv::pyrDown(copyOfSource, smallSource); // prepare for next loop, if any copyOfSource = smallSource.clone(); // next, do the target targetSize.width = (targetSize.width + 1) / 2; targetSize.height = (targetSize.height + 1) / 2; cv::Mat smallTarget(targetSize, target.type()); pyrDown(copyOfTarget, smallTarget); // prepare for next loop, if any copyOfTarget = smallTarget.clone(); } // perform the match on the shrunken images cv::Size smallTargetSize = copyOfTarget.size(); cv::Size smallSourceSize = copyOfSource.size(); cv::Size resultSize; resultSize.width = smallSourceSize.width - smallTargetSize.width + 1; resultSize.height = smallSourceSize.height - smallTargetSize.height + 1; cv::Mat result(resultSize, CV_32FC1); cv::matchTemplate(copyOfSource, copyOfTarget, result, toOpenCVMethod(method)); // find the top match locations QVector<QPoint> locations = multipleMinMaxLoc(result, maximumMatches, method); // search the large images at the returned locations sourceSize = source.size(); targetSize = target.size(); int twoPowerNumDownPyrs = std::pow(2.0f, downPyrs); // create a copy of the source in order to adjust its ROI for searching for(int currMax = 0; currMax < maximumMatches; ++currMax) { // transform the point to its corresponding point in the larger image QPoint &currMaxLocation = locations[currMax]; currMaxLocation *= twoPowerNumDownPyrs; currMaxLocation.setX(currMaxLocation.x() + targetSize.width / 2); currMaxLocation.setY(currMaxLocation.y() + targetSize.height / 2); const QPoint &searchPoint = locations.at(currMax); // if we are searching for multiple targets and we have found a target or // multiple targets, we don't want to search in the same location(s) again if(maximumMatches > 1 && !matchingPointList.isEmpty()) { bool thisTargetFound = false; for(int currPoint = 0; currPoint < matchingPointList.size(); currPoint++) { const QPoint &foundPoint = matchingPointList.at(currPoint).position; if(std::abs(searchPoint.x() - foundPoint.x()) <= searchExpansion * 2 && std::abs(searchPoint.y() - foundPoint.y()) <= searchExpansion * 2) { thisTargetFound = true; break; } } // if the current target has been found, continue onto the next point if(thisTargetFound) continue; } // set the source image's ROI to slightly larger than the target image, // centred at the current point cv::Rect searchRoi; searchRoi.x = searchPoint.x() - (target.size().width) / 2 - searchExpansion; searchRoi.y = searchPoint.y() - (target.size().height) / 2 - searchExpansion; searchRoi.width = target.size().width + searchExpansion * 2; searchRoi.height = target.size().height + searchExpansion * 2; // make sure ROI doesn't extend outside of image if(searchRoi.x < 0) searchRoi.x = 0; if(searchRoi.y < 0) searchRoi.y = 0; if((searchRoi.x + searchRoi.width) > (sourceSize.width - 1)) { int numPixelsOver = (searchRoi.x + searchRoi.width) - (sourceSize.width - 1); searchRoi.width -= numPixelsOver; } if((searchRoi.y + searchRoi.height) > (sourceSize.height - 1)) { int numPixelsOver = (searchRoi.y + searchRoi.height) - (sourceSize.height - 1); searchRoi.height -= numPixelsOver; } cv::Mat searchImage(source, searchRoi); // perform the search on the large images resultSize.width = searchRoi.width - target.size().width + 1; resultSize.height = searchRoi.height - target.size().height + 1; result = cv::Mat(resultSize, CV_32FC1); cv::matchTemplate(searchImage, target, result, toOpenCVMethod(method)); // find the best match location double minValue; double maxValue; cv::Point minLoc; cv::Point maxLoc; cv::minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc); double &value = (method == SquaredDifferenceMethod) ? minValue : maxValue; cv::Point &loc = (method == SquaredDifferenceMethod) ? minLoc : maxLoc; value *= 100.0; // transform point back to original image loc.x += searchRoi.x + target.size().width / 2; loc.y += searchRoi.y + target.size().height / 2; if(method == SquaredDifferenceMethod) value = 100.0f - value; if(value >= matchPercentage) { // add the point to the list matchingPointList.append(MatchingPoint(QPoint(loc.x, loc.y), value, sourceIndex)); // if we are only looking for a single target, we have found it, so we // can return if(maximumMatches <= 1) break; } else break; // skip the rest } } catch(const cv::Exception &e) { mError = OpenCVException; mErrorString = tr("OpenCV exception: %1").arg(e.what()); return MatchingPointList(); } ++sourceIndex; } return matchingPointList; }
//============================================================================= // Assumes that source image exists and numDownPyrs > 1, no ROIs for either // image, and both images have the same depth and number of channels bool CFastMatchTemplate::FastMatchTemplate( const Mat& source, const Mat& target, vector<Point>* foundPointsList, vector<double>* confidencesList, int matchPercentage, bool findMultipleTargets, int numMaxima, int numDownPyrs, int searchExpansion ) { // make sure that the template image is smaller than the source if(target.size().width > source.size().width || target.size().height > source.size().height) { printf( "\nSource image must be larger than target image.\n" ); return false; } if(source.depth() != target.depth()) { printf( "\nSource image and target image must have same depth.\n" ); return false; } if(source.channels() != target.channels()) { printf("%d %d\n",source.channels() , target.channels()); printf("\nSource image and target image must have same number of channels.\n" ); return false; } Size sourceSize = source.size(); Size targetSize = target.size(); // create copies of the images to modify Mat copyOfSource = source.clone(); Mat copyOfTarget = target.clone(); // down pyramid the images for(int ii = 0; ii < numDownPyrs; ii++) { // start with the source image sourceSize.width = (sourceSize.width + 1) / 2; sourceSize.height = (sourceSize.height + 1) / 2; Mat smallSource(sourceSize, source.type()); pyrDown(copyOfSource, smallSource); // prepare for next loop, if any copyOfSource = smallSource.clone(); // next, do the target targetSize.width = (targetSize.width + 1) / 2; targetSize.height = (targetSize.height + 1) / 2; Mat smallTarget(targetSize, target.type()); pyrDown(copyOfTarget, smallTarget); // prepare for next loop, if any copyOfTarget = smallTarget.clone(); } // perform the match on the shrunken images Size smallTargetSize = copyOfTarget.size(); Size smallSourceSize = copyOfSource.size(); Size resultSize; resultSize.width = smallSourceSize.width - smallTargetSize.width + 1; resultSize.height = smallSourceSize.height - smallTargetSize.height + 1; Mat result(resultSize, CV_32FC1); matchTemplate(copyOfSource, copyOfTarget, result, CV_TM_CCOEFF_NORMED); // find the top match locations Point* locations = NULL; MultipleMaxLoc(result, &locations, numMaxima); // search the large images at the returned locations sourceSize = source.size(); targetSize = target.size(); // create a copy of the source in order to adjust its ROI for searching for(int currMax = 0; currMax < numMaxima; currMax++) { // transform the point to its corresponding point in the larger image locations[currMax].x *= (int)pow(2.0f, numDownPyrs); locations[currMax].y *= (int)pow(2.0f, numDownPyrs); locations[currMax].x += targetSize.width / 2; locations[currMax].y += targetSize.height / 2; const Point& searchPoint = locations[currMax]; // if we are searching for multiple targets and we have found a target or // multiple targets, we don't want to search in the same location(s) again if(findMultipleTargets && !foundPointsList->empty()) { bool thisTargetFound = false; int numPoints = foundPointsList->size(); for(int currPoint = 0; currPoint < numPoints; currPoint++) { const Point& foundPoint = (*foundPointsList)[currPoint]; if(abs(searchPoint.x - foundPoint.x) <= searchExpansion * 2 && abs(searchPoint.y - foundPoint.y) <= searchExpansion * 2) { thisTargetFound = true; break; } } // if the current target has been found, continue onto the next point if(thisTargetFound) { continue; } } // set the source image's ROI to slightly larger than the target image, // centred at the current point Rect searchRoi; searchRoi.x = searchPoint.x - (target.size().width) / 2 - searchExpansion; searchRoi.y = searchPoint.y - (target.size().height) / 2 - searchExpansion; searchRoi.width = target.size().width + searchExpansion * 2; searchRoi.height = target.size().height + searchExpansion * 2; // make sure ROI doesn't extend outside of image if(searchRoi.x < 0) { searchRoi.x = 0; } if(searchRoi.y < 0) { searchRoi.y = 0; } if((searchRoi.x + searchRoi.width) > (sourceSize.width - 1)) { int numPixelsOver = (searchRoi.x + searchRoi.width) - (sourceSize.width - 1); searchRoi.width -= numPixelsOver; } if((searchRoi.y + searchRoi.height) > (sourceSize.height - 1)) { int numPixelsOver = (searchRoi.y + searchRoi.height) - (sourceSize.height - 1); searchRoi.height -= numPixelsOver; } Mat searchImage = Mat(source, searchRoi); // perform the search on the large images resultSize.width = searchRoi.width - target.size().width + 1; resultSize.height = searchRoi.height - target.size().height + 1; result = Mat(resultSize, CV_32FC1); matchTemplate(searchImage, target, result, CV_TM_CCOEFF_NORMED); // find the best match location double minValue, maxValue; Point minLoc, maxLoc; minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc); maxValue *= 100; // transform point back to original image maxLoc.x += searchRoi.x + target.size().width / 2; maxLoc.y += searchRoi.y + target.size().height / 2; if(maxValue >= matchPercentage) { // add the point to the list foundPointsList->push_back(maxLoc); confidencesList->push_back(maxValue); // if we are only looking for a single target, we have found it, so we // can return if(!findMultipleTargets) { break; } } } // if(foundPointsList->empty()) // { // printf( "\nTarget was not found to required confidence of %d.\n", // matchPercentage ); // } delete [] locations; return true; }