好的,这是我的源代码。此代码将获取文件中的图像并将其与另一个文件中的图像列表进行比较。在图像文件中,您必须包含一个 .txt 文件,其中包含您尝试比较的文件中所有图像的名称。我遇到的问题是这两个图像非常相似但并不完全相同。我需要一种方法来进一步细化这些匹配。甚至可能是一种全新的方式来比较这两种形状(更大的块、斑点等)。我正在考虑的一种方法实际上是制作整个关键点图,并且仅比较关键点是否位于或接近与两个图像对应的某个点。即:比较点 (12,200) 处的关键点,距 (x, y) +-10 个像素,看看另一张图像上是否有相似的关键点。
我所需要的只是一种从以下方面获得最佳匹配的方法:ActualImplant和XrayOfThatSameImplantButASlightlyDifferentSize。谢谢,麻烦您了!
PS:你会看到我在试验 Sobel Derivatives 和其他类似东西的地方被注释掉了。我最终只是调整了 X 射线的对比度和亮度以获得最佳轮廓。在用于尝试匹配任何东西之前,必须对植入物的图像进行相同的处理。
#include "opencv2\highgui\highgui.hpp"
#include "opencv2\features2d\features2d.hpp"
#include "opencv2\imgproc.hpp"
#include <iostream>
#include <fstream>
#include <ctime>
const string defaultDetector = "ORB";
const string defaultDescriptor = "ORB";
const string defaultMatcher = "BruteForce-Hamming";
const string defaultXrayImagePath = "../../xray.png";
const string defaultImplantImagesTextListPath = "../../implantImage.txt";
const string defaultPathToResultsFolder = "../../results";
static void printIntro(const string& appName)
{
cout << "/* *\n"
<< " * Created by: Alex Gatz. 1/11/12. Created for: Xray Implant Identification *\n"
<< " * This code was created to scan a file full of images of differnt implants, generate keypoint maps *\n"
<< " * for each image, and identifywhich image most closely matches a chosen image in another folder *\n"
<< " */ *\n"
<< endl;
cout << endl << "Format:\n" << endl;
cout << "./" << appName << " [detector] [descriptor] [matcher] [xrayImagePath] [implantImagesTextListPath] [pathToSaveResults]" << endl;
cout << endl;
cout << "\nExample:" << endl
<< "./" << appName << " " << defaultDetector << " " << defaultDescriptor << " " << defaultMatcher << " "
<< defaultXrayImagePath << " " << defaultImplantImagesTextListPath << " " << defaultPathToResultsFolder << endl;
}
static void maskMatchesByImplantImgIdx(const vector<DMatch>& matches, int trainImgIdx, vector<char>& mask)
{
mask.resize(matches.size());
fill(mask.begin(), mask.end(), 0);
for (size_t i = 0; i < matches.size(); i++)
{
if (matches[i].imgIdx == trainImgIdx)
mask[i] = 1;
}
}
static void readImplantFilenames(const string& filename, string& dirName, vector<string>& implantFilenames)
{
implantFilenames.clear();
ifstream file(filename.c_str());
if (!file.is_open())
return;
size_t pos = filename.rfind('\\');
char dlmtr = '\\';
if (pos == String::npos)
{
pos = filename.rfind('/');
dlmtr = '/';
}
dirName = pos == string::npos ? "" : filename.substr(0, pos) + dlmtr;
while (!file.eof())
{
string str; getline(file, str);
if (str.empty()) break;
implantFilenames.push_back(str);
}
file.close();
}
static bool createDetectorDescriptorMatcher(const string& detectorType, const string& descriptorType, const string& matcherType,
Ptr<FeatureDetector>& featureDetector,
Ptr<DescriptorExtractor>& descriptorExtractor,
Ptr<DescriptorMatcher>& descriptorMatcher)
{
cout << "< Creating feature detector, descriptor extractor and descriptor matcher ..." << endl;
featureDetector = ORB::create( //All of these are parameters that can be adjusted to effect match accuracy and process time.
10000, //int nfeatures = Maxiumum number of features to retain; max vaulue unknown, higher number takes longer to process. Default: 500
1.4f, //float scaleFactor= Pyramid decimation ratio; between 1.00 - 2.00. Default: 1.2f
6, //int nlevels = Number of pyramid levels used; more levels more time taken to process, but more accurate results. Default: 8
40, //int edgeThreshold = Size of the border where the features are not detected. Should match patchSize roughly. Default: 31
0, //int firstLevel = Should remain 0 for now. Default: 0
4, //int WTA_K = Should remain 2. Default: 2
ORB::HARRIS_SCORE, //int scoreType = ORB::HARRIS_SCORE is the most accurate ranking possible for ORB. Default: HARRIS_SCORE
33 //int patchSize = size of patch used by the oriented BRIEF descriptor. Should match edgeThreashold. Default: 31
);
//featureDetector = ORB::create(); // <-- Uncomment this and comment the featureDetector above for default detector-
//OpenCV 3.1 got rid of the dynamic naming of detectors and extractors.
//These two are one in the same when using ORB, some detectors and extractors are separate
// in which case you would set "descriptorExtractor = descriptorType::create();" or its equivilant.
descriptorExtractor = featureDetector;
descriptorMatcher = DescriptorMatcher::create(matcherType);
cout << ">" << endl;
bool isCreated = !(featureDetector.empty() || descriptorExtractor.empty() || descriptorMatcher.empty());
if (!isCreated)
cout << "Can not create feature detector or descriptor extractor or descriptor matcher of given types." << endl << ">" << endl;
return isCreated;
}
static void manipulateImage(Mat& image) //Manipulates images into only showing an outline!
{
//Sobel Dirivative edge finder
//int scale = 1;
//int delta = 0;
//int ddepth = CV_16S;
////equalizeHist(image, image); //This will equilize the lighting levels in each image.
//GaussianBlur(image, image, Size(3, 3), 0, 0, BORDER_DEFAULT);
//Mat grad_x, grad_y;
//Mat abs_grad_x, abs_grad_y;
////For x
//Sobel(image, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
//convertScaleAbs(grad_x, abs_grad_x);
////For y
//Sobel(image, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT);
//convertScaleAbs(grad_y, abs_grad_y);
//addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, image);
//Specific Level adjustment (very clean)
double alpha = 20; //Best Result: 20
int beta = -300; //Best Result: -300
image.convertTo(image, -1, alpha, beta);
}
static bool readImages(const string& xrayImageName, const string& implantFilename,
Mat& xrayImage, vector <Mat>& implantImages, vector<string>& implantImageNames)
{
//TODO: Add a funtion call to automatically adjust all images loaded to best settings for matching.
cout << "< Reading the images..." << endl;
xrayImage = imread(xrayImageName, CV_LOAD_IMAGE_GRAYSCALE); //Turns the image gray while loading.
manipulateImage(xrayImage); //Runs image manipulations
if (xrayImage.empty())
{
cout << "Xray image can not be read." << endl << ">" << endl;
return false;
}
string trainDirName;
readImplantFilenames(implantFilename, trainDirName, implantImageNames);
if (implantImageNames.empty())
{
cout << "Implant image filenames can not be read." << endl << ">" << endl;
return false;
}
int readImageCount = 0;
for (size_t i = 0; i < implantImageNames.size(); i++)
{
string filename = trainDirName + implantImageNames[i];
Mat img = imread(filename, CV_LOAD_IMAGE_GRAYSCALE); //Turns imamges gray while loading.
//manipulateImage(img); //Runs Sobel Dirivitage on implant image.
if (img.empty())
{
cout << "Implant image " << filename << " can not be read." << endl;
}
else
{
readImageCount++;
}
implantImages.push_back(img);
}
if (!readImageCount)
{
cout << "All implant images can not be read." << endl << ">" << endl;
return false;
}
else
cout << readImageCount << " implant images were read." << endl;
cout << ">" << endl;
return true;
}
static void detectKeypoints(const Mat& xrayImage, vector<KeyPoint>& xrayKeypoints,
const vector<Mat>& implantImages, vector<vector<KeyPoint> >& implantKeypoints,
Ptr<FeatureDetector>& featureDetector)
{
cout << endl << "< Extracting keypoints from images..." << endl;
featureDetector->detect(xrayImage, xrayKeypoints);
featureDetector->detect(implantImages, implantKeypoints);
cout << ">" << endl;
}
static void computeDescriptors(const Mat& xrayImage, vector<KeyPoint>& implantKeypoints, Mat& implantDescriptors,
const vector<Mat>& implantImages, vector<vector<KeyPoint> >& implantImageKeypoints, vector<Mat>& implantImageDescriptors,
Ptr<DescriptorExtractor>& descriptorExtractor)
{
cout << "< Computing descriptors for keypoints..." << endl;
descriptorExtractor->compute(xrayImage, implantKeypoints, implantDescriptors);
descriptorExtractor->compute(implantImages, implantImageKeypoints, implantImageDescriptors);
int totalTrainDesc = 0;
for (vector<Mat>::const_iterator tdIter = implantImageDescriptors.begin(); tdIter != implantImageDescriptors.end(); tdIter++)
totalTrainDesc += tdIter->rows;
cout << "Query descriptors count: " << implantDescriptors.rows << "; Total train descriptors count: " << totalTrainDesc << endl;
cout << ">" << endl;
}
static void matchDescriptors(const Mat& xrayDescriptors, const vector<Mat>& implantDescriptors,
vector<DMatch>& matches, Ptr<DescriptorMatcher>& descriptorMatcher)
{
cout << "< Set implant image descriptors collection in the matcher and match xray descriptors to them..." << endl;
//time_t timerBegin, timerEnd;
//time(&timerBegin);
descriptorMatcher->add(implantDescriptors);
descriptorMatcher->train();
//time(&timerEnd);
//double buildTime = difftime(timerEnd, timerBegin);
//time(&timerBegin);
descriptorMatcher->match(xrayDescriptors, matches);
//time(&timerEnd);
//double matchTime = difftime(timerEnd, timerBegin);
CV_Assert(xrayDescriptors.rows == (int)matches.size() || matches.empty());
cout << "Number of imageMatches: " << matches.size() << endl;
//cout << "Build time: " << buildTime << " ms; Match time: " << matchTime << " ms" << endl;
cout << ">" << endl;
}
static void saveResultImages(const Mat& xrayImage, const vector<KeyPoint>& xrayKeypoints,
const vector<Mat>& implantImage, const vector<vector<KeyPoint> >& implantImageKeypoints,
const vector<DMatch>& matches, const vector<string>& implantImagesName, const string& resultDir)
{
cout << "< Save results..." << endl;
Mat drawImg;
vector<char> mask;
for (size_t i = 0; i < implantImage.size(); i++)
{
if (!implantImage[i].empty())
{
maskMatchesByImplantImgIdx(matches, (int)i, mask);
drawMatches(xrayImage, xrayKeypoints, implantImage[i], implantImageKeypoints[i],
matches, drawImg, Scalar::all(-1), Scalar(0, 0, 255), mask, 4);
string filename = resultDir + "/result_" + implantImagesName[i];
if (!imwrite(filename, drawImg))
cout << "Image " << filename << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl;
}
}
cout << ">" << endl;
//After all results have been saved, another function will scan and place the final result in a separate folder.
//For now this save process is required to manually access each result and determine if the current settings are working well.
}
int main(int argc, char** argv)
{
//Intialize variables to global defaults.
string detector = defaultDetector;
string descriptor = defaultDescriptor;
string matcher = defaultMatcher;
string xrayImagePath = defaultXrayImagePath;
string implantImagesTextListPath = defaultImplantImagesTextListPath;
string pathToSaveResults = defaultPathToResultsFolder;
//As long as you have 7 arguments, you can procede
if (argc != 7 && argc != 1)
{
//This will be called if the incorrect amount of commands are used to start the program.
printIntro(argv[1]);
system("PAUSE");
return -1;
}
//As long as you still have 7 arguments, I will set the variables for this
// to the arguments you decided on.
//If testing using XrayID --> Properties --> Debugging --> Command Arguments, remember to start with [detector] as the first command
// C++ includes the [appName] command as the first argument automantically.
if (argc != 1) //I suggest placing a break here and stepping through this to ensure the proper commands were sent in. With a
// GUI this would nto matter because the GUI would structure the input and use a default if no input was used.
{
detector = argv[1];
descriptor = argv[2];
matcher = argv[3];
xrayImagePath = argv[4];
implantImagesTextListPath = argv[5];
pathToSaveResults = argv[6];
}
//Set up cv::Ptr's for tools.
Ptr<FeatureDetector> featureDetector;
Ptr<DescriptorExtractor> descriptorExtractor;
Ptr<DescriptorMatcher> descriptorMatcher;
//Check to see if tools are created, if not true print intro and close program.
if (!createDetectorDescriptorMatcher(detector, descriptor, matcher, featureDetector, descriptorExtractor, descriptorMatcher))
{
printIntro(argv[0]);
system("PAUSE");
return -1;
}
Mat testImage;
vector<Mat> implantImages;
vector<string> implantImagesNames;
//Check to see if readImages completes properly, if not true print intro and close program.
if (!readImages(xrayImagePath, implantImagesTextListPath, testImage, implantImages, implantImagesNames))
{
printIntro(argv[0]);
system("PAUSE");
return -1;
}
vector<KeyPoint> xrayKeypoints;
vector<vector<KeyPoint> > implantKeypoints;
detectKeypoints(testImage, xrayKeypoints, implantImages, implantKeypoints, featureDetector);
Mat xrayDescriptors;
vector<Mat> implantTestImageDescriptors;
computeDescriptors(testImage, xrayKeypoints, xrayDescriptors, implantImages, implantKeypoints, implantTestImageDescriptors,
descriptorExtractor);
vector<DMatch> imageMatches;
matchDescriptors(xrayDescriptors, implantTestImageDescriptors, imageMatches, descriptorMatcher);
saveResultImages(testImage, xrayKeypoints, implantImages, implantKeypoints, imageMatches, implantImagesNames, pathToSaveResults);
system("PAUSE");
return 0;
}