Permalink
Browse files

Rewrite the filter

  • Loading branch information...
1 parent ebe3ca4 commit e9fafff681b9b5d26b11d1bbac40b0721f9069f1 @efsavage efsavage committed May 3, 2012
Showing with 35 additions and 147 deletions.
  1. +35 −147 ajah-image/src/main/java/com/ajah/image/BackgroundKnockout.java
@@ -1,4 +1,5 @@
package com.ajah.image;
+
/*
* Copyright 2012 Eric F. Savage, code@efsavage.com
*
@@ -15,11 +16,14 @@
* limitations under the License.
*/
-
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
-import java.util.logging.Logger;
+
+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.
@@ -29,10 +33,9 @@
*/
public class BackgroundKnockout {
- private static Logger log = Logger.getLogger(BackgroundKnockout.class.getName());
-
- private int blend = 1;
- private int fuzziness = 10;
+ 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.
@@ -50,187 +53,72 @@ public BufferedImage knockout(final BufferedImage image, final Color color) thro
}
/**
- * Knocks out the background color.
+ * 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 color with.
+ * The color to replace the background with.
* @return The processed image.
* @throws IOException
* If the image could not be read/written to.
*/
- public BufferedImage knockout(final BufferedImage image, final Color color, final Color knockoutColor) throws IOException {
-
+ public BufferedImage knockout(BufferedImage image, final Color color, final Color knockoutColor) throws IOException {
+ ThresholdFilter filter = new ThresholdFilter();
final BufferedImage knocked = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
- final long start = System.currentTimeMillis();
-
+ 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()];
- // 0 = Unchecked
- // -1 = Fill
- // -2 = No Fill
- // -3 = Possible blend
- // -4 = Definite blend
-
+ // Initialize the mask
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
- final Color test = new Color(image.getRGB(x, y), true);
- final int distance = ColorUtils.getMaxDistance(color, test);
- if (distance > this.fuzziness) {
- mask[x][y] = -2;
- } else if (distance > (this.fuzziness * 3)) {
- mask[x][y] = -3;
+ 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] == 0) {
- if (x == 0 || y == 0 || x == (mask.length - 1) || y == (mask[x].length - 1)) {
- // We're on the edge
- mask[x][y] = -1;
- changed++;
- }
- 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 && mask[x2][y2] == -1) {
- // We've got a fill neighbor
- mask[x][y] = -1;
- changed++;
- }
- }
- }
- }
- }
- }
- log.finest(changed + " fill cells changed");
- } while (changed > 0);
- // At this point, anything that's still 0 is non-contiguous
- for (int x = 0; x < image.getWidth(); x++) {
- for (int y = 0; y < image.getHeight(); y++) {
- if (mask[x][y] == 0) {
- mask[x][y] = -2;
- }
- }
- }
-
- // Let's look at the blend cells
+ 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] == -3) {
+ 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 && (mask[x2][y2] == -1)) {
- // We've got a fill neighbor, we can blend
- mask[x][y] = -4;
- changed++;
+ 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++;
+ }
}
}
}
}
}
}
- log.finest(changed + " blend cells 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] == -2) {
- knocked.setRGB(x, y, image.getRGB(x, y));
- } else if (mask[x][y] == -1) {
+ if (mask[x][y] == KNOCK) {
knocked.setRGB(x, y, knockoutColor.getRGB());
- } else if (mask[x][y] == -3) {
- knocked.setRGB(x, y, image.getRGB(x, y));
- } else if (mask[x][y] == -4) {
- knocked.setRGB(x, y, ImageUtils.blend(new Color(image.getRGB(x, y)), knockoutColor, .9f).getRGB());
- } else if (mask[x][y] == 0) {
+ } else {
knocked.setRGB(x, y, image.getRGB(x, y));
}
}
}
- final long end = System.currentTimeMillis();
- log.fine("Knocked out background in " + (end - start) + "ms");
return knocked;
}
- /**
- * Returns the number of pixels between the image and the background to
- * blend.
- *
- * @return The number of pixels between the image and the background to
- * blend.
- */
- public int getBlend() {
- return this.blend;
- }
-
- /**
- * Sets the number of pixels between the image and the background to blend.
- *
- * @param blend
- * The number of pixels between the image and the background to
- * blend.
- */
- public void setBlend(int blend) {
- this.blend = blend;
- }
-
- /**
- * Sets the number of pixels between the image and the background to blend.
- *
- * @param _blend
- * The number of pixels between the image and the background to
- * blend.
- * @return The current instance, for chaining.
- */
- public BackgroundKnockout blend(int _blend) {
- setBlend(_blend);
- return this;
- }
-
- /**
- * Returns the variation from the background color that is still considered
- * to be part of background.
- *
- * @return The variation from the background color that is still considered
- * to be part of background..
- */
- public int getFuzziness() {
- return this.fuzziness;
- }
-
- /**
- * Sets the variation from the background color that is still considered to
- * be part of background.
- *
- * @param fuzziness
- * The variation from the background color that is still
- * considered to be part of background.
- */
- public void setFuzziness(int fuzziness) {
- this.blend = fuzziness;
- }
-
- /**
- * Sets the variation from the background color that is still considered to
- * be part of background.
- *
- * @param _fuzziness
- * The variation from the background color that is still
- * considered to be part of background.
- * @return The current instance, for chaining.
- */
- public BackgroundKnockout fuzziness(int _fuzziness) {
- setBlend(_fuzziness);
- return this;
- }
-
}

0 comments on commit e9fafff

Please sign in to comment.