├── .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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
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