├── cow.png ├── man.png ├── bungee.png ├── cow-out.png ├── cow-mask.png ├── man-mask.png ├── bungee-mask.png ├── test.m ├── README.md └── patch_inpaint.m /cow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/cow.png -------------------------------------------------------------------------------- /man.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/man.png -------------------------------------------------------------------------------- /bungee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/bungee.png -------------------------------------------------------------------------------- /cow-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/cow-out.png -------------------------------------------------------------------------------- /cow-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/cow-mask.png -------------------------------------------------------------------------------- /man-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/man-mask.png -------------------------------------------------------------------------------- /bungee-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PetterS/patch-inpainting/HEAD/bungee-mask.png -------------------------------------------------------------------------------- /test.m: -------------------------------------------------------------------------------- 1 | clear all; 2 | close all; 3 | clc; 4 | 5 | 6 | image = 'cow'; 7 | % image = 'bungee'; 8 | % image = 'man'; 9 | 10 | Aorg = imread([image '.png']); 11 | Morg = imread([image '-mask.png']); 12 | 13 | tic 14 | A=patch_inpaint(Aorg,Morg); 15 | toc 16 | 17 | imshow(A) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Example output](cow-out.png) 2 | 3 | The source code is written by Petter Strandmark and may be freely distributed. 4 | 5 | This inpainting tool uses Coherency Sensitive Hashing. 6 | You can download a Matlab toolbox at http://www.eng.tau.ac.il/~simonk/CSH/index.html 7 | 8 | The "man" image is from the Berkeley segmentation database. 9 | http://www.eecs.berkeley.edu/Research/Projects/CS/vision/bsds/ 10 | @InProceedings{MartinFTM01, 11 | author = {D. Martin and C. Fowlkes and D. Tal and J. Malik}, 12 | title = {A Database of Human Segmented Natural Images and its 13 | Application to Evaluating Segmentation Algorithms and 14 | Measuring Ecological Statistics}, 15 | booktitle = {Proc. 8th Int'l Conf. Computer Vision}, 16 | year = {2001}, 17 | month = {July}, 18 | volume = {2}, 19 | pages = {416--423} 20 | } 21 | 22 | The "cow" image appears at http://www.irishviews.com/cows.html where it is listed as free for personal use. 23 | 24 | I am not aware of the original author of the "bungee" image. It is a standard test image for inpainting. 25 | -------------------------------------------------------------------------------- /patch_inpaint.m: -------------------------------------------------------------------------------- 1 | % I = PATCH_INPAINT(Iorg, Mask, verbose) inpaints an image 2 | % 3 | % Iorg - original image 4 | % Mask - replacement mask; pixels where mask==1 will be inpainted 5 | % 6 | % I - inpainted image 7 | function A = patch_inpaint(Aorg,Morg, verbose, sigma) 8 | 9 | if nargin < 3 10 | verbose = true; 11 | end 12 | if nargin < 4 13 | sigma = 0.1; 14 | end 15 | 16 | if ~exist('CSH_nn.m','file') 17 | error('CSH_nn not found. Please download and add to path: http://www.eng.tau.ac.il/~simonk/CSH/index.html'); 18 | end 19 | 20 | 21 | LAB = @(I) applycform(I,makecform('srgb2lab')); 22 | RGB = @(I) applycform(I,makecform('lab2srgb')); 23 | 24 | Aorg = LAB(Aorg); 25 | 26 | width = 8; 27 | csh_iterations = 5; 28 | k = 1; 29 | calcBnn = 0; 30 | 31 | diffthresh = 10; 32 | 33 | % Also accept RGB masks, but only use first channel 34 | if size(Morg,3) > 1 35 | Morg = Morg(:,:,1); 36 | end 37 | 38 | % Determinte starting scale 39 | [m n rgb] = size(Aorg); 40 | startscale = -ceil(log2(min(m,n))) + 5; 41 | scale = 2^(startscale); 42 | 43 | % Resize image to starting scale 44 | A = imresize(Aorg,scale); 45 | M = imresize(Morg,scale); 46 | M(M>0)=1; 47 | M3 = repmat(M,[1 1 3])==1; 48 | 49 | % Random starting guess for inpainted image 50 | [m n rgb] = size(A); 51 | Rnd = uint8(255*rand(m,n,rgb)); 52 | A(M3) = Rnd(M3); 53 | 54 | % Go through all scales 55 | for logscale = startscale:0 56 | 57 | scale = 2^(logscale); 58 | 59 | % Maximum number of iterations on this scale; 60 | % oscillations are possible 61 | iterations = 30; 62 | 63 | if verbose 64 | fprintf('Scale = 2^%d\n',logscale); 65 | end 66 | 67 | for iter = 1:iterations 68 | if verbose 69 | fprintf(' Iteration %2d/%2d',iter,iterations); 70 | imshow(RGB(A)); 71 | pause(0.001) 72 | end 73 | 74 | B = A; 75 | B(M3)=0; 76 | 77 | % Compute NN field 78 | CSH_ann = CSH_nn(A,B,width,csh_iterations,k,calcBnn,M); 79 | 80 | % Now be work in double precision 81 | A = double(A)./255; 82 | 83 | % Create new image by letting each patch vote 84 | R = zeros(size(A)); 85 | Rcount = zeros(m,n); 86 | for i = 1:m 87 | for j = 1:n 88 | if M(i,j) == 1 89 | if 1<=i && i+width-1<=m && 1<=j && j+width-1<=n 90 | patch = A(i:i+width-1,j:j+width-1,:); 91 | i2 = CSH_ann(i,j,2); 92 | j2 = CSH_ann(i,j,1); 93 | patch2 = A(i2:i2+width-1,j2:j2+width-1,:); 94 | 95 | d = sum( (patch(:)-patch2(:)).^2 ); 96 | sim = exp( -d / (2*sigma^2) ); 97 | 98 | pi = i:i+width-1; 99 | pj = j:j+width-1; 100 | R(pi,pj,:) = R(pi,pj,:) + sim*patch2; 101 | Rcount(pi,pj) = Rcount(pi,pj) + sim; 102 | end 103 | end 104 | end 105 | end 106 | 107 | % Normalize and 108 | Rcount = repmat(Rcount,[1 1 3]); 109 | R(Rcount>0) = R(Rcount>0) ./ Rcount(Rcount>0); 110 | % Keep pixels outside mask 111 | R(~M3)=A(~M3); 112 | % Convert back to uint8 113 | Aprev = 255*A; 114 | A = uint8(255*R); 115 | 116 | if iter>1 117 | % Measure how much image has changed 118 | diff = sum( (double(A(:))-double(Aprev(:))).^2 ) / sum(M(:)>0); 119 | if verbose 120 | fprintf(' diff = %f\n',diff); 121 | end 122 | % Stop iterating if change is low 123 | if diff < diffthresh 124 | break; 125 | end 126 | elseif verbose 127 | fprintf('\n'); 128 | end 129 | end 130 | 131 | %Upsample A for the next scale 132 | if logscale < 0 133 | Adata = imresize(Aorg,2*scale); 134 | [m n rgb] = size(Adata); 135 | A = imresize(A,[m n]); 136 | 137 | M = imresize(Morg,[m n]); 138 | M(M>0)=1; 139 | M3 = repmat(M,[1 1 3])==1; 140 | 141 | % Outside mask, A is equal to original image 142 | A(~M3) = Adata(~M3); 143 | end 144 | end 145 | 146 | A = RGB(A); 147 | --------------------------------------------------------------------------------