2
2
* Fourier Stripe Filtering
3
3
* ----------------------------------------------
4
4
*
5
- * This short macro shows the destriping procedure presented in the course slides from 4.3.2
5
+ * This macro shows an advanced version of the destriping procedure presented in the course slides from 4.3.2
6
6
*
7
- * Due to the simple nature of this code, no copyright is applicable
7
+ * The procedure has 3 steps
8
+ * 1. Detect the angle of the stripes by checking the mean radial intensity in teh Fourier domain
9
+ * 2. Create a mask that we can use in Fourier to 'hide' the frequencies around the detected angle
10
+ * 3. Apply the mask using the Process>FFT>Custom Filter
11
+ *
12
+ * NOTE that the stripes are detected in fourier by finding the radius with teh highest mean instansity.
13
+ * If the original signal has a strong directionality, this method might fail.
8
14
*
9
15
* Code created for Image Processing and Analysis For Life Scientists MOOC on EdX
10
16
* https://www.edx.org/course/image-processing-and-analysis-for-life-scientists
11
17
*
12
- * 2018 - Olivier Burri, EPFL - SV - BIOP
18
+ *
19
+ * 2018 - CC-BY Olivier Burri, EPFL - SV - BIOP
13
20
* https://biop.epfl.ch
21
+ *
14
22
*/
15
- run("Close All");
16
23
17
- waitForUser("Please Open 'image_with_stripes.tif' and 'Stripe_Filter.tif'");
24
+ thickness = 25;
25
+ start_radius_px = 10;
26
+
27
+
28
+ roiManager("reset");
29
+ // Make sure that the image is opened, or ask the user to open it.
30
+ if( !isOpen("image_with_stripes.tif") ) {
31
+ run("Close All");
32
+ waitForUser("Please Open 'image_with_stripes.tif'");
33
+ }
18
34
19
35
selectImage("image_with_stripes.tif");
36
+ close("\\Others");
20
37
21
38
// Make a copy and apply the stripe filter in Fourier
22
- run("Duplicate...", "title=[Destriped Image]");
39
+ run("Duplicate...", " ");
40
+ rename("Destriped Image");
41
+ run("FFT");
42
+ rename("Original Fourier Transform");
43
+ waitForUser("Notice the bright almost vertical line on the Fourier image.\nThis is mostly due to the contribution of the stripes.");
23
44
24
- // We just run the custom filter on the stiped image
25
- run("Custom Filter...", "filter=Stripe_Filter.tif");
45
+ angle = locateAngle();
46
+ makeCenteredLine(angle, thickness); // For showing if the computed angles was useful, we display it with a custom function
47
+
48
+ waitForUser("The angle was determined to be "+angle+" degrees\nWe will now create the Fourier Mask");
49
+
50
+ // If we know the angle, we can produce a mask in Fourier to hide these frequencies
51
+
52
+ // We can build a line that
53
+ // 1. Goes through the center of the image
54
+ // 2. is at 'angle' degrees from the horizontal.
55
+ // 3. is 'cut' in the center, to avoid removing interesting low variations
56
+ buildCenteredLines(angle, start_radius_px, thickness);
26
57
58
+ // From these lines, we can build a Fourier mask to use in our image
59
+ makeFourierMask();
60
+ waitForUser("This is the resulting Fourier Mask.\nYou can look at the code to see how it was created");
61
+
62
+ // Pick up the name of the fourier mask
63
+ filter_name = getTitle();
64
+
65
+
66
+ // We just run the custom filter on the stiped image
67
+ selectImage("Destriped Image");
68
+ run("Custom Filter...", "filter=["+filter_name+"]");
69
+ run("FFT");
70
+ rename("Destriped Fourier Transform");
27
71
// We can check the difference between the two images to see what was removed
28
72
imageCalculator("Subtract create 32-bit", "Destriped Image","image_with_stripes.tif");
29
73
30
74
rename("Difference between the striped and destriped images");
31
75
32
- run("Tile");
76
+ run("Tile");
77
+
78
+ waitForUser("See how a simple line mask in Fourier can efectively remove artifacts from the image.");
79
+
80
+ waitForUser("You can try it yourself on the original striped image\nand using the 'Stripe Mask.tif' image\nOpen both images and use\nProcess>FFT>Custom FIlter...");
81
+
82
+ function makeFourierMask() {
83
+ // Let's create a Fourier mask
84
+ getDimensions(width, height, channels, slices, frames);
85
+ newImage("Fourier Mask", "32-bit", width, height, 1);
86
+ setForegroundColor(255,255,255);
87
+
88
+ // We fill the two thick lines in the image and slighly blurr them to avoid edge effects
89
+ roiManager("Fill");
90
+
91
+ run("Invert");
92
+ run("Gaussian Blur...", "sigma=5");
93
+
94
+
95
+ }
96
+
97
+ /*
98
+ * locateAngle will make lines from the center of the Fourier image on the two upper quadrants of the image
99
+ * It will compute the line profile and extract the mean value along that line and store it in an array
100
+ * We will then compute, based on the mean values the highest value and find its corresponding angle
101
+ *
102
+ */
103
+ function locateAngle() {
104
+ steps=1000;
105
+ run("Duplicate...", "title=[Angle Detection]");
106
+ // Slight blur to remove noise
107
+ run("Smooth");
108
+ getDimensions(width, height, channels, slices, frames);
109
+ r = width/2;
110
+ angles = newArray(steps);
111
+ intensities = newArray(steps);
112
+ angle = 0;
113
+ current_mean = 0;
114
+
115
+ // Break down the upper two quadrants in small steps and loop through each one
116
+ for(i=0;i<steps; i++) {
117
+ // Get angle in radians
118
+ a = i*PI/steps;
119
+
120
+ // Compute first point for the line
121
+ p1x = r*cos(a) + r;
122
+ p1y = -r*sin(a) + r;
123
+
124
+ // Second point is mirrored
125
+ p2x = -r*cos(a) + r;
126
+ p2y = r*sin(a) + r;
127
+
128
+ // Make a line and extract its intensity profile
129
+ makeLine(p1x, p1y, p2x, p2y,3);
130
+ line_profile = getProfile();
131
+
132
+ // Get the mean intensity along that line
133
+ Array.getStatistics(line_profile, line_min, line_max, line_mean, line_stdDev);
134
+
135
+ // Save the mean intensity
136
+ intensities[i] = line_mean;
137
+
138
+ // Get the current angle in degrees
139
+ angles[i] = a/(2*PI)*360;
140
+ }
141
+
142
+ // Close the temporary image we duplicated
143
+ close();
144
+ // see https://imagej.net/ij/developer/macro/functions.html#Array.findMaxima
145
+ indexes = Array.findMaxima(intensities, 2, 2);
146
+ highest_value = indexes[0];
147
+ return angles[highest_value];
148
+ }
149
+
150
+ /*
151
+ * This is just a convenience function to draw a centered line at an angle to visualisation
152
+ */
153
+ function makeCenteredLine(angle, thickness) {
154
+ angle_radians = angle / 360 * 2*PI;
155
+ width = getWidth();
156
+ r = sqrt(2)*width/2;
157
+
158
+ // first point
159
+ p1x = r*cos(angle_radians) + width/2;
160
+ p1y = -r*sin(angle_radians) + width/2;
161
+
162
+ // Second point is mirrored
163
+ p2x = -r*cos(angle_radians) + width/2;
164
+ p2y = r*sin(angle_radians) + width/2;
165
+
166
+ makeLine(p1x, p1y, p2x, p2y, thickness);
167
+ }
168
+
169
+ /*
170
+ * This function will make a line centered on the image
171
+ */
172
+ function buildCenteredLines(angle, start_radius, thickness) {
173
+
174
+ // Get the dimensions of the image
175
+ getDimensions(width, height, channels, slices, frames);
176
+
177
+ // Assuming the image is square, the longest distance to an edge is at the corner
178
+ r = sqrt(2)*width/2;
179
+ angle_radians = angle / 360 * 2*PI;
180
+ // Compute first point for the line
181
+ p1x = start_radius*cos(angle_radians) + width/2;
182
+ p1y = -start_radius*sin(angle_radians) + width/2;
183
+
184
+ // Second point is mirrored
185
+ p2x = r*cos(angle_radians) + width/2;
186
+ p2y = -r*sin(angle_radians) + width/2;
187
+
188
+ makeLine(p1x, p1y, p2x, p2y, thickness);
189
+
190
+ // This command allows us to have two lines instead of one, you can ignore it for now. It will be covered in Week 6
191
+ roiManager("add");
192
+
193
+ p1x = -start_radius*cos(angle_radians) + width/2;
194
+ p1y = start_radius*sin(angle_radians) + width/2;
195
+
196
+ // Second point is mirrored
197
+ p2x = -r*cos(angle_radians) + width/2;
198
+ p2y = r*sin(angle_radians) + width/2;
199
+
200
+ makeLine(p1x, p1y, p2x, p2y, thickness);
201
+ roiManager("add");
202
+ }
0 commit comments