├── .gitignore ├── DenseTrackStab.cpp ├── DenseTrackStab.h ├── Descriptors.h ├── Initialize.h ├── Makefile ├── OpticalFlow.h ├── README ├── Video.cpp └── make ├── dep.py └── generic.mk /.gitignore: -------------------------------------------------------------------------------- 1 | .build_release/ 2 | release/ 3 | test_sequences/ 4 | *.[ao] 5 | -------------------------------------------------------------------------------- /DenseTrackStab.cpp: -------------------------------------------------------------------------------- 1 | #include "DenseTrackStab.h" 2 | #include "Initialize.h" 3 | #include "Descriptors.h" 4 | #include "OpticalFlow.h" 5 | 6 | #include 7 | 8 | using namespace cv; 9 | 10 | int show_track = 0; // set show_track = 1, if you want to visualize the trajectories 11 | 12 | int main(int argc, char** argv) 13 | { 14 | VideoCapture capture; 15 | char* video = argv[1]; 16 | int flag = arg_parse(argc, argv); 17 | capture.open(video); 18 | 19 | if(!capture.isOpened()) { 20 | fprintf(stderr, "Could not initialize capturing..\n"); 21 | return -1; 22 | } 23 | 24 | int frame_num = 0; 25 | TrackInfo trackInfo; 26 | DescInfo hogInfo, hofInfo, mbhInfo; 27 | 28 | InitTrackInfo(&trackInfo, track_length, init_gap); 29 | InitDescInfo(&hogInfo, 8, false, patch_size, nxy_cell, nt_cell); 30 | InitDescInfo(&hofInfo, 9, true, patch_size, nxy_cell, nt_cell); 31 | InitDescInfo(&mbhInfo, 8, false, patch_size, nxy_cell, nt_cell); 32 | 33 | SeqInfo seqInfo; 34 | InitSeqInfo(&seqInfo, video); 35 | 36 | std::vector bb_list; 37 | if(bb_file) { 38 | LoadBoundBox(bb_file, bb_list); 39 | assert(bb_list.size() == seqInfo.length); 40 | } 41 | 42 | if(flag) 43 | seqInfo.length = end_frame - start_frame + 1; 44 | 45 | // fprintf(stderr, "video size, length: %d, width: %d, height: %d\n", seqInfo.length, seqInfo.width, seqInfo.height); 46 | 47 | if(show_track == 1) 48 | namedWindow("DenseTrackStab", 0); 49 | 50 | SurfFeatureDetector detector_surf(200); 51 | SurfDescriptorExtractor extractor_surf(true, true); 52 | 53 | std::vector prev_pts_flow, pts_flow; 54 | std::vector prev_pts_surf, pts_surf; 55 | std::vector prev_pts_all, pts_all; 56 | 57 | std::vector prev_kpts_surf, kpts_surf; 58 | Mat prev_desc_surf, desc_surf; 59 | Mat flow, human_mask; 60 | 61 | Mat image, prev_grey, grey; 62 | 63 | std::vector fscales(0); 64 | std::vector sizes(0); 65 | 66 | std::vector prev_grey_pyr(0), grey_pyr(0), flow_pyr(0), flow_warp_pyr(0); 67 | std::vector prev_poly_pyr(0), poly_pyr(0), poly_warp_pyr(0); 68 | 69 | std::vector > xyScaleTracks; 70 | int init_counter = 0; // indicate when to detect new feature points 71 | while(true) { 72 | Mat frame; 73 | int i, j, c; 74 | 75 | // get a new frame 76 | capture >> frame; 77 | if(frame.empty()) 78 | break; 79 | 80 | if(frame_num < start_frame || frame_num > end_frame) { 81 | frame_num++; 82 | continue; 83 | } 84 | 85 | if(frame_num == start_frame) { 86 | image.create(frame.size(), CV_8UC3); 87 | grey.create(frame.size(), CV_8UC1); 88 | prev_grey.create(frame.size(), CV_8UC1); 89 | 90 | InitPry(frame, fscales, sizes); 91 | 92 | BuildPry(sizes, CV_8UC1, prev_grey_pyr); 93 | BuildPry(sizes, CV_8UC1, grey_pyr); 94 | BuildPry(sizes, CV_32FC2, flow_pyr); 95 | BuildPry(sizes, CV_32FC2, flow_warp_pyr); 96 | 97 | BuildPry(sizes, CV_32FC(5), prev_poly_pyr); 98 | BuildPry(sizes, CV_32FC(5), poly_pyr); 99 | BuildPry(sizes, CV_32FC(5), poly_warp_pyr); 100 | 101 | xyScaleTracks.resize(scale_num); 102 | 103 | frame.copyTo(image); 104 | cvtColor(image, prev_grey, CV_BGR2GRAY); 105 | 106 | for(int iScale = 0; iScale < scale_num; iScale++) { 107 | if(iScale == 0) 108 | prev_grey.copyTo(prev_grey_pyr[0]); 109 | else 110 | resize(prev_grey_pyr[iScale-1], prev_grey_pyr[iScale], prev_grey_pyr[iScale].size(), 0, 0, INTER_LINEAR); 111 | 112 | // dense sampling feature points 113 | std::vector points(0); 114 | DenseSample(prev_grey_pyr[iScale], points, quality, min_distance); 115 | 116 | // save the feature points 117 | std::list& tracks = xyScaleTracks[iScale]; 118 | for(i = 0; i < points.size(); i++) 119 | tracks.push_back(Track(points[i], trackInfo, hogInfo, hofInfo, mbhInfo)); 120 | } 121 | 122 | // compute polynomial expansion 123 | my::FarnebackPolyExpPyr(prev_grey, prev_poly_pyr, fscales, 7, 1.5); 124 | 125 | human_mask = Mat::ones(frame.size(), CV_8UC1); 126 | if(bb_file) 127 | InitMaskWithBox(human_mask, bb_list[frame_num].BBs); 128 | 129 | detector_surf.detect(prev_grey, prev_kpts_surf, human_mask); 130 | extractor_surf.compute(prev_grey, prev_kpts_surf, prev_desc_surf); 131 | 132 | frame_num++; 133 | continue; 134 | } 135 | 136 | init_counter++; 137 | frame.copyTo(image); 138 | cvtColor(image, grey, CV_BGR2GRAY); 139 | 140 | // match surf features 141 | if(bb_file) 142 | InitMaskWithBox(human_mask, bb_list[frame_num].BBs); 143 | detector_surf.detect(grey, kpts_surf, human_mask); 144 | extractor_surf.compute(grey, kpts_surf, desc_surf); 145 | ComputeMatch(prev_kpts_surf, kpts_surf, prev_desc_surf, desc_surf, prev_pts_surf, pts_surf); 146 | 147 | // compute optical flow for all scales once 148 | my::FarnebackPolyExpPyr(grey, poly_pyr, fscales, 7, 1.5); 149 | my::calcOpticalFlowFarneback(prev_poly_pyr, poly_pyr, flow_pyr, 10, 2); 150 | 151 | MatchFromFlow(prev_grey, flow_pyr[0], prev_pts_flow, pts_flow, human_mask); 152 | MergeMatch(prev_pts_flow, pts_flow, prev_pts_surf, pts_surf, prev_pts_all, pts_all); 153 | 154 | Mat H = Mat::eye(3, 3, CV_64FC1); 155 | if(pts_all.size() > 50) { 156 | std::vector match_mask; 157 | Mat temp = findHomography(prev_pts_all, pts_all, RANSAC, 1, match_mask); 158 | if(countNonZero(Mat(match_mask)) > 25) 159 | H = temp; 160 | } 161 | 162 | Mat H_inv = H.inv(); 163 | Mat grey_warp = Mat::zeros(grey.size(), CV_8UC1); 164 | MyWarpPerspective(prev_grey, grey, grey_warp, H_inv); // warp the second frame 165 | 166 | // compute optical flow for all scales once 167 | my::FarnebackPolyExpPyr(grey_warp, poly_warp_pyr, fscales, 7, 1.5); 168 | my::calcOpticalFlowFarneback(prev_poly_pyr, poly_warp_pyr, flow_warp_pyr, 10, 2); 169 | 170 | for(int iScale = 0; iScale < scale_num; iScale++) { 171 | if(iScale == 0) 172 | grey.copyTo(grey_pyr[0]); 173 | else 174 | resize(grey_pyr[iScale-1], grey_pyr[iScale], grey_pyr[iScale].size(), 0, 0, INTER_LINEAR); 175 | 176 | int width = grey_pyr[iScale].cols; 177 | int height = grey_pyr[iScale].rows; 178 | 179 | // compute the integral histograms 180 | DescMat* hogMat = InitDescMat(height+1, width+1, hogInfo.nBins); 181 | HogComp(prev_grey_pyr[iScale], hogMat->desc, hogInfo); 182 | 183 | DescMat* hofMat = InitDescMat(height+1, width+1, hofInfo.nBins); 184 | HofComp(flow_warp_pyr[iScale], hofMat->desc, hofInfo); 185 | 186 | DescMat* mbhMatX = InitDescMat(height+1, width+1, mbhInfo.nBins); 187 | DescMat* mbhMatY = InitDescMat(height+1, width+1, mbhInfo.nBins); 188 | MbhComp(flow_warp_pyr[iScale], mbhMatX->desc, mbhMatY->desc, mbhInfo); 189 | 190 | // track feature points in each scale separately 191 | std::list& tracks = xyScaleTracks[iScale]; 192 | for (std::list::iterator iTrack = tracks.begin(); iTrack != tracks.end();) { 193 | int index = iTrack->index; 194 | Point2f prev_point = iTrack->point[index]; 195 | int x = std::min(std::max(cvRound(prev_point.x), 0), width-1); 196 | int y = std::min(std::max(cvRound(prev_point.y), 0), height-1); 197 | 198 | Point2f point; 199 | point.x = prev_point.x + flow_pyr[iScale].ptr(y)[2*x]; 200 | point.y = prev_point.y + flow_pyr[iScale].ptr(y)[2*x+1]; 201 | 202 | if(point.x <= 0 || point.x >= width || point.y <= 0 || point.y >= height) { 203 | iTrack = tracks.erase(iTrack); 204 | continue; 205 | } 206 | 207 | iTrack->disp[index].x = flow_warp_pyr[iScale].ptr(y)[2*x]; 208 | iTrack->disp[index].y = flow_warp_pyr[iScale].ptr(y)[2*x+1]; 209 | 210 | // get the descriptors for the feature point 211 | RectInfo rect; 212 | GetRect(prev_point, rect, width, height, hogInfo); 213 | GetDesc(hogMat, rect, hogInfo, iTrack->hog, index); 214 | GetDesc(hofMat, rect, hofInfo, iTrack->hof, index); 215 | GetDesc(mbhMatX, rect, mbhInfo, iTrack->mbhX, index); 216 | GetDesc(mbhMatY, rect, mbhInfo, iTrack->mbhY, index); 217 | iTrack->addPoint(point); 218 | 219 | // draw the trajectories at the first scale 220 | if(show_track == 1 && iScale == 0) 221 | DrawTrack(iTrack->point, iTrack->index, fscales[iScale], image); 222 | 223 | // if the trajectory achieves the maximal length 224 | if(iTrack->index >= trackInfo.length) { 225 | std::vector trajectory(trackInfo.length+1); 226 | for(int i = 0; i <= trackInfo.length; ++i) 227 | trajectory[i] = iTrack->point[i]*fscales[iScale]; 228 | 229 | std::vector displacement(trackInfo.length); 230 | for (int i = 0; i < trackInfo.length; ++i) 231 | displacement[i] = iTrack->disp[i]*fscales[iScale]; 232 | 233 | float mean_x(0), mean_y(0), var_x(0), var_y(0), length(0); 234 | if(IsValid(trajectory, mean_x, mean_y, var_x, var_y, length) && IsCameraMotion(displacement)) { 235 | // output the trajectory 236 | printf("%d\t%f\t%f\t%f\t%f\t%f\t%f\t", frame_num, mean_x, mean_y, var_x, var_y, length, fscales[iScale]); 237 | 238 | // for spatio-temporal pyramid 239 | printf("%f\t", std::min(std::max(mean_x/float(seqInfo.width), 0), 0.999)); 240 | printf("%f\t", std::min(std::max(mean_y/float(seqInfo.height), 0), 0.999)); 241 | printf("%f\t", std::min(std::max((frame_num - trackInfo.length/2.0 - start_frame)/float(seqInfo.length), 0), 0.999)); 242 | 243 | // output the trajectory 244 | for (int i = 0; i < trackInfo.length; ++i) 245 | printf("%f\t%f\t", displacement[i].x, displacement[i].y); 246 | 247 | PrintDesc(iTrack->hog, hogInfo, trackInfo); 248 | PrintDesc(iTrack->hof, hofInfo, trackInfo); 249 | PrintDesc(iTrack->mbhX, mbhInfo, trackInfo); 250 | PrintDesc(iTrack->mbhY, mbhInfo, trackInfo); 251 | printf("\n"); 252 | } 253 | 254 | iTrack = tracks.erase(iTrack); 255 | continue; 256 | } 257 | ++iTrack; 258 | } 259 | ReleDescMat(hogMat); 260 | ReleDescMat(hofMat); 261 | ReleDescMat(mbhMatX); 262 | ReleDescMat(mbhMatY); 263 | 264 | if(init_counter != trackInfo.gap) 265 | continue; 266 | 267 | // detect new feature points every gap frames 268 | std::vector points(0); 269 | for(std::list::iterator iTrack = tracks.begin(); iTrack != tracks.end(); iTrack++) 270 | points.push_back(iTrack->point[iTrack->index]); 271 | 272 | DenseSample(grey_pyr[iScale], points, quality, min_distance); 273 | // save the new feature points 274 | for(i = 0; i < points.size(); i++) 275 | tracks.push_back(Track(points[i], trackInfo, hogInfo, hofInfo, mbhInfo)); 276 | } 277 | 278 | init_counter = 0; 279 | grey.copyTo(prev_grey); 280 | for(i = 0; i < scale_num; i++) { 281 | grey_pyr[i].copyTo(prev_grey_pyr[i]); 282 | poly_pyr[i].copyTo(prev_poly_pyr[i]); 283 | } 284 | 285 | prev_kpts_surf = kpts_surf; 286 | desc_surf.copyTo(prev_desc_surf); 287 | 288 | frame_num++; 289 | 290 | if( show_track == 1 ) { 291 | imshow( "DenseTrackStab", image); 292 | c = cvWaitKey(3); 293 | if((char)c == 27) break; 294 | } 295 | } 296 | 297 | if( show_track == 1 ) 298 | destroyWindow("DenseTrackStab"); 299 | 300 | return 0; 301 | } 302 | -------------------------------------------------------------------------------- /DenseTrackStab.h: -------------------------------------------------------------------------------- 1 | #ifndef DENSETRACKSTAB_H_ 2 | #define DENSETRACKSTAB_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace cv; 28 | 29 | int start_frame = 0; 30 | int end_frame = INT_MAX; 31 | int scale_num = 8; 32 | const float scale_stride = sqrt(2); 33 | char* bb_file = NULL; 34 | 35 | // parameters for descriptors 36 | int patch_size = 32; 37 | int nxy_cell = 2; 38 | int nt_cell = 3; 39 | float epsilon = 0.05; 40 | const float min_flow = 0.4; 41 | 42 | // parameters for tracking 43 | double quality = 0.001; 44 | int min_distance = 5; 45 | int init_gap = 1; 46 | int track_length = 15; 47 | 48 | // parameters for rejecting trajectory 49 | const float min_var = sqrt(3); 50 | const float max_var = 50; 51 | const float max_dis = 20; 52 | 53 | typedef struct { 54 | int x; // top left corner 55 | int y; 56 | int width; 57 | int height; 58 | }RectInfo; 59 | 60 | typedef struct { 61 | int width; // resolution of the video 62 | int height; 63 | int length; // number of frames 64 | }SeqInfo; 65 | 66 | typedef struct { 67 | int length; // length of the trajectory 68 | int gap; // initialization gap for feature re-sampling 69 | }TrackInfo; 70 | 71 | typedef struct { 72 | int nBins; // number of bins for vector quantization 73 | bool isHof; 74 | int nxCells; // number of cells in x direction 75 | int nyCells; 76 | int ntCells; 77 | int dim; // dimension of the descriptor 78 | int height; // size of the block for computing the descriptor 79 | int width; 80 | }DescInfo; 81 | 82 | // integral histogram for the descriptors 83 | typedef struct { 84 | int height; 85 | int width; 86 | int nBins; 87 | float* desc; 88 | }DescMat; 89 | 90 | class Track 91 | { 92 | public: 93 | std::vector point; 94 | std::vector disp; 95 | std::vector hog; 96 | std::vector hof; 97 | std::vector mbhX; 98 | std::vector mbhY; 99 | int index; 100 | 101 | Track(const Point2f& point_, const TrackInfo& trackInfo, const DescInfo& hogInfo, 102 | const DescInfo& hofInfo, const DescInfo& mbhInfo) 103 | : point(trackInfo.length+1), disp(trackInfo.length), hog(hogInfo.dim*trackInfo.length), 104 | hof(hofInfo.dim*trackInfo.length), mbhX(mbhInfo.dim*trackInfo.length), mbhY(mbhInfo.dim*trackInfo.length) 105 | { 106 | index = 0; 107 | point[0] = point_; 108 | } 109 | 110 | void addPoint(const Point2f& point_) 111 | { 112 | index++; 113 | point[index] = point_; 114 | } 115 | }; 116 | 117 | class BoundBox 118 | { 119 | public: 120 | Point2f TopLeft; 121 | Point2f BottomRight; 122 | float confidence; 123 | 124 | BoundBox(float a1, float a2, float a3, float a4, float a5) 125 | { 126 | TopLeft.x = a1; 127 | TopLeft.y = a2; 128 | BottomRight.x = a3; 129 | BottomRight.y = a4; 130 | confidence = a5; 131 | } 132 | }; 133 | 134 | class Frame 135 | { 136 | public: 137 | int frameID; 138 | std::vector BBs; 139 | 140 | Frame(const int& frame_) 141 | { 142 | frameID = frame_; 143 | BBs.clear(); 144 | } 145 | }; 146 | 147 | #endif /*DENSETRACKSTAB_H_*/ 148 | -------------------------------------------------------------------------------- /Descriptors.h: -------------------------------------------------------------------------------- 1 | #ifndef DESCRIPTORS_H_ 2 | #define DESCRIPTORS_H_ 3 | 4 | #include "DenseTrackStab.h" 5 | using namespace cv; 6 | 7 | // get the rectangle for computing the descriptor 8 | void GetRect(const Point2f& point, RectInfo& rect, const int width, const int height, const DescInfo& descInfo) 9 | { 10 | int x_min = descInfo.width/2; 11 | int y_min = descInfo.height/2; 12 | int x_max = width - descInfo.width; 13 | int y_max = height - descInfo.height; 14 | 15 | rect.x = std::min(std::max(cvRound(point.x) - x_min, 0), x_max); 16 | rect.y = std::min(std::max(cvRound(point.y) - y_min, 0), y_max); 17 | rect.width = descInfo.width; 18 | rect.height = descInfo.height; 19 | } 20 | 21 | // compute integral histograms for the whole image 22 | void BuildDescMat(const Mat& xComp, const Mat& yComp, float* desc, const DescInfo& descInfo) 23 | { 24 | float maxAngle = 360.f; 25 | int nDims = descInfo.nBins; 26 | // one more bin for hof 27 | int nBins = descInfo.isHof ? descInfo.nBins-1 : descInfo.nBins; 28 | const float angleBase = float(nBins)/maxAngle; 29 | 30 | int step = (xComp.cols+1)*nDims; 31 | int index = step + nDims; 32 | for(int i = 0; i < xComp.rows; i++, index += nDims) { 33 | const float* xc = xComp.ptr(i); 34 | const float* yc = yComp.ptr(i); 35 | 36 | // summarization of the current line 37 | std::vector sum(nDims); 38 | for(int j = 0; j < xComp.cols; j++) { 39 | float x = xc[j]; 40 | float y = yc[j]; 41 | float mag0 = sqrt(x*x + y*y); 42 | float mag1; 43 | int bin0, bin1; 44 | 45 | // for the zero bin of hof 46 | if(descInfo.isHof && mag0 <= min_flow) { 47 | bin0 = nBins; // the zero bin is the last one 48 | mag0 = 1.0; 49 | bin1 = 0; 50 | mag1 = 0; 51 | } 52 | else { 53 | float angle = fastAtan2(y, x); 54 | if(angle >= maxAngle) angle -= maxAngle; 55 | 56 | // split the mag to two adjacent bins 57 | float fbin = angle * angleBase; 58 | bin0 = cvFloor(fbin); 59 | bin1 = (bin0+1)%nBins; 60 | 61 | mag1 = (fbin - bin0)*mag0; 62 | mag0 -= mag1; 63 | } 64 | 65 | sum[bin0] += mag0; 66 | sum[bin1] += mag1; 67 | 68 | for(int m = 0; m < nDims; m++, index++) 69 | desc[index] = desc[index-step] + sum[m]; 70 | } 71 | } 72 | } 73 | 74 | // get a descriptor from the integral histogram 75 | void GetDesc(const DescMat* descMat, RectInfo& rect, DescInfo descInfo, std::vector& desc, const int index) 76 | { 77 | int dim = descInfo.dim; 78 | int nBins = descInfo.nBins; 79 | int height = descMat->height; 80 | int width = descMat->width; 81 | 82 | int xStride = rect.width/descInfo.nxCells; 83 | int yStride = rect.height/descInfo.nyCells; 84 | int xStep = xStride*nBins; 85 | int yStep = yStride*width*nBins; 86 | 87 | // iterate over different cells 88 | int iDesc = 0; 89 | std::vector vec(dim); 90 | for(int xPos = rect.x, x = 0; x < descInfo.nxCells; xPos += xStride, x++) 91 | for(int yPos = rect.y, y = 0; y < descInfo.nyCells; yPos += yStride, y++) { 92 | // get the positions in the integral histogram 93 | const float* top_left = descMat->desc + (yPos*width + xPos)*nBins; 94 | const float* top_right = top_left + xStep; 95 | const float* bottom_left = top_left + yStep; 96 | const float* bottom_right = bottom_left + xStep; 97 | 98 | for(int i = 0; i < nBins; i++) { 99 | float sum = bottom_right[i] + top_left[i] - bottom_left[i] - top_right[i]; 100 | vec[iDesc++] = std::max(sum, 0) + epsilon; 101 | } 102 | } 103 | 104 | float norm = 0; 105 | for(int i = 0; i < dim; i++) 106 | norm += vec[i]; 107 | if(norm > 0) norm = 1./norm; 108 | 109 | int pos = index*dim; 110 | for(int i = 0; i < dim; i++) 111 | desc[pos++] = sqrt(vec[i]*norm); 112 | } 113 | 114 | // for HOG descriptor 115 | void HogComp(const Mat& img, float* desc, DescInfo& descInfo) 116 | { 117 | Mat imgX, imgY; 118 | Sobel(img, imgX, CV_32F, 1, 0, 1); 119 | Sobel(img, imgY, CV_32F, 0, 1, 1); 120 | BuildDescMat(imgX, imgY, desc, descInfo); 121 | } 122 | 123 | // for HOF descriptor 124 | void HofComp(const Mat& flow, float* desc, DescInfo& descInfo) 125 | { 126 | Mat flows[2]; 127 | split(flow, flows); 128 | BuildDescMat(flows[0], flows[1], desc, descInfo); 129 | } 130 | 131 | // for MBH descriptor 132 | void MbhComp(const Mat& flow, float* descX, float* descY, DescInfo& descInfo) 133 | { 134 | Mat flows[2]; 135 | split(flow, flows); 136 | 137 | Mat flowXdX, flowXdY, flowYdX, flowYdY; 138 | Sobel(flows[0], flowXdX, CV_32F, 1, 0, 1); 139 | Sobel(flows[0], flowXdY, CV_32F, 0, 1, 1); 140 | Sobel(flows[1], flowYdX, CV_32F, 1, 0, 1); 141 | Sobel(flows[1], flowYdY, CV_32F, 0, 1, 1); 142 | 143 | BuildDescMat(flowXdX, flowXdY, descX, descInfo); 144 | BuildDescMat(flowYdX, flowYdY, descY, descInfo); 145 | } 146 | 147 | // check whether a trajectory is valid or not 148 | bool IsValid(std::vector& track, float& mean_x, float& mean_y, float& var_x, float& var_y, float& length) 149 | { 150 | int size = track.size(); 151 | float norm = 1./size; 152 | for(int i = 0; i < size; i++) { 153 | mean_x += track[i].x; 154 | mean_y += track[i].y; 155 | } 156 | mean_x *= norm; 157 | mean_y *= norm; 158 | 159 | for(int i = 0; i < size; i++) { 160 | float temp_x = track[i].x - mean_x; 161 | float temp_y = track[i].y - mean_y; 162 | var_x += temp_x*temp_x; 163 | var_y += temp_y*temp_y; 164 | } 165 | var_x *= norm; 166 | var_y *= norm; 167 | var_x = sqrt(var_x); 168 | var_y = sqrt(var_y); 169 | 170 | // remove static trajectory 171 | if(var_x < min_var && var_y < min_var) 172 | return false; 173 | // remove random trajectory 174 | if( var_x > max_var || var_y > max_var ) 175 | return false; 176 | 177 | float cur_max = 0; 178 | for(int i = 0; i < size-1; i++) { 179 | track[i] = track[i+1] - track[i]; 180 | float temp = sqrt(track[i].x*track[i].x + track[i].y*track[i].y); 181 | 182 | length += temp; 183 | if(temp > cur_max) 184 | cur_max = temp; 185 | } 186 | 187 | if(cur_max > max_dis && cur_max > length*0.7) 188 | return false; 189 | 190 | track.pop_back(); 191 | norm = 1./length; 192 | // normalize the trajectory 193 | for(int i = 0; i < size-1; i++) 194 | track[i] *= norm; 195 | 196 | return true; 197 | } 198 | 199 | bool IsCameraMotion(std::vector& disp) 200 | { 201 | float disp_max = 0; 202 | float disp_sum = 0; 203 | for(int i = 0; i < disp.size(); ++i) { 204 | float x = disp[i].x; 205 | float y = disp[i].y; 206 | float temp = sqrt(x*x + y*y); 207 | 208 | disp_sum += temp; 209 | if(disp_max < temp) 210 | disp_max = temp; 211 | } 212 | 213 | if(disp_max <= 1) 214 | return false; 215 | 216 | float disp_norm = 1./disp_sum; 217 | for (int i = 0; i < disp.size(); ++i) 218 | disp[i] *= disp_norm; 219 | 220 | return true; 221 | } 222 | 223 | // detect new feature points in an image without overlapping to previous points 224 | void DenseSample(const Mat& grey, std::vector& points, const double quality, const int min_distance) 225 | { 226 | int width = grey.cols/min_distance; 227 | int height = grey.rows/min_distance; 228 | 229 | Mat eig; 230 | cornerMinEigenVal(grey, eig, 3, 3); 231 | 232 | double maxVal = 0; 233 | minMaxLoc(eig, 0, &maxVal); 234 | const double threshold = maxVal*quality; 235 | 236 | std::vector counters(width*height); 237 | int x_max = min_distance*width; 238 | int y_max = min_distance*height; 239 | 240 | for(int i = 0; i < points.size(); i++) { 241 | Point2f point = points[i]; 242 | int x = cvFloor(point.x); 243 | int y = cvFloor(point.y); 244 | 245 | if(x >= x_max || y >= y_max) 246 | continue; 247 | x /= min_distance; 248 | y /= min_distance; 249 | counters[y*width+x]++; 250 | } 251 | 252 | points.clear(); 253 | int index = 0; 254 | int offset = min_distance/2; 255 | for(int i = 0; i < height; i++) 256 | for(int j = 0; j < width; j++, index++) { 257 | if(counters[index] > 0) 258 | continue; 259 | 260 | int x = j*min_distance+offset; 261 | int y = i*min_distance+offset; 262 | 263 | if(eig.at(y, x) > threshold) 264 | points.push_back(Point2f(float(x), float(y))); 265 | } 266 | } 267 | 268 | void InitPry(const Mat& frame, std::vector& scales, std::vector& sizes) 269 | { 270 | int rows = frame.rows, cols = frame.cols; 271 | float min_size = std::min(rows, cols); 272 | 273 | int nlayers = 0; 274 | while(min_size >= patch_size) { 275 | min_size /= scale_stride; 276 | nlayers++; 277 | } 278 | 279 | if(nlayers == 0) nlayers = 1; // at least 1 scale 280 | 281 | scale_num = std::min(scale_num, nlayers); 282 | 283 | scales.resize(scale_num); 284 | sizes.resize(scale_num); 285 | 286 | scales[0] = 1.; 287 | sizes[0] = Size(cols, rows); 288 | 289 | for(int i = 1; i < scale_num; i++) { 290 | scales[i] = scales[i-1] * scale_stride; 291 | sizes[i] = Size(cvRound(cols/scales[i]), cvRound(rows/scales[i])); 292 | } 293 | } 294 | 295 | void BuildPry(const std::vector& sizes, const int type, std::vector& grey_pyr) 296 | { 297 | int nlayers = sizes.size(); 298 | grey_pyr.resize(nlayers); 299 | 300 | for(int i = 0; i < nlayers; i++) 301 | grey_pyr[i].create(sizes[i], type); 302 | } 303 | 304 | void DrawTrack(const std::vector& point, const int index, const float scale, Mat& image) 305 | { 306 | Point2f point0 = point[0]; 307 | point0 *= scale; 308 | 309 | for (int j = 1; j <= index; j++) { 310 | Point2f point1 = point[j]; 311 | point1 *= scale; 312 | 313 | line(image, point0, point1, Scalar(0,cvFloor(255.0*(j+1.0)/float(index+1.0)),0), 2, 8, 0); 314 | point0 = point1; 315 | } 316 | circle(image, point0, 2, Scalar(0,0,255), -1, 8, 0); 317 | } 318 | 319 | void PrintDesc(std::vector& desc, DescInfo& descInfo, TrackInfo& trackInfo) 320 | { 321 | int tStride = cvFloor(trackInfo.length/descInfo.ntCells); 322 | float norm = 1./float(tStride); 323 | int dim = descInfo.dim; 324 | int pos = 0; 325 | for(int i = 0; i < descInfo.ntCells; i++) { 326 | std::vector vec(dim); 327 | for(int t = 0; t < tStride; t++) 328 | for(int j = 0; j < dim; j++) 329 | vec[j] += desc[pos++]; 330 | for(int j = 0; j < dim; j++) 331 | printf("%.7f\t", vec[j]*norm); 332 | } 333 | } 334 | 335 | void LoadBoundBox(char* file, std::vector& bb_list) 336 | { 337 | // load the bouding box file 338 | std::ifstream bbFile(file); 339 | std::string line; 340 | 341 | while(std::getline(bbFile, line)) { 342 | std::istringstream iss(line); 343 | 344 | int frameID; 345 | if (!(iss >> frameID)) 346 | continue; 347 | 348 | Frame cur_frame(frameID); 349 | 350 | float temp; 351 | std::vector a(0); 352 | while(iss >> temp) 353 | a.push_back(temp); 354 | 355 | int size = a.size(); 356 | 357 | if(size % 5 != 0) 358 | fprintf(stderr, "Input bounding box format wrong!\n"); 359 | 360 | for(int i = 0; i < size/5; i++) 361 | cur_frame.BBs.push_back(BoundBox(a[i*5], a[i*5+1], a[i*5+2], a[i*5+3], a[i*5+4])); 362 | 363 | bb_list.push_back(cur_frame); 364 | } 365 | } 366 | 367 | void InitMaskWithBox(Mat& mask, std::vector& bbs) 368 | { 369 | int width = mask.cols; 370 | int height = mask.rows; 371 | 372 | for(int i = 0; i < height; i++) { 373 | uchar* m = mask.ptr(i); 374 | for(int j = 0; j < width; j++) 375 | m[j] = 1; 376 | } 377 | 378 | for(int k = 0; k < bbs.size(); k++) { 379 | BoundBox& bb = bbs[k]; 380 | for(int i = cvCeil(bb.TopLeft.y); i <= cvFloor(bb.BottomRight.y); i++) { 381 | uchar* m = mask.ptr(i); 382 | for(int j = cvCeil(bb.TopLeft.x); j <= cvFloor(bb.BottomRight.x); j++) 383 | m[j] = 0; 384 | } 385 | } 386 | } 387 | 388 | static void MyWarpPerspective(Mat& prev_src, Mat& src, Mat& dst, Mat& M0, int flags = INTER_LINEAR, 389 | int borderType = BORDER_CONSTANT, const Scalar& borderValue = Scalar()) 390 | { 391 | int width = src.cols; 392 | int height = src.rows; 393 | dst.create( height, width, CV_8UC1 ); 394 | 395 | Mat mask = Mat::zeros(height, width, CV_8UC1); 396 | const int margin = 5; 397 | 398 | const int BLOCK_SZ = 32; 399 | short XY[BLOCK_SZ*BLOCK_SZ*2], A[BLOCK_SZ*BLOCK_SZ]; 400 | 401 | int interpolation = flags & INTER_MAX; 402 | if( interpolation == INTER_AREA ) 403 | interpolation = INTER_LINEAR; 404 | 405 | double M[9]; 406 | Mat matM(3, 3, CV_64F, M); 407 | M0.convertTo(matM, matM.type()); 408 | if( !(flags & WARP_INVERSE_MAP) ) 409 | invert(matM, matM); 410 | 411 | int x, y, x1, y1; 412 | 413 | int bh0 = std::min(BLOCK_SZ/2, height); 414 | int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, width); 415 | bh0 = std::min(BLOCK_SZ*BLOCK_SZ/bw0, height); 416 | 417 | for( y = 0; y < height; y += bh0 ) { 418 | for( x = 0; x < width; x += bw0 ) { 419 | int bw = std::min( bw0, width - x); 420 | int bh = std::min( bh0, height - y); 421 | 422 | Mat _XY(bh, bw, CV_16SC2, XY); 423 | Mat matA; 424 | Mat dpart(dst, Rect(x, y, bw, bh)); 425 | 426 | for( y1 = 0; y1 < bh; y1++ ) { 427 | 428 | short* xy = XY + y1*bw*2; 429 | double X0 = M[0]*x + M[1]*(y + y1) + M[2]; 430 | double Y0 = M[3]*x + M[4]*(y + y1) + M[5]; 431 | double W0 = M[6]*x + M[7]*(y + y1) + M[8]; 432 | short* alpha = A + y1*bw; 433 | 434 | for( x1 = 0; x1 < bw; x1++ ) { 435 | 436 | double W = W0 + M[6]*x1; 437 | W = W ? INTER_TAB_SIZE/W : 0; 438 | double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0]*x1)*W)); 439 | double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3]*x1)*W)); 440 | 441 | double _X = fX/double(INTER_TAB_SIZE); 442 | double _Y = fY/double(INTER_TAB_SIZE); 443 | 444 | if( _X > margin && _X < width-1-margin && _Y > margin && _Y < height-1-margin ) 445 | mask.at(y+y1, x+x1) = 1; 446 | 447 | int X = saturate_cast(fX); 448 | int Y = saturate_cast(fY); 449 | 450 | xy[x1*2] = saturate_cast(X >> INTER_BITS); 451 | xy[x1*2+1] = saturate_cast(Y >> INTER_BITS); 452 | alpha[x1] = (short)((Y & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (X & (INTER_TAB_SIZE-1))); 453 | } 454 | } 455 | 456 | Mat _matA(bh, bw, CV_16U, A); 457 | remap( src, dpart, _XY, _matA, interpolation, borderType, borderValue ); 458 | } 459 | } 460 | 461 | for( y = 0; y < height; y++ ) { 462 | const uchar* m = mask.ptr(y); 463 | const uchar* s = prev_src.ptr(y); 464 | uchar* d = dst.ptr(y); 465 | for( x = 0; x < width; x++ ) { 466 | if(m[x] == 0) 467 | d[x] = s[x]; 468 | } 469 | } 470 | } 471 | 472 | void ComputeMatch(const std::vector& prev_kpts, const std::vector& kpts, 473 | const Mat& prev_desc, const Mat& desc, std::vector& prev_pts, std::vector& pts) 474 | { 475 | prev_pts.clear(); 476 | pts.clear(); 477 | 478 | if(prev_kpts.size() == 0 || kpts.size() == 0) 479 | return; 480 | 481 | Mat mask = windowedMatchingMask(kpts, prev_kpts, 25, 25); 482 | 483 | BFMatcher desc_matcher(NORM_L2); 484 | std::vector matches; 485 | 486 | desc_matcher.match(desc, prev_desc, matches, mask); 487 | 488 | prev_pts.reserve(matches.size()); 489 | pts.reserve(matches.size()); 490 | 491 | for(size_t i = 0; i < matches.size(); i++) { 492 | const DMatch& dmatch = matches[i]; 493 | // get the point pairs that are successfully matched 494 | prev_pts.push_back(prev_kpts[dmatch.trainIdx].pt); 495 | pts.push_back(kpts[dmatch.queryIdx].pt); 496 | } 497 | 498 | return; 499 | } 500 | 501 | void MergeMatch(const std::vector& prev_pts1, const std::vector& pts1, 502 | const std::vector& prev_pts2, const std::vector& pts2, 503 | std::vector& prev_pts_all, std::vector& pts_all) 504 | { 505 | prev_pts_all.clear(); 506 | prev_pts_all.reserve(prev_pts1.size() + prev_pts2.size()); 507 | 508 | pts_all.clear(); 509 | pts_all.reserve(pts1.size() + pts2.size()); 510 | 511 | for(size_t i = 0; i < prev_pts1.size(); i++) { 512 | prev_pts_all.push_back(prev_pts1[i]); 513 | pts_all.push_back(pts1[i]); 514 | } 515 | 516 | for(size_t i = 0; i < prev_pts2.size(); i++) { 517 | prev_pts_all.push_back(prev_pts2[i]); 518 | pts_all.push_back(pts2[i]); 519 | } 520 | 521 | return; 522 | } 523 | 524 | void MatchFromFlow(const Mat& prev_grey, const Mat& flow, std::vector& prev_pts, std::vector& pts, const Mat& mask) 525 | { 526 | int width = prev_grey.cols; 527 | int height = prev_grey.rows; 528 | prev_pts.clear(); 529 | pts.clear(); 530 | 531 | const int MAX_COUNT = 1000; 532 | goodFeaturesToTrack(prev_grey, prev_pts, MAX_COUNT, 0.001, 3, mask); 533 | 534 | if(prev_pts.size() == 0) 535 | return; 536 | 537 | for(int i = 0; i < prev_pts.size(); i++) { 538 | int x = std::min(std::max(cvRound(prev_pts[i].x), 0), width-1); 539 | int y = std::min(std::max(cvRound(prev_pts[i].y), 0), height-1); 540 | 541 | const float* f = flow.ptr(y); 542 | pts.push_back(Point2f(x+f[2*x], y+f[2*x+1])); 543 | } 544 | } 545 | 546 | #endif /*DESCRIPTORS_H_*/ 547 | -------------------------------------------------------------------------------- /Initialize.h: -------------------------------------------------------------------------------- 1 | #ifndef INITIALIZE_H_ 2 | #define INITIALIZE_H_ 3 | 4 | #include "DenseTrackStab.h" 5 | 6 | using namespace cv; 7 | 8 | void InitTrackInfo(TrackInfo* trackInfo, int track_length, int init_gap) 9 | { 10 | trackInfo->length = track_length; 11 | trackInfo->gap = init_gap; 12 | } 13 | 14 | DescMat* InitDescMat(int height, int width, int nBins) 15 | { 16 | DescMat* descMat = (DescMat*)malloc(sizeof(DescMat)); 17 | descMat->height = height; 18 | descMat->width = width; 19 | descMat->nBins = nBins; 20 | 21 | long size = height*width*nBins; 22 | descMat->desc = (float*)malloc(size*sizeof(float)); 23 | memset(descMat->desc, 0, size*sizeof(float)); 24 | return descMat; 25 | } 26 | 27 | void ReleDescMat(DescMat* descMat) 28 | { 29 | free(descMat->desc); 30 | free(descMat); 31 | } 32 | 33 | void InitDescInfo(DescInfo* descInfo, int nBins, bool isHof, int size, int nxy_cell, int nt_cell) 34 | { 35 | descInfo->nBins = nBins; 36 | descInfo->isHof = isHof; 37 | descInfo->nxCells = nxy_cell; 38 | descInfo->nyCells = nxy_cell; 39 | descInfo->ntCells = nt_cell; 40 | descInfo->dim = nBins*nxy_cell*nxy_cell; 41 | descInfo->height = size; 42 | descInfo->width = size; 43 | } 44 | 45 | void InitSeqInfo(SeqInfo* seqInfo, char* video) 46 | { 47 | VideoCapture capture; 48 | capture.open(video); 49 | 50 | if(!capture.isOpened()) 51 | fprintf(stderr, "Could not initialize capturing..\n"); 52 | 53 | // get the number of frames in the video 54 | int frame_num = 0; 55 | while(true) { 56 | Mat frame; 57 | capture >> frame; 58 | 59 | if(frame.empty()) 60 | break; 61 | 62 | if(frame_num == 0) { 63 | seqInfo->width = frame.cols; 64 | seqInfo->height = frame.rows; 65 | } 66 | 67 | frame_num++; 68 | } 69 | seqInfo->length = frame_num; 70 | } 71 | 72 | void usage() 73 | { 74 | fprintf(stderr, "Extract improved trajectories from a video\n\n"); 75 | fprintf(stderr, "Usage: DenseTrackStab video_file [options]\n"); 76 | fprintf(stderr, "Options:\n"); 77 | fprintf(stderr, " -h Display this message and exit\n"); 78 | fprintf(stderr, " -S [start frame] The start frame to compute feature (default: S=0 frame)\n"); 79 | fprintf(stderr, " -E [end frame] The end frame for feature computing (default: E=last frame)\n"); 80 | fprintf(stderr, " -L [trajectory length] The length of the trajectory (default: L=15 frames)\n"); 81 | fprintf(stderr, " -W [sampling stride] The stride for dense sampling feature points (default: W=5 pixels)\n"); 82 | fprintf(stderr, " -N [neighborhood size] The neighborhood size for computing the descriptor (default: N=32 pixels)\n"); 83 | fprintf(stderr, " -s [spatial cells] The number of cells in the nxy axis (default: nxy=2 cells)\n"); 84 | fprintf(stderr, " -t [temporal cells] The number of cells in the nt axis (default: nt=3 cells)\n"); 85 | fprintf(stderr, " -A [scale number] The number of maximal spatial scales (default: 8 scales)\n"); 86 | fprintf(stderr, " -I [initial gap] The gap for re-sampling feature points (default: 1 frame)\n"); 87 | fprintf(stderr, " -H [human bounding box] The human bounding box file to remove outlier matches (default: None)\n"); 88 | } 89 | 90 | bool arg_parse(int argc, char** argv) 91 | { 92 | int c; 93 | bool flag = false; 94 | char* executable = basename(argv[0]); 95 | while((c = getopt (argc, argv, "hS:E:L:W:N:s:t:A:I:H:")) != -1) 96 | switch(c) { 97 | case 'S': 98 | start_frame = atoi(optarg); 99 | flag = true; 100 | break; 101 | case 'E': 102 | end_frame = atoi(optarg); 103 | flag = true; 104 | break; 105 | case 'L': 106 | track_length = atoi(optarg); 107 | break; 108 | case 'W': 109 | min_distance = atoi(optarg); 110 | break; 111 | case 'N': 112 | patch_size = atoi(optarg); 113 | break; 114 | case 's': 115 | nxy_cell = atoi(optarg); 116 | break; 117 | case 't': 118 | nt_cell = atoi(optarg); 119 | break; 120 | case 'A': 121 | scale_num = atoi(optarg); 122 | break; 123 | case 'I': 124 | init_gap = atoi(optarg); 125 | break; 126 | case 'H': 127 | bb_file = optarg; 128 | break; 129 | case 'h': 130 | usage(); 131 | exit(0); 132 | break; 133 | 134 | default: 135 | fprintf(stderr, "error parsing arguments at -%c\n Try '%s -h' for help.", c, executable ); 136 | abort(); 137 | } 138 | return flag; 139 | } 140 | 141 | #endif /*INITIALIZE_H_*/ 142 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # set the binaries that have to be built 2 | TARGETS := DenseTrackStab Video 3 | 4 | # set the build configuration set 5 | BUILD := release 6 | #BUILD := debug 7 | 8 | # set bin and build dirs 9 | BUILDDIR := .build_$(BUILD) 10 | BINDIR := $(BUILD) 11 | 12 | # libraries 13 | LDLIBS = $(addprefix -l, $(LIBS) $(LIBS_$(notdir $*))) 14 | LIBS := \ 15 | opencv_core opencv_highgui opencv_video opencv_imgproc opencv_calib3d opencv_features2d opencv_nonfree \ 16 | avformat avdevice avutil avcodec swscale \ 17 | opencv_gpu 18 | 19 | # set some flags and compiler/linker specific commands 20 | CXXFLAGS = -pipe -D __STDC_CONSTANT_MACROS -D STD=std -Wall $(CXXFLAGS_$(BUILD)) -I. -I/opt/include 21 | CXXFLAGS_debug := -ggdb 22 | CXXFLAGS_release := -O3 -DNDEBUG -ggdb 23 | LDFLAGS = -L/opt/lib -L/usr/local/lib -pipe -Wall $(LDFLAGS_$(BUILD)) 24 | LDFLAGS_debug := -ggdb 25 | LDFLAGS_release := -O3 -ggdb 26 | 27 | include make/generic.mk 28 | -------------------------------------------------------------------------------- /OpticalFlow.h: -------------------------------------------------------------------------------- 1 | #ifndef OPTICALFLOW_H_ 2 | #define OPTICALFLOW_H_ 3 | 4 | #include "DenseTrackStab.h" 5 | 6 | #include 7 | 8 | using namespace cv; 9 | 10 | namespace my 11 | { 12 | 13 | static void 14 | FarnebackPolyExp( const Mat& src, Mat& dst, int n, double sigma ) 15 | { 16 | int k, x, y; 17 | 18 | assert( src.type() == CV_32FC1 ); 19 | int width = src.cols; 20 | int height = src.rows; 21 | AutoBuffer kbuf(n*6 + 3), _row((width + n*2)*3); 22 | float* g = kbuf + n; 23 | float* xg = g + n*2 + 1; 24 | float* xxg = xg + n*2 + 1; 25 | float *row = (float*)_row + n*3; 26 | 27 | if( sigma < FLT_EPSILON ) 28 | sigma = n*0.3; 29 | 30 | double s = 0.; 31 | for( x = -n; x <= n; x++ ) 32 | { 33 | g[x] = (float)std::exp(-x*x/(2*sigma*sigma)); 34 | s += g[x]; 35 | } 36 | 37 | s = 1./s; 38 | for( x = -n; x <= n; x++ ) 39 | { 40 | g[x] = (float)(g[x]*s); 41 | xg[x] = (float)(x*g[x]); 42 | xxg[x] = (float)(x*x*g[x]); 43 | } 44 | 45 | Mat_ G = Mat_::zeros(6, 6); 46 | 47 | for( y = -n; y <= n; y++ ) 48 | for( x = -n; x <= n; x++ ) 49 | { 50 | G(0,0) += g[y]*g[x]; 51 | G(1,1) += g[y]*g[x]*x*x; 52 | G(3,3) += g[y]*g[x]*x*x*x*x; 53 | G(5,5) += g[y]*g[x]*x*x*y*y; 54 | } 55 | 56 | //G[0][0] = 1.; 57 | G(2,2) = G(0,3) = G(0,4) = G(3,0) = G(4,0) = G(1,1); 58 | G(4,4) = G(3,3); 59 | G(3,4) = G(4,3) = G(5,5); 60 | 61 | // invG: 62 | // [ x e e ] 63 | // [ y ] 64 | // [ y ] 65 | // [ e z ] 66 | // [ e z ] 67 | // [ u ] 68 | Mat_ invG = G.inv(DECOMP_CHOLESKY); 69 | double ig11 = invG(1,1), ig03 = invG(0,3), ig33 = invG(3,3), ig55 = invG(5,5); 70 | 71 | dst.create( height, width, CV_32FC(5) ); 72 | 73 | for( y = 0; y < height; y++ ) 74 | { 75 | float g0 = g[0], g1, g2; 76 | float *srow0 = (float*)(src.data + src.step*y), *srow1 = 0; 77 | float *drow = (float*)(dst.data + dst.step*y); 78 | 79 | // vertical part of convolution 80 | for( x = 0; x < width; x++ ) 81 | { 82 | row[x*3] = srow0[x]*g0; 83 | row[x*3+1] = row[x*3+2] = 0.f; 84 | } 85 | 86 | for( k = 1; k <= n; k++ ) 87 | { 88 | g0 = g[k]; g1 = xg[k]; g2 = xxg[k]; 89 | srow0 = (float*)(src.data + src.step*std::max(y-k,0)); 90 | srow1 = (float*)(src.data + src.step*std::min(y+k,height-1)); 91 | 92 | for( x = 0; x < width; x++ ) 93 | { 94 | float p = srow0[x] + srow1[x]; 95 | float t0 = row[x*3] + g0*p; 96 | float t1 = row[x*3+1] + g1*(srow1[x] - srow0[x]); 97 | float t2 = row[x*3+2] + g2*p; 98 | 99 | row[x*3] = t0; 100 | row[x*3+1] = t1; 101 | row[x*3+2] = t2; 102 | } 103 | } 104 | 105 | // horizontal part of convolution 106 | for( x = 0; x < n*3; x++ ) 107 | { 108 | row[-1-x] = row[2-x]; 109 | row[width*3+x] = row[width*3+x-3]; 110 | } 111 | 112 | for( x = 0; x < width; x++ ) 113 | { 114 | g0 = g[0]; 115 | // r1 ~ 1, r2 ~ x, r3 ~ y, r4 ~ x^2, r5 ~ y^2, r6 ~ xy 116 | double b1 = row[x*3]*g0, b2 = 0, b3 = row[x*3+1]*g0, 117 | b4 = 0, b5 = row[x*3+2]*g0, b6 = 0; 118 | 119 | for( k = 1; k <= n; k++ ) 120 | { 121 | double tg = row[(x+k)*3] + row[(x-k)*3]; 122 | g0 = g[k]; 123 | b1 += tg*g0; 124 | b4 += tg*xxg[k]; 125 | b2 += (row[(x+k)*3] - row[(x-k)*3])*xg[k]; 126 | b3 += (row[(x+k)*3+1] + row[(x-k)*3+1])*g0; 127 | b6 += (row[(x+k)*3+1] - row[(x-k)*3+1])*xg[k]; 128 | b5 += (row[(x+k)*3+2] + row[(x-k)*3+2])*g0; 129 | } 130 | 131 | // do not store r1 132 | drow[x*5+1] = (float)(b2*ig11); 133 | drow[x*5] = (float)(b3*ig11); 134 | drow[x*5+3] = (float)(b1*ig03 + b4*ig33); 135 | drow[x*5+2] = (float)(b1*ig03 + b5*ig33); 136 | drow[x*5+4] = (float)(b6*ig55); 137 | } 138 | } 139 | 140 | row -= n*3; 141 | } 142 | 143 | static void 144 | FarnebackUpdateMatrices( const Mat& _R0, const Mat& _R1, const Mat& _flow, Mat& matM, int _y0, int _y1 ) 145 | { 146 | const int BORDER = 5; 147 | static const float border[BORDER] = {0.14f, 0.14f, 0.4472f, 0.4472f, 0.4472f}; 148 | 149 | int x, y, width = _flow.cols, height = _flow.rows; 150 | const float* R1 = (float*)_R1.data; 151 | size_t step1 = _R1.step/sizeof(R1[0]); 152 | 153 | matM.create(height, width, CV_32FC(5)); 154 | 155 | for( y = _y0; y < _y1; y++ ) 156 | { 157 | const float* flow = (float*)(_flow.data + y*_flow.step); 158 | const float* R0 = (float*)(_R0.data + y*_R0.step); 159 | float* M = (float*)(matM.data + y*matM.step); 160 | 161 | for( x = 0; x < width; x++ ) 162 | { 163 | float dx = flow[x*2], dy = flow[x*2+1]; 164 | float fx = x + dx, fy = y + dy; 165 | 166 | int x1 = cvFloor(fx), y1 = cvFloor(fy); 167 | const float* ptr = R1 + y1*step1 + x1*5; 168 | float r2, r3, r4, r5, r6; 169 | 170 | fx -= x1; fy -= y1; 171 | 172 | if( (unsigned)x1 < (unsigned)(width-1) && 173 | (unsigned)y1 < (unsigned)(height-1) ) 174 | { 175 | float a00 = (1.f-fx)*(1.f-fy), a01 = fx*(1.f-fy), 176 | a10 = (1.f-fx)*fy, a11 = fx*fy; 177 | 178 | r2 = a00*ptr[0] + a01*ptr[5] + a10*ptr[step1] + a11*ptr[step1+5]; 179 | r3 = a00*ptr[1] + a01*ptr[6] + a10*ptr[step1+1] + a11*ptr[step1+6]; 180 | r4 = a00*ptr[2] + a01*ptr[7] + a10*ptr[step1+2] + a11*ptr[step1+7]; 181 | r5 = a00*ptr[3] + a01*ptr[8] + a10*ptr[step1+3] + a11*ptr[step1+8]; 182 | r6 = a00*ptr[4] + a01*ptr[9] + a10*ptr[step1+4] + a11*ptr[step1+9]; 183 | 184 | r4 = (R0[x*5+2] + r4)*0.5f; 185 | r5 = (R0[x*5+3] + r5)*0.5f; 186 | r6 = (R0[x*5+4] + r6)*0.25f; 187 | } 188 | else 189 | { 190 | r2 = r3 = 0.f; 191 | r4 = R0[x*5+2]; 192 | r5 = R0[x*5+3]; 193 | r6 = R0[x*5+4]*0.5f; 194 | } 195 | 196 | r2 = (R0[x*5] - r2)*0.5f; 197 | r3 = (R0[x*5+1] - r3)*0.5f; 198 | 199 | r2 += r4*dy + r6*dx; 200 | r3 += r6*dy + r5*dx; 201 | 202 | if( (unsigned)(x - BORDER) >= (unsigned)(width - BORDER*2) || 203 | (unsigned)(y - BORDER) >= (unsigned)(height - BORDER*2)) 204 | { 205 | float scale = (x < BORDER ? border[x] : 1.f)* 206 | (x >= width - BORDER ? border[width - x - 1] : 1.f)* 207 | (y < BORDER ? border[y] : 1.f)* 208 | (y >= height - BORDER ? border[height - y - 1] : 1.f); 209 | 210 | r2 *= scale; r3 *= scale; r4 *= scale; 211 | r5 *= scale; r6 *= scale; 212 | } 213 | 214 | M[x*5] = r4*r4 + r6*r6; // G(1,1) 215 | M[x*5+1] = (r4 + r5)*r6; // G(1,2)=G(2,1) 216 | M[x*5+2] = r5*r5 + r6*r6; // G(2,2) 217 | M[x*5+3] = r4*r2 + r6*r3; // h(1) 218 | M[x*5+4] = r6*r2 + r5*r3; // h(2) 219 | } 220 | } 221 | } 222 | 223 | static void 224 | FarnebackUpdateFlow_GaussianBlur( const Mat& _R0, const Mat& _R1, 225 | Mat& _flow, Mat& matM, int block_size, 226 | bool update_matrices ) 227 | { 228 | int x, y, i, width = _flow.cols, height = _flow.rows; 229 | int m = block_size/2; 230 | int y0 = 0, y1; 231 | int min_update_stripe = std::max((1 << 10)/width, block_size); 232 | double sigma = m*0.3, s = 1; 233 | 234 | AutoBuffer _vsum((width+m*2+2)*5 + 16), _hsum(width*5 + 16); 235 | AutoBuffer _kernel((m+1)*5 + 16); 236 | AutoBuffer _srow(m*2+1); 237 | float *vsum = alignPtr((float*)_vsum + (m+1)*5, 16), *hsum = alignPtr((float*)_hsum, 16); 238 | float* kernel = (float*)_kernel; 239 | const float** srow = (const float**)&_srow[0]; 240 | kernel[0] = (float)s; 241 | 242 | for( i = 1; i <= m; i++ ) 243 | { 244 | float t = (float)std::exp(-i*i/(2*sigma*sigma) ); 245 | kernel[i] = t; 246 | s += t*2; 247 | } 248 | 249 | s = 1./s; 250 | for( i = 0; i <= m; i++ ) 251 | kernel[i] = (float)(kernel[i]*s); 252 | 253 | #if CV_SSE2 254 | float* simd_kernel = alignPtr(kernel + m+1, 16); 255 | volatile bool useSIMD = checkHardwareSupport(CV_CPU_SSE); 256 | if( useSIMD ) 257 | { 258 | for( i = 0; i <= m; i++ ) 259 | _mm_store_ps(simd_kernel + i*4, _mm_set1_ps(kernel[i])); 260 | } 261 | #endif 262 | 263 | // compute blur(G)*flow=blur(h) 264 | for( y = 0; y < height; y++ ) 265 | { 266 | double g11, g12, g22, h1, h2; 267 | float* flow = (float*)(_flow.data + _flow.step*y); 268 | 269 | // vertical blur 270 | for( i = 0; i <= m; i++ ) 271 | { 272 | srow[m-i] = (const float*)(matM.data + matM.step*std::max(y-i,0)); 273 | srow[m+i] = (const float*)(matM.data + matM.step*std::min(y+i,height-1)); 274 | } 275 | 276 | x = 0; 277 | #if CV_SSE2 278 | if( useSIMD ) 279 | { 280 | for( ; x <= width*5 - 16; x += 16 ) 281 | { 282 | const float *sptr0 = srow[m], *sptr1; 283 | __m128 g4 = _mm_load_ps(simd_kernel); 284 | __m128 s0, s1, s2, s3; 285 | s0 = _mm_mul_ps(_mm_loadu_ps(sptr0 + x), g4); 286 | s1 = _mm_mul_ps(_mm_loadu_ps(sptr0 + x + 4), g4); 287 | s2 = _mm_mul_ps(_mm_loadu_ps(sptr0 + x + 8), g4); 288 | s3 = _mm_mul_ps(_mm_loadu_ps(sptr0 + x + 12), g4); 289 | 290 | for( i = 1; i <= m; i++ ) 291 | { 292 | __m128 x0, x1; 293 | sptr0 = srow[m+i], sptr1 = srow[m-i]; 294 | g4 = _mm_load_ps(simd_kernel + i*4); 295 | x0 = _mm_add_ps(_mm_loadu_ps(sptr0 + x), _mm_loadu_ps(sptr1 + x)); 296 | x1 = _mm_add_ps(_mm_loadu_ps(sptr0 + x + 4), _mm_loadu_ps(sptr1 + x + 4)); 297 | s0 = _mm_add_ps(s0, _mm_mul_ps(x0, g4)); 298 | s1 = _mm_add_ps(s1, _mm_mul_ps(x1, g4)); 299 | x0 = _mm_add_ps(_mm_loadu_ps(sptr0 + x + 8), _mm_loadu_ps(sptr1 + x + 8)); 300 | x1 = _mm_add_ps(_mm_loadu_ps(sptr0 + x + 12), _mm_loadu_ps(sptr1 + x + 12)); 301 | s2 = _mm_add_ps(s2, _mm_mul_ps(x0, g4)); 302 | s3 = _mm_add_ps(s3, _mm_mul_ps(x1, g4)); 303 | } 304 | 305 | _mm_store_ps(vsum + x, s0); 306 | _mm_store_ps(vsum + x + 4, s1); 307 | _mm_store_ps(vsum + x + 8, s2); 308 | _mm_store_ps(vsum + x + 12, s3); 309 | } 310 | 311 | for( ; x <= width*5 - 4; x += 4 ) 312 | { 313 | const float *sptr0 = srow[m], *sptr1; 314 | __m128 g4 = _mm_load_ps(simd_kernel); 315 | __m128 s0 = _mm_mul_ps(_mm_loadu_ps(sptr0 + x), g4); 316 | 317 | for( i = 1; i <= m; i++ ) 318 | { 319 | sptr0 = srow[m+i], sptr1 = srow[m-i]; 320 | g4 = _mm_load_ps(simd_kernel + i*4); 321 | __m128 x0 = _mm_add_ps(_mm_loadu_ps(sptr0 + x), _mm_loadu_ps(sptr1 + x)); 322 | s0 = _mm_add_ps(s0, _mm_mul_ps(x0, g4)); 323 | } 324 | _mm_store_ps(vsum + x, s0); 325 | } 326 | } 327 | #endif 328 | for( ; x < width*5; x++ ) 329 | { 330 | float s0 = srow[m][x]*kernel[0]; 331 | for( i = 1; i <= m; i++ ) 332 | s0 += (srow[m+i][x] + srow[m-i][x])*kernel[i]; 333 | vsum[x] = s0; 334 | } 335 | 336 | // update borders 337 | for( x = 0; x < m*5; x++ ) 338 | { 339 | vsum[-1-x] = vsum[4-x]; 340 | vsum[width*5+x] = vsum[width*5+x-5]; 341 | } 342 | 343 | // horizontal blur 344 | x = 0; 345 | #if CV_SSE2 346 | if( useSIMD ) 347 | { 348 | for( ; x <= width*5 - 8; x += 8 ) 349 | { 350 | __m128 g4 = _mm_load_ps(simd_kernel); 351 | __m128 s0 = _mm_mul_ps(_mm_loadu_ps(vsum + x), g4); 352 | __m128 s1 = _mm_mul_ps(_mm_loadu_ps(vsum + x + 4), g4); 353 | 354 | for( i = 1; i <= m; i++ ) 355 | { 356 | g4 = _mm_load_ps(simd_kernel + i*4); 357 | __m128 x0 = _mm_add_ps(_mm_loadu_ps(vsum + x - i*5), 358 | _mm_loadu_ps(vsum + x + i*5)); 359 | __m128 x1 = _mm_add_ps(_mm_loadu_ps(vsum + x - i*5 + 4), 360 | _mm_loadu_ps(vsum + x + i*5 + 4)); 361 | s0 = _mm_add_ps(s0, _mm_mul_ps(x0, g4)); 362 | s1 = _mm_add_ps(s1, _mm_mul_ps(x1, g4)); 363 | } 364 | 365 | _mm_store_ps(hsum + x, s0); 366 | _mm_store_ps(hsum + x + 4, s1); 367 | } 368 | } 369 | #endif 370 | for( ; x < width*5; x++ ) 371 | { 372 | float sum = vsum[x]*kernel[0]; 373 | for( i = 1; i <= m; i++ ) 374 | sum += kernel[i]*(vsum[x - i*5] + vsum[x + i*5]); 375 | hsum[x] = sum; 376 | } 377 | 378 | for( x = 0; x < width; x++ ) 379 | { 380 | g11 = hsum[x*5]; 381 | g12 = hsum[x*5+1]; 382 | g22 = hsum[x*5+2]; 383 | h1 = hsum[x*5+3]; 384 | h2 = hsum[x*5+4]; 385 | 386 | double idet = 1./(g11*g22 - g12*g12 + 1e-3); 387 | 388 | flow[x*2] = (float)((g11*h2-g12*h1)*idet); 389 | flow[x*2+1] = (float)((g22*h1-g12*h2)*idet); 390 | } 391 | 392 | y1 = y == height - 1 ? height : y - block_size; 393 | if( update_matrices && (y1 == height || y1 >= y0 + min_update_stripe) ) 394 | { 395 | FarnebackUpdateMatrices( _R0, _R1, _flow, matM, y0, y1 ); 396 | y0 = y1; 397 | } 398 | } 399 | } 400 | 401 | // in-place median blur for optical flow 402 | void MedianBlurFlow(Mat& flow, const int ksize) 403 | { 404 | Mat channels[2]; 405 | split(flow, channels); 406 | medianBlur(channels[0], channels[0], ksize); 407 | medianBlur(channels[1], channels[1], ksize); 408 | merge(channels, 2, flow); 409 | } 410 | 411 | void FarnebackPolyExpPyr(const Mat& img, std::vector& poly_exp_pyr, 412 | std::vector& fscales, int poly_n, double poly_sigma) 413 | { 414 | Mat fimg; 415 | 416 | for(int k = 0; k < poly_exp_pyr.size(); k++) 417 | { 418 | double sigma = (fscales[k]-1)*0.5; 419 | int smooth_sz = cvRound(sigma*5)|1; 420 | smooth_sz = std::max(smooth_sz, 3); 421 | 422 | int width = poly_exp_pyr[k].cols; 423 | int height = poly_exp_pyr[k].rows; 424 | 425 | Mat R, I; 426 | 427 | img.convertTo(fimg, CV_32F); 428 | GaussianBlur(fimg, fimg, Size(smooth_sz, smooth_sz), sigma, sigma); 429 | resize(fimg, I, Size(width, height), CV_INTER_LINEAR); 430 | 431 | FarnebackPolyExp(I, R, poly_n, poly_sigma); 432 | R.copyTo(poly_exp_pyr[k]); 433 | } 434 | } 435 | 436 | void calcOpticalFlowFarneback(std::vector& prev_poly_exp_pyr, std::vector& poly_exp_pyr, 437 | std::vector& flow_pyr, int winsize, int iterations) 438 | { 439 | int i, k; 440 | Mat prevFlow, flow; 441 | 442 | for( k = flow_pyr.size() - 1; k >= 0; k-- ) 443 | { 444 | int width = flow_pyr[k].cols; 445 | int height = flow_pyr[k].rows; 446 | 447 | flow.create( height, width, CV_32FC2 ); 448 | 449 | if( !prevFlow.data ) 450 | flow = Mat::zeros( height, width, CV_32FC2 ); 451 | else { 452 | resize( prevFlow, flow, Size(width, height), 0, 0, INTER_LINEAR ); 453 | flow *= scale_stride; 454 | } 455 | 456 | Mat R[2], M; 457 | 458 | prev_poly_exp_pyr[k].copyTo(R[0]); 459 | poly_exp_pyr[k].copyTo(R[1]); 460 | 461 | FarnebackUpdateMatrices( R[0], R[1], flow, M, 0, flow.rows ); 462 | 463 | for( i = 0; i < iterations; i++ ) 464 | FarnebackUpdateFlow_GaussianBlur( R[0], R[1], flow, M, winsize, i < iterations - 1 ); 465 | 466 | MedianBlurFlow(flow, 5); 467 | prevFlow = flow; 468 | flow.copyTo(flow_pyr[k]); 469 | } 470 | } 471 | 472 | } 473 | 474 | #endif /*OPTICALFLOW_H_*/ 475 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ### Compiling ### 2 | 3 | In order to compile the improved trajectories code, you need to have the following libraries installed in your system: 4 | * OpenCV library (tested with OpenCV-2.4.2) 5 | * ffmpeg library (tested with ffmpeg-0.11.1) 6 | 7 | Currently, the libraries are the latest versions. In case they will be out of date, you can also find them on our website: http://lear.inrialpes.fr/people/wang/improved_trajectories 8 | 9 | If these libraries are installed correctly, simply type 'make' to compile the code. The executable will be in the directory './release/'. 10 | 11 | ### test video decoding ### 12 | 13 | The most complicated part of compiling is to install opencv and ffmpeg. To make sure your video is decoded properly, we have a simple code (named 'Video.cpp') for visualization: 14 | 15 | ./release/Video your_video.avi 16 | 17 | If your video plays smoothly, congratulations! You are just one step before getting the features. 18 | 19 | If there is a bug and the video can't be decoded, you need first fix your bug. You can find plenty of instructions about how to install opencv and ffmpeg on the web. 20 | 21 | ### compute features on a test video ### 22 | 23 | Once you are able to decode the video, computing our features is simple: 24 | 25 | ./release/DenseTrackStab ./test_sequences/person01_boxing_d1_uncomp.avi | gzip > out.features.gz 26 | 27 | Now you want to compare your file out.features.gz with the file that we have computed to verify that everything is working correctly. To do so, type: 28 | 29 | vimdiff out.features.gz ./test_sequences/person01_boxing_d1.gz 30 | 31 | Note that due to different versions of codecs, your features may be slightly different with ours. But the major part should be the same. 32 | 33 | Due to the randomness of RANSAC, you may get different features for some videos. But for the example "person01_boxing_d1_uncomp.avi", I don't observe any randomness. 34 | 35 | There are more explanations about our features on the website, and also a list of FAQ. 36 | 37 | ### History ### 38 | 39 | * October 2013: improved_trajectory_release.tar.gz 40 | The code is an extension of dense_trajectory_release_v1.2.tar.gz 41 | 42 | ### Bugs and extensions ### 43 | 44 | If you find bugs, etc., feel free to drop me a line. Also if you developed some extension to the program, let me know and I can include it in the code. You can find my contact data on my webpage, as well. 45 | 46 | http://lear.inrialpes.fr/people/wang/ 47 | 48 | ### LICENSE CONDITIONS ### 49 | 50 | Copyright (C) 2011 Heng Wang 51 | 52 | This program is free software; you can redistribute it and/or 53 | modify it under the terms of the GNU General Public License 54 | as published by the Free Software Foundation; either version 2 55 | of the License, or (at your option) any later version. 56 | 57 | This program is distributed in the hope that it will be useful, 58 | but WITHOUT ANY WARRANTY; without even the implied warranty of 59 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 60 | GNU General Public License for more details. 61 | 62 | You should have received a copy of the GNU General Public License 63 | along with this program; if not, write to the Free Software 64 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 65 | 66 | -------------------------------------------------------------------------------- /Video.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | IplImage* image = 0; 17 | IplImage* prev_image = 0; 18 | CvCapture* capture = 0; 19 | 20 | int show = 1; 21 | 22 | int main( int argc, char** argv ) 23 | { 24 | int frameNum = 0; 25 | 26 | char* video = argv[1]; 27 | capture = cvCreateFileCapture(video); 28 | 29 | if( !capture ) { 30 | printf( "Could not initialize capturing..\n" ); 31 | return -1; 32 | } 33 | 34 | if( show == 1 ) 35 | cvNamedWindow( "Video", 0 ); 36 | 37 | while( true ) { 38 | IplImage* frame = 0; 39 | int i, j, c; 40 | 41 | // get a new frame 42 | frame = cvQueryFrame( capture ); 43 | if( !frame ) 44 | break; 45 | 46 | if( !image ) { 47 | image = cvCreateImage( cvSize(frame->width,frame->height), 8, 3 ); 48 | image->origin = frame->origin; 49 | } 50 | 51 | cvCopy( frame, image, 0 ); 52 | 53 | if( show == 1 ) { 54 | cvShowImage( "Video", image); 55 | c = cvWaitKey(3); 56 | if((char)c == 27) break; 57 | } 58 | 59 | std::cerr << "The " << frameNum << "-th frame" << std::endl; 60 | frameNum++; 61 | } 62 | 63 | if( show == 1 ) 64 | cvDestroyWindow("Video"); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /make/dep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (C) 2009 Alexander Kl"aser 4 | # 5 | # This piece is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | # 19 | # This software has been downloaded from: 20 | # http://lear.inrialpes.fr/people/klaeser/software 21 | # 22 | 23 | import sys 24 | import os 25 | import string 26 | import os.path 27 | import re 28 | 29 | HELP_USAGE = """ 30 | Usage: dep.py ... 31 | """ 32 | 33 | regSuffix = re.compile(r"\.[^.]*$") 34 | regSrc = re.compile(r"^.*\.(c|cc|cpp)$") 35 | regDep = re.compile(r"^.*\.d$") 36 | regDepSplit = re.compile(r"\s*\\*\s*") 37 | 38 | suffixes = ['.cpp', '.c', '.cc'] 39 | includeDirs = [] 40 | 41 | 42 | def parseDepFile(fileName): 43 | # read in the dependency file 44 | depFile = open(fileName, 'r') 45 | depStr = depFile.read() 46 | 47 | # discard everything up to the colon 48 | colonPos = depStr.find(":") 49 | assert colonPos > 0, "the dependency file '" + fileName + "' does not have the correct format" 50 | depStr = depStr[colonPos + 1:] 51 | 52 | # collect all included files 53 | return regDepSplit.split(depStr) 54 | 55 | 56 | def findSourceFile(headerFile): 57 | # get the basename without extension 58 | headerFile = regSuffix.sub('', headerFile) 59 | if not headerFile: 60 | return None 61 | 62 | # iterate over known suffixes 63 | for suffix in suffixes: 64 | srcFile = headerFile + suffix 65 | 66 | # check whether a source file corresponding to the header exists 67 | if os.path.exists(srcFile): 68 | return srcFile 69 | 70 | # we add to the file path directory by directory and check whether it 71 | # exists in one of the include directories 72 | i = headerFile.find('/') + 1 73 | if i != 1: 74 | i = 0 75 | while True: 76 | # check whether a source file exists in one of the given include dirs 77 | for dir in includeDirs: 78 | # check all suffixes for source files 79 | for suffix in suffixes: 80 | srcFile = os.path.join(dir, headerFile[i:] + suffix) 81 | #srcFile = os.path.abspath(srcFile) 82 | if os.path.exists(srcFile): 83 | return srcFile 84 | 85 | # find next position of '/' 86 | i = headerFile.find('/', i) + 1 87 | if i <= 0: 88 | break 89 | 90 | return None 91 | 92 | 93 | def main(argv): 94 | global includeDirs 95 | 96 | # check command line parameters 97 | if len(sys.argv) < 5: 98 | print HELP_USAGE 99 | return 100 | 101 | args = sys.argv 102 | args.pop(0) 103 | ruleTarget = args.pop(0) 104 | linkFile = args.pop(0) 105 | buildDir = args.pop(0) 106 | rootDepFile = args.pop(0) 107 | includeDirs = args 108 | 109 | 110 | # scan all dependency files for files we need to link to 111 | # do this recursively starting at the root dependency file 112 | linkFiles = set() 113 | incFiles = set() 114 | depFileStack = set([rootDepFile]) 115 | depFilesDone = set() 116 | while depFileStack: 117 | # get the next dependency file to process from the stack 118 | depFile = depFileStack.pop() 119 | if depFile in depFilesDone: 120 | continue 121 | depFilesDone.add(depFile) 122 | 123 | # iterate over all source files in the dependency file 124 | for nextFile in parseDepFile(depFile): 125 | newDepFile = "" 126 | 127 | # if we have a source file, we need to link against it 128 | if regSrc.match(nextFile): 129 | linkFiles.add(nextFile) 130 | newDepFile = buildDir + "/" + regSuffix.sub(".d", nextFile) 131 | 132 | # check whether a .cpp/.c/.cc file exist 133 | srcFile = findSourceFile(nextFile) 134 | if srcFile != None: 135 | linkFiles.add(srcFile) 136 | newDepFile = buildDir + "/" + regSuffix.sub(".d", srcFile) 137 | 138 | # if the corresponding .d file exists as parameter, add it to the stack 139 | if newDepFile and os.path.exists(newDepFile): 140 | depFileStack.add(newDepFile) 141 | 142 | # 143 | # generate all necessary rules 144 | # 145 | 146 | # all includes of dependency files 147 | for i in linkFiles: 148 | i = regSuffix.sub(".d", i) 149 | print "-include " + buildDir + "/" + i 150 | print 151 | 152 | # dependencies for link file 153 | print linkFile + ": \\" 154 | for i in linkFiles: 155 | i = regSuffix.sub(".d", i) 156 | print "\t" + buildDir + "/" + i + " \\" 157 | print 158 | 159 | # print out all files we need to link against 160 | print ruleTarget + ": " + linkFile + " \\" 161 | for i in linkFiles: 162 | i = regSuffix.sub(".o", i) 163 | print "\t" + buildDir + "/" + i + " \\" 164 | print 165 | 166 | 167 | if __name__ == "__main__": 168 | main( sys.argv ) 169 | 170 | 171 | -------------------------------------------------------------------------------- /make/generic.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2009 Alexander Kl"aser 3 | # 4 | # This piece is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | # 18 | # This software has been downloaded from: 19 | # http://lear.inrialpes.fr/people/klaeser/software 20 | # 21 | # 22 | # Variables that need to be set in the Makefile that includes this file: 23 | # TARGETS all files that are exectuables without there .cpp extension 24 | # BUILDDIR temporary dir where things are compiled to (optional, by default ".build") 25 | # BINDIR dir where executables are linked to (optional, by default "bin") 26 | # SRCDIRS list of directories in which source files are located 27 | # this variable needs to be set if you do not have your source and 28 | # include files located in the same directory! 29 | # 30 | # Variables used for compiling/linking: 31 | # CXXFLAGS flags for compiling 32 | # LDFLAGS flags used for linking 33 | # LDLIBS list of libraries to be linked 34 | # CXX compiler linker (should be g++ by default) 35 | # 36 | 37 | # set paths for the dependency tool and gcc 38 | DEP = make/dep.py 39 | 40 | # set some standard directories in case they have not been set 41 | BUILDDIR ?= .build 42 | BINDIR ?= bin 43 | 44 | # all include files 45 | INCLUDES := $(addprefix $(BUILDDIR)/,$(TARGETS:=.l)) 46 | 47 | 48 | # 49 | # some general rules 50 | # 51 | 52 | .PHONY: all clean 53 | .PRECIOUS: $(BUILDDIR)/%.d 54 | 55 | all: $(BINDIR) $(addprefix $(BINDIR)/,$(notdir $(TARGETS))) 56 | @echo "=== done ===" 57 | 58 | $(INCLUDES): $(BUILDDIR) 59 | 60 | clean: 61 | @echo "=== cleaning up ===" 62 | @rm -rf $(BUILDDIR) 63 | 64 | $(BUILDDIR) $(BINDIR): 65 | @echo "=== creating directory: $@ ===" 66 | @mkdir -p $@ 67 | 68 | 69 | # 70 | # rules for creating dependency files 71 | # 72 | 73 | # dependencies of .cpp files on other files 74 | $(BUILDDIR)/%.d: %.cpp 75 | @echo "=== creating dependency file: $@ ===" 76 | @test -e $(dir $@) || mkdir -p $(dir $@) 77 | g++ $(CXXFLAGS) -MM -MT $(BUILDDIR)/$*.o -MT $(BUILDDIR)/$*.d -MF $@ $< 78 | 79 | # dependencies for the linking 80 | %.so.l %.l: %.d 81 | @echo "=== creating dependency file: $@ ===" 82 | @test -e $(dir $@) || mkdir -p $(dir $@) 83 | $(DEP) "$(BINDIR)/$(@F:.l=)" $*.l $(BUILDDIR) $< $(SRCDIRS) > $@ 84 | 85 | 86 | # 87 | # rules for compiling and linking 88 | # (link dependencies are defined in .l files) 89 | # 90 | 91 | # compiling 92 | $(BUILDDIR)/%.o: %.cpp 93 | @echo "=== compiling: $@ ===" 94 | @test -e $(dir $@) || mkdir -p $(dir $@) 95 | $(CXX) -fPIC $(CXXFLAGS) -c -o $@ $< 96 | 97 | # linking for shared libraries 98 | $(BINDIR)/%.so: 99 | @echo "=== linking: $@ ===" 100 | @rm -f $@ 101 | $(CXX) -shared $(LDFLAGS) -o $@ $(filter %.o, $^) $(LDLIBS) 102 | 103 | # linking 104 | $(BINDIR)/%: 105 | @echo "=== linking: $@ ===" 106 | @rm -f $@ 107 | $(CXX) $(LDFLAGS) -o $@ $(filter %.o, $^) $(LDLIBS) 108 | 109 | %: %.o 110 | %.h: ; 111 | %.hpp: ; 112 | %.c: ; 113 | %.cpp: ; 114 | 115 | 116 | # 117 | # include dependency files 118 | # 119 | 120 | ifneq ($(MAKECMDGOALS),clean) 121 | -include $(INCLUDES) 122 | endif 123 | --------------------------------------------------------------------------------