<commit_before>#include <vector>
#include <string>
#include <iostream>
#include <opencv2/opencv.hpp>

using std::string;
using std::vector;

// Make sure directory exists
void mkdirp(string path) {
    system((std::string("mkdir -p ") + path).c_str());
}

void features_analysis(string path, cv::Mat image1, cv::Mat image2, cv::Ptr<cv::FeatureDetector> detector, cv::Ptr<cv::DescriptorExtractor> descriptor) {
    mkdirp(path);

    // Detect keypoints
    vector<cv::KeyPoint> keypoint1;
    vector<cv::KeyPoint> keypoint2;

    detector->detect(image1, keypoint1);
    detector->detect(image2, keypoint2);

    // Draw keypoints
    cv::Mat img_keypoints_1;
    cv::Mat img_keypoints_2;
    cv::drawKeypoints(image1, keypoint1, img_keypoints_1, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT);
    cv::drawKeypoints(image2, keypoint2, img_keypoints_2, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT);

    cv::imwrite(path + "/kp1.jpg", img_keypoints_1);
    cv::imwrite(path + "/kp2.jpg", img_keypoints_2);

    cv::Mat descriptor1;
    cv::Mat descriptor2;

    descriptor->compute(image1, keypoint1, descriptor1);
    descriptor->compute(image2, keypoint2, descriptor2);

    if (descriptor1.empty() || descriptor2.empty()) {
        std::cerr << "Empty descriptor!" << std::endl;
    }

    if (descriptor1.type()!= CV_32F) {
        descriptor1.convertTo(descriptor1, CV_32F);
    }
    if (descriptor2.type()!= CV_32F) {
        descriptor2.convertTo(descriptor2, CV_32F);
    }
    // Match using FLANN
    cv::FlannBasedMatcher matcher;
    vector<cv::DMatch> matches;
    matcher.match(descriptor1, descriptor2, matches);
    
    // cv::BFMatcher matcher(cv::NORM_L2);
    // vector<cv::DMatch> matches;
    // matcher.match(descriptor1, descriptor2, matches);

    double max_dist = 0;
    double min_dist = 100;

    // Quick calculation of max and min distances between keypoints
    for( int i = 0; i < descriptor1.rows; i++ ) {
        double dist = matches[i].distance;
        if( dist < min_dist ) min_dist = dist;
        if( dist > max_dist ) max_dist = dist;
    }

    std::cout << "Max dist " << max_dist << std::endl;
    std::cout << "Min dist " << min_dist << std::endl;

    //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist,
    //-- or a small arbitary value ( 0.02 ) in the event that min_dist is very
    //-- small)
    //-- PS.- radiusMatch can also be used here.
    vector<cv::DMatch> good_matches;

    for( int i = 0; i < descriptor1.rows; i++ ) {
        if (matches[i].distance <= cv::max(4*min_dist, 0.02)) {
            good_matches.push_back( matches[i]);
        }
    }

    // Draw only "good" matches
    cv::Mat img_matches_good;
    cv::drawMatches( image1, keypoint1, image2, keypoint2,
            good_matches, img_matches_good, cv::Scalar::all(-1), cv::Scalar::all(-1),
            vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

    cv::imwrite(path + "/matches_good.jpg", img_matches_good);

    cv::Mat img_matches_all;
    cv::drawMatches( image1, keypoint1, image2, keypoint2,
            matches, img_matches_all, cv::Scalar::all(-1), cv::Scalar::all(-1),
            vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

    cv::imwrite(path + "/matches_all.jpg", img_matches_all);

    for( int i = 0; i < (int)good_matches.size(); i++ )
    { printf( "-- Good Match [%d] Keypoint 1: %d  -- Keypoint 2: %d  \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); }
}

// Run features analysis test for an image pair
void test_image_pair(string path, string path1, string path2) {
    // Load images
    cv::Mat image1 = cv::imread(path1);
    cv::Mat image2 = cv::imread(path2);
    if (!image1.data || !image2.data) {
        std::cerr << "Error reading image for " << path << std::endl;
        return;
    }

    mkdirp(path);

    // Perform analysis with different configs
    cv::Ptr<cv::ORB> orb = cv::ORB::create(400);
    features_analysis(path + "/ORB400", image1, image2, orb, orb);
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: ./features_analysis data_dir results_dir" << std::endl;
        return -1;
    }

    string data = std::string(argv[1]);<commit_msg>added structure for distance threshold in matches images<commit_after>#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <opencv2/opencv.hpp>
// #include "opencv2/xfeatures2d.hpp"

using std::string;
using std::vector;

// Make sure directory exists
void mkdirp(string path) {
    system((std::string("mkdir -p ") + path).c_str());
}

void write_matches_image(string path, cv::Mat image1, cv::Mat image2,
                      vector<cv::KeyPoint> keypoint1, vector<cv::KeyPoint> keypoint2,
                      vector<cv::DMatch> matches,
                      double threshold) {
    // Filter based on distance
    vector<cv::DMatch> good_matches;
    for (size_t i = 0; i < matches.size(); i++) {
        if (matches[i].distance <= threshold) {
            good_matches.push_back(matches[i]);
        }
    }
    if (good_matches.size() == 0) {
        return;
    }
    // Draw
    cv::Mat img;
    cv::drawMatches(image1, keypoint1, image2, keypoint2,
            good_matches, img, cv::Scalar::all(-1), cv::Scalar::all(-1),
            vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

    // Write to file
    std::ostringstream oss;
    oss << threshold;
    cv::imwrite(path + "/" + oss.str() + ".jpg", img);
}

void features_analysis(string path, cv::Mat image1, cv::Mat image2, cv::Ptr<cv::FeatureDetector> detector, cv::Ptr<cv::DescriptorExtractor> descriptor) {
    mkdirp(path);

    // Detect keypoints
    vector<cv::KeyPoint> keypoint1;
    vector<cv::KeyPoint> keypoint2;
    detector->detect(image1, keypoint1);
    detector->detect(image2, keypoint2);

    // Draw keypoints
    cv::Mat img_keypoints_1;
    cv::Mat img_keypoints_2;
    cv::drawKeypoints(image1, keypoint1, img_keypoints_1, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT);
    cv::drawKeypoints(image2, keypoint2, img_keypoints_2, cv::Scalar::all(-1), cv::DrawMatchesFlags::DEFAULT);

    cv::imwrite(path + "/kp1.jpg", img_keypoints_1);
    cv::imwrite(path + "/kp2.jpg", img_keypoints_2);

    // Compute descriptors
    cv::Mat descriptor1;
    cv::Mat descriptor2;
    descriptor->compute(image1, keypoint1, descriptor1);
    descriptor->compute(image2, keypoint2, descriptor2);

    if (descriptor1.empty() || descriptor2.empty()) {
        std::cerr << "Empty descriptor!" << std::endl;
    }

    if (descriptor1.type()!= CV_32F) {
        descriptor1.convertTo(descriptor1, CV_32F);
    }
    if (descriptor2.type()!= CV_32F) {
        descriptor2.convertTo(descriptor2, CV_32F);
    }

    // Match using FLANN
    cv::FlannBasedMatcher matcher;
    vector<cv::DMatch> matches;
    matcher.match(descriptor1, descriptor2, matches);

    // Quick calculation of max and min distances between keypoints
    double min_dist, max_dist;
    for (int i = 0; i < descriptor1.rows; i++) {
        double dist = matches[i].distance;
        if (dist < min_dist) min_dist = dist;
        if (dist > max_dist) max_dist = dist;
    }

    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 1e9);
    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 500);
    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 400);
    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 300);
    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 200);
    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 100);
    write_matches_image(path, image1, image2, keypoint1, keypoint2, matches, 50);
}

// Run features analysis test for an image pair
void test_image_pair(string path, string path1, string path2) {
    // Load images
    cv::Mat image1 = cv::imread(path1);
    cv::Mat image2 = cv::imread(path2);
    if (!image1.data || !image2.data) {
        std::cerr << "Error reading image for " << path << std::endl;
        return;
    }

    mkdirp(path);

    // Perform analysis with different configs
    cv::Ptr<cv::ORB> orb = cv::ORB::create(400);
    cv::Ptr<cv::MSER> fast = cv::MSER::create(400);
    cv::Ptr<cv::BRISK> brisk = cv::BRISK::create();
    // cv::Ptr<cv::SURF> surf = cv::SURF::create(400);

    features_analysis(path + "/ORB400", image1, image2, orb, orb);
    features_analysis(path + "/SURF400", image1, image2, fast, orb);
    features_analysis(path + "/BRISK", image1, image2, brisk, brisk);

}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "Usage: ./features_analysis data_dir results_dir" << std::endl;
        return -1;
    }

    string data = std::string(argv[1]);
{'fraction_non_alphanumeric': 0.15217391304347827, 'fraction_numerical': 0.027853260869565216, 'mean_word_length': 3.4678806272129488, 'pattern_counts': {'":': 0, '<': 89, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5127729', 'n_tokens_mistral': 3154, 'n_tokens_neox': 2962, 'n_words': 672}