1
package org.helioviewer.jhv.internal_plugins.filter.sharpen;
3
import org.helioviewer.viewmodel.filter.AbstractFilter;
4
import org.helioviewer.viewmodel.filter.StandardFilter;
5
import org.helioviewer.viewmodel.imagedata.ARGBInt32ImageData;
6
import org.helioviewer.viewmodel.imagedata.ImageData;
7
import org.helioviewer.viewmodel.imagedata.SingleChannelByte8ImageData;
8
import org.helioviewer.viewmodel.imagedata.SingleChannelShortImageData;
9
import org.helioviewer.viewmodel.imagetransport.Byte8ImageTransport;
10
import org.helioviewer.viewmodel.imagetransport.Int32ImageTransport;
11
import org.helioviewer.viewmodel.imagetransport.Short16ImageTransport;
14
* Filter for sharpen an image.
17
* This filter sharpens the image by applying the unsharp mask algorithm. It
18
* uses the following formula:
21
* p_res(x,y) = (1 + a) * p_in(x,y) - a * p_low(x,y)
24
* Here, p_res means the resulting pixel, p_in means the original input pixel
25
* and p_low the pixel of the lowpassed filtered original. As applying the
26
* lowpass, the image is convoluted with the 3x3 Gauss-kernel:
29
* 1/16 * {{1, 2, 1}, {2, 4, 2}, {1, 2, 1}}
32
* If the weighting is zero, the input data stays untouched.
35
* The output of the filter always has the same image format as the input.
38
* This filter supports only software rendering, but there is an OpenGL
39
* implementation in {@link SharpenGLFilter}. This is because the OpenGL
40
* implementation may be invalid due to graphics card restrictions.
42
* @author Markus Langenberg
45
public class SharpenFilter extends AbstractFilter implements StandardFilter {
47
// /////////////////////////
49
// /////////////////////////
51
protected static final int span = 2;
53
protected float weighting = 0.0f;
55
private SharpenPanel panel;
57
private int convolveX[] = null;
58
private int convolveY[] = null;
60
private ImageData lastImageData;
62
private boolean forceRefilter = false;
65
* Sets the corresponding sharpen panel.
68
* Corresponding panel.
70
void setPanel(SharpenPanel panel) {
72
panel.setValue(weighting);
76
* Sets the weighting of the sharpening.
79
* Weighting of the sharpening
81
void setWeighting(float newWeighting) {
82
weighting = newWeighting;
90
* This filter is not a major filter.
92
public boolean isMajorFilter() {
96
// /////////////////////////
98
// /////////////////////////
101
* Blurs a single channel image by applying a 3x3 Gauss lowpass filter.
103
* Since a convolution with a Gauss kernel is separable, this function is
104
* optimized by doing so.
107
* If the image has more than one channel, this function has to be called
113
* Height of the image
115
* Pixel data of the image, given as an integer
116
* @return Blurred single channel image
118
private int[] blur(int width, int height, byte input[]) {
119
if (convolveY == null || convolveY.length < width * height)
120
convolveY = new int[width * height];
122
for (int i = 0; i < width * height; i++) {
123
convolveY[i] = input[i] & 0xFF;
126
return blur(width, height, convolveY);
130
* Blurs a single channel image by applying a 3x3 Gauss lowpass filter.
132
* Since a convolution with a Gauss kernel is separable, this function is
133
* optimized by doing so.
136
* If the image has more than one channel, this function has to be called
142
* Height of the image
144
* Pixel data of the image, given as an integer
146
* to apply on the input data
147
* @return Blurred single channel image
149
private int[] blur(int width, int height, short input[], int mask) {
150
if (convolveY == null || convolveY.length < width * height)
151
convolveY = new int[width * height];
153
for (int i = 0; i < width * height; i++) {
154
convolveY[i] = input[i] & mask;
157
return blur(width, height, convolveY);
161
* Blurs a single channel image by applying a 3x3 Gauss lowpass filter.
163
* Since a convolution with a Gauss kernel is separable, this function is
164
* optimized by doing so.
167
* If the image has more than one channel, this function has to be called
173
* Height of the image
175
* Pixel data of the image, given as an integer
176
* @return Blurred single channel image
178
private int[] blur(int width, int height, int[] input) {
179
if (width < 2 * span || height < 2 * span) {
182
if (convolveX == null || convolveX.length < width * height)
183
convolveX = new int[width * height];
184
if (convolveY == null || convolveY.length < width * height)
185
convolveY = new int[width * height];
189
// convolve borders in x direction
190
for (int i = 0; i < height; i++) {
191
for (int j = 0; j < span; j++) {
192
tmpIndex = i * width + j;
194
convolveX[tmpIndex] = ((input[tmpIndex - j] + (input[tmpIndex] << 1) + input[tmpIndex + span]) >> 2);
196
tmpIndex = (i + 1) * width - 1 - j;
197
convolveX[tmpIndex] = ((input[tmpIndex + j] + (input[tmpIndex] << 1) + input[tmpIndex - span]) >> 2);
201
// convolve inner region in x direction
202
for (int i = 0; i < height; i++) {
203
for (int j = span; j < width - span; j++) {
204
tmpIndex = i * width + j;
205
convolveX[tmpIndex] = ((input[tmpIndex - span] + (input[tmpIndex] << 1) + input[tmpIndex + span]) >> 2);
209
int spanTimesWidth = span * width;
211
// convolve borders in y direction
212
for (int i = 0; i < span; i++) {
213
for (int j = 0; j < width; j++) {
214
tmpIndex = i * width + j;
215
convolveY[tmpIndex] = ((convolveX[tmpIndex - i * width] + (convolveX[tmpIndex] << 1) + convolveX[tmpIndex + spanTimesWidth]) >> 2);
217
tmpIndex = (height - i) * width - 1 - j;
218
convolveY[tmpIndex] = ((convolveX[tmpIndex + i * width] + (convolveX[tmpIndex] << 1) + convolveX[tmpIndex - spanTimesWidth]) >> 2);
222
// convolve inner region in y direction
223
for (int i = span; i < height - span; i++) {
224
for (int j = 0; j < width; j++) {
225
tmpIndex = i * width + j;
226
convolveY[tmpIndex] = ((convolveX[tmpIndex - spanTimesWidth] + (convolveX[tmpIndex] << 1) + convolveX[tmpIndex + spanTimesWidth]) >> 2);
236
public ImageData apply(ImageData data) {
241
if (weighting <= 0.01f) {
245
// Single channel byte image
247
if (data.getImageTransport() instanceof Byte8ImageTransport) {
248
byte[] pixelData = ((Byte8ImageTransport) data.getImageTransport()).getByte8PixelData();
251
if (forceRefilter || lastImageData != data) {
252
blur(data.getWidth(), data.getHeight(), pixelData);
256
byte[] resultPixelData = new byte[pixelData.length];
257
for (int i = 0; i < pixelData.length; i++) {
258
resultPixelData[i] = (byte) Math.min(Math.max((1.0f + weighting) * (pixelData[i] & 0xFF) - weighting * convolveY[i], 0), 0xFF);
261
lastImageData = data;
263
return new SingleChannelByte8ImageData(data, resultPixelData);
265
// Single channel short image
266
} else if (data.getImageTransport() instanceof Short16ImageTransport) {
267
short[] pixelData = ((Short16ImageTransport) data.getImageTransport()).getShort16PixelData();
270
int mask = (1 << data.getImageTransport().getNumBitsPerPixel()) - 1;
273
if (forceRefilter || lastImageData != data) {
274
blur(data.getWidth(), data.getHeight(), pixelData, mask);
278
short[] resultPixelData = new short[pixelData.length];
279
for (int i = 0; i < pixelData.length; i++) {
280
resultPixelData[i] = (short) Math.min(Math.max((1.0f + weighting) * (pixelData[i] & mask) - weighting * convolveY[i], 0), 0xFFFF);
283
lastImageData = data;
285
return new SingleChannelShortImageData(data, resultPixelData);
287
// (A)RGB image: Filter each channel separate
288
} else if (data.getImageTransport() instanceof Int32ImageTransport) {
290
int[] pixelData = ((Int32ImageTransport) data.getImageTransport()).getInt32PixelData();
291
int[] resultPixelData = new int[pixelData.length];
293
int[] channel = new int[pixelData.length];
295
// copy alpha channel unfiltered
296
for (int i = 0; i < pixelData.length; i++) {
297
resultPixelData[i] = pixelData[i] & 0xFF000000;
300
// perform for each color channel
301
for (int c = 0; c < 3; c++) {
302
for (int i = 0; i < pixelData.length; i++) {
303
channel[i] = (pixelData[i] >>> c * 8) & 0xFF;
307
blur(data.getWidth(), data.getHeight(), channel);
310
for (int i = 0; i < pixelData.length; i++) {
311
resultPixelData[i] |= (((int) Math.min(Math.max((1.0f + weighting) * (channel[i] & 0xFF) - weighting * convolveY[i], 0), 0xFF)) << (c * 8));
315
lastImageData = data;
317
return new ARGBInt32ImageData(data, resultPixelData);
321
forceRefilter = false;
330
public void forceRefilter() {
331
forceRefilter = true;
338
public void setState(String state) {
339
setWeighting(Float.parseFloat(state));
340
panel.setValue(weighting);
346
public String getState() {
347
return Float.toString(weighting);