我已经实现了在Python中匹配图像之间关键点的工作流程,并且我正在将其转换为Objective C ++,以便在iOS项目中使用。我在两个平台上都使用OpenCV 3.4。虽然我与我的代码匹配,但结果似乎大不相同。
Python实现:
def simple(source_image_name, sample_image_name):
surf = cv2.xfeatures2d.SURF_create(400, nOctaves=3, nOctaveLayers=4, extended=False, upright=True)
source_image = cv2.imread(source_image_name, 0)
# Detect keypoints in source image
source_keypoints, source_descriptors = surf.detectAndCompute(source_image, None)
print("source keypoints: " + str(len(source_keypoints)))
sample_image = cv2.imread(sample_image_name, 0)
# Detect keypoints in sample image
sample_keypoints, sample_descriptors = surf.detectAndCompute(sample_image, None)
print("sample keypoints: " + str(len(sample_keypoints)))
# Find matches between the images
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict()
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(source_descriptors, sample_descriptors, k=2)
print("matches: " + str(len(matches)))
# Filter to good matches
good_matches = []
for match1, match2 in matches:
if match1.distance < match2.distance * 0.75:
good_matches.append(match1)
print("good matches: " + str(len(good_matches)))
Objective C ++实现:
+ (void)simpleWithSourceImage:(UIImage *)sourceImage sampleImage:(UIImage *)sampleImage {
Ptr<SURF> surf = cv::xfeatures2d::SURF::create(400, 3, 4, false, true);
cv::Mat sourceImageMat;
UIImageToMat(sourceImage, sourceImageMat);
std::vector<KeyPoint> sourceKeypoints;
cv::Mat sourceDescriptors;
//Detect keypoints in source image
surf->detectAndCompute(sourceImageMat, noArray(), sourceKeypoints, sourceDescriptors);
NSLog(@"source keypoints: %lu", sourceKeypoints.size());
cv::Mat sampleImageMat;
UIImageToMat(sampleImage, sampleImageMat);
std::vector<KeyPoint> sampleKeypoints;
cv::Mat sampleDescriptors;
//Detect keypoints in sample image
surf->detectAndCompute(sampleImageMat, noArray(), sampleKeypoints, sampleDescriptors);
NSLog(@"sample keypoints: %lu", sampleKeypoints.size());
//Find matches between the images
FlannBasedMatcher matcher(new cv::flann::KDTreeIndexParams(5));
std::vector<std::vector<DMatch>> matches;
matcher.knnMatch(sampleDescriptors, sourceDescriptors, matches, 2);
NSLog(@"matches: %lu", matches.size());
//Filter to good matches
std::vector<DMatch> goodMatches;
for (int i = 0; i < matches.size(); i++) {
std::vector<DMatch> match = matches[i];
if (match[0].distance < match[1].distance * 0.75) {
DMatch goodMatch = match[0];
goodMatches.push_back(goodMatch);
}
}
NSLog(@"good matches: %lu", goodMatches.size());
}
这两种实现都是:
Python控制台日志:
source keypoints: 16133
sample keypoints: 18265
matches: 16133
good matches: 3785
C ++控制台日志:
source keypoints: 16281
sample keypoints: 18040
matches: 18040
good matches: 558
虽然他们已经获得相同的图像,但两个图像中检测到的关键点的数量略有不同。也许这与它阅读图像的方式有关。那里约1%的细微差别并不是太大的问题。
然而,匹配的差异更大,差异大约为11%,尽管C ++实际上更高。然后,C ++的好匹配数远 - 相差86%。我更多地研究了matches
的差异......
Python匹配:
match 1 average: 0.220831272634
distance average: 0.840705045962
C ++匹配:
match 1 average: 0.298346
distance average: 0.924873
因此,C ++结果的匹配1平均值明显更高,并且第一场比赛和第二场比赛之间的平均距离非常接近。
我尝试将用于加载图像文件的初始代码更改为:
NSFileHandle* sourceHandle = [NSFileHandle fileHandleForReadingAtPath:sourceImageName];
NSData *sourceData = [sourceHandle readDataToEndOfFile];
v::Mat sourceImageMat = cv::imdecode(cv::Mat(1, [sourceData length], CV_8UC1, (void*)sourceData.bytes), CV_LOAD_IMAGE_UNCHANGED);
确实影响了结果,但没有影响结论 - C ++匹配仍然低得多,距离平均值很高,结果之间的距离匹配很近:
source keypoints: 16116
sample keypoints: 18253
matches: 18253
good matches: 518
match 1 average: 0.299299
distance average: 0.925706
我也尝试在Python代码上查看匹配2的平均值,平均值为0.26 - 仍然低于C ++代码的数学1平均值。换句话说,Python的第二好结果平均比C ++的最佳匹配好。
我试图找出这种差异的来源。我的实现不匹配吗?