SIFT描述子实现

参考:SIFT图像匹配原理及python实现(源码实现及基于opencv实现)

#include <iostream>
#include <opencv2/opencv.hpp>
#define _USE_MATH_DEFINES
#include <math.h>
#include <numeric>


float mod_float(float x, float y)	//小数取模
{
	return x - floor(x / y) * y;
}


bool sort_method(const cv::KeyPoint kp1, const cv::KeyPoint kp2)
{
	if (kp1.pt.x != kp2.pt.x)
		return kp1.pt.x < kp2.pt.x;
	if (kp1.pt.y != kp2.pt.y)
		return kp1.pt.y < kp2.pt.y;
	if (kp1.size != kp2.size)
		return kp1.size < kp2.size;
	if (kp1.angle != kp2.angle)
		return kp1.angle < kp2.angle;
	if (kp1.response != kp2.response)
		return kp1.response < kp2.response;
	if (kp1.octave != kp2.octave)
		return kp1.octave < kp2.octave;
	return kp1.class_id < kp2.class_id;
}


cv::Mat vec2mat(std::vector<std::vector<float>> vec)    //二维vector转Mat
{
	cv::Mat m(vec.size(), vec[0].size(), CV_32F);
	for (int i = 0; i < vec.size(); ++i)
		m.row(i) = cv::Mat(vec[i]).t();
	return m;
}


class SIFT
{
public:
	SIFT(int num_octave, int num_scale, float sigma)
	{
		m_num_octave = num_octave;
		m_num_scale = num_scale;
		m_sigma = sigma;
		m_contrast_t = 0.04;
		m_eigenvalue_r = 10;
		m_scale_factor = 1.5;
		m_radius_factor = 3;
		m_num_bins = 36;
		m_peak_ratio = 0.8;
	}

	cv::Mat pre_treat_img(cv::Mat img_src, float sigma, float sigma_camera = 0.5)
	{
		float sigma_mid = sqrt(pow(sigma, 2) - pow(2*sigma_camera, 2));
		cv::Mat img = img_src.clone();
		cv::resize(img, img, cv::Size(img.cols * 2, img.rows * 2));
		cv::GaussianBlur(img, img, cv::Size(0, 0), sigma_mid, sigma_mid);
		return img;
	}

	int get_numOfOctave(cv::Mat img)
	{
		return std::round(log(std::min(img.cols, img.rows)) / log(2)) - 1;
	}

	std::vector<cv::Mat> construct_octave(cv::Mat img_src, float s, float sigma)
	{
		std::vector<cv::Mat> octave;
		octave.push_back(img_src);
		float k = pow(2, 1 / s);
		for (size_t i = 1; i < s + 3; i++)
		{
			cv::Mat img = octave.back().clone(), cur_img;
			float cur_sigma = sigma * pow(k, i);
			float pre_sigma = sigma * pow(k, i - 1);
			float mid_sigma = sqrt(pow(cur_sigma, 2) - pow(pre_sigma, 2));
			cv::GaussianBlur(img, cur_img, cv::Size(0, 0), mid_sigma, mid_sigma);
			octave.push_back(cur_img);
		}
		return octave;
	}

	std::vector<std::vector<cv::Mat>> construct_gaussian_pyramid(cv::Mat img_src)
	{
		std::vector<std::vector<cv::Mat>> pyr;
		cv::Mat img_base = img_src.clone();
		for (size_t i = 0; i < m_num_octave; i++)
		{
			std::vector<cv::Mat> octave = construct_octave(img_base, m_num_scale, m_sigma);
			pyr.push_back(octave);
			img_base = octave[octave.size() - 3];
			cv::resize(img_base, img_base, cv::Size(img_base.cols / 2, img_base.rows / 2));
		}
		return pyr;
	}

	std::vector<std::vector<cv::Mat>>  construct_DOG(std::vector<std::vector<cv::Mat>> pyr)
	{
		std::vector<std::vector<cv::Mat>> dog_pyr;
		for (size_t i = 0; i < pyr.size(); i++)
		{
			std::vector<cv::Mat> octave = pyr[i];
			std::vector<cv::Mat> dog;
			for (size_t j = 0; j < octave.size() - 1; j++)
			{
				cv::Mat diff = octave[j + 1] - octave[j];
				dog.push_back(diff);
			}
			dog_pyr.push_back(dog);
		}
		return dog_pyr;
	}

	bool is_extreme(cv::Mat bot, cv::Mat mid, cv::Mat top, float thr)
	{
		float c = mid.at<float>(1, 1);
		cv::Mat temp;
		cv::vconcat(bot, mid, temp);
		cv::vconcat(temp, top, temp);
		//std::cout << bot << std::endl;
		//std::cout << temp << std::endl;

		if (c > thr)
		{
			int len = 0;
			for (size_t i = 0; i < temp.rows; i++)
			{
				for (size_t j = 0; j < temp.cols; j++)
				{
					if (temp.at<float>(i, j) > c)
						len++;
				}
			}
			return len == 0;
		}
		else if (c < -thr)
		{
			int len = 0;
			for (size_t i = 0; i < temp.rows; i++)
			{
				for (size_t j = 0; j < temp.cols; j++)
				{
					if (temp.at<float>(i, j) < c)
						len++;
				}
			}
			return len == 0;
		}

		return false;
	}

	cv::Mat get_gradient(cv::Mat arr)
	{
		//std::cout << arr << std::endl;
		//std::cout << arr.at<cv::Vec3f>(1, 2)[1] << " " << arr.at<cv::Vec3f>(1, 0)[1] << std::endl;
		float dx = (arr.at<cv::Vec3f>(1, 2)[1] - arr.at<cv::Vec3f>(1, 0)[1]) / 2;
		float dy = (arr.at<cv::Vec3f>(2, 1)[1] - arr.at<cv::Vec3f>(0, 1)[1]) / 2;
		float ds = (arr.at<cv::Vec3f>(1, 1)[2] - arr.at<cv::Vec3f>(1, 1)[0]) / 2;
		cv::Mat ret = (cv::Mat_<float>(3, 1) << dx, dy, ds);
		//std::cout << ret << std::endl;
		return ret;
	}

	cv::Mat get_hessian(cv::Mat arr)
	{
		float dxx = arr.at<cv::Vec3f>(1, 2)[1] - 2 * arr.at<cv::Vec3f>(1, 1)[1] + arr.at<cv::Vec3f>(1, 0)[1];
		float dyy = arr.at<cv::Vec3f>(2, 1)[1] - 2 * arr.at<cv::Vec3f>(1, 1)[1] + arr.at<cv::Vec3f>(0, 1)[1];
		float dss = arr.at<cv::Vec3f>(1, 1)[2] - 2 * arr.at<cv::Vec3f>(1, 1)[1] + arr.at<cv::Vec3f>(1, 1)[0];
		float dxy = 0.25 * (arr.at<cv::Vec3f>(0, 0)[1] + arr.at<cv::Vec3f>(2, 2)[1] - arr.at<cv::Vec3f>(0, 2)[1] - arr.at<cv::Vec3f>(2, 0)[1]);
		float dxs = 0.25 * (arr.at<cv::Vec3f>(1, 0)[0] + arr.at<cv::Vec3f>(1, 2)[2] - arr.at<cv::Vec3f>(1, 2)[0] - arr.at<cv::Vec3f>(1, 0)[2]);
		float dys = 0.25 * (arr.at<cv::Vec3f>(0, 1)[0] + arr.at<cv::Vec3f>(2, 1)[2] - arr.at<cv::Vec3f>(2, 1)[0] - arr.at<cv::Vec3f>(0, 1)[2]);
		cv::Mat ret = (cv::Mat_<float>(3, 3) << dxx, dxy, dxs, dxy, dyy, dys, dxs, dys, dss);
		//std::cout << ret << std::endl;
		return ret;
	}
	
	std::vector<cv::Mat> fit_extreme(cv::Mat bot, cv::Mat mid, cv::Mat top)
	{
		//std::cout << bot << std::endl;
		//std::cout << mid << std::endl;
		//std::cout << top << std::endl;
		std::vector<cv::Mat> arr_split = { bot, mid, top };
		cv::Mat arr, g, h, rt;
		cv::merge(arr_split, arr);
		g = get_gradient(arr/255);
		h = get_hessian(arr/255);
		rt = -h.inv() * g;
		//std::cout << rt << std::endl;
		return { g,h,rt };
	}
	
	std::pair<cv::KeyPoint, int> try_fit_extreme(std::vector<cv::Mat> octave, int s, int i, int j, int board_width, int octave_index)
	{
		bool flag = false;
		cv::Mat bot_img = octave[s - 1];
		cv::Mat mid_img = octave[s];
		cv::Mat top_img = octave[s + 1];
		cv::Mat g, h, offset;
		for (size_t n = 0; n < 5; n++)
		{
			//std::cout << bot_img(cv::Rect(j - 1, i - 1, 3, 3)) << std::endl;

			std::vector<cv::Mat> vec = fit_extreme(bot_img(cv::Rect(j - 1, i - 1, 3, 3)),
				mid_img(cv::Rect(j - 1, i - 1, 3, 3)),
				top_img(cv::Rect(j - 1, i - 1, 3, 3)));
			g = vec[0];
			h = vec[1];
			offset = vec[2];
			//std::cout << offset << std::endl;

			float max_value = 0;
			for (size_t k = 0; k < offset.rows; k++)
			{
				if (max_value < fabs(offset.at<float>(k, 0)))
					max_value = fabs(offset.at<float>(k, 0));
			}

			//std::cout << max_value << std::endl;
			if (max_value < 0.5)
			{
				flag = true;
				break;
			}

			s = round(s + offset.at<float>(2, 0));
			i = round(i + offset.at<float>(1, 0));
			j = round(j + offset.at<float>(0, 0));
			//std::cout << s << " " << i << " " << j << std::endl;

			if (i<board_width || i>bot_img.rows - board_width ||
				j<board_width || j>bot_img.cols - board_width ||
				s<1 || s>octave.size() - 2)
			{
				break;
			}
		}

		if (!flag)
			return {};

		float ex_value = mid_img.at<float>(i, j) / 255 + 0.5 * g.dot(offset);
		if (fabs(ex_value) * m_num_scale < m_contrast_t)
			return {};

		cv::Mat hxy = h(cv::Rect(0, 0, 2, 2));
		float trace_h = cv::trace(hxy)[0];
		float det_h = cv::determinant(hxy);

		if(det_h > 0 && (pow(trace_h,2) / det_h) < (pow(m_eigenvalue_r + 1,2) / m_eigenvalue_r))
		{
			cv::KeyPoint kp;
			kp.response = fabs(ex_value);
			//std::cout << offset << std::endl;
			i = i + offset.at<float>(1, 0);
			j = j + offset.at<float>(0, 0);
			kp.pt = cv::Point2f(j *1.0 / bot_img.cols, i*1.0 / bot_img.rows);
			kp.size = m_sigma * pow(2, (s + offset.at<float>(2, 0)) / m_num_scale) * pow(2, octave_index);
			kp.octave = octave_index + s * pow(2, 8) + int(round((offset.at<float>(2, 0) + 0.5) * 255)) * pow(2, 16);
			return { kp, s };
		}
		return {};
	}

	std::vector<cv::KeyPoint> compute_orientation(cv::KeyPoint kp, int octave_index, cv::Mat img)
	{
		std::vector<cv::KeyPoint> keypoints_with_orientations;
		float cur_scale = kp.size / pow(2, octave_index);
		float radius = round(m_radius_factor * m_scale_factor * cur_scale);
		float weight_sigma = -0.5 / pow(m_scale_factor * cur_scale, 2);
		std::vector<float> raw_histogram(m_num_bins);
		int cx = round(kp.pt.x * img.cols);
		int cy = round(kp.pt.y * img.rows);

		for (int y = cy - radius; y < cy + radius + 1; y++)
		{
			for (int x = cx - radius; x < cx + radius + 1; x++)
			{
				if(y > 0 && y < img.rows - 1 && x > 0 && x < img.cols - 1)
				{
					float dx = img.at<float>(y, x + 1) - img.at<float>(y, x - 1);
					float dy = img.at<float>(y - 1, x) - img.at<float>(y + 1, x);
					float mag = sqrt(dx * dx + dy * dy);
					float angle = atan2(dy, dx) * 180 / M_PI;
					if (angle < 0)
						angle += 360;
					int angle_index = round(angle / (360 / m_num_bins));
					angle_index %= m_num_bins;
					float weight = exp(weight_sigma * (pow(x - cx, 2) + pow(y - cy, 2)));
					raw_histogram[angle_index] = raw_histogram[angle_index] + mag * weight;
				}
			}
		}

		std::vector<float> h = raw_histogram;
		std::vector<float> ha2{ h[0], h[1] };
		ha2.insert(ha2.begin(), h.begin() + 2, h.end());
		std::vector<float> hm2{ h[h.size() - 2], h[h.size() - 1] };
		hm2.insert(hm2.end(), h.begin(), h.end() - 2);
		std::vector<float> ha1{ h[0] };
		ha1.insert(ha1.begin(), h.begin() + 1, h.end());
		std::vector<float> hm1{ h[h.size() - 1] };
		hm1.insert(hm1.end(), h.begin(), h.end() - 1);
		std::vector<float> smooth_histogram(h.size());
		for (size_t i = 0; i < h.size(); i++)
		{
			smooth_histogram[i] = (ha2[i] + hm2[i] + 4 * (ha1[i] + hm1[i]) + 6 * h[i]) / 16;
		}

		std::vector<float> s = smooth_histogram;
		float max_v = *std::max_element(s.begin(), s.end());
		std::vector<float> s1{ s[s.size() - 1] };
		s1.insert(s1.end(), s.begin(), s.end() - 1);
		std::vector<float> s2{ s[0] };
		s2.insert(s2.begin(), s.begin() + 1, s.end());

		std::vector<int> index;
		for (size_t i = 0; i < s.size(); i++)
		{
			if (s[i] >= s1[i] && s[i] >= s2[i])
				index.push_back(i);
		}

		for (int i : index)
		{
			float peak_v = s[i];
			if (peak_v >= m_peak_ratio * max_v)
			{
				float left_v = s[(i - 1) % m_num_bins < 0 ? (m_num_bins - 1) : (i - 1) % m_num_bins];
				float right_v = s[(i + 1) % m_num_bins];
				float index_fit = mod_float(i + 0.5 * (left_v - right_v) / (left_v + right_v - 2 * peak_v), m_num_bins);
				float angle = 360 - index_fit *1.0 / m_num_bins * 360;
				cv::KeyPoint new_kp;
				new_kp.pt = kp.pt;
				new_kp.size = kp.size;
				new_kp.angle = angle;
				new_kp.response = kp.response;
				new_kp.octave = kp.octave;
				keypoints_with_orientations.push_back(new_kp);
			}
		}
		return keypoints_with_orientations;
	}

	std::vector<cv::KeyPoint> get_keypoints(std::vector<std::vector<cv::Mat>> gau_pyr, std::vector<std::vector<cv::Mat>> dog_pyr)
	{
		std::vector<cv::KeyPoint> keypoints;
		float threshold = std::floor(0.5 * m_contrast_t / m_num_scale * 255);
		for (size_t octave_index = 0; octave_index < dog_pyr.size(); octave_index++)
		{
			std::vector<cv::Mat> octave = dog_pyr[octave_index];
			for (size_t s = 1; s < octave.size() - 1; s++)
			{
				std::cout << octave_index << " " << s << "************************************" << std::endl;
				cv::Mat bot_img = octave[s - 1];
				cv::Mat mid_img = octave[s];
				cv::Mat top_img = octave[s + 1];
				int board_width = 5;
				int x_st = board_width, y_st = board_width;
				int x_ed = bot_img.rows - board_width, y_ed = bot_img.cols - board_width;
				for (int i = x_st; i < x_ed; i++)
				{
					for (int j = y_st; j < y_ed; j++)
					{
						//std::cout << i << " " << j  << std::endl;
						//cv::Mat tmp = bot_img(cv::Rect(j - 1, i - 1, 3, 3));
						//std::cout << tmp << std::endl;
						bool flag = is_extreme(bot_img(cv::Rect(j - 1, i - 1, 3, 3)),
							mid_img(cv::Rect(j - 1, i - 1, 3, 3)),
							top_img(cv::Rect(j - 1, i - 1, 3, 3)),
							threshold);

						//std::cout << flag << std::endl;
						if (flag)
						{
							std::pair<cv::KeyPoint, int> reu = try_fit_extreme(octave, s, i, j, board_width, octave_index);
							if (reu.first.size != 0)
							{
								cv::KeyPoint kp = reu.first;
								int stemp = reu.second;
								std::vector<cv::KeyPoint> kp_orientation = compute_orientation(kp, octave_index, gau_pyr[octave_index][stemp]);
								for (auto k : kp_orientation)
									keypoints.push_back(k);
								//std::cout << octave_index << " " << s << " " << i << " " << j << " " << keypoints.size() << std::endl;
							}
						}
					}
				}
			}
		}
		return keypoints;
	}

	std::vector<cv::KeyPoint> remove_duplicate_points(std::vector<cv::KeyPoint> keypoints)
	{
		std::sort(keypoints.begin(), keypoints.end(), sort_method);
		std::vector<cv::KeyPoint> unique_keypoints = { keypoints[0] };
		for (size_t i = 1; i < keypoints.size(); i++)
		{
			cv::KeyPoint last_unique_keypoint = unique_keypoints.back();
			if (last_unique_keypoint.pt.x != keypoints[i].pt.x ||
				last_unique_keypoint.pt.y != keypoints[i].pt.y ||
				last_unique_keypoint.size != keypoints[i].size ||
				last_unique_keypoint.angle != keypoints[i].angle)
			{
				unique_keypoints.push_back(keypoints[i]);
			}
		}
		return unique_keypoints;
	}

	cv::Mat get_descriptor(std::vector<cv::KeyPoint> kps, std::vector<std::vector<cv::Mat>> gau_pyr,
		int win_N = 4, int num_bins = 8, int scale_multiplier = 3, float des_max_value = 0.2)
	{
		std::vector<std::vector<float>> descriptors_vec;
		for (auto kp : kps)
		{
			int octave = kp.octave & 255;
			int layer = (kp.octave >> 8) & 255;
			cv::Mat image = gau_pyr[octave][layer];
			float bins_per_degree = float(num_bins / 360.0);
			float angle = 360.0 - kp.angle;
			float cos_angle = cos(angle * M_PI / 180);
			float sin_angle = sin(angle * M_PI / 180);
			float weight_multiplier = -0.5 / pow(0.5 * win_N, 2);
			std::vector<float> row_bin_list;
			std::vector<float> col_bin_list;
			std::vector<float> magnitude_list;
			std::vector<float> orientation_bin_list;
			std::vector<std::vector<std::vector<float>>> histogram_tensor(win_N + 2, std::vector<std::vector<float>>(win_N + 2, std::vector<float>(num_bins, 0.0)));
	
			float hist_width = scale_multiplier * kp.size / pow(2, octave);
			int radius = int(round(hist_width * sqrt(2) * (win_N + 1) * 0.5));
			radius = int(std::min(double(radius), sqrt(pow(image.rows, 2) + pow(image.cols, 2))));

			for (int row = -radius; row < radius + 1; row++)
			{
				for (int col = -radius; col < radius + 1; col++)
				{
					float row_rot = col * sin_angle + row * cos_angle;
					float col_rot = col * cos_angle - row * sin_angle;
					float row_bin = (row_rot / hist_width) + 0.5 * win_N - 0.5;
					float col_bin = (col_rot / hist_width) + 0.5 * win_N - 0.5;
					if(row_bin > -1 && row_bin < win_N && col_bin > -1 && col_bin < win_N)
					{
						int window_col = int(round(kp.pt.x * image.cols + col));
						int window_row = int(round(kp.pt.y * image.rows + row));
						if(window_row > 0 && window_row < image.rows - 1 && window_col > 0 && window_col < image.cols - 1)
						{
							float dx = image.at<float>(window_row, window_col + 1) - image.at<float>(window_row, window_col - 1);
							float dy = image.at<float>(window_row - 1, window_col) - image.at<float>(window_row + 1, window_col);
							float gradient_magnitude = sqrt(dx * dx + dy * dy);
							float gradient_orientation = mod_float(atan2(dy, dx) * 180 / M_PI, 360);
							float weight = exp(weight_multiplier * (pow(row_rot / hist_width, 2) + pow(col_rot / hist_width, 2)));
							row_bin_list.push_back(row_bin);
							col_bin_list.push_back(col_bin);
							magnitude_list.push_back(weight * gradient_magnitude);
							orientation_bin_list.push_back((gradient_orientation - angle) * bins_per_degree);
						}
					}

				}
			}
		
			for (size_t i = 0; i < row_bin_list.size(); i++)
			{
				int ri = floor(row_bin_list[i]), ci = floor(col_bin_list[i]), oi = floor(orientation_bin_list[i]);
				float rf = row_bin_list[i] - ri, cf = col_bin_list[i] - ci, of = orientation_bin_list[i] - oi;
				
				float c0 = magnitude_list[i] * (1 - rf);
				float c1 = magnitude_list[i] * rf;

				float c00 = c0 * (1 - cf);
				float c01 = c0 * cf;
				float c10 = c1 * (1 - cf);
				float c11 = c1 * cf;

				float c000 = c00 * (1 - of), c001 = c00 * of;
				float c010 = c01 * (1 - of), c011 = c01 * of;
				float c100 = c10 * (1 - of), c101 = c10 * of;
				float c110 = c11 * (1 - of), c111 = c11 * of;

				//std::cout << ri << " " << ci << " " << oi << std::endl;
				if (oi < 0)
					oi += num_bins;
				histogram_tensor[ri + 1][ci + 1][oi] += c000;
				histogram_tensor[ri + 1][ci + 1][(oi + 1) % num_bins] += c001;
				histogram_tensor[ri + 1][ci + 2][oi] += c010;
				histogram_tensor[ri + 1][ci + 2][(oi + 1) % num_bins] += c011;
				histogram_tensor[ri + 2][ci + 1][oi] += c100;
				histogram_tensor[ri + 2][ci + 1][(oi + 1) % num_bins] += c101;
				histogram_tensor[ri + 2][ci + 2][oi] += c110;
				histogram_tensor[ri + 2][ci + 2][(oi + 1) % num_bins] += c111;
			}

			std::vector<float> des_vec;//4*4*8
			for (size_t i = 1; i < histogram_tensor.size() - 1; i++)
				for (size_t j = 1; j < histogram_tensor[0].size() - 1; j++)
					for (size_t k = 0; k < histogram_tensor[0][0].size(); k++)
						des_vec.push_back(histogram_tensor[i][j][k]);

			float norm = 0.0;
			for (size_t i = 0; i < des_vec.size(); i++)
				norm += pow(des_vec[i], 2);
			norm = sqrt(norm);
			for (size_t i = 0; i < des_vec.size(); i++)
				des_vec[i] /= norm;

			for (size_t i = 0; i < des_vec.size(); i++)
				if (des_vec[i] > des_max_value)
					des_vec[i] = des_max_value;

			for (size_t i = 0; i < des_vec.size(); i++)
				des_vec[i] = round(512 * des_vec[i]);

			for (size_t i = 0; i < des_vec.size(); i++)
				if (des_vec[i] < 0)
					des_vec[i] = 0;

			for (size_t i = 0; i < des_vec.size(); i++)
				if (des_vec[i] > 255)
					des_vec[i] = 255;

			descriptors_vec.push_back(des_vec);
		}
		
		cv::Mat descriptors = vec2mat(descriptors_vec);
		return descriptors;
	}

	std::pair<std::vector<cv::KeyPoint>, cv::Mat> do_sift(cv::Mat img_src)
	{
		cv::Mat img = img_src.clone();
		img.convertTo(img, CV_32F);
		img = pre_treat_img(img, m_sigma);
		m_num_octave = get_numOfOctave(img);
		std::vector<std::vector<cv::Mat>> gaussian_pyr = construct_gaussian_pyramid(img);
		std::vector<std::vector<cv::Mat>> dog_pyr = construct_DOG(gaussian_pyr);
		//std::cout << dog_pyr[0][0].at<float>(0, 0) << std::endl;
		std::vector<cv::KeyPoint> keypoints = get_keypoints(gaussian_pyr, dog_pyr);
		keypoints = remove_duplicate_points(keypoints);
		cv::Mat descriptors = get_descriptor(keypoints, gaussian_pyr);
		return { keypoints , descriptors };
	}

	cv::Mat do_match(cv::Mat img_src1, std::vector<cv::KeyPoint> kp1, cv::Mat des1,
		cv::Mat img_src2, std::vector<cv::KeyPoint> kp2, cv::Mat des2,
		int pt_flag = 0, int MIN_MATCH_COUNT = 10)
	{
		cv::FlannBasedMatcher flann(new cv::flann::KDTreeIndexParams(5), new cv::flann::SearchParams(50));
		std::vector<std::vector<cv::DMatch>> matches;
		flann.knnMatch(des1, des2, matches, 2);

		std::vector<cv::DMatch> good_match;
		for (auto m : matches)
		{
			if (m[0].distance < 0.7 * m[1].distance)
				good_match.push_back(m[0]);
		}

		cv::Mat img1 = img_src1.clone();
		cv::Mat img2 = img_src2.clone();
		int h1 = img1.rows, w1 = img1.cols;
		int h2 = img2.rows, w2 = img2.cols;
		int new_w = w1 + w2;
		int new_h = std::max(h1, h2);
		cv::Mat new_img = cv::Mat::zeros(cv::Size(new_w, new_h), CV_8UC1);
		int h_offset1 = int(0.5 * (new_h - h1));
		int h_offset2 = int(0.5 * (new_h - h2));

		cv::Mat roi1 = new_img(cv::Rect(0, h_offset1, w1, h1));
		img1.copyTo(roi1);
		cv::Mat roi2 = new_img(cv::Rect(w1, h_offset2, w2, h2));
		img2.copyTo(roi2);

		std::vector<cv::Point> src_pts;
		std::vector<cv::Point> dst_pts;

		if (good_match.size() > MIN_MATCH_COUNT)
		{
			for (auto m : good_match)
			{
				if (pt_flag == 0)
				{
					src_pts.push_back(cv::Point(kp1[m.queryIdx].pt.x * img1.cols, kp1[m.queryIdx].pt.y * img1.rows));
					dst_pts.push_back(cv::Point(kp2[m.trainIdx].pt.x * img2.cols, kp2[m.trainIdx].pt.y * img2.rows));
				}
				else 
				{
					src_pts.push_back(cv::Point(kp1[m.queryIdx].pt.x, kp1[m.queryIdx].pt.y));
					dst_pts.push_back(cv::Point(kp2[m.trainIdx].pt.x, kp2[m.trainIdx].pt.y));
				}
			}
		}

		cv::cvtColor(new_img, new_img, cv::COLOR_GRAY2BGR);

		for (size_t i = 0; i < src_pts.size(); i++)
		{
			cv::Point pt1(src_pts[i].x, src_pts[i].y + h_offset1);
			cv::Point pt2(dst_pts[i].x + w1, dst_pts[i].y + h_offset2);
			cv::line(new_img, pt1, pt2, (0, 0, 255));
		}

		return new_img;
	}

private:
	int m_num_octave;
	int m_num_scale;
	float m_sigma;
	float m_contrast_t;
	float m_eigenvalue_r;
	float m_scale_factor;
	float m_radius_factor;
	int m_num_bins;
	float m_peak_ratio;
};


int main()
{
	//std::cout << -1 % 36 << std::endl;
	cv::Mat img1 = cv::imread("box.png", 0);
	cv::Mat img2 = cv::imread("box_in_scene.png", 0);

	SIFT sift(3, 3, 1.6);
	std::pair<std::vector<cv::KeyPoint>, cv::Mat> p1, p2;
	p1 = sift.do_sift(img1);
	p2 = sift.do_sift(img2);
	std::vector<cv::KeyPoint> kp1, kp2;
	kp1 = p1.first;
	kp2 = p2.first;
	cv::Mat des1, des2;
	des1 = p1.second;
	des2 = p2.second;

	cv::Mat res;
	res = sift.do_match(img1, kp1, des1, img2, kp2, des2, 0, 3);
	cv::imwrite("res.png", res);

	return 0;
}

结果:
在这里插入图片描述
另一种实现,参考:opencv实战:SIFT的实现
这种实现比上面的实现方式更简略,主要差异在于把构建尺度空间以及生成极值点直接用Harris角点代替。

#include <iostream>
#include <numeric>
#include <opencv2/opencv.hpp>
#define _USE_MATH_DEFINES
#include <math.h>


class Sift
{
public:
	Sift(cv::Mat img)
	{
		m_img = img.clone();
		m_r = m_img.rows, m_c = m_img.cols;
		m_gradient = cv::Mat::zeros(m_r, m_c, CV_32F);
		m_angle = cv::Mat::zeros(m_r, m_c, CV_32F);
	};

	std::tuple<cv::Mat, cv::Mat, int> get_result()
	{
		cv::goodFeaturesToTrack(m_img, m_corners, 233, 0.01, 10);
		//std::cout << corners << std::endl;

		cv::GaussianBlur(m_img, m_img, cv::Size(5, 5), 1, 1);
		m_img.convertTo(m_img, CV_32F);

		grad(m_img);

		m_bins = (m_r + m_c) / 80;
		m_length = m_corners.rows;
		m_feature = cv::Mat::zeros(m_length, 128, CV_32F);

		vote();

		for (size_t i = 0; i < m_length; i++)
		{
			cv::Point2f p(m_corners.at<float>(i, 1), m_corners.at<float>(i, 0));
			std::vector<int> val = get_feature(p, m_direct[i]);
			float m = 0;
			for (float k : val)
				m += k * k;
			m = sqrt(m);
			std::vector<float> l;
			for (float k : val)
				l.push_back(k / m);
			cv::Mat temp_row = cv::Mat(l).reshape(1, 1);
			//std::cout << temp_row << std::endl;
			temp_row.copyTo(m_feature.row(i));
		}
		//std::cout << m_feature << std::endl;
		return { m_feature, m_corners, m_length };
	}

	void grad(cv::Mat img)
	{
		int x = m_r, y = m_c;

		cv::Mat kernel_x = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1) / 6;
		cv::Mat kernel_y = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1) / 6;

		cv::Mat gx, gy;
		cv::filter2D(img, gx, -1, kernel_x);
		cv::filter2D(img, gy, -1, kernel_y);
		//std::cout << gx.at<float>(1, 0) << std::endl;
		//std::cout << gy.at<float>(1, 0) << std::endl;

		for (size_t i = 0; i < x; i++)
		{
			for (size_t j = 0; j < y; j++)
			{
				m_gradient.at<float>(i, j) = sqrt(pow(gx.at<float>(i, j), 2) + pow(gy.at<float>(i, j), 2));
				m_angle.at<float>(i, j) = atan2(gy.at<float>(i, j), gx.at<float>(i, j));
			}
		}
		//std::cout << gradient.at<float>(0, 1) << std::endl;
		//std::cout << angle.at<float>(0, 1) << std::endl;
	}

	void vote()
	{
		for (size_t n = 0; n < m_length; n++)
		{
			int y = m_corners.at<float>(n, 0), x = m_corners.at<float>(n, 1);

			std::vector<int> voting(37);

			for (size_t i = std::max(x - m_bins, 0); i < std::min(x + m_bins + 1, m_r); i++)
			{
				for (size_t j = std::max(y - m_bins, 0); j < std::min(y + m_bins + 1, m_c); j++)
				{
					int k = int((m_angle.at<float>(i, j) + M_PI) / (M_PI / 18) + 1);
					if (k >= 37)
						k = 36;
					voting[k] += m_gradient.at<float>(i, j);
				}
			}

			int p = 1;
			for (size_t i = 2; i < 37; i++)
			{
				if (voting[i] > voting[p])
					p = i;
			}
			m_direct.push_back(float(p / 18.0 - 1 - 1.0 / 36) * M_PI);
		}
	}

	float get_theta(float x, float y)
	{
		if ((x < 0 || x >= m_r) || (y < 0 || y >= m_c))
			return 0;
		float dif = m_angle.at<float>(x, y) - m_theta;
		return dif > 0 ? dif : dif + 2 * M_PI;
	}

	float DB_linear(float x, float y)
	{
		int xx = int(x), yy = int(y);
		float dx1 = x - xx, dx2 = xx + 1 - x;
		float dy1 = y - yy, dy2 = yy + 1 - y;
		float val = get_theta(xx, yy) * dx2 * dy2 + get_theta(xx + 1, yy) * dx1 * dy2 + get_theta(xx, yy + 1) * dx2 * dy1 + get_theta(xx + 1, yy + 1) * dx1 * dy1;
		return val;
	}

	std::vector<int> cnt(int x1, int x2, int y1, int y2, int xsign, int ysign)
	{
		std::vector<int> voting(9);
		for (size_t x = x1; x < x2; x++)
		{
			for (size_t y = y1; y < y2; y++)
			{
				cv::Mat p = m_H * x * xsign + m_V * y * ysign;
				int bin = int((DB_linear(p.at<float>(0, 0) + m_pos.x, p.at<float>(0, 1) + m_pos.y)) / (M_PI / 4) + 1);
				if (bin > 8)
					bin = 8;
				voting[bin] += 1;
			}
		}
		std::vector<int> tmp(8);
		std::copy(voting.begin()+1, voting.end(), tmp.begin());
		return tmp;
	}

	std::vector<int> get_feature(cv::Point2f pos, float theta)
	{
		m_pos = pos;
		m_theta = theta;
		m_H = (cv::Mat_<float>(1, 2) << cos(m_theta), sin(m_theta));
		m_V = (cv::Mat_<float>(1, 2) << -sin(m_theta), cos(m_theta));

		m_bins = (m_r + m_c) / 150;
		std::vector<int> val;
		std::vector<int> tmp;
		for (int xsign = -1; xsign <= 1; xsign += 2)
		{
			for (int ysign = -1; ysign <= 1; ysign += 2)
			{
				tmp = cnt(0, m_bins, 0, m_bins, xsign, ysign);
				val.insert(val.end(), tmp.begin(), tmp.end());
				tmp = cnt(m_bins, m_bins * 2, 0, m_bins, xsign, ysign);
				val.insert(val.end(), tmp.begin(), tmp.end());
				tmp = cnt(m_bins, m_bins * 2, m_bins, m_bins * 2, xsign, ysign);
				val.insert(val.end(), tmp.begin(), tmp.end());
				tmp = cnt(0, m_bins, m_bins, m_bins * 2, xsign, ysign);
				val.insert(val.end(), tmp.begin(), tmp.end());
			}
		}
		return val;
	}

private:
	int m_r;
	int m_c;
	int m_bins;
	int m_length;
	float m_theta;
	cv::Mat m_img;
	cv::Mat m_corners;
	cv::Mat m_gradient;
	cv::Mat m_angle;
	cv::Mat m_H;
	cv::Mat m_V;
	cv::Mat m_feature;
	cv::Point2f m_pos;
	std::vector<float> m_direct;
};


cv::Mat merge(cv::Mat img1, cv::Mat img2)
{
	int h1 = img1.rows, w1 = img1.cols;
	int h2 = img2.rows, w2 = img2.cols;
	cv::Mat img;
	if (h1 < h2)
	{
		img = cv::Mat::zeros( h2, w1 + w2, CV_8UC3);
		cv::Mat roi = img(cv::Rect(0, 0, img1.cols, img1.rows));
		img1.copyTo(roi);
		roi = img(cv::Rect(img1.cols, 0, img2.cols, img2.rows));
		img2.copyTo(roi);
	}
	else if (h1 > h2)
	{
		img = cv::Mat::zeros(h1, w1 + w2, CV_8UC3);
		cv::Mat roi = img(cv::Rect(0, 0, img1.cols, img1.rows));
		img1.copyTo(roi);
		roi = img(cv::Rect(img1.cols, 0, img2.cols, img2.rows));
		img2.copyTo(roi);
	}
	return img;
}


int main(int argc, char** argv)
{
	cv::Mat src = cv::imread("source.jpg", 1), src_gray;
	cv::Mat tgt = cv::imread("target.jpg", 1), tgt_gray;

	cv::cvtColor(src, src_gray, cv::COLOR_BGR2GRAY);
	cv::cvtColor(tgt, tgt_gray, cv::COLOR_BGR2GRAY);

	Sift sift_src(src_gray);
	Sift sift_tgt(tgt_gray);

	auto [fs ,cs ,ls] = sift_src.get_result();
	auto [ft, ct, lt] = sift_tgt.get_result();

	cv::Mat img = merge(tgt, src);

	for (size_t i = 0; i < lt; i++)
	{
		std::vector<float> tmp;
		for (size_t j = 0; j < ls; j++)
		{
			//std::cout << ft.row(i) << std::endl;
			//std::cout << fs.row(j) << std::endl;
			float sc = (ft.row(i)).dot(fs.row(j));
			tmp.push_back(sc);
		}

		int b = std::max_element(tmp.begin(), tmp.end()) - tmp.begin();
		float s = *std::max_element(tmp.begin(), tmp.end());

		if (s < 0.8)
			continue;

		cv::Scalar color(rand() % 255, rand() % 255, rand() % 255);
		cv::Point p_start(ct.at<float>(i, 0), ct.at<float>(i, 1));
		cv::Point p_end(cs.at<float>(b, 0) + tgt.cols, cs.at<float>(b, 1));
		cv::line(img, p_start, p_end, color, 1);
	}

	cv::imwrite("mysift.jpg", img);

	return EXIT_SUCCESS;
}

结果:
在这里插入图片描述
代码传送门:https://github.com/taifyang/OpenCV-algorithm

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值