001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.processing.restoration.inpainting; 031 032import java.util.Set; 033 034import org.openimaj.citation.annotation.Reference; 035import org.openimaj.citation.annotation.ReferenceType; 036import org.openimaj.image.FImage; 037import org.openimaj.image.Image; 038import org.openimaj.image.MBFImage; 039import org.openimaj.image.pixel.Pixel; 040import org.openimaj.image.processing.morphology.StructuringElement; 041import org.openimaj.image.processor.SinglebandImageProcessor; 042 043/** 044 * Implementation of Alexandru Telea's FMM-based inpainting algorithm. The 045 * {@link AbstractFMMInpainter} is extended with a method to inpaint pixels 046 * based on the neighbours and explicitly taking into account the image 047 * gradients in the neighbourhood in order to preserve sharp details and smooth 048 * zones. 049 * 050 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 051 * 052 * @param <IMAGE> 053 * The type of image that this processor can process 054 */ 055@Reference( 056 type = ReferenceType.Article, 057 author = { "Telea, Alexandru" }, 058 title = "An Image Inpainting Technique Based on the Fast Marching Method.", 059 year = "2004", 060 journal = "J. Graphics, GPU, & Game Tools", 061 pages = { "23", "34" }, 062 url = "http://dblp.uni-trier.de/db/journals/jgtools/jgtools9.html#Telea04", 063 number = "1", 064 volume = "9", 065 customData = { 066 "biburl", "http://www.bibsonomy.org/bibtex/2b0bf54e265d011a8e1fe256e6fcf556b/dblp", 067 "doi", "http://dx.doi.org/10.1080/10867651.2004.10487596", 068 "keywords", "dblp" 069 }) 070public class TeleaInpainting<IMAGE extends Image<?, IMAGE> & SinglebandImageProcessor.Processable<Float, FImage, IMAGE>> 071 extends 072 AbstractFMMInpainter<IMAGE> 073{ 074 protected Set<Pixel> region; 075 076 /** 077 * Construct the inpainting operator with the given radius. 078 * 079 * @param radius 080 * the radius for selecting how many pixels are used to make 081 * estimates. 082 */ 083 public TeleaInpainting(int radius) { 084 region = StructuringElement.disk(radius).positive; 085 } 086 087 @Override 088 protected void inpaint(int x, int y, IMAGE image) { 089 if (image instanceof FImage) 090 inpaint(x, y, (FImage) image); 091 else if (image instanceof MBFImage) 092 inpaint(x, y, (MBFImage) image); 093 else 094 throw new UnsupportedOperationException("Image type not supported!"); 095 } 096 097 protected void inpaint(int x, int y, FImage input) { 098 final int width = input.getWidth(); 099 final int height = input.getHeight(); 100 final float gradx_u = gradX(timeMap.pixels, x, y); 101 final float grady_u = gradY(timeMap.pixels, x, y); 102 103 float accum = 0; 104 float norm = 0; 105 106 for (final Pixel p : region) { 107 final int xx = p.x + x; 108 final int yy = p.y + y; 109 110 if (xx <= 1 || xx >= width - 1 || yy <= 1 || yy >= height - 1) 111 continue; 112 if (flag[yy][xx] != KNOWN) 113 continue; 114 115 final int rx = x - xx; 116 final int ry = y - yy; 117 118 // geometric distance. 119 final float geometricDistance = (float) (1. / ((rx * rx + ry * ry) * Math.sqrt((rx * rx + ry * ry)))); 120 121 // levelset distance. 122 final float levelsetDistance = (float) (1. / (1 + Math.abs(timeMap.pixels[yy][xx] - timeMap.pixels[y][x]))); 123 124 // Dot product of final displacement and gradient vectors. 125 float direction = Math.abs(rx * gradx_u + ry * grady_u); 126 if (direction < 0.000001f) 127 direction = 0.000001f; 128 129 final float weight = geometricDistance * levelsetDistance * direction; 130 131 accum += weight * input.pixels[yy][xx]; 132 norm += weight; 133 } 134 135 input.pixels[y][x] = accum / norm; 136 } 137 138 protected void inpaint(int x, int y, MBFImage input) { 139 final int width = input.getWidth(); 140 final int height = input.getHeight(); 141 final float gradx_u = gradX(timeMap.pixels, x, y); 142 final float grady_u = gradY(timeMap.pixels, x, y); 143 144 final int nbands = input.numBands(); 145 final float accum[] = new float[nbands]; 146 float norm = 0; 147 148 for (final Pixel p : region) { 149 final int xx = p.x + x; 150 final int yy = p.y + y; 151 152 if (xx <= 1 || xx >= width - 1 || yy <= 1 || yy >= height - 1) 153 continue; 154 if (flag[yy][xx] != KNOWN) 155 continue; 156 157 final int rx = x - xx; 158 final int ry = y - yy; 159 160 // geometric distance. 161 final float geometricDistance = (float) (1. / ((rx * rx + ry * ry) * Math.sqrt((rx * rx + ry * ry)))); 162 163 // levelset distance. 164 final float levelsetDistance = (float) (1. / (1 + Math.abs(timeMap.pixels[yy][xx] - timeMap.pixels[y][x]))); 165 166 // Dot product of final displacement and gradient vectors. 167 float direction = Math.abs(rx * gradx_u + ry * grady_u); 168 if (direction < 0.000001f) 169 direction = 0.000001f; 170 171 final float weight = geometricDistance * levelsetDistance * direction; 172 173 for (int i = 0; i < nbands; i++) 174 accum[i] += weight * input.getBand(i).pixels[yy][xx]; 175 norm += weight; 176 } 177 178 for (int i = 0; i < nbands; i++) 179 input.getBand(i).pixels[y][x] = accum[i] / norm; 180 } 181 182 private float gradX(float[][] img, int x, int y) { 183 float grad; 184 185 if (flag[y][x + 1] != UNKNOWN) { 186 if (flag[y][x - 1] != UNKNOWN) 187 grad = (img[y][x + 1] - img[y][x - 1]) * 0.5f; 188 else 189 grad = (img[y][x + 1] - img[y][x]); 190 } else { 191 if (flag[y][x - 1] != UNKNOWN) 192 grad = (img[y][x] - img[y][x - 1]); 193 else 194 grad = 0; 195 } 196 197 return grad; 198 } 199 200 private float gradY(float[][] img, int x, int y) { 201 float grad; 202 203 if (flag[y + 1][x] != UNKNOWN) { 204 if (flag[y - 1][x] != UNKNOWN) 205 grad = (img[y + 1][x] - img[y - 1][x]) * 0.5f; 206 else 207 grad = (img[y + 1][x] - img[y][x]); 208 } else { 209 if (flag[y - 1][x] != UNKNOWN) 210 grad = (img[y][x] - img[y - 1][x]); 211 else 212 grad = 0; 213 } 214 215 return grad; 216 } 217}