diff --git a/Descriptor.h b/Descriptor.h index b3e523b..cad0da9 100644 --- a/Descriptor.h +++ b/Descriptor.h @@ -7,22 +7,19 @@ * this code :) ******************************************************/ +#pragma once #ifndef DESCRIPTOR_H #define DESCRIPTOR_H - +#include using namespace std; class Descriptor { public: float xi, yi; // The location - vector fv; // The feature vector - - Descriptor() - { - } + vector fv; // The feature vector - Descriptor(float x, float y, vector const& f) + Descriptor(float x, float y, vector const& f) { xi = x; yi = y; @@ -30,4 +27,4 @@ class Descriptor } }; -#endif \ No newline at end of file +#endif diff --git a/KeyPoint.h b/KeyPoint.h index 745b385..9d6053b 100644 --- a/KeyPoint.h +++ b/KeyPoint.h @@ -7,6 +7,7 @@ * this code :) ******************************************************/ +#pragma once #ifndef _KEYPOINT_H #define _KEYPOINT_H @@ -20,13 +21,12 @@ class Keypoint public: float xi; float yi; // It's location - vector mag; // The list of magnitudes at this point - vector orien; // The list of orientations detected - unsigned int scale; // The scale where this was detected - - Keypoint() { } - Keypoint(float x, float y) { xi=x; yi=y; } - Keypoint(float x, float y, vector const& m, vector const& o, unsigned int s) + vector mag; // The list of magnitudes at this point + vector orien; // The list of orientations detected + int scale; // The scale where this was detected + + //defining the constructor + Keypoint(float x, float y, vectorm, vectoro, int s) { xi = x; yi = y; @@ -36,4 +36,4 @@ class Keypoint } }; -#endif \ No newline at end of file +#endif diff --git a/MySIFT.cpp b/MySIFT.cpp index 2ece459..03d7c2e 100644 --- a/MySIFT.cpp +++ b/MySIFT.cpp @@ -10,26 +10,18 @@ // MySIFT.cpp : Defines the entry point for the console application. // -#include "stdafx.h" +#include +#include +#include "mySIFT.h" +using namespace cv; +using namespace std; -#include "SIFT.h" - -#include -#include - -// The main function! int main() { - // Create an instance of SIFT - SIFT *sift = new SIFT("C:\\house.jpg", 4, 2); - - sift->DoSift(); // Find keypoints - sift->ShowAbsSigma(); // Display the sigma table - sift->ShowKeypoints(); // Show the keypoints - cvWaitKey(0); // Wait for a keypress - - // Cleanup and exit - delete sift; + Mat img = imread("car.jpg"); + meSIFT x(img,4,2); + x.DoSift(); + x.ShowKeypoints(); return 0; } diff --git a/SIFT.cpp b/SIFT.cpp index ae60532..f82c935 100644 --- a/SIFT.cpp +++ b/SIFT.cpp @@ -7,10 +7,11 @@ * this code :) ******************************************************/ -#include "stdafx.h" - #include "SIFT.h" - +#include "stdafx.h" +#include +//This file containes all the method of +//the class meSIFT #define SIGMA_ANTIALIAS 0.5 #define SIGMA_PREBLUR 1.0 #define CURVATURE_THRESHOLD 5.0 @@ -23,1010 +24,813 @@ #define FVSIZE 128 #define FV_THRESHOLD 0.2 -// SaveFloatingPointImage() -// The standard HighGUI functions can save only 8bit images. This -// function converts a floating point image (with values 0..1) into -// a normal 8bit image (with values 0..255), and then saves it. -void SaveFloatingPointImage(const char *filename, IplImage* img) -{ - IplImage* dup = cvCreateImage(cvGetSize(img), 8, 1); - cvCvtScale(img, dup, 255.0); - - cvSaveImage(filename, dup); - cvReleaseImage(&dup); -} +using namespace cv; -// Constructor: Give a preloaded image and provide the desired number -// of octaves and intervals -SIFT::SIFT(IplImage* img, int octaves, int intervals) +//intialising the the meSIFT class +//with a constructor,taking 3 arguments +//the src image, the number of octaves in the image +//and the number of intervals +meSIFT::meSIFT(Mat src,int octaves,int intervals) { - // Store the image internally - m_srcImage = cvCloneImage(img); - - // Set the number of octaves and intervals + srcImage = src.clone(); m_numOctaves = octaves; m_numIntervals = intervals; - - // Proceed to initialize the algorithm - GenerateLists(); -} - -// Constructor: Give a filename and the desired number of octaves -// and intervals -SIFT::SIFT(const char* filename, int octaves, int intervals) -{ - // Load the image - m_srcImage = cvLoadImage(filename); - - // Set the number of octaves and intervals - m_numOctaves = octaves; - m_numIntervals = intervals; - - // Proceed to initialize the algorithm - GenerateLists(); -} - -// AllocateMemory() -// This function allocates memory for the scale space (the multiple gaussian images, -// the difference of guassian images) -void SIFT::GenerateLists() -{ - // A variable for the loops - unsigned int i=0; - - // Create a 2D array of gaussian blurred images - m_gList = new IplImage**[m_numOctaves]; - for(i=0;inChannels==3) - { - cvCvtColor(m_srcImage, imgTemp, CV_BGR2GRAY); - } - else - { - cvCopy(m_srcImage, imgTemp); - } - // Finally, generate the floating point image... convert 0..255 range into 0..1 - for(int x=0;xwidth;x++) - { - for(int y=0;yheight;y++) - { - cvSetReal2D(imgGray, y, x, cvGetReal2D(imgTemp, y, x)/255.0); - } - } + Mat src_gray; + //intialising a floating point image + Mat imgFloat; + //we need a gray scale image + cvtColor(srcImage,src_gray, COLOR_BGR2GRAY); + //we set each and every pixel of the floating point image + //converting from range(0 to 255) to (0 to 1) + src_gray.convertTo(imgFloat,CV_32F,1.0/255,0); // Lowe claims blur the image with a sigma of 0.5 and double it's dimensions // to increase the number of stable keypoints - cvSmooth(imgGray, imgGray, CV_GAUSSIAN, 0, 0, SIGMA_ANTIALIAS); - - // Create an image double the dimensions, resize imgGray and store it in m_gList[0][0] - m_gList[0][0] = cvCreateImage(cvSize(imgGray->width*2, imgGray->height*2), IPL_DEPTH_32F , 1); - cvPyrUp(imgGray, m_gList[0][0]); - - // Preblur this base image - cvSmooth(m_gList[0][0], m_gList[0][0], CV_GAUSSIAN, 0, 0, SIGMA_PREBLUR); - - // SaveFloatingPointImage("C:\\SIFT Test\\Gaussian\\g_octave_0_scale_0.jpg", m_gList[0][0]); - + //so we apply Gaussian blur + GaussianBlur(imgFloat, imgFloat, Size(0, 0), SIGMA_ANTIALIAS); //CHANGEABLE + resize(imgFloat,imgFloat,Size(imgFloat.size()*2)); + m_glist.push_back(vector()); + m_glist[0].push_back(imgFloat.clone()); + pyrUp(imgFloat, m_glist[0][0]); + GaussianBlur(m_glist[0][0],m_glist[0][0],Size(0,0),SIGMA_PREBLUR); + //imshow("0", m_glist[0][0]); + //waitKey(0); + + //keeping a track of the sigmas double initSigma = sqrt(2.0f); + m_absSigma.push_back(vector()); + m_absSigma[0].push_back(initSigma * 0.5); - // Keep a track of the sigmas - m_absSigma[0][0] = initSigma * 0.5; - - // Now for the actual image generation - for(i=0;i()); + /*if (i > 0) + { + m_absSigma.push_back(vector()); + m_glist.push_back(vector()); + }*/ + for (j = 1; j()); + m_glist.push_back(vector()); + m_glist[i+1].push_back(temp3); + m_absSigma[i+1].push_back(m_absSigma[i][m_numIntervals]); } } } // DetectExtrema() // Locates extreme points (maxima and minima) -// Relatively simple stuff -void SIFT::DetectExtrema() +void meSIFT::DetectExtrema() { - printf("Detecting extrema...\n"); - - // Looping variables - unsigned int i, j, xi, yi; - - // Some variables we'll use later on double curvature_ratio, curvature_threshold; - IplImage *middle, *up, *down; int scale; double dxx, dyy, dxy, trH, detH; - - unsigned int num=0; // Number of keypoins detected - unsigned int numRemoved=0; // The number of key points rejected because they failed a test - - curvature_threshold = (CURVATURE_THRESHOLD+1)*(CURVATURE_THRESHOLD+1)/CURVATURE_THRESHOLD; - - // Detect extrema in the DoG images - for(i=0;i()); + for (int j = 1; j < m_numIntervals + 1; j++) { // Allocate memory and set all points to zero ("not key point") - m_extrema[i][j-1] = cvCreateImage(cvGetSize(m_dogList[i][0]), 8, 1); - cvZero(m_extrema[i][j-1]); + Mat temp = Mat::zeros(m_dogList[i][0].size(), CV_8UC1); + m_extrema[i].push_back(temp); - // Images just above and below, in the current octave + //finding the images just above and below + //for the current octave middle = m_dogList[i][j]; - up = m_dogList[i][j+1]; - down = m_dogList[i][j-1]; + up = m_dogList[i][j + 1]; + down = m_dogList[i][j - 1]; - for(xi=1;xiwidth-1;xi++) + //intializing certain variables + int xi, yi; + for (xi = 1; xi < m_dogList[i][j].rows - 1; xi++) { - for(yi=1;yiheight-1;yi++) + for (yi = 1; yi < m_dogList[i][j].cols - 1; yi++) { // true if a keypoint is a maxima/minima // but needs to be tested for contrast/edge thingy bool justSet = false; - - double currentPixel = cvGetReal2D(middle, yi, xi); - - // Check for a maximum - if (currentPixel > cvGetReal2D(middle, yi-1, xi ) && - currentPixel > cvGetReal2D(middle, yi+1, xi ) && - currentPixel > cvGetReal2D(middle, yi , xi-1) && - currentPixel > cvGetReal2D(middle, yi , xi+1) && - currentPixel > cvGetReal2D(middle, yi-1, xi-1) && - currentPixel > cvGetReal2D(middle, yi-1, xi+1) && - currentPixel > cvGetReal2D(middle, yi+1, xi+1) && - currentPixel > cvGetReal2D(middle, yi+1, xi-1) && - currentPixel > cvGetReal2D(up, yi , xi ) && - currentPixel > cvGetReal2D(up, yi-1, xi ) && - currentPixel > cvGetReal2D(up, yi+1, xi ) && - currentPixel > cvGetReal2D(up, yi , xi-1) && - currentPixel > cvGetReal2D(up, yi , xi+1) && - currentPixel > cvGetReal2D(up, yi-1, xi-1) && - currentPixel > cvGetReal2D(up, yi-1, xi+1) && - currentPixel > cvGetReal2D(up, yi+1, xi+1) && - currentPixel > cvGetReal2D(up, yi+1, xi-1) && - currentPixel > cvGetReal2D(down, yi , xi ) && - currentPixel > cvGetReal2D(down, yi-1, xi ) && - currentPixel > cvGetReal2D(down, yi+1, xi ) && - currentPixel > cvGetReal2D(down, yi , xi-1) && - currentPixel > cvGetReal2D(down, yi , xi+1) && - currentPixel > cvGetReal2D(down, yi-1, xi-1) && - currentPixel > cvGetReal2D(down, yi-1, xi+1) && - currentPixel > cvGetReal2D(down, yi+1, xi+1) && - currentPixel > cvGetReal2D(down, yi+1, xi-1) ) + float curr = middle.at(Point(yi, xi)); + if (curr > middle.at(Point(yi + 1, xi)) && + curr > middle.at(Point(yi - 1, xi)) && + curr > middle.at(Point(yi, xi + 1)) && + curr > middle.at(Point(yi, xi - 1)) && + curr > middle.at(Point(yi + 1, xi + 1)) && + curr > middle.at(Point(yi - 1, xi - 1)) && + curr > middle.at(Point(yi - 1, xi + 1)) && + curr > middle.at(Point(yi + 1, xi - 1)) && + curr > up.at(Point(yi, xi)) && + curr > up.at(Point(yi + 1, xi)) && + curr > up.at(Point(yi - 1, xi)) && + curr > up.at(Point(yi, xi + 1)) && + curr > up.at(Point(yi, xi - 1)) && + curr > up.at(Point(yi + 1, xi + 1)) && + curr > up.at(Point(yi - 1, xi - 1)) && + curr > up.at(Point(yi - 1, xi + 1)) && + curr > up.at(Point(yi + 1, xi - 1)) && + curr > down.at(Point(yi, xi)) && + curr > down.at(Point(yi + 1, xi)) && + curr > down.at(Point(yi - 1, xi)) && + curr > down.at(Point(yi, xi + 1)) && + curr > down.at(Point(yi, xi - 1)) && + curr > down.at(Point(yi + 1, xi + 1)) && + curr > down.at(Point(yi - 1, xi - 1)) && + curr > down.at(Point(yi - 1, xi + 1)) && + curr > down.at(Point(yi + 1, xi - 1)) + ) { - cvSetReal2D(m_extrema[i][j-1], yi, xi, 255); num++; justSet = true; + m_extrema[i][j - 1].at(Point(yi, xi)) = 255; } - // Check if it's a minimum - else if (currentPixel < cvGetReal2D(middle, yi-1, xi ) && - currentPixel < cvGetReal2D(middle, yi+1, xi ) && - currentPixel < cvGetReal2D(middle, yi , xi-1) && - currentPixel < cvGetReal2D(middle, yi , xi+1) && - currentPixel < cvGetReal2D(middle, yi-1, xi-1) && - currentPixel < cvGetReal2D(middle, yi-1, xi+1) && - currentPixel < cvGetReal2D(middle, yi+1, xi+1) && - currentPixel < cvGetReal2D(middle, yi+1, xi-1) && - currentPixel < cvGetReal2D(up, yi , xi ) && - currentPixel < cvGetReal2D(up, yi-1, xi ) && - currentPixel < cvGetReal2D(up, yi+1, xi ) && - currentPixel < cvGetReal2D(up, yi , xi-1) && - currentPixel < cvGetReal2D(up, yi , xi+1) && - currentPixel < cvGetReal2D(up, yi-1, xi-1) && - currentPixel < cvGetReal2D(up, yi-1, xi+1) && - currentPixel < cvGetReal2D(up, yi+1, xi+1) && - currentPixel < cvGetReal2D(up, yi+1, xi-1) && - currentPixel < cvGetReal2D(down, yi , xi ) && - currentPixel < cvGetReal2D(down, yi-1, xi ) && - currentPixel < cvGetReal2D(down, yi+1, xi ) && - currentPixel < cvGetReal2D(down, yi , xi-1) && - currentPixel < cvGetReal2D(down, yi , xi+1) && - currentPixel < cvGetReal2D(down, yi-1, xi-1) && - currentPixel < cvGetReal2D(down, yi-1, xi+1) && - currentPixel < cvGetReal2D(down, yi+1, xi+1) && - currentPixel < cvGetReal2D(down, yi+1, xi-1) ) + + //checking if it's a minima + if (curr < middle.at(Point(yi + 1, xi)) && + curr < middle.at(Point(yi - 1, xi)) && + curr < middle.at(Point(yi, xi + 1)) && + curr < middle.at(Point(yi, xi - 1)) && + curr < middle.at(Point(yi + 1, xi + 1)) && + curr < middle.at(Point(yi - 1, xi - 1)) && + curr < middle.at(Point(yi - 1, xi + 1)) && + curr < middle.at(Point(yi + 1, xi - 1)) && + curr < up.at(Point(yi, xi)) && + curr < up.at(Point(yi + 1, xi)) && + curr < up.at(Point(yi - 1, xi)) && + curr < up.at(Point(yi, xi + 1)) && + curr < up.at(Point(yi, xi - 1)) && + curr < up.at(Point(yi + 1, xi + 1)) && + curr < up.at(Point(yi - 1, xi - 1)) && + curr < up.at(Point(yi - 1, xi + 1)) && + curr < up.at(Point(yi + 1, xi - 1)) && + curr < down.at(Point(yi, xi)) && + curr < down.at(Point(yi + 1, xi)) && + curr < down.at(Point(yi - 1, xi)) && + curr < down.at(Point(yi, xi + 1)) && + curr < down.at(Point(yi, xi - 1)) && + curr < down.at(Point(yi + 1, xi + 1)) && + curr < down.at(Point(yi - 1, xi - 1)) && + curr < down.at(Point(yi - 1, xi + 1)) && + curr < down.at(Point(yi + 1, xi - 1)) + ) { - cvSetReal2D(m_extrema[i][j-1], yi, xi, 255); num++; justSet = true; + m_extrema[i][j - 1].at(Point(yi, xi)) = 255; } - // The contrast check - if(justSet && fabs(cvGetReal2D(middle, yi, xi)) < CONTRAST_THRESHOLD) + //contrast check + if (justSet && fabs(middle.at(Point(yi, xi))) < CONTRAST_THRESHOLD) { - cvSetReal2D(m_extrema[i][j-1], yi, xi, 0); num--; numRemoved++; - - justSet=false; + m_extrema[i][j - 1].at(Point(yi, xi)) = 0; + justSet = false; } - - // The edge check - if(justSet) + //edge check + if (justSet) { - dxx = (cvGetReal2D(middle, yi-1, xi) + - cvGetReal2D(middle, yi+1, xi) - - 2.0*cvGetReal2D(middle, yi, xi)); - - dyy = (cvGetReal2D(middle, yi, xi-1) + - cvGetReal2D(middle, yi, xi+1) - - 2.0*cvGetReal2D(middle, yi, xi)); + dxx = (middle.at(Point(yi + 1, xi)) + middle.at(Point(yi - 1, xi)) - 2.0 * middle.at(Point(yi, xi))); + dyy = (middle.at(Point(yi, xi + 1)) + middle.at(Point(yi, xi - 1)) - 2.0 * middle.at(Point(yi, xi))); + dxy = (middle.at(Point(yi + 1, xi + 1)) + middle.at(Point(yi - 1, xi - 1)) - middle.at(Point(yi - 1, xi + 1)) - middle.at(Point(yi + 1, xi - 1))) / 4.0; - dxy = (cvGetReal2D(middle, yi-1, xi-1) + - cvGetReal2D(middle, yi+1, xi+1) - - cvGetReal2D(middle, yi+1, xi-1) - - cvGetReal2D(middle, yi-1, xi+1)) / 4.0; trH = dxx + dyy; - detH = dxx*dyy - dxy*dxy; + detH = dxx * dyy - dxy * dxy; - curvature_ratio = trH*trH/detH; + curvature_ratio = trH * trH / detH; //printf("Threshold: %f - Ratio: %f\n", curvature_threshold, curvature_ratio); - if(detH<0 || curvature_ratio>curvature_threshold) + if (detH<0 || curvature_ratio>curvature_threshold) { - cvSetReal2D(m_extrema[i][j-1], yi, xi, 0); num--; numRemoved++; - - justSet=false; + m_extrema[i][j - 1].at(Point(yi, xi)) = 0; + justSet = false; } } } } - // Save the image - /*char* filename = new char[200]; - sprintf(filename, "C:\\SIFT Test\\Extrema\\extrema_oct_%d_scale_%d.jpg", i, j-1); - cvSaveImage(filename, m_extrema[i][j-1]);*/ + //imshow(to_string(10*j+i),m_extrema[i][j-1]); + //waitKey(0); } } m_numKeypoints = num; - printf("Found %d keypoints\n", num); - printf("Rejected %d keypoints\n", numRemoved); + //printf("Found %d keypoints\n", num); + //printf("Rejected %d keypoints\n", numRemoved); +} + +// GetKernelSize() +// Returns the size of the kernal for the Gaussian blur given the sigma and +// cutoff value. +int meSIFT::GetKernelSize(double sigma, double cut_off) +{ + int i; + for (i = 0; i < MAX_KERNEL_SIZE; i++) + if (exp(-((double)(i * i)) / (2.0 * sigma * sigma)) < cut_off) + break; + int size = 2 * i - 1; + return size; } + // AssignOrientations() // For all the key points, generate an orientation. -void SIFT::AssignOrientations() + +void meSIFT::AssignOrientations() { - printf("Assigning orientations...\n"); - unsigned int i, j, k, xi, yi; + int i, j, k, xi, yi; int kk, tt; - // These images hold the magnitude and direction of gradient - // for all blurred out images - IplImage*** magnitude = new IplImage**[m_numOctaves]; - IplImage*** orientation = new IplImage**[m_numOctaves]; + //the 2-D vectors for Magnitude and orientation + vector >magnitude; + vector >orientation; - // Allocate some memory - for(i=0;i()); + orientation.push_back(vector()); + for (j = 1; j < m_numIntervals + 1; j++) { - magnitude[i][j-1] = cvCreateImage(cvGetSize(m_gList[i][j]), 32, 1); - orientation[i][j-1] = cvCreateImage(cvGetSize(m_gList[i][j]), 32, 1); - - cvZero(magnitude[i][j-1]); - cvZero(orientation[i][j-1]); + Mat temp1 = Mat::zeros(m_glist[i][j].size(),CV_32FC1); + Mat temp2 = Mat::zeros(m_glist[i][j].size(), CV_32FC1); + magnitude[i].push_back(temp1); + orientation[i].push_back(temp2); // Iterate over the gaussian image with the current octave and interval - for(xi=1;xiwidth-1;xi++) + for (xi = 1; xi < m_glist[i][j].rows-1; xi++) { - for(yi=1;yiheight-1;yi++) + for (yi = 1; yi < m_glist[i][j].cols-1; yi++) { - // Calculate gradient - double dx = cvGetReal2D(m_gList[i][j], yi, xi+1) - cvGetReal2D(m_gList[i][j], yi, xi-1); - double dy = cvGetReal2D(m_gList[i][j], yi+1, xi) - cvGetReal2D(m_gList[i][j], yi-1, xi); + float dx = m_glist[i][j].at(Point(yi, xi + 1)) - m_glist[i][j].at(Point(yi, xi - 1)); + float dy= m_glist[i][j].at(Point(yi+1, xi)) - m_glist[i][j].at(Point(yi-1, xi)); - // Store magnitude - cvSetReal2D(magnitude[i][j-1], yi, xi, sqrt(dx*dx + dy*dy)); + //storing the magnitude in the new magnitude grid + magnitude[i][j - 1].at(Point(yi, xi)) = sqrt(dx*dx+dy*dy); - // Store orientation as radians - double ori=atan(dy/dx); - cvSet2D(orientation[i][j-1], yi, xi, cvScalar(ori)); + //calculating and storing the orientation + float ori = atan(dy / dx); + orientation[i][j - 1].at(Point(yi, xi)) = ori; } } - - // Save these images for fun - /*char* filename = new char[200]; - sprintf(filename, "C:\\SIFT Test\\Mag\\mag_oct_%d_scl_%d.jpg", i, j-1); - cvSaveImage(filename, magnitude[i][j-1]); - - sprintf(filename, "C:\\SIFT Test\\Ori\\ori_oct_%d_scl_%d.jpg", i, j-1); - cvSaveImage(filename, orientation[i][j-1]);*/ + //imshow(to_string(10 * j + i), magnitude[i][j-1]); + //waitKey(0); + //imshow(to_string(10 * j + i), orientation[i][j-1]); + //waitKey(0); } } // The histogram with 8 bins - double* hist_orient = new double[NUM_BINS]; - - // Go through all octaves - for(i=0;iwidth; - unsigned int height= m_gList[i][0]->height; - - // Go through all intervals in the current scale - for(j=1;j(Point(yi, xi)); + if (val!=0) { // Reset the histogram thingy - for(k=0;k=width || yi+tt<0 || yi+tt>=height) + if (xi + kk < 0 || xi + kk >= height || yi + tt < 0 || yi + tt >= width) continue; - double sampleOrient = cvGetReal2D(orientation[i][j-1], yi+tt, xi+kk); + float sample_orient = orientation[i][j-1].at(Point(yi + tt, xi + kk)); - if(sampleOrient <=-M_PI || sampleOrient>M_PI) - printf("Bad Orientation: %f\n", sampleOrient); - - sampleOrient+=M_PI; + //if (sampleOrient <= -M_PI || sampleOrient > M_PI) + //printf("Bad Orientation: %f\n", sampleOrient); //CHANGEABLE + + sample_orient+=(float)M_PI; + + int sample_orient_degrees = int(sample_orient * 180 / M_PI); + hist_orient[(int)sample_orient_degrees / (360 / NUM_BINS)] += img_weight.at(Point(yi + tt, xi + kk)); + img_mask.at(Point(yi + tt, xi + kk))=255; - // Convert to degrees - unsigned int sampleOrientDegrees = sampleOrient * 180 / M_PI; - hist_orient[(int)sampleOrientDegrees / (360/NUM_BINS)] += cvGetReal2D(imgWeight, yi+tt, xi+kk); - cvSetReal2D(imgMask, yi+tt, xi+kk, 255); } } - + // We've computed the histogram. Now check for the maximum - double max_peak = hist_orient[0]; - unsigned int max_peak_index = 0; - for(k=1;kmax_peak) + if (hist_orient[k] > max_peak) { - max_peak=hist_orient[k]; + max_peak = hist_orient[k]; max_peak_index = k; } } - // List of magnitudes and orientations at the current extrema - vector orien; - vector mag; - for(k=0;k orien; + vector mag; + + for (int k = 0; k < NUM_BINS; k++) { // Do we have a good peak? - if(hist_orient[k]> 0.8*max_peak) + if (hist_orient[k] > 0.8 * max_peak) { // Three points. (x2,y2) is the peak and (x1,y1) // and (x3,y3) are the neigbours to the left and right. // If the peak occurs at the extreme left, the "left // neighbour" is equal to the right most. Similarly for // the other case (peak is rightmost) - double x1 = k-1; - double y1; - double x2 = k; - double y2 = hist_orient[k]; - double x3 = k+1; - double y3; - - if(k==0) + float x1 = (float)(k - 1); + float y1; + float x2 = (float)k; + float y2 = hist_orient[k]; + float x3 = (float)(k + 1); + float y3; + //cout << x1 << " " << x2 << " " << x3 << endl; + if (k ==0) { - y1 = hist_orient[NUM_BINS-1]; + y1 = hist_orient[NUM_BINS - 1]; y3 = hist_orient[1]; } - else if(k==NUM_BINS-1) + else if (k == NUM_BINS - 1) { - y1 = hist_orient[NUM_BINS-1]; + y1 = hist_orient[NUM_BINS - 2]; ///CHANGED y3 = hist_orient[0]; } else { - y1 = hist_orient[k-1]; - y3 = hist_orient[k+1]; + y1 = hist_orient[k - 1]; + y3 = hist_orient[k + 1]; } + + // Next we fit a downward parabola aound // these three points for better accuracy // A downward parabola has the general form // - // y = a * x^2 + bx + c - // Now the three equations stem from the three points - // (x1,y1) (x2,y2) (x3.y3) are + // y = a * x^2 + bx + c + // Now the three equations stem from the three points + // (x1,y1) (x2,y2) (x3.y3) are // - // y1 = a * x1^2 + b * x1 + c - // y2 = a * x2^2 + b * x2 + c - // y3 = a * x3^2 + b * x3 + c + // y1 = a * x1^2 + b * x1 + c + // y2 = a * x2^2 + b * x2 + c + // y3 = a * x3^2 + b * x3 + c // - // in Matrix notation, this is y = Xb, where - // y = (y1 y2 y3)' b = (a b c)' and - // - // x1^2 x1 1 - // X = x2^2 x2 1 - // x3^2 x3 1 - // - // OK, we need to solve this equation for b - // this is done by inverse the matrix X - // - // b = inv(X) Y - - double *b = new double[3]; - CvMat *X = cvCreateMat(3, 3, CV_32FC1); - CvMat *matInv = cvCreateMat(3, 3, CV_32FC1); - - cvSetReal2D(X, 0, 0, x1*x1); - cvSetReal2D(X, 1, 0, x1); - cvSetReal2D(X, 2, 0, 1); - - cvSetReal2D(X, 0, 1, x2*x2); - cvSetReal2D(X, 1, 1, x2); - cvSetReal2D(X, 2, 1, 1); - - cvSetReal2D(X, 0, 2, x3*x3); - cvSetReal2D(X, 1, 2, x3); - cvSetReal2D(X, 2, 2, 1); - - // Invert the matrix - cvInv(X, matInv); - - b[0] = cvGetReal2D(matInv, 0, 0)*y1 + cvGetReal2D(matInv, 1, 0)*y2 + cvGetReal2D(matInv, 2, 0)*y3; - b[1] = cvGetReal2D(matInv, 0, 1)*y1 + cvGetReal2D(matInv, 1, 1)*y2 + cvGetReal2D(matInv, 2, 1)*y3; - b[2] = cvGetReal2D(matInv, 0, 2)*y1 + cvGetReal2D(matInv, 1, 2)*y2 + cvGetReal2D(matInv, 2, 2)*y3; - - double x0 = -b[1]/(2*b[0]); - + // in Matrix notation, this is y = Xb, where + // y = (y1 y2 y3)' b = (a b c)' and + // + // x1^2 x1 1 + // X = x2^2 x2 1 + // x3^2 x3 1 + // + // OK, we need to solve this equation for b + // this is done by inverse the matrix X + // + // b = inv(X) Y + + float* b = new float[3]; + Mat X = Mat(Size(3,3),CV_32FC1); + Mat matInv = Mat(Size(3,3),CV_32FC1); + X.at(Point(0,0))=x1*x1; + X.at(Point(1,0))=x1; + X.at(Point(2,0))=1; + X.at(Point(0,1))=x2*x2; + X.at(Point(1,1))=x2; + X.at(Point(2,1))=1; + X.at(Point(0,2))=x3*x3; + X.at(Point(1,2))=x3; + X.at(Point(2, 2)) =1; + + //finding and storing the inverse + invert(X,matInv); + /*for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + cout << matInv.at(Point(j,i)) << " "; + } + cout << endl; + }*/ + + b[0] = matInv.at(Point(0,0))*y1+matInv.at(Point(1,0))*y2+matInv.at(Point(2,0))*y3; + b[1] = matInv.at(Point(0,1))*y1+matInv.at(Point(1,1))*y2+matInv.at(Point(2,1))*y3; + b[2] = matInv.at(Point(0, 2))*y1 + matInv.at(Point(1, 2))*y2 + matInv.at(Point(2, 2))*y3; + + float x0 = -b[1] / (2 * b[0]); + // + //cout << b[0] << endl; // Anomalous situation - if(fabs(x0)>2*NUM_BINS) - x0=x2; - - while(x0<0) + if (fabs(x0) > 2 * NUM_BINS) + x0 = x2; + while (x0 < 0) x0 += NUM_BINS; - while(x0>= NUM_BINS) - x0-= NUM_BINS; + while (x0 >= NUM_BINS) + x0 -= NUM_BINS; // Normalize it - double x0_n = x0*(2*M_PI/NUM_BINS); - - assert(x0_n>=0 && x0_n<2*M_PI); - x0_n -= M_PI; - assert(x0_n>=-M_PI && x0_n= 0 && x0_n < 2 * M_PI); + x0_n -= (float)M_PI; + assert(x0_n >= -M_PI && x0_n < M_PI); orien.push_back(x0_n); + //cout << orien.back() << " "; mag.push_back(hist_orient[k]); + + + } } - + //cout << endl; // Save this keypoint into the list - m_keyPoints.push_back(Keypoint(xi*scale/2, yi*scale/2, mag, orien, i*m_numIntervals+j-1)); + float r = float(xi * scale / 2); + float s = float(yi * scale / 2); + //cout << orien[0] << endl; + //if (orien.empty()) cout << "EMpty!" << endl; + Keypoint k(r, s, mag, orien, i* m_numIntervals + j - 1); + m_keyPoints.push_back(k); } } } - // Save the regions! - /*char* filename = new char[200]; - sprintf(filename, "C:\\SIFT Test\\Orientation Region\\ori_region_oct_%d_scl_%d.jpg", i, j-1); - cvSaveImage(filename, imgMask);*/ - cvReleaseImage(&imgMask); + //imshow(to_string(10 * j + i), img_mask); + //waitKey(0); } } // Finally, we're done with all the magnitude and orientation images. - // Erase them from RAM - assert(m_keyPoints.size() == m_numKeypoints); - for(i=0;i(Point(j, i)) = (float)temp; + sog += (float)temp; + } } - // These two loops calculate the interpolated thingy for all octaves - // and subimages - for(i=0;iwidth; - unsigned int height =m_gList[i][j]->height; + float val = ret.at(Point(j, i)); + ret.at(Point(j, i)) = (float)(1.0 / (sog * val)); + } + } - // Create an image and zero it out. - IplImage* imgTemp = cvCreateImage(cvSize(width*2, height*2), 32, 1); - cvZero(imgTemp); + return ret; +} - // Scale it up. This will give us "access" to in betweens - cvPyrUp(m_gList[i][j], imgTemp); +// gaussian2D +// Returns the value of the bell curve at a (x,y) for a given sigma +double meSIFT::gaussian2D(double x, double y, double sigma) +{ + double ret = 1.0 / (2 * M_PI * sigma * sigma) * exp(-(x * x + y * y) / (2.0 * sigma * sigma)); - // Allocate memory and zero them - imgInterpolatedMagnitude[i][j-1] = cvCreateImage(cvSize(width+1, height+1), 32, 1); - imgInterpolatedOrientation[i][j-1] = cvCreateImage(cvSize(width+1, height+1), 32, 1); - cvZero(imgInterpolatedMagnitude[i][j-1]); - cvZero(imgInterpolatedOrientation[i][j-1]); + return ret; +} + +// ExtractKeypointDescriptors() +// Generates a unique descriptor for each keypoint descriptor +/* +void meSIFT::ExtractKeypointDescriptors() +{ + vector >imginterpolatedMagnitude; + vector >imginterpolatedOrientation; + for (int i = 0; i < m_numOctaves; i++) + { + imginterpolatedMagnitude.push_back(vector()); + imginterpolatedOrientation.push_back(vector()); + for (int j = 1; j < m_numIntervals + 1; j++) + { + Mat temp = Mat::zeros(m_glist[i][j].size()*2,CV_32FC1); + // Scale it up. This will give us "access" to in betweens + pyrUp(m_glist[i][j], temp); + int width = m_glist[i][j].cols; + int height = m_glist[i][j].rows; + Mat temp1 = Mat::zeros(Size(width+1,height+1), CV_32FC1); + Mat temp2 = Mat::zeros(Size(width + 1, height + 1), CV_32FC1); + imginterpolatedMagnitude[i].push_back(temp1); + imginterpolatedOrientation[i].push_back(temp2); // Do the calculations - for(float ii=1.5;ii(Point(jj,ii+1))+m_glist[i][j].at(Point(jj,ii)))/2.0-(float)(m_glist[i][j].at(Point(jj,ii))+m_glist[i][j].at(Point(jj,ii-1)))/2.0; + float dy = (float)(m_glist[i][j].at(Point(jj,ii+1))+m_glist[i][j].at(Point(jj,ii)))/2.0-(float)(m_glist[i][j].at(Point(jj,ii))+m_glist[i][j].at(Point(jj,ii-1)))/2.0; + int iii = ii + 1; + int jjj = jj + 1; + if (iii <= height && jjj <= width) + { + imginterpolatedMagnitude[i][j - 1].at(Point(jjj, iii)) = sqrt(dx*dx+dy*dy); + imginterpolatedOrientation[i][j-1].at(Point(jjj,iii))= (atan2(dy, dx) == M_PI) ? float(-M_PI) : float(atan2(dy, dx)); + } } } - // Pad the edges with zeros - for(unsigned int iii=0;iii(Point(0,iii))= 0.0; + imginterpolatedMagnitude[i][j-1].at(Point(width,iii))= 0.0; + imginterpolatedOrientation[i][j-1].at(Point(0,iii))= 0.0; + imginterpolatedOrientation[i][j-1].at(Point(width,iii))= 0.0; } - - for(unsigned int jjj=0;jjj(Point(jjj, 0)) = 0.0; + imginterpolatedMagnitude[i][j - 1].at(Point(jjj,height)) = 0.0; + imginterpolatedOrientation[i][j - 1].at(Point(jjj,0)) = 0.0; + imginterpolatedOrientation[i][j - 1].at(Point(jjj,height)) = 0.0; } - - // Now we have the imgInterpolated* ready. Store and get started - /*char* filename = new char[200]; - sprintf(filename, "C:\\SIFT Test\\Interpolated Mag\\intmag_oct_%d_scl_%d.jpg", i, j-1); - cvSaveImage(filename, imgInterpolatedMagnitude[i][j-1]); - - sprintf(filename, "C:\\SIFT Test\\Interpolated Ori\\intori_oct_%d_scl_%d.jpg", i, j-1); - cvSaveImage(filename, imgInterpolatedOrientation[i][j-1]);*/ - - cvReleaseImage(&imgTemp); - + //imshow(to_string(10 * j + i),imginterpolatedMagnitude[i][j-1]); + //waitKey(0); + //imshow(to_string(10 * j + i),imginterpolatedOrientation[i][j-1]); + //waitKey(0); } } - // Build an Interpolated Gaussian Table of size FEATURE_WINDOW_SIZE // Lowe suggests sigma should be half the window size // This is used to construct the "circular gaussian window" to weight // magnitudes - CvMat *G = BuildInterpolatedGaussianTable(FEATURE_WINDOW_SIZE, 0.5*FEATURE_WINDOW_SIZE); - - vector hist(DESC_NUM_BINS); - - // Loop over all keypoints - for(unsigned int ikp = 0;ikp hist(DESC_NUM_BINS); + for (int ikp = 0; ikp < m_numKeypoints; ikp++) { - unsigned int scale = m_keyPoints[ikp].scale; + int scale = m_keyPoints[ikp].scale; float kpxi = m_keyPoints[ikp].xi; float kpyi = m_keyPoints[ikp].yi; - float descxi = kpxi; float descyi = kpyi; - - unsigned int ii = (unsigned int)(kpxi*2) / (unsigned int)(pow(2.0, (double)scale/m_numIntervals)); - unsigned int jj = (unsigned int)(kpyi*2) / (unsigned int)(pow(2.0, (double)scale/m_numIntervals)); - - unsigned int width = m_gList[scale/m_numIntervals][0]->width; - unsigned int height = m_gList[scale/m_numIntervals][0]->height; - - vector orien = m_keyPoints[ikp].orien; - vector mag = m_keyPoints[ikp].mag; - + int ii = (int)(kpxi * 2) / (int)(pow(2.0, (double)scale / m_numIntervals)); + int jj = (int)(kpyi * 2) / (int)(pow(2.0, (double)scale / m_numIntervals)); + int width = m_glist[scale / m_numIntervals][0].cols; + int height = m_glist[scale / m_numIntervals][0].rows; + vector orien = m_keyPoints[ikp].orien; + vector magn = m_keyPoints[ikp].mag; + //cout << orien[2] << endl; + //cout << magn[0] << endl; // Find the orientation and magnitude that have the maximum impact // on the feature - double main_mag = mag[0]; - double main_orien = orien[0]; - for(unsigned int orient_count=1;orient_countmain_mag) + if (magn[orient_count] > main_magn) { main_orien = orien[orient_count]; - main_mag = mag[orient_count]; + main_magn = magn[orient_count]; } } - - unsigned int hfsz = FEATURE_WINDOW_SIZE/2; - CvMat *weight = cvCreateMat(FEATURE_WINDOW_SIZE, FEATURE_WINDOW_SIZE, CV_32FC1); - vector fv(FVSIZE); - - for(i=0;i fv(FVSIZE); + int i, j; + for (i = 0; i < FEATURE_WINDOW_SIZE; i++) { - for(j=0;jwidth+hfsz || jj+j+1height+hfsz) - cvSetReal2D(weight, j, i, 0); + if (ii + i + 1 < hfsz || ii + i + 1 > width + hfsz || jj + j + 1 < hfsz || jj + j + 1 > height + hfsz) + weight.at(Point(j, i)) = 0; else - cvSetReal2D(weight, j, i, cvGetReal2D(G, j, i)*cvGetReal2D(imgInterpolatedMagnitude[scale/m_numIntervals][scale%m_numIntervals], jj+j+1-hfsz, ii+i+1-hfsz)); + { + float val1 = G.at(Point(j, i)); + float val2 = imginterpolatedMagnitude[scale / m_numIntervals][scale % m_numIntervals].at(Point(jj + j + 1 - hfsz, ii + i + 1 - hfsz)); + weight.at(Point(j, i)) = val1 * val2; + } } } - // Now that we've weighted the required magnitudes, we proceed to generating // the feature vector - // The next two two loops are for splitting the 16x16 window // into sixteen 4x4 blocks - for(i=0;iwidth || t<0 || t>height) + if (k<0 || k>width || t<0 || t>height) continue; - // This is where rotation invariance is done - double sample_orien = cvGetReal2D(imgInterpolatedOrientation[scale/m_numIntervals][scale%m_numIntervals], t, k); + float sample_orien = imginterpolatedOrientation[scale / m_numIntervals][scale % m_numIntervals].at(Point(t,k)); sample_orien -= main_orien; - - while(sample_orien<0) - sample_orien+=2*M_PI; - - while(sample_orien>2*M_PI) - sample_orien-=2*M_PI; - + if (sample_orien < 0) + { + while (sample_orien < 0) + sample_orien +=(float) 2 * M_PI; + } + if (sample_orien > 2 * M_PI) + { + sample_orien = sample_orien - float(2 * M_PI); + } + int sample_orien_d=0; + int bin=0; + float bin_f=0.0; + float vxls=0.0; // This should never happen - if(!(sample_orien>=0 && sample_orien<2*M_PI)) - printf("BAD: %f\n", sample_orien); - assert(sample_orien>=0 && sample_orien<2*M_PI); - - unsigned int sample_orien_d = sample_orien*180/M_PI; - assert(sample_orien_d<360); - - unsigned int bin = sample_orien_d/(360/DESC_NUM_BINS); // The bin - double bin_f = (double)sample_orien_d/(double)(360/DESC_NUM_BINS); // The actual entry - - assert(bin= 0 && sample_orien < 2 * M_PI)) + //printf("BAD: %f\n", sample_orien); + if (sample_orien >= 0 && sample_orien < 2 * M_PI) + { + sample_orien_d = sample_orien * 180 / M_PI; + if (sample_orien_d < 360) + { + bin = sample_orien_d / (360 / DESC_NUM_BINS); + bin_f= (float)sample_orien_d / (float)(360 / DESC_NUM_BINS); // The actual entry// The bin + } + } + if (bin < DESC_NUM_BINS && k + hfsz - 1 - ii < FEATURE_WINDOW_SIZE && t + hfsz - 1 - jj < FEATURE_WINDOW_SIZE) + { + // Add to the bin + vxls = weight.at(Point(t + hfsz - 1 - jj, k + hfsz - 1 - ii)); + hist[bin] += (1 - fabs(bin_f - (bin + 0.5))) * vxls; + } } } - + // Keep adding these numbers to the feature vector - for(unsigned int t=0;tFV_THRESHOLD) + for (int t = 0; t < FVSIZE; t++) + if (fv[t] > FV_THRESHOLD) fv[t] = FV_THRESHOLD; - // Normalize yet again - norm=0; - for(unsigned int t=0;t +#include /****************************************************** * Code by Utkarsh Sinha * Based on JIFT by Jun Liu @@ -6,48 +9,39 @@ * Use, reuse, modify, hack, kick. Do whatever you want with * this code :) ******************************************************/ - -#include -#include - +#include +#include #include "keypoint.h" #include "descriptor.h" - -class SIFT +using namespace cv; +class meSIFT { -public: - SIFT(IplImage* img, int octaves, int intervals); - SIFT(const char* filename, int octaves, int intervals); - ~SIFT(); - - void DoSift(); - - void ShowKeypoints(); - void ShowAbsSigma(); - private: - void GenerateLists(); + vector >m_glist; // A 2D array to hold the different gaussian blurred images + vector >m_dogList; // A 2D array to hold the different DoG images + vector >m_extrema; // A 2D array to hold binary images. In the binary image, 1 = extrema, 0 = not extrema + vector >m_absSigma; // A 2D array to hold the sigma used to blur a particular image + vector m_keyPoints; // Holds each keypoint's basic info + vector m_keyDescs; // Holds each keypoint's descriptor + Mat srcImage; // The image we're working on + int m_numOctaves; // The desired number of octaves + int m_numIntervals; // The desired number of intervals + int m_numKeypoints; // The number of keypoints detected + + //void GenerateLists(); void BuildScaleSpace(); void DetectExtrema(); void AssignOrientations(); void ExtractKeypointDescriptors(); - - unsigned int GetKernelSize(double sigma, double cut_off=0.001); - CvMat* BuildInterpolatedGaussianTable(unsigned int size, double sigma); + int GetKernelSize(double sigma, double cut_off = 0.001); + Mat BuildInterpolatedGaussianTable(int size, double sigma); double gaussian2D(double x, double y, double sigma); +public: + meSIFT(Mat img, int octaves, int intervals); -private: - IplImage* m_srcImage; // The image we're working on - unsigned int m_numOctaves; // The desired number of octaves - unsigned int m_numIntervals; // The desired number of intervals - unsigned int m_numKeypoints; // The number of keypoints detected - - IplImage*** m_gList; // A 2D array to hold the different gaussian blurred images - IplImage*** m_dogList; // A 2D array to hold the different DoG images - IplImage*** m_extrema; // A 2D array to hold binary images. In the binary image, 1 = extrema, 0 = not extrema - double** m_absSigma; // A 2D array to hold the sigma used to blur a particular image + void DoSift(); + void ShowKeypoints(); + //void ShowAbsSigma(); - vector m_keyPoints; // Holds each keypoint's basic info - vector m_keyDescs; // Holds each keypoint's descriptor -}; \ No newline at end of file +};