package com.ajah.image; /* * Copyright 2012 Eric F. Savage, code@efsavage.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.awt.Color; import java.awt.image.BufferedImage; import java.io.IOException; import com.jhlabs.image.BoxBlurFilter; import com.jhlabs.image.DespeckleFilter; import com.jhlabs.image.SmartBlurFilter; import com.jhlabs.image.ThresholdFilter; /** * Attempts to replace the background of an image. * * @author <a href="http://efsavage.com">Eric F. Savage</a>, <a * href="mailto:code@efsavage.com">code@efsavage.com</a>. */ public class BackgroundKnockout { private static final int UNKNOWN = 0; private static final int KNOCK = 1; private static final int SOURCE = 2; /** * Knocks out the background with a transparent (0 alpha) one. * * @param image * The image to modify. * @param color * The background color. * @return The processed image. * @throws IOException * If the image could not be read/written to. */ public static BufferedImage knockout(final BufferedImage image, final Color color) throws IOException { return knockout(image, color, new Color(255, 255, 255, 0)); } /** * Knocks out the background with a specified color. * * @param image * The image to modify. * @param color * The background color. * @param knockoutColor * The color to replace the background with. * @return The processed image. * @throws IOException * If the image could not be read/written to. */ public static BufferedImage knockout(final BufferedImage image, final Color color, final Color knockoutColor) throws IOException { final ThresholdFilter filter = new ThresholdFilter(); final BufferedImage knocked = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); new DespeckleFilter().filter(image, knocked); new BoxBlurFilter().filter(knocked, knocked); new SmartBlurFilter().filter(knocked, knocked); filter.filter(knocked, knocked); new SmartBlurFilter().filter(knocked, knocked); filter.filter(knocked, knocked); final int[][] mask = new int[image.getWidth()][image.getHeight()]; // Initialize the mask for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { if (knocked.getRGB(x, y) != -1) { mask[x][y] = SOURCE; } } } long changed = 0; do { changed = 0; for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { if (mask[x][y] == UNKNOWN) { for (int x2 = x - 1; x2 <= (x + 1); x2++) { for (int y2 = y - 1; y2 <= (y + 1); y2++) { if (x2 >= 0 && y2 >= 0 && x2 < mask.length && y2 < mask[x2].length) { if (mask[x2][y2] == KNOCK || x2 == 0 || y2 == 0 || x2 == mask.length - 1 || y2 == mask[x2].length - 1) { // Do we have a neighbor that is a // knock, or are we on the edge? mask[x][y] = KNOCK; changed++; } } } } } } } // Keep looping until we haven't changed anything. } while (changed > 0); // Write to the new image for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { if (mask[x][y] == KNOCK) { knocked.setRGB(x, y, knockoutColor.getRGB()); } else { knocked.setRGB(x, y, image.getRGB(x, y)); } } } return knocked; } }