1 | // Copyright 2005 Huxtable.com. All rights reserved. |
2 | // For more information, see <http://www.jhlabs.com/ip/blurring.html>. |
3 | package com.jhlabs.image; |
4 | |
5 | import java.awt.image.BufferedImage; |
6 | import java.awt.image.Kernel; |
7 | |
8 | /** |
9 | * A thresholded blur for ironing out wrinkles. |
10 | * |
11 | * @author Jerry Huxtable |
12 | */ |
13 | public class SmartBlurFilter extends AbstractBufferedImageOp |
14 | { |
15 | private static final int DEFAULT_RADIUS = 5; |
16 | private static final int DEFAULT_THRESHOLD = 10; |
17 | |
18 | private int hRadius; |
19 | private int threshold; |
20 | private int vRadius; |
21 | |
22 | public SmartBlurFilter(int radius, int threshold) { |
23 | super(); |
24 | setRadius(radius); |
25 | setThreshold(threshold); |
26 | } |
27 | |
28 | public SmartBlurFilter() { |
29 | this(DEFAULT_RADIUS, DEFAULT_THRESHOLD); |
30 | } |
31 | |
32 | public void setHRadius(int hRadius) { |
33 | this.hRadius = hRadius; |
34 | } |
35 | |
36 | public void setRadius(int radius) { |
37 | this.hRadius = this.vRadius = radius; |
38 | } |
39 | |
40 | public void setThreshold(int threshold) { |
41 | this.threshold = threshold; |
42 | } |
43 | |
44 | public void setVRadius(int vRadius) { |
45 | this.vRadius = vRadius; |
46 | } |
47 | |
48 | public int getHRadius() { |
49 | return hRadius; |
50 | } |
51 | |
52 | public int getRadius() { |
53 | return hRadius; |
54 | } |
55 | |
56 | public int getThreshold() { |
57 | return threshold; |
58 | } |
59 | |
60 | public int getVRadius() { |
61 | return vRadius; |
62 | } |
63 | |
64 | public BufferedImage filter(BufferedImage src, BufferedImage dst) { |
65 | int width = src.getWidth(); |
66 | int height = src.getHeight(); |
67 | |
68 | if (dst == null) { |
69 | dst = createCompatibleDestImage(src, null); |
70 | } |
71 | |
72 | int[] inPixels = new int[width * height]; |
73 | int[] outPixels = new int[width * height]; |
74 | |
75 | getRGB(src, 0, 0, width, height, inPixels); |
76 | |
77 | Kernel kernel = GaussianFilter.makeKernel(hRadius); |
78 | |
79 | thresholdBlur(kernel, inPixels, outPixels, width, height, true); |
80 | thresholdBlur(kernel, outPixels, inPixels, height, width, true); |
81 | |
82 | setRGB(dst, 0, 0, width, height, inPixels); |
83 | return dst; |
84 | } |
85 | |
86 | /** |
87 | * Convolve with a kernel consisting of one row. |
88 | */ |
89 | public void thresholdBlur(Kernel kernel, int[] inPixels, int[] outPixels, int width, int height, boolean alpha) { |
90 | float[] matrix = kernel.getKernelData(null); |
91 | int cols = kernel.getWidth(); |
92 | int cols2 = cols / 2; |
93 | |
94 | for (int y = 0; y < height; y++) { |
95 | int ioffset = y * width; |
96 | int outIndex = y; |
97 | |
98 | for (int x = 0; x < width; x++) { |
99 | float r = 0; |
100 | float g = 0; |
101 | float b = 0; |
102 | float a = 0; |
103 | int moffset = cols2; |
104 | |
105 | int rgb1 = inPixels[ioffset + x]; |
106 | int a1 = (rgb1 >> 24) & 0xff; |
107 | int r1 = (rgb1 >> 16) & 0xff; |
108 | int g1 = (rgb1 >> 8) & 0xff; |
109 | int b1 = rgb1 & 0xff; |
110 | float af = 0; |
111 | float rf = 0; |
112 | float gf = 0; |
113 | float bf = 0; |
114 | |
115 | for (int col = -cols2; col <= cols2; col++) { |
116 | float f = matrix[moffset + col]; |
117 | |
118 | if (f != 0) { |
119 | int ix = x + col; |
120 | |
121 | if (!(0 <= ix && ix < width)) { |
122 | ix = x; |
123 | } |
124 | int rgb2 = inPixels[ioffset + ix]; |
125 | int a2 = (rgb2 >> 24) & 0xff; |
126 | int r2 = (rgb2 >> 16) & 0xff; |
127 | int g2 = (rgb2 >> 8) & 0xff; |
128 | int b2 = rgb2 & 0xff; |
129 | |
130 | int d; |
131 | |
132 | d = a1 - a2; |
133 | if (d >= -threshold && d <= threshold) { |
134 | a += f * a2; |
135 | af += f; |
136 | } |
137 | d = r1 - r2; |
138 | if (d >= -threshold && d <= threshold) { |
139 | r += f * r2; |
140 | rf += f; |
141 | } |
142 | d = g1 - g2; |
143 | if (d >= -threshold && d <= threshold) { |
144 | g += f * g2; |
145 | gf += f; |
146 | } |
147 | d = b1 - b2; |
148 | if (d >= -threshold && d <= threshold) { |
149 | b += f * b2; |
150 | bf += f; |
151 | } |
152 | } |
153 | } |
154 | a = af == 0 ? a1 : a / af; |
155 | r = rf == 0 ? r1 : r / rf; |
156 | g = gf == 0 ? g1 : g / gf; |
157 | b = bf == 0 ? b1 : b / bf; |
158 | |
159 | int ia = alpha ? ImageMath.clamp((int) (a + 0.5)) : 0xff; |
160 | int ir = ImageMath.clamp((int) (r + 0.5)); |
161 | int ig = ImageMath.clamp((int) (g + 0.5)); |
162 | int ib = ImageMath.clamp((int) (b + 0.5)); |
163 | |
164 | outPixels[outIndex] = (ia << 24) | (ir << 16) | (ig << 8) | ib; |
165 | outIndex += height; |
166 | } |
167 | } |
168 | } |
169 | |
170 | public String toString() { |
171 | return "Blur/Smart Blur..."; |
172 | } |
173 | } |