├── CMakeLists.txt ├── Makefile ├── README ├── deepflow.c ├── image.c ├── image.h ├── io.c ├── io.h ├── opticalflow.c ├── opticalflow.h ├── opticalflow_aux.c ├── opticalflow_aux.h ├── py_fastdeepflow ├── __init__.py └── fastdeepflow.py ├── solver.c ├── solver.h └── tests ├── __init__.py ├── data ├── artificial │ ├── 20_0 │ │ ├── 20_midl_minsize10.flo │ │ ├── img0.png │ │ └── img1.png │ └── 48_0 │ │ ├── 48_midl.flo │ │ ├── 48_midl_minsize10.flo │ │ ├── img0.png │ │ └── img1.png ├── sintel1.png ├── sintel2.png └── sintel_cmd.flo ├── test_fastdeepflow.py └── test_parameters.py /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(deepflow_release1_0_1) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -msse4") 5 | 6 | 7 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) 8 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 10 | 11 | set(SOURCE_FILES 12 | deepflow.c 13 | image.c 14 | image.h 15 | io.c 16 | io.h 17 | opticalflow.c 18 | opticalflow.h 19 | opticalflow_aux.c 20 | opticalflow_aux.h 21 | README 22 | solver.c 23 | solver.h) 24 | 25 | 26 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin") 27 | add_executable(fastdeepflow_release1_0_1 ${SOURCE_FILES}) 28 | 29 | target_link_libraries(fastdeepflow_release1_0_1 jpeg m png) 30 | 31 | 32 | set(LIB_SOURCE_FILES 33 | io.c 34 | io.h 35 | image.h 36 | image.c 37 | opticalflow.c 38 | opticalflow.h 39 | opticalflow_aux.c 40 | opticalflow_aux.h 41 | solver.c 42 | solver.h 43 | ) 44 | add_library(libdeepflow SHARED ${LIB_SOURCE_FILES}) 45 | 46 | target_link_libraries(libdeepflow jpeg m png) 47 | set_target_properties(libdeepflow PROPERTIES PREFIX "" OUTPUT_NAME "libdeepflow") 48 | #caffe_default_properties(pycaffe) -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | 3 | CFLAGS=-Wall -g -O3 -msse4 4 | LDFLAGS=-g -Wall -O3 -msse4 5 | LIBFLAGS=-lm -ljpeg -lpng 6 | LIBAFLAGS=-static /usr/lib/libjpeg.a /usr/lib/libpng.a /usr/lib/libz.a /usr/lib/libm.a 7 | 8 | all: fastdeepflow 9 | 10 | fastdeepflow: deepflow.o image.o io.o opticalflow_aux.o opticalflow.o solver.o 11 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBFLAGS) 12 | 13 | fastdeepflow-static: deepflow.o image.o io.o opticalflow_aux.o opticalflow.o solver.o 14 | $(CC) $(LIBFLAGS) -o $@ $^ $(LIBAFLAGS) 15 | 16 | %.o: %.c 17 | $(CC) -o $@ $(CFLAGS) -c $+ 18 | 19 | clean: 20 | rm -f *.o deepflow 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | FastDeepFlow, v1.0.1 2 | 3 | Our code is mentioned only for scientific or personal use. 4 | Please contact us for commercial use. 5 | 6 | ### Compiling ### 7 | 8 | Simply type 'make' and the code must compile. 9 | The program was only tested under a 64-bit Linux distribution. 10 | We do not give any support for other OS. 11 | This version use SSE instructions and thus require a compatible CPU. 12 | Please consider using DeepFlow instead of FastDeepFlow for others CPUs. 13 | 14 | ### Using FastDeepFlow ### 15 | 16 | You can use FastDeepFlow without any input matches. 17 | The general command to use FastDeepFlow is: 18 | ./fastdeepflow [options] 19 | 20 | FastDeepFlow takes benefit of SSE instructions to speed up the computation of DeepFlow. 21 | The results are slightly different due to floating point approximations. 22 | The endpoint difference is around 1e-4 pixels in average but can be around 1 pixel near discontinuities. 23 | 24 | For a list of the available options, you can type ./fastdeepflow -h 25 | You can basically set some parameters, or directly use the parameter we use in the ICCV 2013 paper for MPI-Sintel, Middlebury or KITTI. 26 | The option -match and -matchf allows to give input matches, either from an input file or from stdin. 27 | 28 | ### Example including deep matching ### 29 | 30 | To test FastDeepFlow including the deep matching, you need to download the deep matching code on Jerome Revaud's webpage: http://lear.inrialpes.fr/people/revaud 31 | The matches output by deep matching can directly be piped to FastDeepFlow like in the following example: 32 | 33 | /deepmatching sintel1.png sintel2.png -iccv_settings | python /rescore.py sintel1.png sintel2.png | ./fastdeepflow sintel1.png sintel2.png sintel.flo -matchf -sintel 34 | 35 | It creates a .flo file in a usual format. 36 | For instance, code for reading and displaying such format is available with the Middlebury dataset. 37 | 38 | The matches are similar to the one we use in ICCV'13 paper. If you need exactly the same, you can download them on the webpage. 39 | If you want better results, just replace -iccv_settings by -improved_settings. 40 | 41 | ### Bugs and extensions ### 42 | 43 | If you find bugs, etc., please feel free to contact me. 44 | Contact details are available on my webpage. 45 | http://lear.inrialpes.fr/people/pweinzae 46 | 47 | ### History ### 48 | 49 | December 2013 v1.0.0 50 | March 2014 v1.0.1: memory leak fix + support for png images 51 | March 2014 FastDeepFlow_v1.0.1: faster (~2x) version using SSE instructions. Results are slightly different due to floating point approximations. 52 | 53 | ### LICENCE CONDITIONS ### 54 | 55 | Copyright (C) 2013 Philippe Weinzaepfel 56 | 57 | This program is free software: you can redistribute it and/or modify 58 | it under the terms of the GNU General Public License as published by 59 | the Free Software Foundation, either version 3 of the License, or 60 | (at your option) any later version. 61 | 62 | This program is distributed in the hope that it will be useful, 63 | but WITHOUT ANY WARRANTY; without even the implied warranty of 64 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 65 | GNU General Public License for more details. 66 | 67 | You should have received a copy of the GNU General Public License 68 | along with this program. If not, see . 69 | -------------------------------------------------------------------------------- /deepflow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "image.h" 6 | #include "opticalflow.h" 7 | #include "io.h" 8 | 9 | int load_matches_from_file(image_t *match_x, image_t *match_y, image_t *match_z, int wm, int hm, 10 | const char *path_match_file); 11 | 12 | void usage(){ 13 | printf("usage:\n"); 14 | printf("./deepflow image1 image2 outputfile [options] \n"); 15 | printf("Compute the flow between two images and store it into a file\n"); 16 | printf("Images must be in PPM or JPG format"); 17 | printf("\n"); 18 | printf("options:\n"); 19 | printf(" -h, --help print this message\n"); 20 | printf(" -a, -alpha (12.0) weight of smoothness terms\n"); 21 | printf(" -b, -beta (300.0) weight of descriptor matching\n"); 22 | printf(" -g, -gamma (3.0) weight of gradient constancy assumption\n"); 23 | printf(" -d, -delta (2.0) weight of color constancy assumption\n"); 24 | printf(" -s, -sigma (0.8) standard deviation of Gaussian presmoothing kernel\n"); 25 | printf(" -e, -eta (0.95) ratio factor for coarse-to-fine scheme\n"); 26 | printf(" -minsize (25) size of the coarsest level\n"); 27 | printf(" -inner (5) number of inner fixed point iterations\n"); 28 | printf(" -iter (25) number of iterations for the solver\n"); 29 | printf(" -soromega (1.6) omega parameter of the sor method\n"); 30 | printf(" -bk (0.0) use decreasing beta i.e. beta(k) = beta*( k / kmax )^betak, if 0, a last iteration is done with beta=0\n"); 31 | printf("\n"); 32 | printf(" -match '-match filename' reads matches from a file and '-match' from stdin. Matches must be given with the format x1 y1 x2 y2 score at a resolution of 512x256\n"); 33 | printf(" -matchf same as -match with images at original resolution\n"); 34 | printf("\n"); 35 | printf(" -sintel set the parameters to the one used in the ICCV paper for MPI-Sintel dataset\n"); 36 | printf(" -middlebury set the parameters to the one used in the ICCV paper for middlebury dataset\n"); 37 | printf(" -kitti set the parameters to the one used in the ICCV paper for KITTI dataset\n"); 38 | printf("\n"); 39 | } 40 | 41 | void require_argument(const char *arg){ 42 | fprintf(stderr, "Require an argument after option %s\n", arg); 43 | exit(1); 44 | } 45 | 46 | int main(int argc, char ** argv){ 47 | image_t *match_x = NULL, *match_y = NULL, *match_z = NULL; 48 | 49 | // load images 50 | if(argc < 4){ 51 | fprintf(stderr,"Wrong command, require at least 3 arguments.\n\n"); 52 | usage(); 53 | exit(1); 54 | } 55 | color_image_t *im1 = color_image_load(argv[1]), *im2 = color_image_load(argv[2]); 56 | if(im1->width != im2->width || im1->height != im2->height){ 57 | fprintf(stderr,"Image dimensions does not match\n"); 58 | exit(1); 59 | } 60 | 61 | // set params to default 62 | optical_flow_params_t* params = (optical_flow_params_t*) malloc(sizeof(optical_flow_params_t)); 63 | if(!params){ 64 | fprintf(stderr,"error deepflow(): not enough memory\n"); 65 | exit(1); 66 | } 67 | optical_flow_params_default(params); 68 | 69 | // parse options 70 | int current_arg = 4; 71 | while(1){ 72 | if( current_arg >= argc) break; 73 | if(!strcmp(argv[current_arg],"-h") || !strcmp(argv[current_arg],"--help") ){ 74 | usage(); 75 | exit(1); 76 | }else if(!strcmp(argv[current_arg],"-a") || !strcmp(argv[current_arg],"-alpha") ){ 77 | current_arg++; 78 | if(current_arg >= argc) require_argument("alpha"); 79 | float alpha = atof(argv[current_arg++]); 80 | if(alpha<0){ 81 | fprintf(stderr,"Alpha argument cannot be negative\n"); 82 | exit(1); 83 | } 84 | params->alpha = alpha; 85 | }else if(!strcmp(argv[current_arg],"-b") || !strcmp(argv[current_arg],"-beta") ){ 86 | current_arg++; 87 | if(current_arg >= argc) require_argument("beta"); 88 | float beta = atof(argv[current_arg++]); 89 | if(beta<0){ 90 | fprintf(stderr,"Beta argument cannot be negative\n"); 91 | exit(1); 92 | } 93 | params->beta = beta; 94 | }else if(!strcmp(argv[current_arg],"-g") || !strcmp(argv[current_arg],"-gamma") ){ 95 | current_arg++; 96 | if(current_arg >= argc) require_argument("gamma"); 97 | float gamma = atof(argv[current_arg++]); 98 | if(gamma<0){ 99 | fprintf(stderr,"Gamma argument cannot be negative\n"); 100 | exit(1); 101 | } 102 | params->gamma = gamma; 103 | }else if(!strcmp(argv[current_arg],"-d") || !strcmp(argv[current_arg],"-delta") ){ 104 | current_arg++; 105 | if(current_arg >= argc) require_argument("delta"); 106 | float delta = atof(argv[current_arg++]); 107 | if(delta<0) { 108 | fprintf(stderr,"Delta argument cannot be negative\n"); 109 | exit(1); 110 | } 111 | params->delta = delta; 112 | }else if(!strcmp(argv[current_arg],"-s") || !strcmp(argv[current_arg],"-sigma") ){ 113 | current_arg++; 114 | if(current_arg >= argc) require_argument("sigma"); 115 | float sigma = atof(argv[current_arg++]); 116 | if(sigma<0){ 117 | fprintf(stderr,"Sigma argument is negative\n"); 118 | exit(1); 119 | } 120 | params->sigma = sigma; 121 | }else if(!strcmp(argv[current_arg],"-bk")) { 122 | current_arg++; 123 | if(current_arg >= argc) require_argument("bk"); 124 | float betak = atof(argv[current_arg++]); 125 | if(betak<0.0f){ 126 | fprintf(stderr,"Bk argument must be positive\n"); 127 | exit(1); 128 | } 129 | params->bk = betak; 130 | }else if(!strcmp(argv[current_arg],"-e") || !strcmp(argv[current_arg],"-eta") ){ 131 | current_arg++; 132 | if(current_arg >= argc) require_argument("eta"); 133 | float eta = atof(argv[current_arg++]); 134 | if(eta<0.25 || eta>0.98){ 135 | fprintf(stderr,"Eta argument has to be between 0.25 and 0.98\n"); 136 | exit(1); 137 | } 138 | params->eta = eta; 139 | }else if( !strcmp(argv[current_arg],"-minsize") ){ 140 | current_arg++; 141 | if(current_arg >= argc) require_argument("minsize"); 142 | int minsize = atoi(argv[current_arg++]); 143 | if(minsize < 10){ 144 | fprintf(stderr,"Minsize argument has to be higher than 10\n"); 145 | exit(1); 146 | } 147 | params->min_size = minsize; 148 | }else if(!strcmp(argv[current_arg],"-inner") ){ 149 | current_arg++; 150 | if(current_arg >= argc) require_argument("inner"); 151 | int inner = atoi(argv[current_arg++]); 152 | if(inner<=0){ 153 | fprintf(stderr,"Inner argument must be strictly positive\n"); 154 | exit(1); 155 | } 156 | params->n_inner_iteration = inner; 157 | }else if(!strcmp(argv[current_arg],"-iter") ){ 158 | current_arg++; 159 | if(current_arg >= argc) require_argument("iter"); 160 | int iter = atoi(argv[current_arg++]); 161 | if(iter<=0){ 162 | fprintf(stderr,"Iter argument must be strictly positive\n"); 163 | exit(1); 164 | } 165 | params->n_solver_iteration = iter; 166 | }else if( !strcmp(argv[current_arg],"-match") || !strcmp(argv[current_arg],"-matchf")){ 167 | int wm = im1->width, hm = im1->height; 168 | if( !strcmp(argv[current_arg++],"-match") ){ 169 | wm = 512; 170 | hm = 256; 171 | } 172 | image_delete(match_x); image_delete(match_y); image_delete(match_z); 173 | match_x = image_new(wm, hm); match_y = image_new(wm, hm); match_z = image_new(wm, hm); 174 | 175 | char *path_match_file = 0; 176 | if( current_argwidth,im1->height), *wy = image_new(im1->width,im1->height); 204 | optical_flow(wx, wy, im1, im2, params, match_x, match_y, match_z); 205 | const char *dst_filename = argv[3]; 206 | if (writeFlowFile(dst_filename, wx, wy) != 0) { 207 | fprintf(stderr, "Error while opening %s\n", dst_filename); 208 | exit(1); 209 | } 210 | image_delete(wx); 211 | image_delete(wy); 212 | image_delete(match_x); image_delete(match_y); image_delete(match_z); 213 | color_image_delete(im1); color_image_delete(im2); 214 | free(params); 215 | 216 | return 0; 217 | } 218 | 219 | int load_matches_from_file(image_t *match_x, image_t *match_y, image_t *match_z, int wm, int hm, 220 | const char *path_match_file) { 221 | FILE *fid = stdin; 222 | if( path_match_file != 0 ){ 223 | fid = fopen(path_match_file, "r"); 224 | if(fid==NULL){ 225 | fprintf(stderr, "Cannot read matches from file %s", path_match_file); 226 | return 1; 227 | } 228 | } 229 | 230 | image_erase(match_x); 231 | image_erase(match_y); 232 | image_erase(match_z); 233 | 234 | int x1, x2, y1, y2; 235 | float score; 236 | while(!feof(fid) && fscanf(fid, "%d %d %d %d %f\n", &x1, &y1, &x2, &y2, &score)==5){ 237 | if( x1<0 || y1<0 || x2<0 || y2<0 || x1>=wm || y1>=hm || x2>=wm || y2>=hm){ 238 | fprintf(stderr, "Error while reading matches %d %d -> %d %d, out of bounds\n", x1, y1, x2, y2); 239 | return 1; 240 | } 241 | match_x->data[ y1*match_x->stride+x1 ] = (float) (x2-x1); 242 | match_y->data[ y1*match_x->stride+x1 ] = (float) (y2-y1); 243 | match_z->data[ y1*match_x->stride+x1 ] = score; 244 | } 245 | return 0; 246 | } 247 | -------------------------------------------------------------------------------- /image.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "image.h" 8 | 9 | #include 10 | typedef __v4sf v4sf; 11 | 12 | /********** Create/Delete **********/ 13 | 14 | /* allocate a new image of size width x height */ 15 | image_t *image_new(const int width, const int height){ 16 | image_t *image = (image_t*) malloc(sizeof(image_t)); 17 | if(image == NULL){ 18 | fprintf(stderr, "Error: image_new() - not enough memory !\n"); 19 | exit(1); 20 | } 21 | image->width = width; 22 | image->height = height; 23 | image->stride = ( (width+3) / 4 ) * 4; 24 | image->data = (float*) memalign(16, image->stride*height*sizeof(float)); 25 | if(image->data == NULL){ 26 | fprintf(stderr, "Error: image_new() - not enough memory !\n"); 27 | exit(1); 28 | } 29 | return image; 30 | } 31 | 32 | /* allocate a new image and copy the content from src */ 33 | image_t *image_cpy(const image_t *src){ 34 | image_t *dst = image_new(src->width, src->height); 35 | memcpy(dst->data, src->data, src->stride*src->height*sizeof(float)); 36 | return dst; 37 | } 38 | 39 | /* set all pixels values to zeros */ 40 | void image_erase(image_t *image){ 41 | memset(image->data, 0, image->stride*image->height*sizeof(float)); 42 | } 43 | 44 | 45 | /* multiply an image by a scalar */ 46 | void image_mul_scalar(image_t *image, const float scalar){ 47 | int i; 48 | v4sf* imp = (v4sf*) image->data; 49 | const v4sf scalarp = {scalar,scalar,scalar,scalar}; 50 | for( i=0 ; istride/4*image->height ; i++){ 51 | (*imp) *= scalarp; 52 | imp+=1; 53 | } 54 | } 55 | 56 | /* free memory of an image */ 57 | void image_delete(image_t *image){ 58 | if(image == NULL){ 59 | //fprintf(stderr, "Warning: Delete image --> Ignore action (image not allocated)\n"); 60 | }else{ 61 | free(image->data); 62 | free(image); 63 | } 64 | } 65 | 66 | 67 | /* allocate a new color image of size width x height */ 68 | color_image_t *color_image_new(const int width, const int height){ 69 | color_image_t *image = (color_image_t*) malloc(sizeof(color_image_t)); 70 | if(image == NULL){ 71 | fprintf(stderr, "Error: color_image_new() - not enough memory !\n"); 72 | exit(1); 73 | } 74 | image->width = width; 75 | image->height = height; 76 | image->stride = ( (width+3) / 4 ) * 4; 77 | image->c1 = (float*) memalign(16, 3*image->stride*height*sizeof(float)); 78 | if(image->c1 == NULL){ 79 | fprintf(stderr, "Error: color_image_new() - not enough memory !\n"); 80 | exit(1); 81 | } 82 | image->c2 = image->c1+image->stride*height; 83 | image->c3 = image->c2+image->stride*height; 84 | return image; 85 | } 86 | 87 | /* allocate a new color image and copy the content from src */ 88 | color_image_t *color_image_cpy(const color_image_t *src){ 89 | color_image_t *dst = color_image_new(src->width, src->height); 90 | memcpy(dst->c1, src->c1, 3*src->stride*src->height*sizeof(float)); 91 | return dst; 92 | } 93 | 94 | /* set all pixels values to zeros */ 95 | void color_image_erase(color_image_t *image){ 96 | memset(image->c1, 0, 3*image->stride*image->height*sizeof(float)); 97 | } 98 | 99 | /* free memory of a color image */ 100 | void color_image_delete(color_image_t *image){ 101 | if(image){ 102 | free(image->c1); // c2 and c3 was allocated at the same moment 103 | free(image); 104 | } 105 | } 106 | 107 | /* reallocate the memory of an image to fit the new width height */ 108 | void resize_if_needed_newsize(image_t *im, const int w, const int h){ 109 | if(im->width != w || im->height != h){ 110 | im->width = w; 111 | im->height = h; 112 | im->stride = ((w+3)/4)*4; 113 | float *data = (float *) memalign(16, im->stride*h*sizeof(float)); 114 | if(data == NULL){ 115 | fprintf(stderr, "Error: resize_if_needed_newsize() - not enough memory !\n"); 116 | exit(1); 117 | } 118 | free(im->data); 119 | im->data = data; 120 | } 121 | } 122 | 123 | 124 | /************ Resizing *********/ 125 | 126 | /* resize an image to a new size (assumes a difference only in width) */ 127 | static void image_resize_horiz(image_t *dst, const image_t *src){ 128 | const float real_scale = ((float) src->width-1) / ((float) dst->width-1); 129 | int i; 130 | for(i = 0; i < dst->height; i++){ 131 | int j; 132 | for(j = 0; j < dst->width; j++){ 133 | const int x = floor((float) j * real_scale); 134 | const float dx = j * real_scale - x; 135 | if(x >= (src->width - 1)){ 136 | dst->data[i * dst->stride + j] = src->data[i * src->stride + src->width - 1]; 137 | }else{ 138 | dst->data[i * dst->stride + j] = 139 | (1.0f - dx) * src->data[i * src->stride + x ] + 140 | ( dx) * src->data[i * src->stride + x + 1]; 141 | } 142 | } 143 | } 144 | } 145 | 146 | /* resize a color image to a new size (assumes a difference only in width) */ 147 | static void color_image_resize_horiz(color_image_t *dst, const color_image_t *src){ 148 | const float real_scale = ((float) src->width-1) / ((float) dst->width-1); 149 | int i; 150 | for(i = 0; i < dst->height; i++){ 151 | int j; 152 | for(j = 0; j < dst->width; j++){ 153 | const int x = floor((float) j * real_scale); 154 | const float dx = j * real_scale - x; 155 | if(x >= (src->width - 1)){ 156 | dst->c1[i * dst->stride + j] = src->c1[i * src->stride + src->width - 1]; 157 | dst->c2[i * dst->stride + j] = src->c2[i * src->stride + src->width - 1]; 158 | dst->c3[i * dst->stride + j] = src->c3[i * src->stride + src->width - 1]; 159 | }else{ 160 | dst->c1[i * dst->stride + j] = 161 | (1.0f - dx) * src->c1[i * src->stride + x ] + 162 | ( dx) * src->c1[i * src->stride + x + 1]; 163 | dst->c2[i * dst->stride + j] = 164 | (1.0f - dx) * src->c2[i * src->stride + x ] + 165 | ( dx) * src->c2[i * src->stride + x + 1]; 166 | dst->c3[i * dst->stride + j] = 167 | (1.0f - dx) * src->c3[i * src->stride + x ] + 168 | ( dx) * src->c3[i * src->stride + x + 1]; 169 | } 170 | } 171 | } 172 | } 173 | 174 | /* resize an image to a new size (assumes a difference only in height) */ 175 | static void image_resize_vert(image_t *dst, const image_t *src){ 176 | const float real_scale = ((float) src->height-1) / ((float) dst->height-1); 177 | int i; 178 | for(i = 0; i < dst->width; i++){ 179 | int j; 180 | for(j = 0; j < dst->height; j++){ 181 | const int y = floor((float) j * real_scale); 182 | const float dy = j * real_scale - y; 183 | if(y >= (src->height - 1)){ 184 | dst->data[j * dst->stride + i] = src->data[i + (src->height - 1) * src->stride]; 185 | }else{ 186 | dst->data[j * dst->stride + i] = 187 | (1.0f - dy) * src->data[i + (y ) * src->stride] + 188 | ( dy) * src->data[i + (y + 1) * src->stride]; 189 | } 190 | } 191 | } 192 | } 193 | 194 | /* resize a color image to a new size (assumes a difference only in height) */ 195 | static void color_image_resize_vert(color_image_t *dst, const color_image_t *src){ 196 | const float real_scale = ((float) src->height) / ((float) dst->height); 197 | int i; 198 | for(i = 0; i < dst->width; i++){ 199 | int j; 200 | for(j = 0; j < dst->height; j++){ 201 | const int y = floor((float) j * real_scale); 202 | const float dy = j * real_scale - y; 203 | if(y >= (src->height - 1)){ 204 | dst->c1[j * dst->stride + i] = src->c1[i + (src->height - 1) * src->stride]; 205 | dst->c2[j * dst->stride + i] = src->c2[i + (src->height - 1) * src->stride]; 206 | dst->c3[j * dst->stride + i] = src->c3[i + (src->height - 1) * src->stride]; 207 | }else{ 208 | dst->c1[j * dst->stride + i] = 209 | (1.0f - dy) * src->c1[i + y * src->stride] + 210 | ( dy) * src->c1[i + (y + 1) * src->stride]; 211 | dst->c2[j * dst->stride + i] = 212 | (1.0f - dy) * src->c2[i + y * src->stride] + 213 | ( dy) * src->c2[i + (y + 1) * src->stride]; 214 | dst->c3[j * dst->stride + i] = 215 | (1.0f - dy) * src->c3[i + y * src->stride] + 216 | ( dy) * src->c3[i + (y + 1) * src->stride]; 217 | } 218 | } 219 | } 220 | } 221 | 222 | /* return a resize version of the image with bilinear interpolation */ 223 | image_t *image_resize_bilinear(const image_t *src, const float scale){ 224 | const int width = src->width, height = src->height; 225 | const int newwidth = (int) (1.5f + (width-1) / scale); // 0.5f for rounding instead of flooring, and the remaining comes from scale = (dst-1)/(src-1) 226 | const int newheight = (int) (1.5f + (height-1) / scale); 227 | image_t *dst = image_new(newwidth,newheight); 228 | if(height*newwidth < width*newheight){ 229 | image_t *tmp = image_new(newwidth,height); 230 | image_resize_horiz(tmp,src); 231 | image_resize_vert(dst,tmp); 232 | image_delete(tmp); 233 | }else{ 234 | image_t *tmp = image_new(width,newheight); 235 | image_resize_vert(tmp,src); 236 | image_resize_horiz(dst,tmp); 237 | image_delete(tmp); 238 | } 239 | return dst; 240 | } 241 | 242 | /* resize an image with bilinear interpolation to fit the new weidht, height ; reallocation is done if necessary */ 243 | void image_resize_bilinear_newsize(image_t *dst, const image_t *src, const int new_width, const int new_height){ 244 | resize_if_needed_newsize(dst,new_width,new_height); 245 | if(new_width < new_height){ 246 | image_t *tmp = image_new(new_width,src->height); 247 | image_resize_horiz(tmp,src); 248 | image_resize_vert(dst,tmp); 249 | image_delete(tmp); 250 | }else{ 251 | image_t *tmp = image_new(src->width,new_height); 252 | image_resize_vert(tmp,src); 253 | image_resize_horiz(dst,tmp); 254 | image_delete(tmp); 255 | } 256 | } 257 | 258 | /* resize a color image with bilinear interpolation */ 259 | color_image_t *color_image_resize_bilinear(const color_image_t *src, const float scale){ 260 | const int width = src->width, height = src->height; 261 | const int newwidth = (int) (1.5f + (width-1) / scale); // 0.5f for rounding instead of flooring, and the remaining comes from scale = (dst-1)/(src-1) 262 | const int newheight = (int) (1.5f + (height-1) / scale); 263 | color_image_t *dst = color_image_new(newwidth,newheight); 264 | if(height*newwidth < width*newheight){ 265 | color_image_t *tmp = color_image_new(newwidth,height); 266 | color_image_resize_horiz(tmp,src); 267 | color_image_resize_vert(dst,tmp); 268 | color_image_delete(tmp); 269 | }else{ 270 | color_image_t *tmp = color_image_new(width,newheight); 271 | color_image_resize_vert(tmp,src); 272 | color_image_resize_horiz(dst,tmp); 273 | color_image_delete(tmp); 274 | } 275 | return dst; 276 | } 277 | 278 | /************ Convolution ******/ 279 | 280 | /* return half coefficient of a gaussian filter 281 | Details: 282 | - return a float* containing the coefficient from middle to border of the filter, so starting by 0, 283 | - it so contains half of the coefficient. 284 | - sigma is the standard deviation. 285 | - filter_order is an output where the size of the output array is stored */ 286 | float *gaussian_filter(const float sigma, int *filter_order){ 287 | if(sigma == 0.0f){ 288 | fprintf(stderr, "gaussian_filter() error: sigma is zeros\n"); 289 | exit(1); 290 | } 291 | if(!filter_order){ 292 | fprintf(stderr, "gaussian_filter() error: filter_order is null\n"); 293 | exit(1); 294 | } 295 | // computer the filter order as 1 + 2* floor(3*sigma) 296 | *filter_order = floor(3*sigma); 297 | if ( *filter_order == 0 ) 298 | *filter_order = 1; 299 | // compute coefficients 300 | float *data = (float*) malloc(sizeof(float) * (2*(*filter_order)+1)); 301 | if(data == NULL ){ 302 | fprintf(stderr, "gaussian_filter() error: not enough memory\n"); 303 | exit(1); 304 | } 305 | const float alpha = 1.0f/(2.0f*sigma*sigma); 306 | float sum = 0.0f; 307 | int i; 308 | for(i=-(*filter_order) ; i<=*filter_order ; i++){ 309 | data[i+(*filter_order)] = exp(-i*i*alpha); 310 | sum += data[i+(*filter_order)]; 311 | } 312 | for(i=-(*filter_order) ; i<=*filter_order ; i++){ 313 | data[i+(*filter_order)] /= sum; 314 | } 315 | // fill the output 316 | float *data2 = (float*) malloc(sizeof(float)*(*filter_order+1)); 317 | if(data2 == NULL ){ 318 | fprintf(stderr, "gaussian_filter() error: not enough memory\n"); 319 | exit(1); 320 | } 321 | memcpy(data2, &data[*filter_order], sizeof(float)*(*filter_order)+sizeof(float)); 322 | free(data); 323 | return data2; 324 | } 325 | 326 | /* given half of the coef, compute the full coefficients and the accumulated coefficients */ 327 | static void convolve_extract_coeffs(const int order, const float *half_coeffs, float *coeffs, float *coeffs_accu, const int even){ 328 | int i; 329 | float accu = 0.0; 330 | if(even){ 331 | for(i = 0 ; i <= order; i++){ 332 | coeffs[order - i] = coeffs[order + i] = half_coeffs[i]; 333 | } 334 | for(i = 0 ; i <= order; i++){ 335 | accu += coeffs[i]; 336 | coeffs_accu[2 * order - i] = coeffs_accu[i] = accu; 337 | } 338 | }else{ 339 | for(i = 0; i <= order; i++){ 340 | coeffs[order - i] = +half_coeffs[i]; 341 | coeffs[order + i] = -half_coeffs[i]; 342 | } 343 | for(i = 0 ; i <= order; i++){ 344 | accu += coeffs[i]; 345 | coeffs_accu[i] = accu; 346 | coeffs_accu[2 * order - i]= -accu; 347 | } 348 | } 349 | } 350 | 351 | /* create a convolution structure with a given order, half_coeffs, symmetric or anti-symmetric according to even parameter */ 352 | convolution_t *convolution_new(const int order, const float *half_coeffs, const int even){ 353 | convolution_t *conv = (convolution_t *) malloc(sizeof(convolution_t)); 354 | if(conv == NULL){ 355 | fprintf(stderr, "Error: convolution_new() - not enough memory !\n"); 356 | exit(1); 357 | } 358 | conv->order = order; 359 | conv->coeffs = (float *) malloc((2 * order + 1) * sizeof(float)); 360 | if(conv->coeffs == NULL){ 361 | fprintf(stderr, "Error: convolution_new() - not enough memory !\n"); 362 | free(conv); 363 | exit(1); 364 | } 365 | conv->coeffs_accu = (float *) malloc((2 * order + 1) * sizeof(float)); 366 | if(conv->coeffs_accu == NULL){ 367 | fprintf(stderr, "Error: convolution_new() - not enough memory !\n"); 368 | free(conv->coeffs); 369 | free(conv); 370 | exit(1); 371 | } 372 | convolve_extract_coeffs(order, half_coeffs, conv->coeffs,conv->coeffs_accu, even); 373 | return conv; 374 | } 375 | 376 | static void convolve_vert_fast_3(image_t *dst, const image_t *src, const convolution_t *conv){ 377 | const int iterline = (src->stride>>2)+1; 378 | const float *coeff = conv->coeffs; 379 | //const float *coeff_accu = conv->coeffs_accu; 380 | v4sf *srcp = (v4sf*) src->data, *dstp = (v4sf*) dst->data; 381 | v4sf *srcp_p1 = (v4sf*) (src->data+src->stride); 382 | int i; 383 | for(i=iterline ; --i ; ){ // first line 384 | *dstp = (coeff[0]+coeff[1])*(*srcp) + coeff[2]*(*srcp_p1); 385 | dstp+=1; srcp+=1; srcp_p1+=1; 386 | } 387 | v4sf* srcp_m1 = (v4sf*) src->data; 388 | for(i=src->height-1 ; --i ; ){ // others line 389 | int j; 390 | for(j=iterline ; --j ; ){ 391 | *dstp = coeff[0]*(*srcp_m1) + coeff[1]*(*srcp) + coeff[2]*(*srcp_p1); 392 | dstp+=1; srcp_m1+=1; srcp+=1; srcp_p1+=1; 393 | } 394 | } 395 | for(i=iterline ; --i ; ){ // last line 396 | *dstp = coeff[0]*(*srcp_m1) + (coeff[1]+coeff[2])*(*srcp); 397 | dstp+=1; srcp_m1+=1; srcp+=1; 398 | } 399 | } 400 | 401 | static void convolve_vert_fast_5(image_t *dst, const image_t *src, const convolution_t *conv){ 402 | const int iterline = (src->stride>>2)+1; 403 | const float *coeff = conv->coeffs; 404 | //const float *coeff_accu = conv->coeffs_accu; 405 | v4sf *srcp = (v4sf*) src->data, *dstp = (v4sf*) dst->data; 406 | v4sf *srcp_p1 = (v4sf*) (src->data+src->stride); 407 | v4sf *srcp_p2 = (v4sf*) (src->data+2*src->stride); 408 | int i; 409 | for(i=iterline ; --i ; ){ // first line 410 | *dstp = (coeff[0]+coeff[1]+coeff[2])*(*srcp) + coeff[3]*(*srcp_p1) + coeff[4]*(*srcp_p2); 411 | dstp+=1; srcp+=1; srcp_p1+=1; srcp_p2+=1; 412 | } 413 | v4sf* srcp_m1 = (v4sf*) src->data; 414 | for(i=iterline ; --i ; ){ // second line 415 | *dstp = (coeff[0]+coeff[1])*(*srcp_m1) + coeff[2]*(*srcp) + coeff[3]*(*srcp_p1) + coeff[4]*(*srcp_p2); 416 | dstp+=1; srcp_m1+=1; srcp+=1; srcp_p1+=1; srcp_p2+=1; 417 | } 418 | v4sf* srcp_m2 = (v4sf*) src->data; 419 | for(i=src->height-3 ; --i ; ){ // others line 420 | int j; 421 | for(j=iterline ; --j ; ){ 422 | *dstp = coeff[0]*(*srcp_m2) + coeff[1]*(*srcp_m1) + coeff[2]*(*srcp) + coeff[3]*(*srcp_p1) + coeff[4]*(*srcp_p2); 423 | dstp+=1; srcp_m2+=1;srcp_m1+=1; srcp+=1; srcp_p1+=1; srcp_p2+=1; 424 | } 425 | } 426 | for(i=iterline ; --i ; ){ // second to last line 427 | *dstp = coeff[0]*(*srcp_m2) + coeff[1]*(*srcp_m1) + coeff[2]*(*srcp) + (coeff[3]+coeff[4])*(*srcp_p1); 428 | dstp+=1; srcp_m2+=1;srcp_m1+=1; srcp+=1; srcp_p1+=1; 429 | } 430 | for(i=iterline ; --i ; ){ // last line 431 | *dstp = coeff[0]*(*srcp_m2) + coeff[1]*(*srcp_m1) + (coeff[2]+coeff[3]+coeff[4])*(*srcp); 432 | dstp+=1; srcp_m2+=1;srcp_m1+=1; srcp+=1; 433 | } 434 | } 435 | 436 | static void convolve_horiz_fast_3(image_t *dst, const image_t *src, const convolution_t *conv){ 437 | const int stride_minus_1 = src->stride-1; 438 | const int iterline = (src->stride>>2); 439 | const float *coeff = conv->coeffs; 440 | v4sf *srcp = (v4sf*) src->data, *dstp = (v4sf*) dst->data; 441 | // create shifted version of src 442 | float *src_p1 = (float*) malloc(sizeof(float)*src->stride), 443 | *src_m1 = (float*) malloc(sizeof(float)*src->stride); 444 | int j; 445 | for(j=0;jheight;j++){ 446 | int i; 447 | float *srcptr = (float*) srcp; 448 | const float right_coef = srcptr[src->width-1]; 449 | for(i=src->width;istride;i++) 450 | srcptr[i] = right_coef; 451 | src_m1[0] = srcptr[0]; 452 | memcpy(src_m1+1, srcptr , sizeof(float)*stride_minus_1); 453 | src_p1[stride_minus_1] = right_coef; 454 | memcpy(src_p1, srcptr+1, sizeof(float)*stride_minus_1); 455 | v4sf *srcp_p1 = (v4sf*) src_p1, *srcp_m1 = (v4sf*) src_m1; 456 | 457 | for(i=0;istride-1; 468 | const int stride_minus_2 = src->stride-2; 469 | const int iterline = (src->stride>>2); 470 | const float *coeff = conv->coeffs; 471 | v4sf *srcp = (v4sf*) src->data, *dstp = (v4sf*) dst->data; 472 | float *src_p1 = (float*) malloc(sizeof(float)*src->stride*4); 473 | float *src_p2 = src_p1+src->stride; 474 | float *src_m1 = src_p2+src->stride; 475 | float *src_m2 = src_m1+src->stride; 476 | int j; 477 | for(j=0;jheight;j++){ 478 | int i; 479 | float *srcptr = (float*) srcp; 480 | const float right_coef = srcptr[src->width-1]; 481 | for(i=src->width;istride;i++) 482 | srcptr[i] = right_coef; 483 | src_m1[0] = srcptr[0]; 484 | memcpy(src_m1+1, srcptr , sizeof(float)*stride_minus_1); 485 | src_m2[0] = srcptr[0]; 486 | src_m2[1] = srcptr[0]; 487 | memcpy(src_m2+2, srcptr , sizeof(float)*stride_minus_2); 488 | src_p1[stride_minus_1] = right_coef; 489 | memcpy(src_p1, srcptr+1, sizeof(float)*stride_minus_1); 490 | src_p2[stride_minus_1] = right_coef; 491 | src_p2[stride_minus_2] = right_coef; 492 | memcpy(src_p2, srcptr+2, sizeof(float)*stride_minus_2); 493 | 494 | v4sf *srcp_p1 = (v4sf*) src_p1, *srcp_p2 = (v4sf*) src_p2, *srcp_m1 = (v4sf*) src_m1, *srcp_m2 = (v4sf*) src_m2; 495 | 496 | for(i=0;iorder==1){ 507 | convolve_horiz_fast_3(dest,src,conv); 508 | return; 509 | }else if(conv->order==2){ 510 | convolve_horiz_fast_5(dest,src,conv); 511 | return; 512 | } 513 | float *in = src->data; 514 | float * out = dest->data; 515 | int i, j, ii; 516 | float *o = out; 517 | int i0 = -conv->order; 518 | int i1 = +conv->order; 519 | float *coeff = conv->coeffs + conv->order; 520 | float *coeff_accu = conv->coeffs_accu + conv->order; 521 | for(j = 0; j < src->height; j++){ 522 | const float *al = in + j * src->stride; 523 | const float *f0 = coeff + i0; 524 | float sum; 525 | for(i = 0; i < -i0; i++){ 526 | sum=coeff_accu[-i - 1] * al[0]; 527 | for(ii = i1 + i; ii >= 0; ii--){ 528 | sum += coeff[ii - i] * al[ii]; 529 | } 530 | *o++ = sum; 531 | } 532 | for(; i < src->width - i1; i++){ 533 | sum = 0; 534 | for(ii = i1 - i0; ii >= 0; ii--){ 535 | sum += f0[ii] * al[ii]; 536 | } 537 | al++; 538 | *o++ = sum; 539 | } 540 | for(; i < src->width; i++){ 541 | sum = coeff_accu[src->width - i] * al[src->width - i0 - 1 - i]; 542 | for(ii = src->width - i0 - 1 - i; ii >= 0; ii--){ 543 | sum += f0[ii] * al[ii]; 544 | } 545 | al++; 546 | *o++ = sum; 547 | } 548 | for(i = 0; i < src->stride - src->width; i++){ 549 | o++; 550 | } 551 | } 552 | } 553 | 554 | /* perform a vertical convolution of an image */ 555 | void convolve_vert(image_t *dest, const image_t *src, const convolution_t *conv){ 556 | if(conv->order==1){ 557 | convolve_vert_fast_3(dest,src,conv); 558 | return; 559 | }else if(conv->order==2){ 560 | convolve_vert_fast_5(dest,src,conv); 561 | return; 562 | } 563 | float *in = src->data; 564 | float *out = dest->data; 565 | int i0 = -conv->order; 566 | int i1 = +conv->order; 567 | float *coeff = conv->coeffs + conv->order; 568 | float *coeff_accu = conv->coeffs_accu + conv->order; 569 | int i, j, ii; 570 | float *o = out; 571 | const float *alast = in + src->stride * (src->height - 1); 572 | const float *f0 = coeff + i0; 573 | for(i = 0; i < -i0; i++){ 574 | float fa = coeff_accu[-i - 1]; 575 | const float *al = in + i * src->stride; 576 | for(j = 0; j < src->width; j++){ 577 | float sum = fa * in[j]; 578 | for(ii = -i; ii <= i1; ii++){ 579 | sum += coeff[ii] * al[j + ii * src->stride]; 580 | } 581 | *o++ = sum; 582 | } 583 | for(j = 0; j < src->stride - src->width; j++) 584 | { 585 | o++; 586 | } 587 | } 588 | for(; i < src->height - i1; i++){ 589 | const float *al = in + (i + i0) * src->stride; 590 | for(j = 0; j < src->width; j++){ 591 | float sum = 0; 592 | const float *al2 = al; 593 | for(ii = 0; ii <= i1 - i0; ii++){ 594 | sum += f0[ii] * al2[0]; 595 | al2 += src->stride; 596 | } 597 | *o++ = sum; 598 | al++; 599 | } 600 | for(j = 0; j < src->stride - src->width; j++){ 601 | o++; 602 | } 603 | } 604 | for(;i < src->height; i++){ 605 | float fa = coeff_accu[src->height - i]; 606 | const float *al = in + i * src->stride; 607 | for(j = 0; j < src->width; j++){ 608 | float sum = fa * alast[j]; 609 | for(ii = i0; ii <= src->height - 1 - i; ii++){ 610 | sum += coeff[ii] * al[j + ii * src->stride]; 611 | } 612 | *o++ = sum; 613 | } 614 | for(j = 0; j < src->stride - src->width; j++){ 615 | o++; 616 | } 617 | } 618 | } 619 | 620 | /* free memory of a convolution structure */ 621 | void convolution_delete(convolution_t *conv){ 622 | if(conv) 623 | { 624 | free(conv->coeffs); 625 | free(conv->coeffs_accu); 626 | free(conv); 627 | } 628 | } 629 | 630 | /* perform horizontal and/or vertical convolution to a color image */ 631 | void color_image_convolve_hv(color_image_t *dst, const color_image_t *src, const convolution_t *horiz_conv, const convolution_t *vert_conv){ 632 | const int width = src->width, height = src->height, stride = src->stride; 633 | // separate channels of images 634 | image_t src_red = {width,height,stride,src->c1}, src_green = {width,height,stride,src->c2}, src_blue = {width,height,stride,src->c3}, 635 | dst_red = {width,height,stride,dst->c1}, dst_green = {width,height,stride,dst->c2}, dst_blue = {width,height,stride,dst->c3}; 636 | // horizontal and vertical 637 | if(horiz_conv != NULL && vert_conv != NULL){ 638 | float *tmp_data = (float*)malloc(sizeof(float)*stride*height); 639 | if(tmp_data == NULL){ 640 | fprintf(stderr,"error color_image_convolve_hv(): not enough memory\n"); 641 | exit(1); 642 | } 643 | image_t tmp = {width,height,stride,tmp_data}; 644 | // perform convolution for each channel 645 | convolve_horiz(&tmp,&src_red,horiz_conv); 646 | convolve_vert(&dst_red,&tmp,vert_conv); 647 | convolve_horiz(&tmp,&src_green,horiz_conv); 648 | convolve_vert(&dst_green,&tmp,vert_conv); 649 | convolve_horiz(&tmp,&src_blue,horiz_conv); 650 | convolve_vert(&dst_blue,&tmp,vert_conv); 651 | free(tmp_data); 652 | }else if(horiz_conv != NULL && vert_conv == NULL){ // only horizontal 653 | convolve_horiz(&dst_red,&src_red,horiz_conv); 654 | convolve_horiz(&dst_green,&src_green,horiz_conv); 655 | convolve_horiz(&dst_blue,&src_blue,horiz_conv); 656 | }else if(vert_conv != NULL && horiz_conv == NULL){ // only vertical 657 | convolve_vert(&dst_red,&src_red,vert_conv); 658 | convolve_vert(&dst_green,&src_green,vert_conv); 659 | convolve_vert(&dst_blue,&src_blue,vert_conv); 660 | } 661 | } 662 | 663 | /************ Pyramid **********/ 664 | 665 | /* create new color image pyramid structures */ 666 | static color_image_pyramid_t* color_image_pyramid_new(){ 667 | color_image_pyramid_t* pyr = (color_image_pyramid_t*) malloc(sizeof(color_image_pyramid_t)); 668 | if(pyr == NULL){ 669 | fprintf(stderr,"Error in color_image_pyramid_new(): not enough memory\n"); 670 | exit(1); 671 | } 672 | pyr->min_size = -1; 673 | pyr->scale_factor = -1.0f; 674 | pyr->size = -1; 675 | pyr->images = NULL; 676 | return pyr; 677 | } 678 | 679 | /* set the size of the color image pyramid structures (reallocate the array of pointers to images) */ 680 | static void color_image_pyramid_set_size(color_image_pyramid_t* pyr, const int size){ 681 | if(size<0){ 682 | fprintf(stderr,"Error in color_image_pyramid_set_size(): size is negative\n"); 683 | exit(1); 684 | } 685 | if(pyr->images == NULL){ 686 | pyr->images = (color_image_t**) malloc(sizeof(color_image_t*)*size); 687 | }else{ 688 | pyr->images = (color_image_t**) realloc(pyr->images,sizeof(color_image_t*)*size); 689 | } 690 | if(pyr->images == NULL){ 691 | fprintf(stderr,"Error in color_image_pyramid_set_size(): not enough memory\n"); 692 | exit(1); 693 | } 694 | pyr->size = size; 695 | } 696 | 697 | /* create a pyramid of color images using a given scale factor, stopping when one dimension reach min_size and with applying a gaussian smoothing of standard deviation spyr (no smoothing if 0) */ 698 | color_image_pyramid_t *color_image_pyramid_create(const color_image_t *src, const float scale_factor, const int min_size, const float spyr){ 699 | const int nb_max_scale = 1000; 700 | // allocate structure 701 | color_image_pyramid_t *pyramid = color_image_pyramid_new(); 702 | pyramid->min_size = min_size; 703 | pyramid->scale_factor = scale_factor; 704 | convolution_t *conv = NULL; 705 | if(spyr>0.0f){ 706 | int fsize; 707 | float *filter_coef = gaussian_filter(spyr, &fsize); 708 | conv = convolution_new(fsize, filter_coef, 1); 709 | free(filter_coef); 710 | } 711 | color_image_pyramid_set_size(pyramid, nb_max_scale); 712 | pyramid->images[0] = color_image_cpy(src); 713 | int i; 714 | for( i=1 ; iimages[i-1]->width, oldheight = pyramid->images[i-1]->height; 716 | const int newwidth = (int) (1.5f + (oldwidth-1) / scale_factor); 717 | const int newheight = (int) (1.5f + (oldheight-1) / scale_factor); 718 | if( newwidth <= min_size || newheight <= min_size){ 719 | color_image_pyramid_set_size(pyramid, i); 720 | break; 721 | } 722 | if(spyr>0.0f){ 723 | color_image_t* tmp = color_image_new(oldwidth, oldheight); 724 | color_image_convolve_hv(tmp,pyramid->images[i-1], conv, conv); 725 | pyramid->images[i]= color_image_resize_bilinear(tmp, scale_factor); 726 | color_image_delete(tmp); 727 | }else{ 728 | pyramid->images[i] = color_image_resize_bilinear(pyramid->images[i-1], scale_factor); 729 | } 730 | } 731 | if(spyr>0.0f){ 732 | convolution_delete(conv); 733 | } 734 | return pyramid; 735 | } 736 | 737 | /* delete the structure of a pyramid of color images and all the color images in it*/ 738 | void color_image_pyramid_delete(color_image_pyramid_t *pyr){ 739 | if(pyr==NULL){ 740 | return; 741 | } 742 | int i; 743 | for(i=0 ; isize ; i++){ 744 | color_image_delete(pyr->images[i]); 745 | } 746 | free(pyr->images); 747 | free(pyr); 748 | } 749 | -------------------------------------------------------------------------------- /image.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H_ 2 | #define __IMAGE_H_ 3 | 4 | #include 5 | 6 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 7 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 8 | #define MINMAX(a,b) MIN( MAX(a,0) , b-1 ) 9 | 10 | /********** STRUCTURES *********/ 11 | 12 | /* structure for 1-channel image */ 13 | typedef struct image_s 14 | { 15 | int width; /* Width of the image */ 16 | int height; /* Height of the image */ 17 | int stride; /* Width of the memory (width + paddind such that it is a multiple of 4) */ 18 | float *data; /* Image data, aligned */ 19 | } image_t; 20 | 21 | /* structure for 3-channels image stored with one layer per color, it assumes that c2 = c1+width*height and c3 = c2+width*height. */ 22 | typedef struct color_image_s 23 | { 24 | int width; /* Width of the image */ 25 | int height; /* Height of the image */ 26 | int stride; /* Width of the memory (width + paddind such that it is a multiple of 4) */ 27 | float *c1; /* Color 1, aligned */ 28 | float *c2; /* Color 2, consecutive to c1*/ 29 | float *c3; /* Color 3, consecutive to c2 */ 30 | } color_image_t; 31 | 32 | /* structure for color image pyramid */ 33 | typedef struct color_image_pyramid_s 34 | { 35 | float scale_factor; /* difference of scale between two levels */ 36 | int min_size; /* minimum size for width or height at the coarsest level */ 37 | int size; /* number of levels in the pyramid */ 38 | color_image_t **images; /* list of images with images[0] the original one, images[size-1] the finest one */ 39 | } color_image_pyramid_t; 40 | 41 | /* structure for convolutions */ 42 | typedef struct convolution_s 43 | { 44 | int order; /* Order of the convolution */ 45 | float *coeffs; /* Coefficients */ 46 | float *coeffs_accu; /* Accumulated coefficients */ 47 | } convolution_t; 48 | 49 | /********** Create/Delete **********/ 50 | 51 | /* allocate a new image of size width x height */ 52 | image_t *image_new(const int width, const int height); 53 | 54 | /* allocate a new image and copy the content from src */ 55 | image_t *image_cpy(const image_t *src); 56 | 57 | /* set all pixels values to zeros */ 58 | void image_erase(image_t *image); 59 | 60 | /* free memory of an image */ 61 | void image_delete(image_t *image); 62 | 63 | /* multiply an image by a scalar */ 64 | void image_mul_scalar(image_t *image, const float scalar); 65 | 66 | /* allocate a new color image of size width x height */ 67 | color_image_t *color_image_new(const int width, const int height); 68 | 69 | /* allocate a new color image and copy the content from src */ 70 | color_image_t *color_image_cpy(const color_image_t *src); 71 | 72 | /* set all pixels values to zeros */ 73 | void color_image_erase(color_image_t *image); 74 | 75 | /* free memory of a color image */ 76 | void color_image_delete(color_image_t *image); 77 | 78 | /* reallocate the memory of an image to fit the new width height */ 79 | void resize_if_needed_newsize(image_t *im, const int w, const int h); 80 | 81 | /************ Resizing *********/ 82 | 83 | /* resize an image with bilinear interpolation */ 84 | image_t *image_resize_bilinear(const image_t *src, const float scale); 85 | 86 | /* resize an image with bilinear interpolation to fit the new weidht, height ; reallocation is done if necessary */ 87 | void image_resize_bilinear_newsize(image_t *dst, const image_t *src, const int new_width, const int new_height); 88 | 89 | /* resize a color image with bilinear interpolation */ 90 | color_image_t *color_image_resize_bilinear(const color_image_t *src, const float scale); 91 | 92 | /************ Convolution ******/ 93 | 94 | /* return half coefficient of a gaussian filter */ 95 | float *gaussian_filter(const float sigma, int *fSize); 96 | 97 | /* create a convolution structure with a given order, half_coeffs, symmetric or anti-symmetric according to even parameter */ 98 | convolution_t *convolution_new(int order, const float *half_coeffs, const int even); 99 | 100 | /* perform an horizontal convolution of an image */ 101 | void convolve_horiz(image_t *dest, const image_t *src, const convolution_t *conv); 102 | 103 | /* perform a vertical convolution of an image */ 104 | void convolve_vert(image_t *dest, const image_t *src, const convolution_t *conv); 105 | 106 | /* free memory of a convolution structure */ 107 | void convolution_delete(convolution_t *conv); 108 | 109 | /* perform horizontal and/or vertical convolution to a color image */ 110 | void color_image_convolve_hv(color_image_t *dst, const color_image_t *src, const convolution_t *horiz_conv, const convolution_t *vert_conv); 111 | 112 | /************ Pyramid **********/ 113 | 114 | /* create a pyramid of color images using a given scale factor, stopping when one dimension reach min_size and with applying a gaussian smoothing of standard deviation spyr (no smoothing if 0) */ 115 | color_image_pyramid_t *color_image_pyramid_create(const color_image_t *src, const float scale_factor, const int min_size, const float spyr); 116 | 117 | /* delete the structure of a pyramid of color images */ 118 | void color_image_pyramid_delete(color_image_pyramid_t *pyr); 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | //#include 6 | #include 7 | #include "io.h" 8 | 9 | /* FLOW */ 10 | 11 | /* read a flow file and returns a pointer with two images containing the flow along x and y axis */ 12 | image_t** readFlowFile(const char* filename){ 13 | FILE *fid = fopen(filename, "rb"); 14 | if (fid == 0){ 15 | return 0; 16 | } 17 | float help; 18 | fread(&help,sizeof(float),1,fid); 19 | int aXSize,aYSize; 20 | fread(&aXSize,sizeof(int),1,fid); 21 | fread(&aYSize,sizeof(int),1,fid); 22 | image_t** flow = (image_t**) malloc(sizeof(image_t*)*2); 23 | flow[0] = image_new(aXSize, aYSize); 24 | flow[1] = image_new(aXSize, aYSize); 25 | int x,y; 26 | for (y = 0; y < aYSize; y++) 27 | for (x = 0; x < aXSize ; x++) { 28 | fread(&(flow[0]->data[y*flow[0]->stride+x]),sizeof(float),1,fid); 29 | fread(&(flow[1]->data[y*flow[0]->stride+x]),sizeof(float),1,fid); 30 | } 31 | fclose(fid); 32 | return flow; 33 | } 34 | 35 | /* write a flow to a file */ 36 | int writeFlowFile(const char *filename, const image_t *flowx, const image_t *flowy){ 37 | FILE *stream = fopen(filename, "wb"); 38 | if (stream == 0) { 39 | return 1; 40 | } 41 | const float help=202021.25; 42 | fwrite(&help,sizeof(float),1,stream); 43 | const int aXSize = flowx->width, aYSize = flowx->height; 44 | fwrite(&aXSize,sizeof(int),1,stream); 45 | fwrite(&aYSize,sizeof(int),1,stream); 46 | int y,x; 47 | for (y = 0; y < aYSize ; y++) 48 | for (x = 0; x < aXSize ; x++) { 49 | fwrite(&flowx->data[y*flowx->stride+x],sizeof(float),1,stream); 50 | fwrite(&flowy->data[y*flowy->stride+x],sizeof(float),1,stream); 51 | } 52 | fclose(stream); 53 | return 0; 54 | } 55 | 56 | /* IMAGE */ 57 | 58 | // PPM 59 | 60 | typedef struct{ 61 | int magic; 62 | int width; 63 | int height; 64 | int pixmax; 65 | } ppm_hdr_t; 66 | 67 | static void get_magic(FILE *fp, ppm_hdr_t *ppm_hdr){ 68 | char str[1024]; 69 | fgets(str, 1024, fp); 70 | if(str[0] == 'P' && (str[1] <= '6' || str[1] >= '1')){ 71 | ppm_hdr->magic = str[1] - '0'; 72 | } 73 | } 74 | 75 | static int skip_comment(FILE *fp){ 76 | char c; 77 | do{ 78 | c = (char) fgetc(fp); 79 | } 80 | while (c == ' ' || c == '\t' || c == '\n'); 81 | if(c == '#'){ 82 | do { 83 | c = (char) fgetc(fp); 84 | 85 | } while(c != 0x0A); 86 | return 1; 87 | }else{ 88 | ungetc(c, fp); 89 | } 90 | return 0; 91 | } 92 | 93 | /*----------------------------------------------------------------------------*/ 94 | 95 | static void skip_comments(FILE *fp){ 96 | while(skip_comment(fp)); 97 | } 98 | 99 | /*----------------------------------------------------------------------------*/ 100 | 101 | static int get_image_size(FILE *fp, ppm_hdr_t *ppm_hdr){ 102 | skip_comments(fp); 103 | if(fscanf(fp, "%d %d", &ppm_hdr->width, &ppm_hdr->height) != 2){ 104 | fprintf(stderr, "Warning: PGM --> File currupted\n"); 105 | return 0; 106 | } 107 | return 1; 108 | } 109 | 110 | /*----------------------------------------------------------------------------*/ 111 | 112 | static int get_pixmax(FILE *fp, ppm_hdr_t *ppm_hdr){ 113 | skip_comments(fp); 114 | ppm_hdr->pixmax = 1; 115 | if(ppm_hdr->magic == 2 || ppm_hdr->magic == 3 || ppm_hdr->magic == 5 || ppm_hdr->magic == 6){ 116 | if(fscanf(fp, "%d", &ppm_hdr->pixmax) != 1){ 117 | fprintf(stderr, "Warning: PGM --> pixmax not valid\n"); 118 | return 0; 119 | } 120 | } 121 | fgetc(fp); 122 | return 1; 123 | } 124 | 125 | /*----------------------------------------------------------------------------*/ 126 | 127 | static int get_ppm_hdr(FILE *fp, ppm_hdr_t *ppm_hdr){ 128 | get_magic(fp, ppm_hdr); 129 | if(!get_image_size(fp, ppm_hdr)){ 130 | return 0; 131 | } 132 | if(!get_pixmax(fp, ppm_hdr)){ 133 | return 0; 134 | } 135 | return 1; 136 | } 137 | 138 | static void raw_read_color(FILE *fp, color_image_t *image){ 139 | int j; 140 | for( j=0 ; jheight ; j++){ 141 | int o = j*image->stride, i; 142 | for( i=0 ; iwidth ; i++,o++ ){ 143 | image->c1[o] = (float) fgetc(fp); 144 | image->c2[o] = (float) fgetc(fp); 145 | image->c3[o] = (float) fgetc(fp); 146 | } 147 | } 148 | } 149 | 150 | color_image_t *color_image_pnm_load(FILE *fp){ 151 | color_image_t *image = NULL; 152 | ppm_hdr_t ppm_hdr; 153 | if(!get_ppm_hdr(fp, &ppm_hdr)) { 154 | return NULL; 155 | } 156 | switch(ppm_hdr.magic) { 157 | case 1: /* PBM ASCII */ 158 | case 2: /* PGM ASCII */ 159 | case 3: /* PPM ASCII */ 160 | case 4: /* PBM RAW */ 161 | case 5: /* PGM RAW */ 162 | fprintf(stderr, "color_image_pnm_load: only PPM raw with maxval 255 supported\n"); 163 | break; 164 | case 6: /* PPM RAW */ 165 | image = color_image_new(ppm_hdr.width, ppm_hdr.height); 166 | raw_read_color(fp, image); 167 | break; 168 | } 169 | return image; 170 | } 171 | 172 | // JPG 173 | 174 | color_image_t *color_image_jpeg_load(FILE *fp){ 175 | struct jpeg_decompress_struct cinfo; 176 | struct jpeg_error_mgr jerr; 177 | JSAMPARRAY buffer; 178 | int row_stride; 179 | int index = 0; 180 | color_image_t *image = NULL; 181 | float *r_p, *g_p, *b_p; 182 | JSAMPROW buffer_p; 183 | cinfo.err = jpeg_std_error(&jerr); 184 | jpeg_create_decompress(&cinfo); 185 | jpeg_stdio_src(&cinfo, fp); 186 | jpeg_read_header(&cinfo, TRUE); 187 | cinfo.out_color_space = JCS_RGB; 188 | cinfo.quantize_colors = FALSE; 189 | image = color_image_new(cinfo.image_width, cinfo.image_height); 190 | if(image == NULL){ 191 | return NULL; 192 | } 193 | jpeg_start_decompress(&cinfo); 194 | row_stride = cinfo.output_width * cinfo.output_components; 195 | buffer = (*cinfo.mem->alloc_sarray) 196 | ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); 197 | 198 | r_p = image->c1; 199 | g_p = image->c2; 200 | b_p = image->c3; 201 | 202 | const int incr_line = image->stride-image->width; 203 | 204 | while (cinfo.output_scanline < cinfo.output_height){ 205 | jpeg_read_scanlines(&cinfo, buffer, 1); 206 | buffer_p = buffer[0]; 207 | index = cinfo.output_width; 208 | while(index--){ 209 | *r_p++ = (float) *buffer_p++; 210 | *g_p++ = (float) *buffer_p++; 211 | *b_p++ = (float) *buffer_p++; 212 | } 213 | r_p += incr_line; g_p += incr_line; b_p += incr_line; 214 | } 215 | jpeg_finish_decompress(&cinfo); 216 | jpeg_destroy_decompress(&cinfo); 217 | return image; 218 | } 219 | 220 | // PNG 221 | 222 | color_image_t *color_image_png_load( FILE* fp, const char* file_name ){ 223 | // read the header 224 | png_byte header[8]; 225 | fread(header, 1, 8, fp); 226 | 227 | if (png_sig_cmp(header, 0, 8)){ 228 | fprintf(stderr, "error: %s is not a PNG.\n", file_name); 229 | fclose(fp); 230 | return 0; 231 | } 232 | 233 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 234 | if (!png_ptr){ 235 | fprintf(stderr, "error: png_create_read_struct returned 0.\n"); 236 | fclose(fp); 237 | return 0; 238 | } 239 | 240 | // create png info struct 241 | png_infop info_ptr = png_create_info_struct(png_ptr); 242 | if (!info_ptr){ 243 | fprintf(stderr, "error: png_create_info_struct returned 0.\n"); 244 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); 245 | fclose(fp); 246 | return 0; 247 | } 248 | 249 | // create png info struct 250 | png_infop end_info = png_create_info_struct(png_ptr); 251 | if (!end_info){ 252 | fprintf(stderr, "error: png_create_info_struct returned 0.\n"); 253 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); 254 | fclose(fp); 255 | return 0; 256 | } 257 | 258 | // the code in this if statement gets called if libpng encounters an error 259 | if (setjmp(png_jmpbuf(png_ptr))) { 260 | fprintf(stderr, "error from libpng\n"); 261 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 262 | fclose(fp); 263 | return 0; 264 | } 265 | 266 | // init png reading 267 | png_init_io(png_ptr, fp); 268 | 269 | // let libpng know you already read the first 8 bytes 270 | png_set_sig_bytes(png_ptr, 8); 271 | 272 | // read all the info up to the image data 273 | png_read_info(png_ptr, info_ptr); 274 | 275 | // variables to pass to get info 276 | int bit_depth, color_type; 277 | png_uint_32 temp_width, temp_height; 278 | 279 | // get info about png 280 | png_get_IHDR(png_ptr, info_ptr, &temp_width, &temp_height, &bit_depth, &color_type, 281 | NULL, NULL, NULL); 282 | 283 | // Update the png info struct. 284 | png_read_update_info(png_ptr, info_ptr); 285 | 286 | // Row size in bytes. 287 | int rowbytes = png_get_rowbytes(png_ptr, info_ptr); 288 | 289 | // Allocate the image_data as a big block, to be given to opengl 290 | png_byte * image_data; 291 | image_data = (png_byte*) malloc(sizeof(png_byte)*rowbytes*temp_height); 292 | assert(image_data!=NULL); 293 | 294 | // row_pointers is for pointing to image_data for reading the png with libpng 295 | png_bytep * row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*temp_height); 296 | assert(row_pointers!=NULL); 297 | 298 | // set the individual row_pointers to point at the correct offsets of image_data 299 | unsigned int i; 300 | for (i = 0; i c1[i*image->stride+j] = image->c2[i*image->stride+j] = image->c3[i*image->stride+j] = image_data[i*image->width+j]; 314 | } 315 | } else if( color_type == 2 ) { 316 | assert((unsigned)rowbytes == 3*temp_width || !"error: not a proper color png image"); 317 | for(i=0; ic1[i*image->stride+j] = image_data[3*(i*image->width+j)+0]; 321 | image->c2[i*image->stride+j] = image_data[3*(i*image->width+j)+1]; 322 | image->c3[i*image->stride+j] = image_data[3*(i*image->width+j)+2]; 323 | } 324 | } 325 | } else 326 | assert(!"error: unknown PNG color type" ); 327 | 328 | // clean up 329 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 330 | free(row_pointers); 331 | 332 | return image; 333 | } 334 | 335 | // GENERAL LOAD 336 | 337 | /* load a color image from a file */ 338 | color_image_t *color_image_load(const char *fname){ 339 | FILE *fp; 340 | char magic[2]; 341 | unsigned short *magic_short = (unsigned short *) magic; 342 | color_image_t *image = NULL; 343 | if((fp = fopen(fname, "rb")) == NULL){ 344 | fprintf(stderr, "Error in color_image_load() - can not open file `%s' !\n", fname); 345 | exit(1); 346 | } 347 | fread(magic, sizeof(char), 2, fp); 348 | rewind(fp); 349 | if(magic_short[0] == 0xd8ff){ 350 | image = color_image_jpeg_load(fp); 351 | } else if(magic[0]=='P' && (magic[1]=='6' || magic[1]=='5')){ /* PPM raw */ 352 | image = color_image_pnm_load(fp); 353 | } else if( magic[0]==-119 && magic[1]=='P' ) { 354 | image = color_image_png_load( fp, fname ); 355 | } else{ 356 | fprintf(stderr, "Error in color_image_load(%s) - image format not supported, can only read jpg or ppm\n",fname); 357 | exit(1); 358 | } 359 | fclose(fp); 360 | return image; 361 | } 362 | -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | #ifndef __IO_H__ 2 | #define __IO_H__ 3 | 4 | #include 5 | 6 | #include "image.h" 7 | 8 | /* read a flow file and returns a pointer with two images containing the flow along x and y axis */ 9 | image_t** readFlowFile(const char* filename); 10 | 11 | /* write a flow to a file */ 12 | int writeFlowFile(const char* filename, const image_t *flowx, const image_t *flowy); 13 | 14 | /* load a color image from a file in jpg or ppm*/ 15 | color_image_t *color_image_load(const char *fname); 16 | 17 | #endif -------------------------------------------------------------------------------- /opticalflow.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "opticalflow.h" 7 | #include "opticalflow_aux.h" 8 | #include "solver.h" 9 | #include "image.h" 10 | 11 | #include 12 | typedef __v4sf v4sf; 13 | 14 | convolution_t *deriv, *deriv_flow; 15 | float quarter_alpha, half_delta_over3, half_beta, half_gamma_over3; 16 | 17 | /* perform flow computation at one level of the pyramid */ 18 | void compute_one_level(image_t *wx, image_t *wy, color_image_t *im1, color_image_t *im2, image_t *desc_flow_x, image_t *desc_flow_y, image_t *desc_weight, const optical_flow_params_t *params){ 19 | const int width = wx->width, height = wx->height, stride=wx->stride; 20 | int i_inner_iteration; 21 | 22 | image_t *du = image_new(width,height), *dv = image_new(width,height), // the flow increment 23 | *mask = image_new(width,height), // mask containing 0 if a point goes outside image boundary, 1 otherwise 24 | *smooth_horiz = image_new(width,height), *smooth_vert = image_new(width,height), // horiz: (i,j) contains the diffusivity coeff from (i,j) to (i+1,j) 25 | *uu = image_new(width,height), *vv = image_new(width,height), // flow plus flow increment 26 | *a11 = image_new(width,height), *a12 = image_new(width,height), *a22 = image_new(width,height), // system matrix A of Ax=b for each pixel 27 | *b1 = image_new(width,height), *b2 = image_new(width,height); // system matrix b of Ax=b for each pixel 28 | 29 | color_image_t *w_im2 = color_image_new(width,height), // warped second image 30 | *Ix = color_image_new(width,height), *Iy = color_image_new(width,height), *Iz = color_image_new(width,height), // first order derivatives 31 | *Ixx = color_image_new(width,height), *Ixy = color_image_new(width,height), *Iyy = color_image_new(width,height), *Ixz = color_image_new(width,height), *Iyz = color_image_new(width,height); // second order derivatives 32 | 33 | // warp second image 34 | color_image_warp(w_im2, mask, im2, wx, wy); 35 | // compute derivatives 36 | get_derivatives(im1, w_im2, deriv, Ix, Iy, Iz, Ixx, Ixy, Iyy, Ixz, Iyz); 37 | // erase du and dv 38 | image_erase(du); 39 | image_erase(dv); 40 | // initialize uu and vv 41 | memcpy(uu->data,wx->data,wx->stride*wx->height*sizeof(float)); 42 | memcpy(vv->data,wy->data,wy->stride*wy->height*sizeof(float)); 43 | // inner fixed point iterations 44 | for(i_inner_iteration = 0 ; i_inner_iteration < params->n_inner_iteration ; i_inner_iteration++){ 45 | // compute robust function and system 46 | compute_smoothness(smooth_horiz, smooth_vert, uu, vv, deriv_flow, quarter_alpha ); 47 | compute_data_and_match(a11, a12, a22, b1, b2, mask, wx, wy, du, dv, uu, vv, Ix, Iy, Iz, Ixx, Ixy, Iyy, Ixz, Iyz, desc_weight, desc_flow_x, desc_flow_y, half_delta_over3, half_beta, half_gamma_over3); 48 | sub_laplacian(b1, wx, smooth_horiz, smooth_vert); 49 | sub_laplacian(b2, wy, smooth_horiz, smooth_vert); 50 | // solve system 51 | sor_coupled(du, dv, a11, a12, a22, b1, b2, smooth_horiz, smooth_vert, params->n_solver_iteration, params->sor_omega); 52 | // update flow plus flow increment 53 | int i; 54 | v4sf *uup = (v4sf*) uu->data, *vvp = (v4sf*) vv->data, *wxp = (v4sf*) wx->data, *wyp = (v4sf*) wy->data, *dup = (v4sf*) du->data, *dvp = (v4sf*) dv->data; 55 | for( i=0 ; idata,uu->data,uu->stride*uu->height*sizeof(float)); 63 | memcpy(wy->data,vv->data,vv->stride*vv->height*sizeof(float)); 64 | // free memory 65 | image_delete(du); image_delete(dv); 66 | image_delete(mask); 67 | image_delete(smooth_horiz); image_delete(smooth_vert); 68 | image_delete(uu); image_delete(vv); 69 | image_delete(a11); image_delete(a12); image_delete(a22); 70 | image_delete(b1); image_delete(b2); 71 | color_image_delete(w_im2); 72 | color_image_delete(Ix); color_image_delete(Iy); color_image_delete(Iz); 73 | color_image_delete(Ixx); color_image_delete(Ixy); color_image_delete(Iyy); color_image_delete(Ixz); color_image_delete(Iyz); 74 | } 75 | 76 | /* set flow parameters to default */ 77 | void optical_flow_params_default(optical_flow_params_t *params){ 78 | if(!params){ 79 | fprintf(stderr,"Error optical_flow_params_default: argument is null\n"); 80 | exit(1); 81 | } 82 | params->alpha = 6.0f; 83 | params->beta = 390.0f; 84 | params->gamma = 5.0f; 85 | params->delta = 0.5f; 86 | params->sigma = 0.6f; 87 | params->bk = 1.0f; 88 | params->eta = 0.95f; 89 | params->min_size = 25; 90 | params->n_inner_iteration = 5; 91 | params->n_solver_iteration = 25; 92 | params->sor_omega = 1.60f; 93 | } 94 | 95 | /* set flow parameters to sintel one */ 96 | void optical_flow_params_sintel(optical_flow_params_t *params){ 97 | if(!params){ 98 | fprintf(stderr,"Error optical_flow_params_sintel: argument is null\n"); 99 | exit(1); 100 | } 101 | params->alpha = 7.55f; 102 | params->beta = 390.0f; 103 | params->gamma = 4.95f; 104 | params->delta = 0.0; 105 | params->sigma = 0.7f; 106 | params->bk = 0.6; 107 | params->eta = 0.95f; 108 | params->min_size = 25; 109 | params->n_inner_iteration = 5; 110 | params->n_solver_iteration = 25; 111 | params->sor_omega = 1.60f; 112 | } 113 | 114 | /* set flow parameters to middlebury one */ 115 | void optical_flow_params_middlebury(optical_flow_params_t *params){ 116 | if(!params){ 117 | fprintf(stderr,"Error optical_flow_params_middlebury: argument is null\n"); 118 | exit(1); 119 | } 120 | params->alpha = 4.4f; 121 | params->beta = 0.2f; 122 | params->gamma = 5.95f; 123 | params->delta = 3.45f; 124 | params->sigma = 0.55f; 125 | params->bk = 0.8f; 126 | params->eta = 0.95f; 127 | params->min_size = 25; 128 | params->n_inner_iteration = 5; 129 | params->n_solver_iteration = 25; 130 | params->sor_omega = 1.60f; 131 | } 132 | 133 | /* set flow parameters to kitti one */ 134 | void optical_flow_params_kitti(optical_flow_params_t *params){ 135 | if(!params){ 136 | fprintf(stderr,"Error optical_flow_params_kitti: argument is null\n"); 137 | exit(1); 138 | } 139 | params->alpha = 5.4f; 140 | params->beta = 390.0f; 141 | params->gamma = 6.9f; 142 | params->delta = 0.1f; 143 | params->sigma = 0.45f; 144 | params->bk = 1.7f; 145 | params->eta = 0.95f; 146 | params->min_size = 25; 147 | params->n_inner_iteration = 5; 148 | params->n_solver_iteration = 25; 149 | params->sor_omega = 1.60f; 150 | } 151 | 152 | /* Compute the optical flow between im1 and im2 and store it as two 1-channel images in wx for flow along x-axis and wy for flow along y-axis. match_x, match_y and match_z contains eventually the input matches (NULL for no match) at any scale. */ 153 | void optical_flow(image_t *wx, image_t *wy, const color_image_t *im1, const color_image_t *im2, optical_flow_params_t *params, const image_t *match_x, const image_t *match_y, const image_t *match_z){ 154 | 155 | // Check parameters 156 | if(!params){ 157 | params = (optical_flow_params_t*) malloc(sizeof(optical_flow_params_t)); 158 | if(!params){ 159 | fprintf(stderr,"error color_image_convolve_hv(): not enough memory\n"); 160 | exit(1); 161 | } 162 | optical_flow_params_default(params); 163 | } 164 | 165 | // initialize global variables 166 | quarter_alpha = 0.25f*params->alpha; 167 | half_gamma_over3 = params->gamma*0.5f/3.0f; 168 | half_delta_over3 = params->delta*0.5f/3.0f; 169 | half_beta = params->beta*0.5f; 170 | float deriv_filter[3] = {0.0f, -8.0f/12.0f, 1.0f/12.0f}; 171 | deriv = convolution_new(2, deriv_filter, 0); 172 | float deriv_filter_flow[2] = {0.0f, -0.5f}; 173 | deriv_flow = convolution_new(1, deriv_filter_flow, 0); 174 | 175 | // presmooth images 176 | int width = im1->width, height = im1->height, filter_size; 177 | color_image_t *smooth_im1 = color_image_new(width, height), *smooth_im2 = color_image_new(width, height); 178 | float *presmooth_filter = gaussian_filter(params->sigma, &filter_size); 179 | convolution_t *presmoothing = convolution_new(filter_size, presmooth_filter, 1); 180 | color_image_convolve_hv(smooth_im1, im1, presmoothing, presmoothing); 181 | color_image_convolve_hv(smooth_im2, im2, presmoothing, presmoothing); 182 | convolution_delete(presmoothing); 183 | free(presmooth_filter); 184 | 185 | // check descriptors 186 | image_t *desc_flow_x, *desc_flow_y, *desc_weight, *desc_flow_x_original=NULL, *desc_flow_y_original=NULL, *desc_weight_original=NULL; 187 | desc_flow_x = image_new(0,0); 188 | desc_flow_y = image_new(0,0); 189 | desc_weight = image_new(0,0); 190 | if(params->beta){ 191 | if(match_x == NULL){ 192 | params->beta = 0.0f; 193 | half_beta = 0.0f; 194 | }else{ 195 | desc_flow_x_original = image_cpy(match_x); 196 | desc_flow_y_original = image_cpy(match_y); 197 | desc_weight_original = image_cpy(match_z); 198 | } 199 | } 200 | 201 | // building pyramid 202 | color_image_pyramid_t *pyr1 = color_image_pyramid_create(smooth_im1, 1.0f/params->eta, params->min_size, 0.0f), 203 | *pyr2 = color_image_pyramid_create(smooth_im2, 1.0f/params->eta, params->min_size, 0.0f); 204 | 205 | // loop over levels 206 | int k; 207 | for(k=pyr1->size-1; k>=0 ; k--){ 208 | if(params->bk>0.0f) half_beta = 0.5f*params->beta * pow(((float)k)/((float)pyr1->size-1),params->bk); 209 | if(k == pyr1->size-1){ 210 | // first level 211 | // allocate wx and wy 212 | resize_if_needed_newsize(wx, pyr1->images[k]->width, pyr1->images[k]->height); 213 | resize_if_needed_newsize(wy, pyr1->images[k]->width, pyr1->images[k]->height); 214 | image_erase(wx); image_erase(wy); 215 | }else{ 216 | // resize flow to the new pyramid level size and multiply it by 1/eta 217 | image_t *tmp = image_new(pyr1->images[k]->width, pyr1->images[k]->height); 218 | image_resize_bilinear_newsize(tmp, wx, pyr1->images[k]->width, pyr1->images[k]->height); 219 | resize_if_needed_newsize(wx, pyr1->images[k]->width, pyr1->images[k]->height); 220 | memcpy(wx->data, tmp->data, tmp->stride*tmp->height*sizeof(float)); 221 | image_mul_scalar(wx, 1.0f/params->eta); 222 | image_resize_bilinear_newsize(tmp, wy, pyr1->images[k]->width, pyr1->images[k]->height); 223 | resize_if_needed_newsize(wy, pyr1->images[k]->width, pyr1->images[k]->height); 224 | memcpy(wy->data, tmp->data, tmp->stride*tmp->height*sizeof(float)); 225 | image_mul_scalar(wy, 1.0f/params->eta); 226 | image_delete(tmp); 227 | } 228 | 229 | // resize descriptors 230 | if(params->beta){ 231 | resize_if_needed_newsize(desc_flow_x, pyr1->images[k]->width, pyr1->images[k]->height); 232 | resize_if_needed_newsize(desc_flow_y, pyr1->images[k]->width, pyr1->images[k]->height); 233 | resize_if_needed_newsize(desc_weight, pyr1->images[k]->width, pyr1->images[k]->height); 234 | descflow_resize(desc_flow_x,desc_flow_y,desc_weight,desc_flow_x_original,desc_flow_y_original,desc_weight_original); 235 | } 236 | 237 | compute_one_level(wx, wy, pyr1->images[k], pyr2->images[k], desc_flow_x, desc_flow_y, desc_weight, params); 238 | 239 | } 240 | 241 | color_image_pyramid_delete(pyr1); color_image_pyramid_delete(pyr2); 242 | 243 | // do a last iteration without descriptor if bk==0 244 | if(params->beta>0.0f && params->bk==0.0f){ 245 | half_beta = 0.0f; 246 | compute_one_level(wx, wy, smooth_im1, smooth_im2, desc_flow_x, desc_flow_y, desc_weight, params); 247 | half_beta = 0.5f*params->beta; 248 | } 249 | 250 | // free memory 251 | color_image_delete(smooth_im1); 252 | color_image_delete(smooth_im2); 253 | image_delete(desc_flow_x); image_delete(desc_flow_y); image_delete(desc_weight); 254 | convolution_delete(deriv); 255 | convolution_delete(deriv_flow); 256 | if(params->beta){image_delete(desc_flow_x_original); image_delete(desc_flow_y_original); image_delete(desc_weight_original);} 257 | } 258 | -------------------------------------------------------------------------------- /opticalflow.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "image.h" 5 | 6 | typedef struct optical_flow_params_s { 7 | float alpha; // smoothness weight 8 | float beta; // matching weight 9 | float gamma; // gradient constancy assumption weight 10 | float delta; // color constancy assumption weight 11 | float sigma; // presmoothing of the images 12 | float bk; // downweight parameter of the matching weight 13 | float eta; // downscale factor 14 | int min_size; // minimum size of the first level 15 | int n_inner_iteration; // number of inner fixed point iterations 16 | int n_solver_iteration; // number of solver iterations 17 | float sor_omega; // omega parameter of sor method 18 | } optical_flow_params_t; 19 | 20 | /* set flow parameters to default */ 21 | void optical_flow_params_default(optical_flow_params_t *params); 22 | 23 | /* set flow parameters to sintel one */ 24 | void optical_flow_params_sintel(optical_flow_params_t *params); 25 | 26 | /* set flow parameters to middlebury one */ 27 | void optical_flow_params_middlebury(optical_flow_params_t *params); 28 | 29 | /* set flow parameters to kitti one */ 30 | void optical_flow_params_kitti(optical_flow_params_t *params); 31 | 32 | /* Compute the optical flow between im1 and im2 and store it as two 1-channel images in wx for flow along x-axis and wy for flow along y-axis. match_x, match_y and match_z contains eventually the input matches (NULL for no match) at any scale. */ 33 | void optical_flow(image_t *wx, image_t *wy, const color_image_t *im1, const color_image_t *im2, optical_flow_params_t *params, const image_t *match_x, const image_t *match_y, const image_t *match_z); 34 | 35 | -------------------------------------------------------------------------------- /opticalflow_aux.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "opticalflow_aux.h" 6 | 7 | #include 8 | typedef __v4sf v4sf; 9 | 10 | #define datanorm 0.1f*0.1f//0.01f // square of the normalization factor 11 | #define epsilon_color (0.001f*0.001f)//0.000001f 12 | #define epsilon_grad (0.001f*0.001f)//0.000001f 13 | #define epsilon_desc (0.001f*0.001f)//0.000001f 14 | #define epsilon_smooth (0.001f*0.001f)//0.000001f 15 | 16 | /* warp a color image according to a flow. src is the input image, wx and wy, the input flow. dst is the warped image and mask contains 0 or 1 if the pixels goes outside/inside image boundaries */ 17 | void color_image_warp(color_image_t *dst, image_t *mask, const color_image_t *src, const image_t *wx, const image_t *wy){ 18 | int i, j, offset, incr_line = mask->stride-mask->width, x, y, x1, x2, y1, y2; 19 | float xx, yy, dx, dy; 20 | for(j=0,offset=0 ; jheight ; j++){ 21 | for(i=0 ; iwidth ; i++,offset++){ 22 | xx = i+wx->data[offset]; 23 | yy = j+wy->data[offset]; 24 | x = floor(xx); 25 | y = floor(yy); 26 | dx = xx-x; 27 | dy = yy-y; 28 | mask->data[offset] = (xx>=0 && xx<=src->width-1 && yy>=0 && yy<=src->height-1); 29 | x1 = MINMAX(x,src->width); 30 | x2 = MINMAX(x+1,src->width); 31 | y1 = MINMAX(y,src->height); 32 | y2 = MINMAX(y+1,src->height); 33 | dst->c1[offset] = 34 | src->c1[y1*src->stride+x1]*(1.0f-dx)*(1.0f-dy) + 35 | src->c1[y1*src->stride+x2]*dx*(1.0f-dy) + 36 | src->c1[y2*src->stride+x1]*(1.0f-dx)*dy + 37 | src->c1[y2*src->stride+x2]*dx*dy; 38 | dst->c2[offset] = 39 | src->c2[y1*src->stride+x1]*(1.0f-dx)*(1.0f-dy) + 40 | src->c2[y1*src->stride+x2]*dx*(1.0f-dy) + 41 | src->c2[y2*src->stride+x1]*(1.0f-dx)*dy + 42 | src->c2[y2*src->stride+x2]*dx*dy; 43 | dst->c3[offset] = 44 | src->c3[y1*src->stride+x1]*(1.0f-dx)*(1.0f-dy) + 45 | src->c3[y1*src->stride+x2]*dx*(1.0f-dy) + 46 | src->c3[y2*src->stride+x1]*(1.0f-dx)*dy + 47 | src->c3[y2*src->stride+x2]*dx*dy; 48 | } 49 | offset += incr_line; 50 | } 51 | } 52 | 53 | /** 54 | * @brief Warp a grayscale image according to a flow 55 | * 56 | * This API provides certain actions as an example. 57 | * 58 | * @param [in] dst Resulting image 59 | * @param [in] mask Mask contains 0 or 1 if the pixels goes outside/inside image boundaries. 60 | * @param [in] src Source image. 61 | * @param [in] wx Input flow, x-component. 62 | * @param [in] wy Input flow, y-component. 63 | * 64 | * 65 | * @retval none 66 | */ 67 | void image_warp(image_t *dst, image_t *mask, const image_t *src, const image_t *wx, const image_t *wy){ 68 | int i, j, offset, incr_line = mask->stride-mask->width, x, y, x1, x2, y1, y2; 69 | float xx, yy, dx, dy; 70 | for(j=0,offset=0 ; jheight ; j++){ 71 | for(i=0 ; iwidth ; i++,offset++){ 72 | xx = i+wx->data[offset]; 73 | yy = j+wy->data[offset]; 74 | x = floor(xx); 75 | y = floor(yy); 76 | dx = xx-x; 77 | dy = yy-y; 78 | mask->data[offset] = (xx>=0 && xx<=src->width-1 && yy>=0 && yy<=src->height-1); 79 | x1 = MINMAX(x,src->width); 80 | x2 = MINMAX(x+1,src->width); 81 | y1 = MINMAX(y,src->height); 82 | y2 = MINMAX(y+1,src->height); 83 | dst->data[offset] = 84 | src->data[y1*src->stride+x1]*(1.0f-dx)*(1.0f-dy) + 85 | src->data[y1*src->stride+x2]*dx*(1.0f-dy) + 86 | src->data[y2*src->stride+x1]*(1.0f-dx)*dy + 87 | src->data[y2*src->stride+x2]*dx*dy; 88 | } 89 | offset += incr_line; 90 | } 91 | } 92 | 93 | /* compute image first and second order spatio-temporal derivatives of a color image */ 94 | void get_derivatives(const color_image_t *im1, const color_image_t *im2, const convolution_t *deriv, 95 | color_image_t *dx, color_image_t *dy, color_image_t *dt, 96 | color_image_t *dxx, color_image_t *dxy, color_image_t *dyy, color_image_t *dxt, color_image_t *dyt){ 97 | // derivatives are computed on the mean of the first image and the warped second image 98 | color_image_t *tmp_im2 = color_image_new(im2->width,im2->height); 99 | v4sf *tmp_im2p = (v4sf*) tmp_im2->c1, *dtp = (v4sf*) dt->c1, *im1p = (v4sf*) im1->c1, *im2p = (v4sf*) im2->c1; 100 | const v4sf half = {0.5f,0.5f,0.5f,0.5f}; 101 | int i=0; 102 | for(i=0 ; i<3*im1->height*im1->stride/4 ; i++){ 103 | *tmp_im2p = half * ( (*im2p) + (*im1p) ); 104 | *dtp = (*im2p)-(*im1p); 105 | dtp+=1; im1p+=1; im2p+=1; tmp_im2p+=1; 106 | } 107 | // compute all other derivatives 108 | color_image_convolve_hv(dx, tmp_im2, deriv, NULL); 109 | color_image_convolve_hv(dy, tmp_im2, NULL, deriv); 110 | color_image_convolve_hv(dxx, dx, deriv, NULL); 111 | color_image_convolve_hv(dxy, dx, NULL, deriv); 112 | color_image_convolve_hv(dyy, dy, NULL, deriv); 113 | color_image_convolve_hv(dxt, dt, deriv, NULL); 114 | color_image_convolve_hv(dyt, dt, NULL, deriv); 115 | // free memory 116 | color_image_delete(tmp_im2); 117 | } 118 | 119 | /* compute the smoothness term */ 120 | /* It is represented as two images, the first one for horizontal smoothness, the second for vertical 121 | in dst_horiz, the pixel i,j represents the smoothness weight between pixel i,j and i,j+1 122 | in dst_vert, the pixel i,j represents the smoothness weight between pixel i,j and i+1,j */ 123 | void compute_smoothness(image_t *dst_horiz, image_t *dst_vert, const image_t *uu, const image_t *vv, const convolution_t *deriv_flow, const float quarter_alpha){ 124 | const int width = uu->width, height = vv->height, stride = uu->stride; 125 | int j; 126 | image_t *ux = image_new(width,height), *vx = image_new(width,height), *uy = image_new(width,height), *vy = image_new(width,height), *smoothness = image_new(width,height); 127 | // compute derivatives [-0.5 0 0.5] 128 | convolve_horiz(ux, uu, deriv_flow); 129 | convolve_horiz(vx, vv, deriv_flow); 130 | convolve_vert(uy, uu, deriv_flow); 131 | convolve_vert(vy, vv, deriv_flow); 132 | // compute smoothness 133 | v4sf *uxp = (v4sf*) ux->data, *vxp = (v4sf*) vx->data, *uyp = (v4sf*) uy->data, *vyp = (v4sf*) vy->data, *sp = (v4sf*) smoothness->data; 134 | const v4sf qa = {quarter_alpha,quarter_alpha,quarter_alpha,quarter_alpha}; 135 | const v4sf epsmooth = {epsilon_smooth,epsilon_smooth,epsilon_smooth,epsilon_smooth}; 136 | for(j=0 ; j< height*stride/4 ; j++){ 137 | *sp = qa / __builtin_ia32_sqrtps( (*uxp)*(*uxp) + (*uyp)*(*uyp) + (*vxp)*(*vxp) + (*vyp)*(*vyp) + epsmooth ); 138 | sp+=1;uxp+=1; uyp+=1; vxp+=1; vyp+=1; 139 | } 140 | image_delete(ux); image_delete(uy); image_delete(vx); image_delete(vy); 141 | // compute dst_horiz 142 | v4sf *dsthp = (v4sf*) dst_horiz->data; sp = (v4sf*) smoothness->data; 143 | float *sp_shift = (float*) memalign(16, stride*sizeof(float)); // aligned shifted copy of the current line 144 | for(j=0;jdata[j*stride+width-1], 0, sizeof(float)*(stride-width+1)); 155 | } 156 | free(sp_shift); 157 | // compute dst_vert 158 | v4sf *dstvp = (v4sf*) dst_vert->data, *sp_bottom = (v4sf*) (smoothness->data+stride); sp = (v4sf*) smoothness->data; 159 | for(j=0 ; j<(height-1)*stride/4 ; j++){ 160 | *dstvp = (*sp) + (*sp_bottom); 161 | dstvp+=1; sp+=1; sp_bottom+=1; 162 | } 163 | memset( &dst_vert->data[(height-1)*stride], 0, sizeof(float)*stride); 164 | image_delete(smoothness); 165 | } 166 | 167 | /* sub the laplacian (smoothness term) to the right-hand term */ 168 | void sub_laplacian(image_t *dst, const image_t *src, const image_t *weight_horiz, const image_t *weight_vert){ 169 | int j; 170 | const int offsetline = src->stride-src->width; 171 | float *src_ptr = src->data, *dst_ptr = dst->data, *weight_horiz_ptr = weight_horiz->data; 172 | // horizontal filtering 173 | for(j=src->height+1;--j;){ // faster than for(j=0;jheight;j++) 174 | int i; 175 | for(i=src->width;--i;){ 176 | const float tmp = (*weight_horiz_ptr)*((*(src_ptr+1))-(*src_ptr)); 177 | *dst_ptr += tmp; 178 | *(dst_ptr+1) -= tmp; 179 | dst_ptr++; 180 | src_ptr++; 181 | weight_horiz_ptr++; 182 | } 183 | dst_ptr += offsetline+1; 184 | src_ptr += offsetline+1; 185 | weight_horiz_ptr += offsetline+1; 186 | } 187 | 188 | v4sf *wvp = (v4sf*) weight_vert->data, *srcp = (v4sf*) src->data, *srcp_s = (v4sf*) (src->data+src->stride), *dstp = (v4sf*) dst->data, *dstp_s = (v4sf*) (dst->data+src->stride); 189 | for(j=1+(src->height-1)*src->stride/4 ; --j ;){ 190 | const v4sf tmp = (*wvp) * ((*srcp_s)-(*srcp)); 191 | *dstp += tmp; 192 | *dstp_s -= tmp; 193 | wvp+=1; srcp+=1; srcp_s+=1; dstp+=1; dstp_s+=1; 194 | } 195 | } 196 | 197 | /* compute the dataterm and the matching term 198 | a11 a12 a22 represents the 2x2 diagonal matrix, b1 and b2 the right hand side 199 | other (color) images are input */ 200 | void compute_data_and_match(image_t *a11, image_t *a12, image_t *a22, image_t *b1, image_t *b2, image_t *mask, image_t *wx, image_t *wy, image_t *du, image_t *dv, image_t *uu, image_t *vv, color_image_t *Ix, color_image_t *Iy, color_image_t *Iz, color_image_t *Ixx, color_image_t *Ixy, color_image_t *Iyy, color_image_t *Ixz, color_image_t *Iyz, image_t *desc_weight, image_t *desc_flow_x, image_t *desc_flow_y, const float half_delta_over3, const float half_beta, const float half_gamma_over3){ 201 | 202 | const v4sf dnorm = {datanorm, datanorm, datanorm, datanorm}; 203 | const v4sf hdover3 = {half_delta_over3, half_delta_over3, half_delta_over3, half_delta_over3}; 204 | const v4sf epscolor = {epsilon_color, epsilon_color, epsilon_color, epsilon_color}; 205 | const v4sf hgover3 = {half_gamma_over3, half_gamma_over3, half_gamma_over3, half_gamma_over3}; 206 | const v4sf epsgrad = {epsilon_grad, epsilon_grad, epsilon_grad, epsilon_grad}; 207 | const v4sf hbeta = {half_beta,half_beta,half_beta,half_beta}; 208 | const v4sf epsdesc = {epsilon_desc,epsilon_desc,epsilon_desc,epsilon_desc}; 209 | 210 | v4sf *dup = (v4sf*) du->data, *dvp = (v4sf*) dv->data, 211 | *maskp = (v4sf*) mask->data, 212 | *a11p = (v4sf*) a11->data, *a12p = (v4sf*) a12->data, *a22p = (v4sf*) a22->data, 213 | *b1p = (v4sf*) b1->data, *b2p = (v4sf*) b2->data, 214 | *ix1p=(v4sf*)Ix->c1, *iy1p=(v4sf*)Iy->c1, *iz1p=(v4sf*)Iz->c1, *ixx1p=(v4sf*)Ixx->c1, *ixy1p=(v4sf*)Ixy->c1, *iyy1p=(v4sf*)Iyy->c1, *ixz1p=(v4sf*)Ixz->c1, *iyz1p=(v4sf*) Iyz->c1, 215 | *ix2p=(v4sf*)Ix->c2, *iy2p=(v4sf*)Iy->c2, *iz2p=(v4sf*)Iz->c2, *ixx2p=(v4sf*)Ixx->c2, *ixy2p=(v4sf*)Ixy->c2, *iyy2p=(v4sf*)Iyy->c2, *ixz2p=(v4sf*)Ixz->c2, *iyz2p=(v4sf*) Iyz->c2, 216 | *ix3p=(v4sf*)Ix->c3, *iy3p=(v4sf*)Iy->c3, *iz3p=(v4sf*)Iz->c3, *ixx3p=(v4sf*)Ixx->c3, *ixy3p=(v4sf*)Ixy->c3, *iyy3p=(v4sf*)Iyy->c3, *ixz3p=(v4sf*)Ixz->c3, *iyz3p=(v4sf*) Iyz->c3, 217 | *uup = (v4sf*) uu->data, *vvp = (v4sf*)vv->data, *wxp = (v4sf*)wx->data, *wyp = (v4sf*)wy->data, 218 | *descflowxp = (v4sf*)desc_flow_x->data, *descflowyp = (v4sf*)desc_flow_y->data, *descweightp = (v4sf*)desc_weight->data; 219 | 220 | memset(a11->data, 0, sizeof(float)*uu->height*uu->stride); 221 | memset(a12->data, 0, sizeof(float)*uu->height*uu->stride); 222 | memset(a22->data, 0, sizeof(float)*uu->height*uu->stride); 223 | memset(b1->data , 0, sizeof(float)*uu->height*uu->stride); 224 | memset(b2->data , 0, sizeof(float)*uu->height*uu->stride); 225 | 226 | int i; 227 | for(i = 0 ; iheight*uu->stride/4 ; i++){ 228 | v4sf tmp, tmp2, tmp3, tmp4, tmp5, tmp6, n1, n2, n3, n4, n5, n6; 229 | // dpsi color 230 | if(half_delta_over3){ 231 | tmp = *iz1p + (*ix1p)*(*dup) + (*iy1p)*(*dvp); 232 | n1 = (*ix1p) * (*ix1p) + (*iy1p) * (*iy1p) + dnorm; 233 | tmp2 = *iz2p + (*ix2p)*(*dup) + (*iy2p)*(*dvp); 234 | n2 = (*ix2p) * (*ix2p) + (*iy2p) * (*iy2p) + dnorm; 235 | tmp3 = *iz3p + (*ix3p)*(*dup) + (*iy3p)*(*dvp); 236 | n3 = (*ix3p) * (*ix3p) + (*iy3p) * (*iy3p) + dnorm; 237 | tmp = (*maskp) * hdover3 / __builtin_ia32_sqrtps(tmp*tmp/n1 + tmp2*tmp2/n2 + tmp3*tmp3/n3 + epscolor); 238 | tmp3 = tmp/n3; tmp2 = tmp/n2; tmp /= n1; 239 | *a11p += tmp * (*ix1p) * (*ix1p); 240 | *a12p += tmp * (*ix1p) * (*iy1p); 241 | *a22p += tmp * (*iy1p) * (*iy1p); 242 | *b1p -= tmp * (*iz1p) * (*ix1p); 243 | *b2p -= tmp * (*iz1p) * (*iy1p); 244 | *a11p += tmp2 * (*ix2p) * (*ix2p); 245 | *a12p += tmp2 * (*ix2p) * (*iy2p); 246 | *a22p += tmp2 * (*iy2p) * (*iy2p); 247 | *b1p -= tmp2 * (*iz2p) * (*ix2p); 248 | *b2p -= tmp2 * (*iz2p) * (*iy2p); 249 | *a11p += tmp3 * (*ix3p) * (*ix3p); 250 | *a12p += tmp3 * (*ix3p) * (*iy3p); 251 | *a22p += tmp3 * (*iy3p) * (*iy3p); 252 | *b1p -= tmp3 * (*iz3p) * (*ix3p); 253 | *b2p -= tmp3 * (*iz3p) * (*iy3p); 254 | } 255 | // dpsi gradient 256 | n1 = (*ixx1p) * (*ixx1p) + (*ixy1p) * (*ixy1p) + dnorm; 257 | n2 = (*iyy1p) * (*iyy1p) + (*ixy1p) * (*ixy1p) + dnorm; 258 | tmp = *ixz1p + (*ixx1p) * (*dup) + (*ixy1p) * (*dvp); 259 | tmp2 = *iyz1p + (*ixy1p) * (*dup) + (*iyy1p) * (*dvp); 260 | n3 = (*ixx2p) * (*ixx2p) + (*ixy2p) * (*ixy2p) + dnorm; 261 | n4 = (*iyy2p) * (*iyy2p) + (*ixy2p) * (*ixy2p) + dnorm; 262 | tmp3 = *ixz2p + (*ixx2p) * (*dup) + (*ixy2p) * (*dvp); 263 | tmp4 = *iyz2p + (*ixy2p) * (*dup) + (*iyy2p) * (*dvp); 264 | n5 = (*ixx3p) * (*ixx3p) + (*ixy3p) * (*ixy3p) + dnorm; 265 | n6 = (*iyy3p) * (*iyy3p) + (*ixy3p) * (*ixy3p) + dnorm; 266 | tmp5 = *ixz3p + (*ixx3p) * (*dup) + (*ixy3p) * (*dvp); 267 | tmp6 = *iyz3p + (*ixy3p) * (*dup) + (*iyy3p) * (*dvp); 268 | tmp = (*maskp) * hgover3 / __builtin_ia32_sqrtps(tmp*tmp/n1 + tmp2*tmp2/n2 + tmp3*tmp3/n3 + tmp4*tmp4/n4 + tmp5*tmp5/n5 + tmp6*tmp6/n6 + epsgrad); 269 | tmp6 = tmp/n6; tmp5 = tmp/n5; tmp4 = tmp/n4; tmp3 = tmp/n3; tmp2 = tmp/n2; tmp /= n1; 270 | *a11p += tmp *(*ixx1p)*(*ixx1p) + tmp2*(*ixy1p)*(*ixy1p); 271 | *a12p += tmp *(*ixx1p)*(*ixy1p) + tmp2*(*ixy1p)*(*iyy1p); 272 | *a22p += tmp2*(*iyy1p)*(*iyy1p) + tmp *(*ixy1p)*(*ixy1p); 273 | *b1p -= tmp *(*ixx1p)*(*ixz1p) + tmp2*(*ixy1p)*(*iyz1p); 274 | *b2p -= tmp2*(*iyy1p)*(*iyz1p) + tmp *(*ixy1p)*(*ixz1p); 275 | *a11p += tmp3*(*ixx2p)*(*ixx2p) + tmp4*(*ixy2p)*(*ixy2p); 276 | *a12p += tmp3*(*ixx2p)*(*ixy2p) + tmp4*(*ixy2p)*(*iyy2p); 277 | *a22p += tmp4*(*iyy2p)*(*iyy2p) + tmp3*(*ixy2p)*(*ixy2p); 278 | *b1p -= tmp3*(*ixx2p)*(*ixz2p) + tmp4*(*ixy2p)*(*iyz2p); 279 | *b2p -= tmp4*(*iyy2p)*(*iyz2p) + tmp3*(*ixy2p)*(*ixz2p); 280 | *a11p += tmp5*(*ixx3p)*(*ixx3p) + tmp6*(*ixy3p)*(*ixy3p); 281 | *a12p += tmp5*(*ixx3p)*(*ixy3p) + tmp6*(*ixy3p)*(*iyy3p); 282 | *a22p += tmp6*(*iyy3p)*(*iyy3p) + tmp5*(*ixy3p)*(*ixy3p); 283 | *b1p -= tmp5*(*ixx3p)*(*ixz3p) + tmp6*(*ixy3p)*(*iyz3p); 284 | *b2p -= tmp6*(*iyy3p)*(*iyz3p) + tmp5*(*ixy3p)*(*ixz3p); 285 | if(half_beta){ // dpsi_match 286 | tmp = *uup - (*descflowxp); 287 | tmp2 = *vvp - (*descflowyp); 288 | tmp = hbeta*(*descweightp)/__builtin_ia32_sqrtps(tmp*tmp+tmp2*tmp2+epsdesc); 289 | *a11p += tmp; 290 | *a22p += tmp; 291 | *b1p -= tmp*((*wxp)-(*descflowxp)); 292 | *b2p -= tmp*((*wyp)-(*descflowyp)); 293 | } 294 | dup+=1; dvp+=1; maskp+=1; a11p+=1; a12p+=1; a22p+=1; b1p+=1; b2p+=1; 295 | ix1p+=1; iy1p+=1; iz1p+=1; ixx1p+=1; ixy1p+=1; iyy1p+=1; ixz1p+=1; iyz1p+=1; 296 | ix2p+=1; iy2p+=1; iz2p+=1; ixx2p+=1; ixy2p+=1; iyy2p+=1; ixz2p+=1; iyz2p+=1; 297 | ix3p+=1; iy3p+=1; iz3p+=1; ixx3p+=1; ixy3p+=1; iyy3p+=1; ixz3p+=1; iyz3p+=1; 298 | uup+=1;vvp+=1;wxp+=1; wyp+=1;descflowxp+=1;descflowyp+=1;descweightp+=1; 299 | } 300 | } 301 | 302 | /* resize the descriptors to the new size using a weighted mean */ 303 | void descflow_resize(image_t *dst_flow_x, image_t *dst_flow_y, image_t *dst_weight, const image_t *src_flow_x, const image_t *src_flow_y, const image_t *src_weight){ 304 | const int src_width = src_flow_x->width, src_height = src_flow_x->height, src_stride = src_flow_x->stride, 305 | dst_width = dst_flow_x->width, dst_height = dst_flow_x->height, dst_stride = dst_flow_x->stride; 306 | const float scale_x = ((float)dst_width-1)/((float)src_width-1), scale_y = ((float)dst_height-1)/((float)src_height-1); 307 | image_erase(dst_flow_x); image_erase(dst_flow_y); image_erase(dst_weight); 308 | int j; 309 | for( j=0 ; jdata[j*src_stride+i]; 318 | if( weight<0.0000000001f ) continue; 319 | const float xx = ((float)i)*scale_x; 320 | const float xxf = floor(xx); 321 | const float dx = xx-xxf; 322 | const int x1 = MINMAX( (int) xxf , dst_width); 323 | const int x2 = MINMAX( (int) xxf+1 , dst_width); 324 | float weightxy, newweight; 325 | if( dx ){ 326 | if( dy ){ 327 | weightxy = weight*dx*dy; 328 | newweight = dst_weight->data[y2*dst_stride+x2] + weightxy; 329 | dst_flow_x->data[y2*dst_stride+x2] = (dst_flow_x->data[y2*dst_stride+x2]*dst_weight->data[y2*dst_stride+x2] + src_flow_x->data[j*src_stride+i]*weightxy*scale_x)/newweight; 330 | dst_flow_y->data[y2*dst_stride+x2] = (dst_flow_y->data[y2*dst_stride+x2]*dst_weight->data[y2*dst_stride+x2] + src_flow_y->data[j*src_stride+i]*weightxy*scale_y)/newweight; 331 | dst_weight->data[y2*dst_stride+x2] = newweight; 332 | } 333 | weightxy = weight*dx*(1.0f-dy); 334 | newweight = dst_weight->data[y1*dst_stride+x2] + weightxy; 335 | dst_flow_x->data[y1*dst_stride+x2] = (dst_flow_x->data[y1*dst_stride+x2]*dst_weight->data[y1*dst_stride+x2] + src_flow_x->data[j*src_stride+i]*weightxy*scale_x)/newweight; 336 | dst_flow_y->data[y1*dst_stride+x2] = (dst_flow_y->data[y1*dst_stride+x2]*dst_weight->data[y1*dst_stride+x2] + src_flow_y->data[j*src_stride+i]*weightxy*scale_y)/newweight; 337 | dst_weight->data[y1*dst_stride+x2] = newweight; 338 | } 339 | if( dy ) { 340 | weightxy = weight*(1.0f-dx)*dy; 341 | newweight = dst_weight->data[y2*dst_stride+x1] + weightxy; 342 | dst_flow_x->data[y2*dst_stride+x1] = (dst_flow_x->data[y2*dst_stride+x1]*dst_weight->data[y2*dst_stride+x1] + src_flow_x->data[j*src_stride+i]*weightxy*scale_x)/newweight; 343 | dst_flow_y->data[y2*dst_stride+x1] = (dst_flow_y->data[y2*dst_stride+x1]*dst_weight->data[y2*dst_stride+x1] + src_flow_y->data[j*src_stride+i]*weightxy*scale_y)/newweight; 344 | dst_weight->data[y2*dst_stride+x1] = newweight; 345 | } 346 | weightxy = weight*(1.0f-dx)*(1.0f-dy); 347 | newweight = dst_weight->data[y1*dst_stride+x1] + weightxy; 348 | dst_flow_x->data[y1*dst_stride+x1] = (dst_flow_x->data[y1*dst_stride+x1]*dst_weight->data[y1*dst_stride+x1] + src_flow_x->data[j*src_stride+i]*weightxy*scale_x)/newweight; 349 | dst_flow_y->data[y1*dst_stride+x1] = (dst_flow_y->data[y1*dst_stride+x1]*dst_weight->data[y1*dst_stride+x1] + src_flow_y->data[j*src_stride+i]*weightxy*scale_y)/newweight; 350 | dst_weight->data[y1*dst_stride+x1] = newweight; 351 | } 352 | } 353 | } 354 | 355 | /* resize the descriptors to the new size using a nearest neighbor method while keeping the descriptor with the higher weight at the end */ 356 | void descflow_resize_nn(image_t *dst_flow_x, image_t *dst_flow_y, image_t *dst_weight, const image_t *src_flow_x, const image_t *src_flow_y, const image_t *src_weight){ 357 | const int src_width = src_flow_x->width, src_height = src_flow_x->height, src_stride = src_flow_x->stride, 358 | dst_width = dst_flow_x->width, dst_height = dst_flow_x->height, dst_stride = dst_flow_x->stride; 359 | const float scale_x = ((float)dst_width-1)/((float)src_width-1), scale_y = ((float)dst_height-1)/((float)src_height-1); 360 | image_erase(dst_flow_x); image_erase(dst_flow_y); image_erase(dst_weight); 361 | int j; 362 | for( j=0 ; jdata[j*src_stride+i]; 368 | if( !weight ) 369 | continue; 370 | const float xx = ((float)i)*scale_x; 371 | const int x = (int) 0.5f+xx; // equivalent to round(xx) 372 | if( dst_weight->data[y*dst_stride+x] < weight ){ 373 | dst_weight->data[y*dst_stride+x] = weight; 374 | dst_flow_x->data[y*dst_stride+x] = src_flow_x->data[j*src_stride+i]*scale_x; 375 | dst_flow_y->data[y*dst_stride+x] = src_flow_y->data[j*src_stride+i]*scale_y; 376 | } 377 | } 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /opticalflow_aux.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPTICALFLOW_AUX_ 2 | #define __OPTICALFLOW_AUX_ 3 | 4 | #include 5 | 6 | #include "image.h" 7 | 8 | /* warp a color image according to a flow. src is the input image, wx and wy, the input flow. dst is the warped image and mask contains 0 or 1 if the pixels goes outside/inside image boundaries */ 9 | void color_image_warp(color_image_t *dst, image_t *mask, const color_image_t *src, const image_t *wx, const image_t *wy); 10 | 11 | /** 12 | * @brief Warp a grayscale image according to a flow 13 | * 14 | * This API provides certain actions as an example. 15 | * 16 | * @param [in] dst Resulting image 17 | * @param [in] mask Mask contains 0 or 1 if the pixels goes outside/inside image boundaries. 18 | * @param [in] src Source image. 19 | * @param [in] wx Input flow, x-component. 20 | * @param [in] wy Input flow, y-component. 21 | * 22 | * 23 | * @retval none 24 | */ 25 | void image_warp(image_t *dst, image_t *mask, const image_t *src, const image_t *wx, const image_t *wy); 26 | 27 | /* compute image first and second order spatio-temporal derivatives of a color image */ 28 | void get_derivatives(const color_image_t *im1, const color_image_t *im2, const convolution_t *deriv, color_image_t *dx, color_image_t *dy, color_image_t *dt, color_image_t *dxx, color_image_t *dxy, color_image_t *dyy, color_image_t *dxt, color_image_t *dyt); 29 | 30 | /* compute the smoothness term */ 31 | void compute_smoothness(image_t *dst_horiz, image_t *dst_vert, const image_t *uu, const image_t *vv, const convolution_t *deriv_flow, const float quarter_alpha); 32 | 33 | /* sub the laplacian (smoothness term) to the right-hand term */ 34 | void sub_laplacian(image_t *dst, const image_t *src, const image_t *weight_horiz, const image_t *weight_vert); 35 | 36 | /* compute the dataterm and the matching term 37 | a11 a12 a22 represents the 2x2 diagonal matrix, b1 and b2 the right hand side 38 | other (color) images are input */ 39 | void compute_data_and_match(image_t *a11, image_t *a12, image_t *a22, image_t *b1, image_t *b2, image_t *mask, image_t *wx, image_t *wy, image_t *du, image_t *dv, image_t *uu, image_t *vv, color_image_t *Ix, color_image_t *Iy, color_image_t *Iz, color_image_t *Ixx, color_image_t *Ixy, color_image_t *Iyy, color_image_t *Ixz, color_image_t *Iyz, image_t *desc_weight, image_t *desc_flow_x, image_t *desc_flow_y, const float half_delta_over3, const float half_beta, const float half_gamma_over3); 40 | 41 | /* resize the descriptors to the new size using a weighted mean */ 42 | void descflow_resize(image_t *dst_flow_x, image_t *dst_flow_y, image_t *dst_weight, const image_t *src_flow_x, const image_t *src_flow_y, const image_t *src_weight); 43 | 44 | /* resize the descriptors to the new size using a nearest neighbor method while keeping the descriptor with the higher weight at the end */ 45 | void descflow_resize_nn(image_t *dst_flow_x, image_t *dst_flow_y, image_t *dst_weight, const image_t *src_flow_x, const image_t *src_flow_y, const image_t *src_weight); 46 | 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /py_fastdeepflow/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | -------------------------------------------------------------------------------- /py_fastdeepflow/fastdeepflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | import sys 5 | import os 6 | 7 | import ctypes 8 | import numpy 9 | 10 | path_lib = os.path.join(os.path.dirname(__file__), 'libdeepflow.so') 11 | lib = ctypes.cdll.LoadLibrary(path_lib) 12 | 13 | 14 | class image_t(ctypes.Structure): 15 | _fields_ = [("width", ctypes.c_int), 16 | ("height", ctypes.c_int), 17 | ("stride", ctypes.c_int), 18 | ("data", ctypes.POINTER(ctypes.c_float)), 19 | ] 20 | 21 | class color_image_t(ctypes.Structure): 22 | _fields_ = [("width", ctypes.c_int), 23 | ("height", ctypes.c_int), 24 | ("stride", ctypes.c_int), 25 | ("c1", ctypes.POINTER(ctypes.c_float)), 26 | ("c2", ctypes.POINTER(ctypes.c_float)), 27 | ("c3", ctypes.POINTER(ctypes.c_float)), 28 | ] 29 | 30 | 31 | class optical_flow_params_t(ctypes.Structure): 32 | _fields_ = [("alpha", ctypes.c_float), 33 | ("beta", ctypes.c_float), 34 | ("gamma", ctypes.c_float), 35 | ("delta", ctypes.c_float), 36 | ("sigma", ctypes.c_float), 37 | ("bk", ctypes.c_float), 38 | ("eta", ctypes.c_float), 39 | ("min_size", ctypes.c_int), 40 | ("n_inner_iteration", ctypes.c_int), 41 | ("n_solver_iteration", ctypes.c_int), 42 | ("sor_omega", ctypes.c_float), 43 | ] 44 | 45 | lib.readFlowFile.restype = ctypes.POINTER(ctypes.POINTER(image_t)) 46 | lib.readFlowFile.argtypes = [ctypes.c_char_p] 47 | lib.writeFlowFile.restype = ctypes.c_int 48 | 49 | lib.color_image_load.restype = ctypes.POINTER(color_image_t) 50 | lib.color_image_load.argtypes = [ctypes.c_char_p] 51 | lib.image_delete.restype = None 52 | 53 | lib.color_image_new.restype = ctypes.POINTER(color_image_t) 54 | lib.image_new.restype = ctypes.POINTER(image_t) 55 | 56 | 57 | def create_params(preset="default"): 58 | """ 59 | Create instance of optical flow parameters according to one of predefined presets 60 | :param preset: string, available values: default, sintel, middlebury, kitti 61 | :return: 62 | """ 63 | 64 | dict_generators = dict(default=lib.optical_flow_params_default, 65 | sintel=lib.optical_flow_params_sintel, 66 | middlebury=lib.optical_flow_params_middlebury, 67 | kitti=lib.optical_flow_params_kitti) 68 | params = optical_flow_params_t() 69 | func = dict_generators[preset] 70 | 71 | func(ctypes.byref(params)) 72 | return params 73 | 74 | 75 | def numpy_from_image_t(img_t): 76 | full = numpy.ctypeslib.as_array(img_t.data, shape=(img_t.height, img_t.stride)) 77 | return full[:,:img_t.width].copy() 78 | 79 | def numpy_from_color_image_t_1(img_t): 80 | shape=(img_t.height, img_t.stride) 81 | 82 | channels = [numpy.ctypeslib.as_array(c, shape=shape)[:,:img_t.width] for c in [img_t.c1, img_t.c2, img_t.c3]] 83 | 84 | return numpy.dstack(channels) 85 | 86 | 87 | def numpy_from_color_image_t(img_t): 88 | shape=(3, img_t.height, img_t.stride) 89 | return numpy.ctypeslib.as_array(img_t.c1, shape=shape).transpose(1,2,0)[:,:,:img_t.width].copy() 90 | 91 | 92 | def numpy_from_lib_image(img_t): 93 | if type(img_t) == image_t: 94 | return numpy_from_image_t(img_t) 95 | if type(img_t) == color_image_t: 96 | return numpy_from_color_image_t(img_t) 97 | raise Exception("Unsupported input type") 98 | 99 | 100 | def read_flow(path): 101 | t = lib.readFlowFile(path) 102 | 103 | if not t: 104 | raise Exception("Failed to load optical flow from file") 105 | 106 | u = numpy_from_image_t(t[0].contents) 107 | v = numpy_from_image_t(t[1].contents) 108 | 109 | lib.image_delete(t[0]) 110 | lib.image_delete(t[1]) 111 | 112 | return u, v 113 | 114 | 115 | def write_flow(path, u, v): 116 | """ 117 | Save optical flow (u,v) to .flo file. 118 | :param path: destination path 119 | :param u: x component of optical flow 120 | :param v: y component of optical flow 121 | :return: None 122 | """ 123 | if u.shape != v.shape: 124 | raise Exception("Shapes of x- and y-components of optical flow must match") 125 | 126 | if u.ndim != 2: 127 | raise Exception("Input optical flow components must me 2-dimensional") 128 | 129 | h, w = u.shape 130 | 131 | wx = lib.image_new(w, h) 132 | wy = lib.image_new(w, h) 133 | 134 | fill_image_t(u, wx.contents) 135 | fill_image_t(v, wy.contents) 136 | 137 | res = lib.writeFlowFile(path, wx, wy) 138 | 139 | lib.image_delete(wx) 140 | lib.image_delete(wy) 141 | 142 | if res != 0: 143 | raise Exception("Failed to save optical flow") 144 | 145 | 146 | def fill_colot_image_t(img, new_c_img): 147 | h, w, s = new_c_img.height, new_c_img.width, new_c_img.stride 148 | shape = (h, s) 149 | c1 = numpy.ctypeslib.as_array(new_c_img.c1, shape=shape) 150 | c1[:, :w] = img[:,:,0] 151 | 152 | c2 = numpy.ctypeslib.as_array(new_c_img.c2, shape=shape) 153 | c2[:, :w] = img[:,:,1] 154 | 155 | c3 = numpy.ctypeslib.as_array(new_c_img.c3, shape=shape) 156 | c3[:, :w] = img[:,:,2] 157 | 158 | 159 | def fill_image_t(img_src, img_dst): 160 | h, w, s = img_dst.height, img_dst.width, img_dst.stride 161 | shape = (h, s) 162 | data = numpy.ctypeslib.as_array(img_dst.data, shape=shape) 163 | data[:, :w] = img_src[:,:] 164 | 165 | 166 | def calc_flow(img0, img1, params=None): 167 | """ 168 | Calculate optical flow between two images. For each pixel in the source image the algorithm searches for 169 | corresponding location in the reference image. 170 | :param img0: Source image 171 | :param img1: Reference image 172 | :param params: parameters of optical flow algorithm 173 | :return: (u, v) tuple for horizontal (u) and vertical (v) components of optical flow 174 | """ 175 | h, w, c = img0.shape 176 | wx = lib.image_new(w, h) 177 | wy = lib.image_new(w, h) 178 | 179 | im1 = lib.color_image_new(w, h) 180 | im2 = lib.color_image_new(w, h) 181 | 182 | fill_colot_image_t(img0, im1.contents) 183 | fill_colot_image_t(img1, im2.contents) 184 | 185 | match_x, match_y, match_z = [ctypes.POINTER(image_t)(),] * 3 186 | 187 | if params is None: 188 | params = optical_flow_params_t() 189 | lib.optical_flow_params_default(ctypes.byref(params)) 190 | 191 | lib.optical_flow(wx, wy, im1, im2, ctypes.byref(params), match_x, match_y, match_z) 192 | 193 | u_computed = numpy_from_image_t(wx.contents) 194 | v_computed = numpy_from_image_t(wy.contents) 195 | 196 | lib.image_delete(wx) 197 | lib.image_delete(wy) 198 | lib.image_delete(match_x) 199 | lib.image_delete(match_y) 200 | lib.image_delete(match_z) 201 | lib.color_image_delete(im1) 202 | lib.color_image_delete(im2) 203 | 204 | return u_computed, v_computed 205 | 206 | 207 | def warp_image(image, u, v): 208 | shape = list(image.shape) 209 | if len(shape) == 2: 210 | shape.append(1) 211 | elif len(shape) != 3: 212 | raise Exception("Unsupported number of dimensions") 213 | h, w, channels = shape 214 | 215 | if channels == 3: 216 | func_new_image = lib.color_image_new 217 | func_fill_image = fill_colot_image_t 218 | func_delete_image = lib.color_image_delete 219 | func_warp = lib.color_image_warp 220 | elif channels == 1: 221 | func_new_image = lib.image_new 222 | func_fill_image = fill_image_t 223 | func_delete_image = lib.image_delete 224 | func_warp = lib.image_warp 225 | else: 226 | raise Exception("Unsupported format") 227 | 228 | 229 | im_warped = func_new_image(w, h) 230 | im = func_new_image(w, h) 231 | 232 | mask = lib.image_new(w, h) 233 | wx = lib.image_new(w, h) 234 | wy = lib.image_new(w, h) 235 | 236 | fill_image_t(u, wx.contents) 237 | fill_image_t(v, wy.contents) 238 | 239 | func_fill_image(image, im.contents) 240 | 241 | func_warp(im_warped, mask, im, wx, wy) 242 | im_warped_np = numpy_from_lib_image(im_warped.contents) 243 | 244 | lib.image_delete(wx) 245 | lib.image_delete(wy); 246 | lib.image_delete(mask); 247 | 248 | func_delete_image(im_warped); 249 | func_delete_image(im); 250 | 251 | assert (0 <= im_warped_np.shape[0] - h <= 2) 252 | assert (0 <= im_warped_np.shape[1] - w <= 2) 253 | im_warped_np = im_warped_np[:h, :w] 254 | assert (im_warped_np.ndim == 2 and channels == 1 or im_warped_np.ndim == 3 and im_warped_np.shape[-1] == channels) 255 | return im_warped_np 256 | 257 | -------------------------------------------------------------------------------- /solver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "image.h" 8 | #include "solver.h" 9 | 10 | #include 11 | typedef __v4sf v4sf; 12 | 13 | //THIS IS A SLOW VERSION BUT READABLE 14 | //Perform n iterations of the sor_coupled algorithm 15 | //du and dv are used as initial guesses 16 | //The system form is the same as in opticalflow.c 17 | void sor_coupled_slow_but_readable(image_t *du, image_t *dv, const image_t *a11, const image_t *a12, const image_t *a22, const image_t *b1, const image_t *b2, const image_t *dpsis_horiz, const image_t *dpsis_vert, const int iterations, const float omega){ 18 | int i,j,iter; 19 | float sigma_u,sigma_v,sum_dpsis,A11,A22,A12,B1,B2,det; 20 | for(iter = 0 ; iterheight ; j++){ 22 | for(i=0 ; iwidth ; i++){ 23 | sigma_u = 0.0f; 24 | sigma_v = 0.0f; 25 | sum_dpsis = 0.0f; 26 | if(j>0){ 27 | sigma_u -= dpsis_vert->data[(j-1)*du->stride+i]*du->data[(j-1)*du->stride+i]; 28 | sigma_v -= dpsis_vert->data[(j-1)*du->stride+i]*dv->data[(j-1)*du->stride+i]; 29 | sum_dpsis += dpsis_vert->data[(j-1)*du->stride+i]; 30 | } 31 | if(i>0){ 32 | sigma_u -= dpsis_horiz->data[j*du->stride+i-1]*du->data[j*du->stride+i-1]; 33 | sigma_v -= dpsis_horiz->data[j*du->stride+i-1]*dv->data[j*du->stride+i-1]; 34 | sum_dpsis += dpsis_horiz->data[j*du->stride+i-1]; 35 | } 36 | if(jheight-1){ 37 | sigma_u -= dpsis_vert->data[j*du->stride+i]*du->data[(j+1)*du->stride+i]; 38 | sigma_v -= dpsis_vert->data[j*du->stride+i]*dv->data[(j+1)*du->stride+i]; 39 | sum_dpsis += dpsis_vert->data[j*du->stride+i]; 40 | } 41 | if(iwidth-1){ 42 | sigma_u -= dpsis_horiz->data[j*du->stride+i]*du->data[j*du->stride+i+1]; 43 | sigma_v -= dpsis_horiz->data[j*du->stride+i]*dv->data[j*du->stride+i+1]; 44 | sum_dpsis += dpsis_horiz->data[j*du->stride+i]; 45 | } 46 | A11 = a11->data[j*du->stride+i]+sum_dpsis; 47 | A12 = a12->data[j*du->stride+i]; 48 | A22 = a22->data[j*du->stride+i]+sum_dpsis; 49 | det = A11*A22-A12*A12; 50 | B1 = b1->data[j*du->stride+i]-sigma_u; 51 | B2 = b2->data[j*du->stride+i]-sigma_v; 52 | du->data[j*du->stride+i] = (1.0f-omega)*du->data[j*du->stride+i] +omega*( A22*B1-A12*B2)/det; 53 | dv->data[j*du->stride+i] = (1.0f-omega)*dv->data[j*du->stride+i] +omega*(-A12*B1+A11*B2)/det; 54 | } 55 | } 56 | } 57 | } 58 | 59 | // THIS IS A FASTER VERSION BUT UNREADABLE 60 | // the first iteration is separated from the other to compute the inverse of the 2x2 block diagonal 61 | // each iteration is split in two first line / middle lines / last line, and the left block is computed separately on each line 62 | void sor_coupled(image_t *du, image_t *dv, image_t *a11, image_t *a12, image_t *a22, const image_t *b1, const image_t *b2, const image_t *dpsis_horiz, const image_t *dpsis_vert, const int iterations, const float omega){ 63 | //sor_coupled_slow(du,dv,a11,a12,a22,b1,b2,dpsis_horiz,dpsis_vert,iterations,omega); return; printf("test\n"); 64 | 65 | if(du->width<2 || du->height<2 || iterations < 1){ 66 | sor_coupled_slow_but_readable(du,dv,a11,a12,a22,b1,b2,dpsis_horiz,dpsis_vert,iterations,omega); 67 | return; 68 | } 69 | 70 | const int stride = du->stride, width = du->width; 71 | const int iterheight = du->height-1, iterline = (stride)/4, width_minus_1_sizeoffloat = sizeof(float)*(width-1); 72 | int j,iter,i,k; 73 | float *floatarray = (float*) memalign(16, stride*sizeof(float)*3); 74 | if(floatarray==NULL){ 75 | fprintf(stderr, "error in sor_coupled(): not enough memory\n"); 76 | exit(1); 77 | } 78 | float *f1 = floatarray; 79 | float *f2 = f1+stride; 80 | float *f3 = f2+stride; 81 | f1[0] = 0.0f; 82 | memset(&f1[width], 0, sizeof(float)*(stride-width)); 83 | memset(&f2[width-1], 0, sizeof(float)*(stride-width+1)); 84 | memset(&f3[width-1], 0, sizeof(float)*(stride-width+1)); 85 | 86 | { // first iteration 87 | v4sf *a11p = (v4sf*) a11->data, *a12p = (v4sf*) a12->data, *a22p = (v4sf*) a22->data, *b1p = (v4sf*) b1->data, *b2p = (v4sf*) b2->data, *hp = (v4sf*) dpsis_horiz->data, *vp = (v4sf*) dpsis_vert->data; 88 | float *du_ptr = du->data, *dv_ptr = dv->data; 89 | v4sf *dub = (v4sf*) (du_ptr+stride), *dvb = (v4sf*) (dv_ptr+stride); 90 | 91 | { // first iteration - first line 92 | 93 | memcpy(f1+1, ((float*) hp), width_minus_1_sizeoffloat); 94 | memcpy(f2, du_ptr+1, width_minus_1_sizeoffloat); 95 | memcpy(f3, dv_ptr+1, width_minus_1_sizeoffloat); 96 | v4sf* hpl = (v4sf*) f1, *dur = (v4sf*) f2, *dvr = (v4sf*) f3; 97 | 98 | { // left block 99 | // reverse 2x2 diagonal block 100 | const v4sf dpsis = (*hpl) + (*hp) + (*vp); 101 | const v4sf A11 = (*a22p)+dpsis, A22 = (*a11p)+dpsis; 102 | const v4sf det = A11*A22 - (*a12p)*(*a12p); 103 | *a11p = A11/det; 104 | *a22p = A22/det; 105 | *a12p /= -det; 106 | // do one iteration 107 | const v4sf s1 = (*hp)*(*dur) + (*vp)*(*dub) + (*b1p); 108 | const v4sf s2 = (*hp)*(*dvr) + (*vp)*(*dvb) + (*b2p); 109 | du_ptr[0] += omega*( a11p[0][0]*s1[0] + a12p[0][0]*s2[0] - du_ptr[0] ); 110 | dv_ptr[0] += omega*( a12p[0][0]*s1[0] + a22p[0][0]*s2[0] - dv_ptr[0] ); 111 | for(k=1;k<4;k++){ 112 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 113 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 114 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 115 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 116 | } 117 | // increment pointer 118 | hpl+=1; hp+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 119 | dur+=1; dvr+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 120 | du_ptr += 4; dv_ptr += 4; 121 | } 122 | for(i=iterline;--i;){ 123 | // reverse 2x2 diagonal block 124 | const v4sf dpsis = (*hpl) + (*hp) + (*vp); 125 | const v4sf A11 = (*a22p)+dpsis, A22 = (*a11p)+dpsis; 126 | const v4sf det = A11*A22 - (*a12p)*(*a12p); 127 | *a11p = A11/det; 128 | *a22p = A22/det; 129 | *a12p /= -det; 130 | // do one iteration 131 | const v4sf s1 = (*hp)*(*dur) + (*vp)*(*dub) + (*b1p); 132 | const v4sf s2 = (*hp)*(*dvr) + (*vp)*(*dvb) + (*b2p); 133 | for(k=0;k<4;k++){ 134 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 135 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 136 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 137 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 138 | } 139 | // increment pointer 140 | hpl+=1; hp+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 141 | dur+=1; dvr+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 142 | du_ptr += 4; dv_ptr += 4; 143 | } 144 | 145 | } 146 | 147 | v4sf *vpt = (v4sf*) dpsis_vert->data; 148 | v4sf *dut = (v4sf*) du->data, *dvt = (v4sf*) dv->data; 149 | 150 | for(j=iterheight;--j;){ // first iteration - middle lines 151 | memcpy(f1+1, ((float*) hp), width_minus_1_sizeoffloat); 152 | memcpy(f2, du_ptr+1, width_minus_1_sizeoffloat); 153 | memcpy(f3, dv_ptr+1, width_minus_1_sizeoffloat); 154 | v4sf* hpl = (v4sf*) f1, *dur = (v4sf*) f2, *dvr = (v4sf*) f3; 155 | 156 | { // left block 157 | // reverse 2x2 diagonal block 158 | const v4sf dpsis = (*hpl) + (*hp) + (*vpt) + (*vp); 159 | const v4sf A11 = (*a22p)+dpsis, A22 = (*a11p)+dpsis; 160 | const v4sf det = A11*A22 - (*a12p)*(*a12p); 161 | *a11p = A11/det; 162 | *a22p = A22/det; 163 | *a12p /= -det; 164 | // do one iteration 165 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*vp)*(*dub) + (*b1p); 166 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*vp)*(*dvb) + (*b2p); 167 | du_ptr[0] += omega*( a11p[0][0]*s1[0] + a12p[0][0]*s2[0] - du_ptr[0] ); 168 | dv_ptr[0] += omega*( a12p[0][0]*s1[0] + a22p[0][0]*s2[0] - dv_ptr[0] ); 169 | for(k=1;k<4;k++){ 170 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 171 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 172 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 173 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 174 | } 175 | // increment pointer 176 | hpl+=1; hp+=1; vpt+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 177 | dur+=1; dvr+=1; dut+=1; dvt+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 178 | du_ptr += 4; dv_ptr += 4; 179 | } 180 | for(i=iterline;--i;){ 181 | // reverse 2x2 diagonal block 182 | const v4sf dpsis = (*hpl) + (*hp) + (*vpt) + (*vp); 183 | const v4sf A11 = (*a22p)+dpsis, A22 = (*a11p)+dpsis; 184 | const v4sf det = A11*A22 - (*a12p)*(*a12p); 185 | *a11p = A11/det; 186 | *a22p = A22/det; 187 | *a12p /= -det; 188 | // do one iteration 189 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*vp)*(*dub) + (*b1p); 190 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*vp)*(*dvb) + (*b2p); 191 | for(k=0;k<4;k++){ 192 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 193 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 194 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 195 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 196 | } 197 | // increment pointer 198 | hpl+=1; hp+=1; vpt+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 199 | dur+=1; dvr+=1; dut+=1; dvt+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 200 | du_ptr += 4; dv_ptr += 4; 201 | } 202 | 203 | } 204 | 205 | { // first iteration - last line 206 | memcpy(f1+1, ((float*) hp), width_minus_1_sizeoffloat); 207 | memcpy(f2, du_ptr+1, width_minus_1_sizeoffloat); 208 | memcpy(f3, dv_ptr+1, width_minus_1_sizeoffloat); 209 | v4sf* hpl = (v4sf*) f1, *dur = (v4sf*) f2, *dvr = (v4sf*) f3; 210 | 211 | { // left block 212 | // reverse 2x2 diagonal block 213 | const v4sf dpsis = (*hpl) + (*hp) + (*vpt); 214 | const v4sf A11 = (*a22p)+dpsis, A22 = (*a11p)+dpsis; 215 | const v4sf det = A11*A22 - (*a12p)*(*a12p); 216 | *a11p = A11/det; 217 | *a22p = A22/det; 218 | *a12p /= -det; 219 | // do one iteration 220 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*b1p); 221 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*b2p); 222 | du_ptr[0] += omega*( a11p[0][0]*s1[0] + a12p[0][0]*s2[0] - du_ptr[0] ); 223 | dv_ptr[0] += omega*( a12p[0][0]*s1[0] + a22p[0][0]*s2[0] - dv_ptr[0] ); 224 | for(k=1;k<4;k++){ 225 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 226 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 227 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 228 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 229 | } 230 | // increment pointer 231 | hpl+=1; hp+=1; vpt+=1; a11p+=1; a12p+=1; a22p+=1; 232 | dur+=1; dvr+=1; dut+=1; dvt+=1; b1p+=1; b2p+=1; 233 | du_ptr += 4; dv_ptr += 4; 234 | } 235 | for(i=iterline;--i;){ 236 | // reverse 2x2 diagonal block 237 | const v4sf dpsis = (*hpl) + (*hp) + (*vpt); 238 | const v4sf A11 = (*a22p)+dpsis, A22 = (*a11p)+dpsis; 239 | const v4sf det = A11*A22 - (*a12p)*(*a12p); 240 | *a11p = A11/det; 241 | *a22p = A22/det; 242 | *a12p /= -det; 243 | // do one iteration 244 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*b1p); 245 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*b2p); 246 | for(k=0;k<4;k++){ 247 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 248 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 249 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 250 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 251 | } 252 | // increment pointer 253 | hpl+=1; hp+=1; vpt+=1; a11p+=1; a12p+=1; a22p+=1; 254 | dur+=1; dvr+=1; dut+=1; dvt+=1; b1p+=1; b2p+=1; 255 | du_ptr += 4; dv_ptr += 4; 256 | } 257 | 258 | } 259 | } 260 | 261 | 262 | 263 | for(iter=iterations;--iter;){ // other iterations 264 | v4sf *a11p = (v4sf*) a11->data, *a12p = (v4sf*) a12->data, *a22p = (v4sf*) a22->data, *b1p = (v4sf*) b1->data, *b2p = (v4sf*) b2->data, *hp = (v4sf*) dpsis_horiz->data, *vp = (v4sf*) dpsis_vert->data; 265 | float *du_ptr = du->data, *dv_ptr = dv->data; 266 | v4sf *dub = (v4sf*) (du_ptr+stride), *dvb = (v4sf*) (dv_ptr+stride); 267 | 268 | { // other iteration - first line 269 | 270 | memcpy(f1+1, ((float*) hp), width_minus_1_sizeoffloat); 271 | memcpy(f2, du_ptr+1, width_minus_1_sizeoffloat); 272 | memcpy(f3, dv_ptr+1, width_minus_1_sizeoffloat); 273 | v4sf* hpl = (v4sf*) f1, *dur = (v4sf*) f2, *dvr = (v4sf*) f3; 274 | 275 | { // left block 276 | // do one iteration 277 | const v4sf s1 = (*hp)*(*dur) + (*vp)*(*dub) + (*b1p); 278 | const v4sf s2 = (*hp)*(*dvr) + (*vp)*(*dvb) + (*b2p); 279 | du_ptr[0] += omega*( a11p[0][0]*s1[0] + a12p[0][0]*s2[0] - du_ptr[0] ); 280 | dv_ptr[0] += omega*( a12p[0][0]*s1[0] + a22p[0][0]*s2[0] - dv_ptr[0] ); 281 | for(k=1;k<4;k++){ 282 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 283 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 284 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 285 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 286 | } 287 | // increment pointer 288 | hpl+=1; hp+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 289 | dur+=1; dvr+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 290 | du_ptr += 4; dv_ptr += 4; 291 | } 292 | for(i=iterline;--i;){ 293 | // do one iteration 294 | const v4sf s1 = (*hp)*(*dur) + (*vp)*(*dub) + (*b1p); 295 | const v4sf s2 = (*hp)*(*dvr) + (*vp)*(*dvb) + (*b2p); 296 | for(k=0;k<4;k++){ 297 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 298 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 299 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 300 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 301 | } 302 | // increment pointer 303 | hpl+=1; hp+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 304 | dur+=1; dvr+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 305 | du_ptr += 4; dv_ptr += 4; 306 | } 307 | 308 | } 309 | 310 | v4sf *vpt = (v4sf*) dpsis_vert->data; 311 | v4sf *dut = (v4sf*) du->data, *dvt = (v4sf*) dv->data; 312 | 313 | for(j=iterheight;--j;){ // other iteration - middle lines 314 | memcpy(f1+1, ((float*) hp), width_minus_1_sizeoffloat); 315 | memcpy(f2, du_ptr+1, width_minus_1_sizeoffloat); 316 | memcpy(f3, dv_ptr+1, width_minus_1_sizeoffloat); 317 | v4sf* hpl = (v4sf*) f1, *dur = (v4sf*) f2, *dvr = (v4sf*) f3; 318 | 319 | { // left block 320 | // do one iteration 321 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*vp)*(*dub) + (*b1p); 322 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*vp)*(*dvb) + (*b2p); 323 | du_ptr[0] += omega*( a11p[0][0]*s1[0] + a12p[0][0]*s2[0] - du_ptr[0] ); 324 | dv_ptr[0] += omega*( a12p[0][0]*s1[0] + a22p[0][0]*s2[0] - dv_ptr[0] ); 325 | for(k=1;k<4;k++){ 326 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 327 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 328 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 329 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 330 | } 331 | // increment pointer 332 | hpl+=1; hp+=1; vpt+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 333 | dur+=1; dvr+=1; dut+=1; dvt+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 334 | du_ptr += 4; dv_ptr += 4; 335 | } 336 | for(i=iterline;--i;){ 337 | // do one iteration 338 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*vp)*(*dub) + (*b1p); 339 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*vp)*(*dvb) + (*b2p); 340 | for(k=0;k<4;k++){ 341 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 342 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 343 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 344 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 345 | } 346 | // increment pointer 347 | hpl+=1; hp+=1; vpt+=1; vp+=1; a11p+=1; a12p+=1; a22p+=1; 348 | dur+=1; dvr+=1; dut+=1; dvt+=1; dub+=1; dvb +=1; b1p+=1; b2p+=1; 349 | du_ptr += 4; dv_ptr += 4; 350 | } 351 | 352 | } 353 | 354 | { // other iteration - last line 355 | memcpy(f1+1, ((float*) hp), width_minus_1_sizeoffloat); 356 | memcpy(f2, du_ptr+1, width_minus_1_sizeoffloat); 357 | memcpy(f3, dv_ptr+1, width_minus_1_sizeoffloat); 358 | v4sf* hpl = (v4sf*) f1, *dur = (v4sf*) f2, *dvr = (v4sf*) f3; 359 | 360 | { // left block 361 | // do one iteration 362 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*b1p); 363 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*b2p); 364 | du_ptr[0] += omega*( a11p[0][0]*s1[0] + a12p[0][0]*s2[0] - du_ptr[0] ); 365 | dv_ptr[0] += omega*( a12p[0][0]*s1[0] + a22p[0][0]*s2[0] - dv_ptr[0] ); 366 | for(k=1;k<4;k++){ 367 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 368 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 369 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 370 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 371 | } 372 | // increment pointer 373 | hpl+=1; hp+=1; vpt+=1; a11p+=1; a12p+=1; a22p+=1; 374 | dur+=1; dvr+=1; dut+=1; dvt+=1; b1p+=1; b2p+=1; 375 | du_ptr += 4; dv_ptr += 4; 376 | } 377 | for(i=iterline;--i;){ 378 | // do one iteration 379 | const v4sf s1 = (*hp)*(*dur) + (*vpt)*(*dut) + (*b1p); 380 | const v4sf s2 = (*hp)*(*dvr) + (*vpt)*(*dvt) + (*b2p); 381 | for(k=0;k<4;k++){ 382 | const float B1 = hpl[0][k]*du_ptr[k-1] + s1[k]; 383 | const float B2 = hpl[0][k]*dv_ptr[k-1] + s2[k]; 384 | du_ptr[k] += omega*( a11p[0][k]*B1 + a12p[0][k]*B2 - du_ptr[k] ); 385 | dv_ptr[k] += omega*( a12p[0][k]*B1 + a22p[0][k]*B2 - dv_ptr[k] ); 386 | } 387 | // increment pointer 388 | hpl+=1; hp+=1; vpt+=1; a11p+=1; a12p+=1; a22p+=1; 389 | dur+=1; dvr+=1; dut+=1; dvt+=1; b1p+=1; b2p+=1; 390 | du_ptr += 4; dv_ptr += 4; 391 | } 392 | 393 | } 394 | } 395 | 396 | 397 | 398 | free(floatarray); 399 | 400 | } 401 | -------------------------------------------------------------------------------- /solver.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "image.h" 5 | 6 | // Perform n iterations of the sor_coupled algorithm for a system of the form as described in opticalflow.c 7 | void sor_coupled(image_t *du, image_t *dv, image_t *a11, image_t *a12, image_t *a22, const image_t *b1, const image_t *b2, const image_t *dpsis_horiz, const image_t *dpsis_vert, const int iterations, const float omega); 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/__init__.py -------------------------------------------------------------------------------- /tests/data/artificial/20_0/20_midl_minsize10.flo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/20_0/20_midl_minsize10.flo -------------------------------------------------------------------------------- /tests/data/artificial/20_0/img0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/20_0/img0.png -------------------------------------------------------------------------------- /tests/data/artificial/20_0/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/20_0/img1.png -------------------------------------------------------------------------------- /tests/data/artificial/48_0/48_midl.flo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/48_0/48_midl.flo -------------------------------------------------------------------------------- /tests/data/artificial/48_0/48_midl_minsize10.flo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/48_0/48_midl_minsize10.flo -------------------------------------------------------------------------------- /tests/data/artificial/48_0/img0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/48_0/img0.png -------------------------------------------------------------------------------- /tests/data/artificial/48_0/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/artificial/48_0/img1.png -------------------------------------------------------------------------------- /tests/data/sintel1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/sintel1.png -------------------------------------------------------------------------------- /tests/data/sintel2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/sintel2.png -------------------------------------------------------------------------------- /tests/data/sintel_cmd.flo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge-m/deepflow/c3dee90e4acad72cc1b5557af360bbc41fd21944/tests/data/sintel_cmd.flo -------------------------------------------------------------------------------- /tests/test_fastdeepflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import unittest 4 | from py_fastdeepflow import fastdeepflow 5 | import cv2 6 | import os 7 | import numpy 8 | import ctypes 9 | 10 | path_dir_data = os.path.join(os.path.dirname(__file__), 'data/') 11 | 12 | class TestStringMethods(unittest.TestCase): 13 | 14 | 15 | def test_image_reading(self): 16 | img = fastdeepflow.lib.color_image_load(path_dir_data + 'sintel1.png') 17 | i = fastdeepflow.numpy_from_color_image_t(img.contents) 18 | self.assertTrue(i.shape == (436, 1024, 3)) 19 | 20 | def test1(self): 21 | img0 = cv2.imread(path_dir_data + 'sintel1.png')[:,:,::-1] 22 | img1 = cv2.imread(path_dir_data + 'sintel2.png')[:,:,::-1] 23 | 24 | def test_params(self): 25 | params = fastdeepflow.optical_flow_params_t() 26 | fastdeepflow.lib.optical_flow_params_default(ctypes.byref(params)) 27 | self.assertTrue(params.n_solver_iteration==25) 28 | self.assertTrue(abs(params.sor_omega-1.6) < 1e-5) 29 | 30 | def test_of(self): 31 | img0 = cv2.imread(path_dir_data + 'sintel1.png')[:,:,::-1] 32 | img1 = cv2.imread(path_dir_data + 'sintel2.png')[:,:,::-1] 33 | u, v = fastdeepflow.calc_flow(img0, img1) 34 | u1, v1 = fastdeepflow.read_flow(path_dir_data + 'sintel_cmd.flo') 35 | self.assertTrue(numpy.allclose(u, u1, atol=1e-8)) 36 | self.assertTrue(numpy.allclose(v, v1, atol=1e-8)) 37 | 38 | def test_wrap(self): 39 | img0 = cv2.imread(path_dir_data + 'sintel1.png')[:,:,::-1] 40 | img1 = cv2.imread(path_dir_data + 'sintel2.png')[:,:,::-1] 41 | u, v = fastdeepflow.calc_flow(img0, img1) 42 | img0warped = fastdeepflow.warp_image(img0, u, v) 43 | img1warped = fastdeepflow.warp_image(img1, u, v) 44 | 45 | d01 = numpy.sum(numpy.abs(img0-img1.astype('float32'))) 46 | d0w1= numpy.sum(numpy.abs(img0warped-img1)) 47 | d01w= numpy.sum(numpy.abs(img0-img1warped)) 48 | print "d01 {}, d0w1 {}, d01w {}".format(d01, d0w1, d01w) 49 | self.assertTrue(d01wd01) 51 | 52 | 53 | def test_wrap_grayscale(self): 54 | img0 = cv2.imread(path_dir_data + 'sintel1.png')[:,:,::-1] 55 | img1 = cv2.imread(path_dir_data + 'sintel2.png')[:,:,::-1] 56 | u, v = fastdeepflow.read_flow(path_dir_data + 'sintel_cmd.flo') 57 | img1warped = fastdeepflow.warp_image(img1, u, v) 58 | 59 | z = [fastdeepflow.warp_image(img1[:,:,k], u, v) for k in range(3)] 60 | zz = numpy.dstack(z, ) 61 | 62 | 63 | self.assertTrue(numpy.allclose(img1warped, zz, atol=1e-8)) 64 | 65 | 66 | class ReadyFlow(unittest.TestCase): 67 | def setUp(self): 68 | self.u, self.v = fastdeepflow.read_flow(path_dir_data + 'sintel_cmd.flo') 69 | self.assertTrue(self.u.max()-self.u.min()>5) 70 | 71 | 72 | class TestIO(ReadyFlow): 73 | def test_read_write(self): 74 | path_tmp_flow = os.path.join(path_dir_data, 'flow_read_write_test.flo') 75 | if os.path.exists(path_tmp_flow): 76 | os.remove(path_tmp_flow) 77 | self.assertFalse(os.path.exists(path_tmp_flow)) 78 | 79 | self.assertEqual(self.u.shape, self.v.shape) 80 | 81 | fastdeepflow.write_flow(path_tmp_flow, self.u, self.v) 82 | self.assertTrue(os.path.exists(path_tmp_flow)) 83 | u_loaded, v_loaded = fastdeepflow.read_flow(path_tmp_flow) 84 | self.assertTrue(numpy.array_equal(self.u, u_loaded)) 85 | self.assertTrue(numpy.array_equal(self.v, v_loaded)) 86 | 87 | 88 | class TestExceptions(ReadyFlow): 89 | def test_read_write(self): 90 | with self.assertRaises(Exception): 91 | fastdeepflow.write_flow('123123/123/123123', self.u, self.v) 92 | 93 | with self.assertRaises(Exception): 94 | res = fastdeepflow.read_flow('123123/234234') 95 | 96 | 97 | if __name__ == '__main__': 98 | unittest.main() 99 | -------------------------------------------------------------------------------- /tests/test_parameters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import unittest 4 | from py_fastdeepflow import fastdeepflow 5 | import cv2 6 | import os 7 | import numpy 8 | import ctypes 9 | 10 | path_dir_data = os.path.join(os.path.dirname(__file__), 'data/') 11 | 12 | 13 | def imread(path): 14 | """Read image from file 15 | 16 | param: path path to source image 17 | 18 | return: image as numpy array 19 | """ 20 | img = cv2.imread(path) 21 | 22 | if img is None: 23 | raise Exception("Unable to open '{}'".format(path)) 24 | 25 | if img.ndim != 1 and img.ndim != 3: 26 | raise Exception("Unsupported number of dimensions") 27 | 28 | if img.ndim == 3 and img.shape[-1] == 3: 29 | img = img[:,:,::-1] 30 | 31 | return img 32 | 33 | 34 | class TestParameters(unittest.TestCase): 35 | def prepare(self, dir_name, path_flow_reference): 36 | 37 | img0 = imread(os.path.join(path_dir_data, 'artificial/%s/img0.png' % dir_name)) 38 | img1 = imread(os.path.join(path_dir_data, 'artificial/%s/img1.png' % dir_name)) 39 | 40 | flow_reference = fastdeepflow.read_flow(os.path.join(path_dir_data, ('artificial/%s/' + path_flow_reference) % dir_name)) 41 | 42 | return img0, img1, flow_reference 43 | 44 | def test_middleburry(self): 45 | img0, img1, flow_reference = self.prepare(dir_name='48_0', path_flow_reference='48_midl.flo') 46 | 47 | params = fastdeepflow.optical_flow_params_t() 48 | fastdeepflow.lib.optical_flow_params_middlebury(ctypes.byref(params)) 49 | flow = fastdeepflow.calc_flow(img0, img1, params) 50 | 51 | self.assertTrue(numpy.array_equal(numpy.array(flow), numpy.array(flow_reference))) 52 | 53 | def test_middleburry__min_size_10(self): 54 | img0, img1, flow_reference = self.prepare(dir_name='48_0', path_flow_reference='48_midl_minsize10.flo') 55 | 56 | params = fastdeepflow.optical_flow_params_t() 57 | fastdeepflow.lib.optical_flow_params_middlebury(ctypes.byref(params)) 58 | params.min_size = 10 59 | flow = fastdeepflow.calc_flow(img0, img1, params) 60 | 61 | self.assertTrue(numpy.array_equal(numpy.array(flow), numpy.array(flow_reference))) 62 | 63 | def test_preset_loader(self): 64 | img0, img1, flow_reference = self.prepare(dir_name='48_0', path_flow_reference='48_midl_minsize10.flo') 65 | 66 | params = fastdeepflow.create_params("middlebury") 67 | params.min_size = 10 68 | flow = fastdeepflow.calc_flow(img0, img1, params) 69 | 70 | self.assertTrue(numpy.array_equal(numpy.array(flow), numpy.array(flow_reference))) 71 | 72 | if __name__ == '__main__': 73 | unittest.main() 74 | --------------------------------------------------------------------------------