├── .gitignore ├── testpatchmatch.m ├── make.m ├── README.md ├── patchmatch.m ├── LICENSE ├── visualizer.m ├── patchmatch.cbp └── patchmatch.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.obj 2 | *.o 3 | -------------------------------------------------------------------------------- /testpatchmatch.m: -------------------------------------------------------------------------------- 1 | % test_patchmatch.m 2 | clear; % make; 3 | clc; clear; 4 | disp('------------------------------------'); 5 | img = double(imresize(imread('lena.png'),0.25)); 6 | patchsize = 5; nn = 10; 7 | tic; 8 | cnn = patchmatch(img, [], patchsize, nn, 20, [], 1, [], [], [], 2); 9 | toc; 10 | 11 | visualizer(img, cnn, patchsize); 12 | 13 | 14 | -------------------------------------------------------------------------------- /make.m: -------------------------------------------------------------------------------- 1 | function make() 2 | disp('Compiling...'); 3 | p = input('Parallel?(Y/N)','s'); 4 | if(strcmpi(p,'y')==1) 5 | if(exist('OCTAVE_VERSION')) 6 | disp('Compile: P'); 7 | str = ' -D__PARALLEL__'; 8 | else 9 | disp('Compile: P'); 10 | str = ' -largeArrayDims CXXFLAGS="\$CXXFLAGS -fopenmp" LDFLAGS="\$LDFLAGS -fopenmp" -D__PARALLEL__'; 11 | end 12 | else 13 | disp('Compile: NP'); 14 | str = ' -largeArrayDims'; 15 | end 16 | 17 | eval(['mex patchmatch.cpp' str]); 18 | disp('Done.'); 19 | end 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | patchmatch 2 | ========== 3 | 4 | BruteForce Image Patchmatch 5 | 6 | This library is a partial implementation of the method proposed in the article "Symmetric Smoothing Filters from Global Consistency Constraints" (Sk. Mohammadul Haque, Gautam Pai, Venu Madhav Govindu), In IEEE Transactions on Image Processing, 2014. 7 | 8 | 9 | If you use this library, please cite the following article. 10 | 11 | > 12 | @article{symm, 13 | author = "Haque, Sk. Mohammadul and Pai, Gautam and Govindu, Venu Madhav", 14 | title = "Symmetric Smoothing Filters from Global Consistency Constraints", 15 | journal = "IEEE Transactions on Image Processing", 16 | year = "2014", 17 | url = { http://www.ee.iisc.ernet.in/labs/cvl/papers/consistency.pdf } 18 | } 19 | > 20 | 21 | 22 | Visit https://mohammadul.github.io/patchmatch for more details. 23 | -------------------------------------------------------------------------------- /patchmatch.m: -------------------------------------------------------------------------------- 1 | %PATCHMATCH - Matches patches in an image and gives an NN-field 2 | % nnf = patchmatch(img_src, img_dst, patchsize, nnk, searchradius, mask, searchstep, includeself, incomplete, threshold, disttype) 3 | % img_src - input source image (RGB/Grayscale in double datatype) 4 | % img_dst - input destination image (RGB/Grayscale in double datatype) (default - img_src) 5 | % patchsize - size of search patch (default - [5 5]) 6 | % nnk - number of nearest neighbours (default - 5) 7 | % searchradius - patch search radius (default - 10) 8 | % mask - patch distance weight mask (size - patchsize X patchsize)(default - ones) 9 | % searchstep - patch search step (default - 2) 10 | % includeself - 0/1 (default - 1) 11 | % incomplete - allow nan values in img - 0/1 (default - 0) 12 | % threshold - threshold for incomplete image input (default - 0) 13 | % disttype - distance type, 0 - LARK / 1 - l1 / 2 - l2 / 3 - poisson (default - 2) (LARK not completely implemented) 14 | % nnf - output nearest neighbour field 15 | % 16 | % Author: Sk. Mohammadul Haque 17 | % 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Sk Mohammadul Haque 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /visualizer.m: -------------------------------------------------------------------------------- 1 | function visualizer(img, cnn, patchsize) 2 | %VISUALIZER - Visualizes the Similarity 3 | % visualizer(img, cnn, patchsize) 4 | % img - input image (default - 'img' from workspace) 5 | % cnn - nn-field (default - 'cnn' from workspace) 6 | % patchsize - patchsize (default - 'patchsize' from workspace) 7 | % 8 | % Author: Sk. Mohammadul Haque 9 | % Copyright (c) 2013 Sk. Mohammadul Haque 10 | % 11 | 12 | 13 | ok = 0; 14 | h2 = 0; 15 | 16 | if(nargin<3), patchsize = evalin('base','patchsize'); end; 17 | if(nargin<2), cnn = evalin('base','cnn'); end; 18 | if(nargin<1), img = evalin('base','img'); end; 19 | h = figure; 20 | imshow(uint8(img)); 21 | sz = size(img); 22 | 23 | if(numel(patchsize)==1), patchsize = [patchsize patchsize]; end; 24 | 25 | while true 26 | try 27 | % try getting 28 | while(~ok) 29 | figure(h); 30 | [y, x] = ginput(1); 31 | x = fix(x); y = fix(y); 32 | if(x>0 && x<=(sz(1)-patchsize(1)) && y>0 && y<=(sz(2)-patchsize(2))) 33 | ok = 1; 34 | end 35 | end 36 | 37 | hold off; 38 | figure(h); 39 | for k = find(h2~=0) 40 | delete(h2(k,1)); 41 | end 42 | hold on; 43 | 44 | % get indices 45 | nx = squeeze(full(cnn(x,y,1,:)))+1; 46 | ny = squeeze(full(cnn(x,y,2,:)))+1; 47 | nd = squeeze(full(cnn(x,y,3,:))); 48 | 49 | % now draw 50 | figure(h); 51 | h2 = zeros(length(nd),1); 52 | [~,ndi] = sort(nd); 53 | ndim = max(ndi); 54 | for k = 1:length(nd) 55 | color = [(1-(find(ndi==k,1,'first')/ndim)^0.6) 0.15 0.15]; 56 | h2(k,1) = rectangle('Position',[nx(k) ny(k) patchsize(2) patchsize(1)],'LineWidth', 2,'EdgeColor', color); 57 | end 58 | 59 | % reset 60 | ok = 0; 61 | catch dummy 62 | return; 63 | end 64 | 65 | end 66 | end 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /patchmatch.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 65 | 66 | -------------------------------------------------------------------------------- /patchmatch.cpp: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | ** This software is furnished "as is", without technical support, 3 | ** and with no warranty, express or implied, as to its usefulness for 4 | ** any purpose. 5 | ** 6 | ** patchmatch.cpp 7 | ** Matches patches between images 8 | ** 9 | ** Author: Sk. Mohammadul Haque 10 | ** Copyright (c) 2013, 2022 Sk. Mohammadul Haque 11 | ** -------------------------------------------------------------------------*/ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #ifdef __PARALLEL__ 21 | #include 22 | #endif 23 | using namespace std; 24 | 25 | #ifndef FMT64 26 | #define FMT64 "" 27 | #endif 28 | 29 | #define DATA_TYPE int64_t 30 | #define LNEG_VAL (INT_MIN) 31 | #define LPOS_VAL (INT_MAX) 32 | 33 | #define IS_REAL_2D_FULL_DOUBLE(P) (!mxIsComplex(P) && \ 34 | mxGetNumberOfDimensions(P)==2 && !mxIsSparse(P) && mxIsDouble(P)) 35 | #define IS_REAL_2D_OR_3D_FULL_DOUBLE(P) (!mxIsComplex(P) && \ 36 | (mxGetNumberOfDimensions(P)==2 || mxGetNumberOfDimensions(P)==3) && !mxIsEmpty(P) && !mxIsSparse(P) && mxIsDouble(P)) 37 | #define IS_REAL_SCALAR(P) (IS_REAL_2D_FULL_DOUBLE(P) && mxGetNumberOfElements(P)==1) 38 | #define NNF_OUT plhs[0] 39 | #define IMG_SRC_IN prhs[0] 40 | #define IMG_DST_IN prhs[1] 41 | #define PATCHSIZE_IN prhs[2] 42 | #define NNK_IN prhs[3] 43 | #define SEARCHRADIUS_IN prhs[4] 44 | #define MASK_IN prhs[5] 45 | #define SEARCHSTEP_IN prhs[6] 46 | #define INCLUDESELF_IN prhs[7] 47 | #define INCOMPLETE_IN prhs[8] 48 | #define THRESHOLD_IN prhs[9] 49 | #define DISTTYPE_IN prhs[10] 50 | 51 | struct pxpair 52 | { 53 | DATA_TYPE x, y; 54 | }; 55 | 56 | #ifndef isnan 57 | #define isnan(x) ((x)!=(x)) 58 | #endif 59 | 60 | #ifndef min 61 | #define min(x, y) ((x)<(y)?(x):(y)) 62 | #endif 63 | 64 | /* (-0.06432142068211566+(s)*1.522888134780505+(s)*(s)*-0.4851696063969466) */ 65 | 66 | template 67 | __inline T gapprox(T s) /* gamma approximated */ 68 | { 69 | return (-2.061134883172860/((s-0.042820630947894)*(s-3.09605722681828))); 70 | } 71 | 72 | /* (-1.793304391995467+(s)*2.651503596311144+(s)*(s)*-0.8599428008731907) */ 73 | 74 | template 75 | __inline T lapprox(T s) /* log approximated */ 76 | { 77 | return (-0.85994280087319*(s-1.00187482340903)*(s-2.0814739686488)); 78 | } 79 | 80 | typedef vector::const_iterator vecintiter; 81 | typedef vector::const_iterator vecdoubleiter; 82 | struct ordering 83 | { 84 | bool operator ()(pair const& a, pair const& b) 85 | { 86 | return *(a.second)<*(b.second); 87 | } 88 | }; 89 | 90 | template 91 | vector sort_from_ref( vector const& in, vector::const_iterator > > const& reference) 92 | { 93 | vector ret(in.size()); 94 | DATA_TYPE const size = in.size(); 95 | for (DATA_TYPE i=0; i 114 | __inline double blockmatch( 115 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 116 | const double *const * const mask, const double * const img_src, const double * const img_dst, 117 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 118 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 119 | { 120 | return 0; 121 | } 122 | 123 | template <> 124 | __inline double blockmatch( 125 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 126 | const double *const * const mask, const double * const img_src, const double * const img_dst, 127 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 128 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 129 | { 130 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti; 131 | double dist = 0.0; 132 | for(j=0; j 147 | __inline double blockmatch( 148 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 149 | const double *const * const mask, const double * const img_src, const double * const img_dst, 150 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 151 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 152 | { 153 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti, numels_src = imgsM*imgsN, numels_dst = imgtM*imgtN; 154 | double dist = 0.0; 155 | /* channel R */ 156 | for(j=0; j 196 | __inline double blockmatch( 197 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 198 | const double *const * const mask, const double * const img_src, const double * const img_dst, 199 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 200 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 201 | { 202 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti; 203 | double dist = 0.0; 204 | for(j=0; j 219 | __inline double blockmatch( 220 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 221 | const double *const * const mask, const double * const img_src, const double * const img_dst, 222 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 223 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 224 | { 225 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti, numels_src = imgsM*imgsN, numels_dst = imgtM*imgtN; 226 | double dist = 0.0; 227 | /* channel R */ 228 | for(j=0; j 268 | __inline double blockmatch( 269 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 270 | const double *const * const mask, const double * const img_src, const double * const img_dst, 271 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 272 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 273 | { 274 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti; 275 | double dist = 0.0; 276 | for(j=0; j 290 | __inline double blockmatch( 291 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 292 | const double *const * const mask, const double * const img_src, const double * const img_dst, 293 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 294 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 295 | { 296 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti, numels_src = imgsM*imgsN, numels_dst = imgtM*imgtN; 297 | double dist = 0.0; 298 | /* channel R */ 299 | for(j=0; j 338 | __inline double blockmatch( 339 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 340 | const double *const * const mask, const double * const img_src, const double * const img_dst, 341 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 342 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 343 | { 344 | DATA_TYPE i, j, src_idx = sj*imgsM+si, p_m_one[2] = {patchsize[0]-1, patchsize[1]-1}; 345 | double dist = 0.0, tempxx, tempxy, tempyy; 346 | double s_C[3] = {0,0,0}; 347 | 348 | //calculate Cs 349 | src_idx = sj*imgsM+si; 350 | 351 | for(j=0; j(si-ti); tempxx *= tempxx; 368 | tempyy = static_cast(sj-tj); tempyy *= tempyy; 369 | tempxy = static_cast(si-ti); tempxy *= static_cast(sj-tj); 370 | 371 | 372 | dist = s_C[0]*tempxx+s_C[1]*tempyy+2*s_C[2]*tempxy; 373 | //mexPrintf("(si, sj) = (%d, %d), (ti, tj) = (%d, %d),|| i = %d, j= %d, || sval = %f, tval = %f, dist = %f\n", si,sj,ti,tj, i,j,img[src_idx+i],img[tgt_idx+i], (img[src_idx+i]-img[tgt_idx+i])*(img[src_idx+i]-img[tgt_idx+i])); 374 | return dist; 375 | } 376 | 377 | template <> 378 | __inline double blockmatch( 379 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 380 | const double *const * const mask, const double * const img_src, const double * const img_dst, 381 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 382 | const DATA_TYPE imgtM, const DATA_TYPE imgtN) 383 | { 384 | DATA_TYPE i, j, src_idx = sj*imgsM+si, p_m_one[2] = {patchsize[0]-1,patchsize[1]-1}, numels = imgsM*imgsN; 385 | double dist = 0.0, tempxx, tempxy, tempyy; 386 | double s_C[3][3] = {{0,0,0},{0,0,0},{0,0,0}}; 387 | 388 | //for R channel 389 | 390 | //calculate Cs 391 | for(j=0; j(si-ti); tempxx *= tempxx; 447 | tempyy = static_cast(sj-tj); tempyy *= tempyy; 448 | tempxy = static_cast(si-ti); tempxy *= static_cast(sj-tj); 449 | 450 | dist = (s_C[0][0]+s_C[1][0]+s_C[2][0])*tempxx+(s_C[0][1]+s_C[1][1]+s_C[2][1])*tempyy+2*(s_C[0][2]+s_C[1][2]+s_C[2][2])*tempxy; 451 | //mexPrintf("(si, sj) = (%d, %d), (ti, tj) = (%d, %d),|| i = %d, j= %d, || sval = %f, tval = %f, dist = %f\n", si,sj,ti,tj, i,j,img[src_idx+i],img[tgt_idx+i], (img[src_idx+i]-img[tgt_idx+i])*(img[src_idx+i]-img[tgt_idx+i])); 452 | 453 | return dist; 454 | } 455 | 456 | 457 | template 458 | void func(const double * const * const mask, const double * const img_src, const double * const img_dst, 459 | const DATA_TYPE * const patchsize, const DATA_TYPE * const IMG_DIMS_SRC, 460 | const DATA_TYPE * const IMG_DIMS_DST, int32_t *nnf, const DATA_TYPE searchradius, 461 | const DATA_TYPE searchstep, const bool includeself, const int nnk) 462 | { 463 | 464 | const DATA_TYPE max_X = IMG_DIMS_SRC[0]-patchsize[0]; 465 | const DATA_TYPE max_Y = IMG_DIMS_SRC[1]-patchsize[1]; 466 | const DATA_TYPE nelems = IMG_DIMS_SRC[0]*IMG_DIMS_SRC[1]; 467 | 468 | #ifdef __PARALLEL__ 469 | #pragma omp parallel for shared(IMG_DIMS_SRC, IMG_DIMS_DST, nnf, img_src, img_dst, searchradius, patchsize, searchstep, mask, nelems) 470 | #endif 471 | 472 | for(DATA_TYPE sj=0; sj<=max_Y; ++sj) 473 | { 474 | for(DATA_TYPE si=0; si<=max_X; ++si) 475 | { 476 | DATA_TYPE startx, starty, endx, endy; 477 | DATA_TYPE Ms = IMG_DIMS_SRC[0], Ns = IMG_DIMS_SRC[1]; 478 | DATA_TYPE Mt = IMG_DIMS_DST[0], Nt = IMG_DIMS_DST[1]; 479 | vector pxall; 480 | vector distall; 481 | double curr_dist; 482 | 483 | startx = ((si*IMG_DIMS_DST[0])/IMG_DIMS_SRC[0])-searchradius; 484 | startx = (startx>0)?startx: 0; 485 | starty = ((sj*IMG_DIMS_DST[1])/IMG_DIMS_SRC[1])-searchradius; 486 | starty = (starty>0)?starty: 0; 487 | 488 | endx = ((si*IMG_DIMS_DST[0])/IMG_DIMS_SRC[0])+searchradius; 489 | endx = (endx<(Mt-patchsize[0]))?endx: (Mt-patchsize[0]); 490 | endy = ((sj*IMG_DIMS_DST[1])/IMG_DIMS_SRC[1])+searchradius; 491 | endy = (endy<(Nt-patchsize[1]))?endy: (Nt-patchsize[1]); 492 | 493 | for(DATA_TYPE nj = starty; nj<=endy; nj+=searchstep) 494 | { 495 | for(DATA_TYPE ni = startx; ni<=endx; ni+=searchstep) 496 | { 497 | if(si!=ni || sj!=nj||includeself) 498 | { 499 | curr_dist = blockmatch( 500 | si, sj, ni, nj, mask, img_src, 501 | img_dst, patchsize, Ms, Ns, Mt, Nt); 502 | //mexPrintf("Source: (%Ld, %Ld) || Target: (%Ld, %Ld) || Dist: %f\n", si, sj, ni, nj, curr_dist); 503 | pxpair curr_px = {ni, nj}; 504 | pxall.push_back(curr_px); 505 | distall.push_back(curr_dist); 506 | } 507 | } 508 | } 509 | 510 | //sort and take 1st few 511 | vector > order(distall.size()); 512 | 513 | DATA_TYPE n = 0; 514 | for(vecdoubleiter it = distall.begin(); it != distall.end(); ++it, ++n) order[n] = make_pair(n, it); 515 | sort(order.begin(), order.end(), ordering()); 516 | 517 | vector sorted_distall = sort_from_ref(distall, order); 518 | vector sorted_pxall = sort_from_ref(pxall, order); 519 | DATA_TYPE lmax = (nnk(sorted_distall.size()))? nnk: static_cast(sorted_distall.size()); 520 | DATA_TYPE lidx = si+sj*IMG_DIMS_SRC[0]; 521 | for(DATA_TYPE l=0; l(sorted_distall[l]); 526 | } 527 | for(DATA_TYPE l=lmax; l 540 | __inline double blockmatch( 541 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 542 | const double *const * const mask, const double * const img_src, const double * const img_dst, 543 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 544 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 545 | { 546 | return 0; 547 | } 548 | 549 | template <> 550 | __inline double blockmatch( 551 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 552 | const double *const * const mask, const double * const img_src, const double * const img_dst, 553 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 554 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 555 | { 556 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti; 557 | double dist = 0.0, den = 0, temp; 558 | for(j=0; j(LPOS_VAL); 575 | return dist; 576 | } 577 | 578 | template <> 579 | __inline double blockmatch( 580 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 581 | const double *const * const mask, const double * const img_src, const double * const img_dst, 582 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 583 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 584 | { 585 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti, numels_src = imgsM*imgsN, numels_dst = imgtM*imgtN; 586 | double dist = 0.0, den = 0.0, temp; 587 | /* channel R */ 588 | for(j=0; j(LPOS_VAL); 640 | return dist; 641 | } 642 | 643 | 644 | template <> 645 | __inline double blockmatch( 646 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 647 | const double *const * const mask, const double * const img_src, const double * const img_dst, 648 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 649 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 650 | { 651 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti; 652 | double dist = 0.0, den = 0.0, temp; 653 | for(j=0; j(LPOS_VAL); 670 | return dist; 671 | } 672 | 673 | template <> 674 | __inline double blockmatch( 675 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 676 | const double *const * const mask, const double * const img_src, const double * const img_dst, 677 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 678 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 679 | { 680 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti, numels_src = imgsM*imgsN, numels_dst = imgtM*imgtN; 681 | double dist = 0.0, den = 0.0, temp; 682 | /* channel R */ 683 | for(j=0; j(LPOS_VAL); 735 | return dist; 736 | } 737 | 738 | 739 | template <> 740 | __inline double blockmatch( 741 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 742 | const double *const * const mask, const double * const img_src, const double * const img_dst, 743 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 744 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 745 | { 746 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti; 747 | double dist = 0.0, den = 0.0, temp; 748 | for(j=0; j(LPOS_VAL); 765 | return dist*256.0; 766 | } 767 | 768 | template <> 769 | __inline double blockmatch( 770 | const DATA_TYPE si, const DATA_TYPE sj, const DATA_TYPE ti, const DATA_TYPE tj, 771 | const double *const * const mask, const double * const img_src, const double * const img_dst, 772 | const DATA_TYPE * const patchsize, const DATA_TYPE imgsM, const DATA_TYPE imgsN, 773 | const DATA_TYPE imgtM, const DATA_TYPE imgtN, const double threshold) 774 | { 775 | DATA_TYPE i, j, src_idx = sj*imgsM+si, tgt_idx = tj*imgtM+ti, numels_src = imgsM*imgsN, numels_dst = imgtM*imgtN; 776 | double dist = 0.0, den = 0.0, temp; 777 | /* channel R */ 778 | for(j=0; j(LPOS_VAL); 830 | return dist*256.0; 831 | } 832 | 833 | template 834 | void func(const double * const * const mask, const double * const img_src, const double * const img_dst, 835 | const DATA_TYPE * const patchsize, const DATA_TYPE * const IMG_DIMS_SRC, 836 | const DATA_TYPE * const IMG_DIMS_DST, int32_t *nnf, const DATA_TYPE searchradius, 837 | const DATA_TYPE searchstep, const bool includeself, const int nnk, const double threshold) 838 | { 839 | const DATA_TYPE max_X = IMG_DIMS_SRC[0]-patchsize[0]; 840 | const DATA_TYPE max_Y = IMG_DIMS_SRC[1]-patchsize[1]; 841 | const DATA_TYPE nelems = IMG_DIMS_SRC[0]*IMG_DIMS_SRC[1]; 842 | 843 | #ifdef __PARALLEL__ 844 | #pragma omp parallel for shared(IMG_DIMS_SRC, IMG_DIMS_DST, nnf, img_src, img_dst, searchradius, patchsize, searchstep, mask, nelems, threshold) 845 | #endif 846 | 847 | for(DATA_TYPE sj=0; sj<=max_Y; ++sj) 848 | { 849 | for(DATA_TYPE si=0; si<=max_X; ++si) 850 | { 851 | DATA_TYPE startx, starty, endx, endy; 852 | DATA_TYPE Ms = IMG_DIMS_SRC[0], Ns = IMG_DIMS_SRC[1]; 853 | DATA_TYPE Mt = IMG_DIMS_DST[0], Nt = IMG_DIMS_DST[1]; 854 | vector pxall; 855 | vector distall; 856 | double curr_dist, thresh = threshold; 857 | 858 | startx = ((si*IMG_DIMS_DST[0])/IMG_DIMS_SRC[0])-searchradius; 859 | startx = (startx>0)?startx: 0; 860 | starty = ((sj*IMG_DIMS_DST[1])/IMG_DIMS_SRC[1])-searchradius; 861 | starty = (starty>0)?starty: 0; 862 | 863 | endx = ((si*IMG_DIMS_DST[0])/IMG_DIMS_SRC[0])+searchradius; 864 | endx = (endx<(Mt-patchsize[0]))?endx: (Mt-patchsize[0]); 865 | endy = ((sj*IMG_DIMS_DST[1])/IMG_DIMS_SRC[1])+searchradius; 866 | endy = (endy<(Nt-patchsize[1]))?endy: (Nt-patchsize[1]); 867 | 868 | for(DATA_TYPE nj=starty; nj<=endy; nj+=searchstep) 869 | { 870 | for(DATA_TYPE ni=startx; ni<=endx; ni+=searchstep) 871 | { 872 | if(si!=ni || sj!=nj||includeself) 873 | { 874 | curr_dist = blockmatch(si, sj, ni, nj, mask, img_src, img_dst, patchsize, Ms, Ns, Mt, Nt, thresh); 875 | //mexPrintf("Source: (%Ld, %Ld) || Target: (%Ld, %Ld) || Dist: %f\n", si, sj, ni, nj, curr_dist); 876 | pxpair curr_px = {ni, nj}; 877 | pxall.push_back(curr_px); 878 | distall.push_back(curr_dist); 879 | } 880 | } 881 | } 882 | 883 | //sort and take 1st few 884 | vector > order(distall.size()); 885 | 886 | DATA_TYPE n = 0; 887 | for(vecdoubleiter it = distall.begin(); it != distall.end(); ++it, ++n) order[n] = make_pair(n, it); 888 | sort(order.begin(), order.end(), ordering()); 889 | 890 | vector sorted_distall = sort_from_ref(distall, order); 891 | vector sorted_pxall = sort_from_ref(pxall, order); 892 | DATA_TYPE lmax = (nnk(sorted_distall.size()))? nnk: static_cast(sorted_distall.size()); 893 | DATA_TYPE lidx = si+sj*IMG_DIMS_SRC[0]; 894 | for(DATA_TYPE l=0; l(sorted_distall[l]); 899 | } 900 | for(DATA_TYPE l=lmax; l11) mexErrMsgTxt("Wrong number of input arguments."); 924 | else if(nlhs>1) mexErrMsgTxt("Too many output arguments."); 925 | 926 | /* check all arguments one by one if valid or set default if empty */ 927 | 928 | if(!IS_REAL_2D_OR_3D_FULL_DOUBLE(IMG_SRC_IN)||mxIsEmpty(IMG_SRC_IN)) mexErrMsgTxt("IMG_SRC must be a real 2D or 3D full array."); 929 | if(nrhs>1 && !IS_REAL_2D_OR_3D_FULL_DOUBLE(IMG_DST_IN)&& !mxIsEmpty(IMG_DST_IN)) mexErrMsgTxt("IMG_DST must be a real 2D or 3D full array."); 930 | 931 | if(nrhs<11) disttype = 2; 932 | else 933 | { 934 | if(mxIsEmpty(DISTTYPE_IN)) disttype = 2; 935 | else 936 | { 937 | if(!IS_REAL_SCALAR(DISTTYPE_IN)) mexErrMsgTxt("DISTTYPE must be 0, 1, 2 or 3."); 938 | disttype = static_cast(mxGetScalar(DISTTYPE_IN)); 939 | } 940 | } 941 | 942 | if(nrhs<10) threshold = 0.0; 943 | else 944 | { 945 | if(mxIsEmpty(THRESHOLD_IN)) threshold = false; 946 | else 947 | { 948 | if(!IS_REAL_SCALAR(THRESHOLD_IN)) mexErrMsgTxt("THRESHOLD must be 0 and sum of mask."); 949 | threshold = static_cast(mxGetScalar(THRESHOLD_IN)); 950 | } 951 | } 952 | 953 | if(nrhs<9) incomplete = false; 954 | else 955 | { 956 | if(mxIsEmpty(INCOMPLETE_IN)) incomplete = false; 957 | else 958 | { 959 | if(!IS_REAL_SCALAR(INCOMPLETE_IN)) mexErrMsgTxt("INCOMPLETE must be 0 or 1."); 960 | incomplete = static_cast(mxGetScalar(INCOMPLETE_IN)); 961 | } 962 | } 963 | 964 | if(nrhs<8) includeself = true; 965 | else 966 | { 967 | if(mxIsEmpty(INCLUDESELF_IN)) includeself = true; 968 | else 969 | { 970 | if(!IS_REAL_SCALAR(INCLUDESELF_IN)) mexErrMsgTxt("INCLUDESELF must be 0 or 1."); 971 | includeself = static_cast(mxGetScalar(INCLUDESELF_IN)); 972 | } 973 | } 974 | 975 | if(nrhs<7) searchstep = 2; 976 | else 977 | { 978 | if(mxIsEmpty(SEARCHSTEP_IN)) searchstep = 2; 979 | else 980 | { 981 | if(!IS_REAL_SCALAR(SEARCHSTEP_IN)|| (mxGetScalar(SEARCHSTEP_IN)<1.0)) mexErrMsgTxt("SEARCHSTEP must be real positive scalar integer."); 982 | searchstep = static_cast(mxGetScalar(SEARCHSTEP_IN)); 983 | } 984 | } 985 | 986 | if(nrhs<5) searchradius = 10; 987 | else 988 | { 989 | if(mxIsEmpty(SEARCHRADIUS_IN)) searchradius = 10; 990 | else 991 | { 992 | if(!IS_REAL_SCALAR(SEARCHRADIUS_IN)||mxGetScalar(SEARCHRADIUS_IN)<1.0) mexErrMsgTxt("SEARCHRADIUS must be real positive scalar integer or empty."); 993 | searchradius = static_cast(mxGetScalar(SEARCHRADIUS_IN)); 994 | } 995 | } 996 | 997 | if(nrhs<4) nnk = 5; 998 | else 999 | { 1000 | if(mxIsEmpty(NNK_IN)) nnk = 5; 1001 | else 1002 | { 1003 | if(!IS_REAL_SCALAR(NNK_IN)||mxGetScalar(NNK_IN)<1.0) mexErrMsgTxt("NNK must be real positive scalar integer or empty."); 1004 | nnk = static_cast(mxGetScalar(NNK_IN)); 1005 | } 1006 | } 1007 | 1008 | if(nrhs<3) 1009 | { 1010 | patchsize[0] = 5; 1011 | patchsize[1] = 5; 1012 | } 1013 | else 1014 | { 1015 | if(mxIsEmpty(PATCHSIZE_IN)) 1016 | { 1017 | patchsize[0] = 5; 1018 | patchsize[1] = 5; 1019 | } 1020 | else 1021 | { 1022 | if(!IS_REAL_SCALAR(PATCHSIZE_IN)||mxGetScalar(PATCHSIZE_IN)<1.0||(static_cast(mxGetScalar(PATCHSIZE_IN))%2==0)) 1023 | { 1024 | if(!IS_REAL_2D_FULL_DOUBLE(PATCHSIZE_IN)||(mxGetM(PATCHSIZE_IN)*mxGetN(PATCHSIZE_IN))!=2||(static_cast(mxGetPr(PATCHSIZE_IN)[0])%2==0)||(static_cast(mxGetPr(PATCHSIZE_IN)[1])%2==0)) mexErrMsgTxt("PATCHSIZE must be real positive scalar odd integer or 2 element odd integer matrix or empty."); 1025 | patchsize[0] = static_cast(mxGetPr(PATCHSIZE_IN)[0]); 1026 | patchsize[1] = static_cast(mxGetPr(PATCHSIZE_IN)[1]); 1027 | } 1028 | else 1029 | { 1030 | patchsize[0] = static_cast(mxGetScalar(PATCHSIZE_IN)); 1031 | patchsize[1] = patchsize[0]; 1032 | } 1033 | } 1034 | } 1035 | 1036 | /* get pointer to mask and its dimensions */ 1037 | /* ndims_src = mxGetNumberOfDimensions(MASK_IN); */ /* dont know why I added these unnecesssary lines */ 1038 | /* ndims_dst = mxGetNumberOfDimensions(MASK_IN); */ /* dont know why I added these unnecesssary lines */ 1039 | if(nrhs<6||mxIsEmpty(MASK_IN)) 1040 | { 1041 | mask = new double*[patchsize[0]]; 1042 | for(int i=0; i(IMG_DIMS_SRC_ORI[0]); 1068 | IMG_DIMS_SRC[1] = static_cast(IMG_DIMS_SRC_ORI[1]); 1069 | IMG_DIMS_SRC[2] = 1; 1070 | break; 1071 | case 3: 1072 | IMG_DIMS_SRC[0] = static_cast(IMG_DIMS_SRC_ORI[0]); 1073 | IMG_DIMS_SRC[1] = static_cast(IMG_DIMS_SRC_ORI[1]); 1074 | IMG_DIMS_SRC[2] = static_cast(IMG_DIMS_SRC_ORI[2]); 1075 | break; 1076 | default: 1077 | mexErrMsgTxt("IMG_SRC size error."); 1078 | break; 1079 | } 1080 | 1081 | if((patchsize[0]>IMG_DIMS_SRC[0])||(patchsize[1]>IMG_DIMS_SRC[1])) mexErrMsgTxt("PATCHSIZE size is greater than IMG_SRC size."); 1082 | 1083 | /* get pointer to image and its dimensions */ 1084 | if(nrhs<2) 1085 | { 1086 | img_dst = mxGetPr(IMG_SRC_IN); 1087 | IMG_DIMS_DST_ORI = mxGetDimensions(IMG_SRC_IN); 1088 | ndims_dst = mxGetNumberOfDimensions(IMG_SRC_IN); 1089 | } 1090 | else if(mxIsEmpty(IMG_DST_IN)) 1091 | { 1092 | img_dst = mxGetPr(IMG_SRC_IN); 1093 | IMG_DIMS_DST_ORI = mxGetDimensions(IMG_SRC_IN); 1094 | ndims_dst = mxGetNumberOfDimensions(IMG_SRC_IN); 1095 | } 1096 | else 1097 | { 1098 | img_dst = mxGetPr(IMG_DST_IN); 1099 | IMG_DIMS_DST_ORI = mxGetDimensions(IMG_DST_IN); 1100 | ndims_dst = mxGetNumberOfDimensions(IMG_DST_IN); 1101 | } 1102 | 1103 | switch(ndims_dst) 1104 | { 1105 | case 2: 1106 | IMG_DIMS_DST[0] = static_cast(IMG_DIMS_DST_ORI[0]); 1107 | IMG_DIMS_DST[1] = static_cast(IMG_DIMS_DST_ORI[1]); 1108 | IMG_DIMS_DST[2] = 1; 1109 | break; 1110 | case 3: 1111 | IMG_DIMS_DST[0] = static_cast(IMG_DIMS_DST_ORI[0]); 1112 | IMG_DIMS_DST[1] = static_cast(IMG_DIMS_DST_ORI[1]); 1113 | IMG_DIMS_DST[2] = static_cast(IMG_DIMS_DST_ORI[2]); 1114 | break; 1115 | default: 1116 | mexErrMsgTxt("IMG_DST size error."); 1117 | break; 1118 | } 1119 | 1120 | if((patchsize[0]>IMG_DIMS_DST[0])||(patchsize[1]>IMG_DIMS_DST[1])) mexErrMsgTxt("PATCHSIZE size is greater than IMG_DST size."); 1121 | 1122 | mexPrintf("Src Img size : %" FMT64 "d %" FMT64 "d %" FMT64 "d\n", IMG_DIMS_SRC[0], IMG_DIMS_SRC[1], IMG_DIMS_SRC[2]); 1123 | mexPrintf("Dst Img size : %" FMT64 "d %" FMT64 "d %" FMT64 "d\n", IMG_DIMS_DST[0], IMG_DIMS_DST[1], IMG_DIMS_DST[2]); 1124 | mexPrintf("Patch size : (%" FMT64 "d,%" FMT64 "d), Step size: %" FMT64 "d, Search radius: %" FMT64 "d\n", patchsize[0], patchsize[1], searchstep, searchradius); 1125 | if(IMG_DIMS_SRC[2]==1) mexPrintf("Type: Grayscale\n"); 1126 | if(IMG_DIMS_SRC[2]==3) mexPrintf("Type: Color\n"); 1127 | mexEvalString("drawnow;"); 1128 | 1129 | DATA_TYPE max_X = IMG_DIMS_SRC[0]-patchsize[0]; 1130 | DATA_TYPE max_Y = IMG_DIMS_SRC[1]-patchsize[1]; 1131 | mwSize nnfdims[4] = {static_cast(IMG_DIMS_SRC[0]), static_cast(IMG_DIMS_SRC[1]), 3, static_cast(nnk)}; 1132 | NNF_OUT = mxCreateNumericArray(4, nnfdims, mxINT32_CLASS, mxREAL); 1133 | nnf = reinterpret_cast(mxGetPr(NNF_OUT)); 1134 | DATA_TYPE nelems = IMG_DIMS_SRC[0]*IMG_DIMS_SRC[1]; 1135 | 1136 | if(!incomplete) 1137 | { 1138 | if(disttype==3) /* poisson */ 1139 | { 1140 | switch(IMG_DIMS_SRC[2]) 1141 | { 1142 | case 1: 1143 | func( 1144 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1145 | nnf, searchradius, searchstep, includeself, nnk); 1146 | break; 1147 | 1148 | case 3: 1149 | func( 1150 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1151 | nnf, searchradius, searchstep, includeself, nnk); 1152 | break; 1153 | 1154 | default: 1155 | { 1156 | // free mask memory created here before abnormal exit 1157 | for(int i=0; i( 1173 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1174 | nnf, searchradius, searchstep, includeself, nnk); 1175 | break; 1176 | 1177 | case 3: 1178 | func( 1179 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1180 | nnf, searchradius, searchstep, includeself, nnk); 1181 | break; 1182 | 1183 | default: 1184 | { 1185 | // free mask memory created here before abnormal exit 1186 | for(int i=0; i( 1202 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1203 | nnf, searchradius, searchstep, includeself, nnk); 1204 | break; 1205 | 1206 | case 3: 1207 | func( 1208 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1209 | nnf, searchradius, searchstep, includeself, nnk); 1210 | break; 1211 | 1212 | default: 1213 | { 1214 | // free mask memory created here before abnormal exit 1215 | for(int i=0; i( 1231 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1232 | nnf, searchradius, searchstep, includeself, nnk); 1233 | break; 1234 | 1235 | case 3: 1236 | func( 1237 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1238 | nnf, searchradius, searchstep, includeself, nnk); 1239 | break; 1240 | 1241 | default: 1242 | { 1243 | // free mask memory created here before abnormal exit 1244 | for(int i=0; i( 1310 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1311 | nnf, searchradius, searchstep, includeself, nnk, threshold); 1312 | break; 1313 | 1314 | case 3: 1315 | func( 1316 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1317 | nnf, searchradius, searchstep, includeself, nnk, threshold); 1318 | break; 1319 | 1320 | default: 1321 | { 1322 | // free mask memory created here before abnormal exit 1323 | for(int i=0; i( 1340 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1341 | nnf, searchradius, searchstep, includeself, nnk, threshold); 1342 | break; 1343 | 1344 | case 3: 1345 | func( 1346 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1347 | nnf, searchradius, searchstep, includeself, nnk, threshold); 1348 | break; 1349 | 1350 | default: 1351 | { 1352 | // free mask memory created here before abnormal exit 1353 | for(int i=0; i( 1369 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1370 | nnf, searchradius, searchstep, includeself, nnk, threshold); 1371 | break; 1372 | 1373 | case 3: 1374 | func( 1375 | mask, img_src, img_dst, patchsize, IMG_DIMS_SRC, IMG_DIMS_DST, 1376 | nnf, searchradius, searchstep, includeself, nnk, threshold); 1377 | break; 1378 | 1379 | default: 1380 | { 1381 | // free mask memory created here before abnormal exit 1382 | for(int i=0; i