Skip to content
This repository was archived by the owner on Feb 21, 2021. It is now read-only.

Commit 9eb2afd

Browse files
committed
published version
0 parents  commit 9eb2afd

File tree

12 files changed

+432
-0
lines changed

12 files changed

+432
-0
lines changed

.classpath

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<classpath>
3+
<classpathentry kind="src" path="src"/>
4+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
5+
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/opencv2"/>
6+
<classpathentry kind="con" path="org.eclipse.fx.ide.jdt.core.JAVAFX_CONTAINER"/>
7+
<classpathentry kind="output" path="bin"/>
8+
</classpath>

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin

.project

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectDescription>
3+
<name>Lab4</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
<buildCommand>
14+
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
15+
<arguments>
16+
</arguments>
17+
</buildCommand>
18+
</buildSpec>
19+
<natures>
20+
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
21+
<nature>org.eclipse.jdt.core.javanature</nature>
22+
</natures>
23+
</projectDescription>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3+
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4+
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5+
org.eclipse.jdt.core.compiler.compliance=1.7
6+
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7+
org.eclipse.jdt.core.compiler.debug.localVariable=generate
8+
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9+
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10+
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11+
org.eclipse.jdt.core.compiler.source=1.7

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Discrete Fourier Transform with OpenCV and JavaFX
2+
3+
*Computer Vision course - [Politecnico di Torino](http://www.polito.it) - academic year 2014-2015*
4+
5+
A project, made in Eclipse (Luna), for experimenting with the Discrete Fourier transform (and its inverse), starting from two grayscale images: a circle and the *sin* function. Example images are provided in the `images` folder.
6+
7+
Please, note that the project is an Eclipse project, made for teaching purposes. Before using it, you need to install the OpenCV library (version 2.4.9) and JavaFX (version 2 or superior) and create a `User Library` named `opencv2` that links to the OpenCV jar and native libraries.

build.fxbuild

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="ASCII"?>
2+
<anttasks:AntTask xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:anttasks="http://org.eclipse.fx.ide.jdt/1.0" buildDirectory="${project}/build">
3+
<deploy>
4+
<application name="Lab5"/>
5+
<info/>
6+
</deploy>
7+
<signjar/>
8+
</anttasks:AntTask>

images/circle.png

504 Bytes
Loading

images/sinFunction.png

618 Bytes
Loading
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<?import javafx.geometry.*?>
4+
<?import javafx.scene.control.*?>
5+
<?import javafx.scene.layout.*?>
6+
<?import javafx.scene.image.*?>
7+
<?import javafx.scene.*?>
8+
9+
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="it.polito.teaching.cv.FourierController">
10+
<left>
11+
<VBox alignment="CENTER">
12+
<padding>
13+
<Insets right="10" left="10" />
14+
</padding>
15+
<ImageView fx:id="originalImage" />
16+
</VBox>
17+
</left>
18+
<right>
19+
<VBox alignment="CENTER" spacing="10">
20+
<padding>
21+
<Insets right="10" left="10" />
22+
</padding>
23+
<ImageView fx:id="transformedImage" />
24+
<ImageView fx:id="antitransformedImage" />
25+
</VBox>
26+
</right>
27+
<bottom>
28+
<HBox alignment="CENTER" spacing="10">
29+
<padding>
30+
<Insets top="25" right="25" bottom="25" left="25" />
31+
</padding>
32+
<Button alignment="center" text="Load Image" onAction="#loadImage"/>
33+
<Button fx:id="transformButton" alignment="center" text="Apply transformation" onAction="#transformImage" disable="true" />
34+
<Button fx:id="antitransformButton" alignment="center" text="Apply anti transformation" onAction="#antitransformImage" disable="true" />
35+
</HBox>
36+
</bottom>
37+
</BorderPane>
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
package it.polito.teaching.cv;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.File;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
import org.opencv.core.Core;
9+
import org.opencv.core.CvType;
10+
import org.opencv.core.Mat;
11+
import org.opencv.core.MatOfByte;
12+
import org.opencv.core.Rect;
13+
import org.opencv.core.Scalar;
14+
import org.opencv.highgui.Highgui;
15+
import org.opencv.imgproc.Imgproc;
16+
17+
import javafx.fxml.FXML;
18+
import javafx.scene.control.Button;
19+
import javafx.scene.image.Image;
20+
import javafx.scene.image.ImageView;
21+
import javafx.stage.FileChooser;
22+
import javafx.stage.Stage;
23+
24+
/**
25+
* The controller associated to the only view of our application. The
26+
* application logic is implemented here. It handles the button for opening an
27+
* image and perform all the operation related to the Fourier transformation and
28+
* antitransformation.
29+
*
30+
* @author <a href="mailto:luigi.derussis@polito.it">Luigi De Russis</a>
31+
* @since 2013-12-11
32+
*
33+
*/
34+
public class FourierController
35+
{
36+
// images to show in the view
37+
@FXML
38+
private ImageView originalImage;
39+
@FXML
40+
private ImageView transformedImage;
41+
@FXML
42+
private ImageView antitransformedImage;
43+
// a FXML button for performing the transformation
44+
@FXML
45+
private Button transformButton;
46+
// a FXML button for performing the antitransformation
47+
@FXML
48+
private Button antitransformButton;
49+
50+
// the main stage
51+
private Stage stage;
52+
// the JavaFX file chooser
53+
private FileChooser fileChooser;
54+
// support variables
55+
private Mat image;
56+
private List<Mat> planes;
57+
// the final complex image
58+
private Mat complexImage;
59+
60+
/**
61+
* Init the needed variables
62+
*/
63+
protected void init()
64+
{
65+
this.fileChooser = new FileChooser();
66+
this.image = new Mat();
67+
this.planes = new ArrayList<>();
68+
this.complexImage = new Mat();
69+
}
70+
71+
/**
72+
* Load an image from disk
73+
*/
74+
@FXML
75+
protected void loadImage()
76+
{
77+
// show the open dialog window
78+
File file = this.fileChooser.showOpenDialog(this.stage);
79+
if (file != null)
80+
{
81+
// read the image in gray scale
82+
this.image = Highgui.imread(file.getAbsolutePath(), Highgui.CV_LOAD_IMAGE_GRAYSCALE);
83+
// show the image
84+
this.originalImage.setImage(this.mat2Image(this.image));
85+
// set a fixed width
86+
this.originalImage.setFitWidth(250);
87+
// preserve image ratio
88+
this.originalImage.setPreserveRatio(true);
89+
// update the UI
90+
this.transformButton.setDisable(false);
91+
92+
// empty the image planes and the image views if it is not the first
93+
// loaded image
94+
if (!this.planes.isEmpty())
95+
{
96+
this.planes.clear();
97+
this.transformedImage.setImage(null);
98+
this.antitransformedImage.setImage(null);
99+
}
100+
101+
}
102+
}
103+
104+
/**
105+
* The action triggered by pushing the button for apply the dft to the
106+
* loaded image
107+
*/
108+
@FXML
109+
protected void transformImage()
110+
{
111+
// optimize the dimension of the loaded image
112+
Mat padded = this.optimizeImageDim(this.image);
113+
padded.convertTo(padded, CvType.CV_32F);
114+
// prepare the image planes to obtain the complex image
115+
this.planes.add(padded);
116+
this.planes.add(Mat.zeros(padded.size(), CvType.CV_32F));
117+
// prepare a complex image for performing the dft
118+
Core.merge(this.planes, this.complexImage);
119+
120+
// dft
121+
Core.dft(this.complexImage, this.complexImage);
122+
123+
// optimize the image resulting from the dft operation
124+
Mat magnitude = this.createOptimizedMagnitude(this.complexImage);
125+
126+
// show the result of the transformation as an image
127+
this.transformedImage.setImage(this.mat2Image(magnitude));
128+
// set a fixed width
129+
this.transformedImage.setFitWidth(250);
130+
// preserve image ratio
131+
this.transformedImage.setPreserveRatio(true);
132+
133+
// enable the button for performing the antitransformation
134+
this.antitransformButton.setDisable(false);
135+
// disable the button for applying the dft
136+
this.transformButton.setDisable(true);
137+
}
138+
139+
/**
140+
* The action triggered by pushing the button for apply the inverse dft to
141+
* the loaded image
142+
*/
143+
@FXML
144+
protected void antitransformImage()
145+
{
146+
Core.idft(this.complexImage, this.complexImage);
147+
148+
Mat restoredImage = new Mat();
149+
Core.split(this.complexImage, this.planes);
150+
Core.normalize(this.planes.get(0), restoredImage, 0, 255, Core.NORM_MINMAX);
151+
152+
this.antitransformedImage.setImage(this.mat2Image(restoredImage));
153+
// set a fixed width
154+
this.antitransformedImage.setFitWidth(250);
155+
// preserve image ratio
156+
this.antitransformedImage.setPreserveRatio(true);
157+
158+
// disable the button for performing the antitransformation
159+
this.antitransformButton.setDisable(true);
160+
}
161+
162+
/**
163+
* Optimize the image dimensions
164+
*
165+
* @param image
166+
* the {@link Mat} to optimize
167+
* @return the image whose dimensions have been optimized
168+
*/
169+
private Mat optimizeImageDim(Mat image)
170+
{
171+
// init
172+
Mat padded = new Mat();
173+
// get the optimal rows size for dft
174+
int addPixelRows = Core.getOptimalDFTSize(image.rows());
175+
// get the optimal cols size for dft
176+
int addPixelCols = Core.getOptimalDFTSize(image.cols());
177+
// apply the optimal cols and rows size to the image
178+
Imgproc.copyMakeBorder(image, padded, 0, addPixelRows - image.rows(), 0, addPixelCols - image.cols(),
179+
Imgproc.BORDER_CONSTANT, Scalar.all(0));
180+
181+
return padded;
182+
}
183+
184+
/**
185+
* Optimize the magnitude of the complex image obtained from the DFT, to
186+
* improve its visualization
187+
*
188+
* @param complexImage
189+
* the complex image obtained from the DFT
190+
* @return the optimized image
191+
*/
192+
private Mat createOptimizedMagnitude(Mat complexImage)
193+
{
194+
// init
195+
List<Mat> newPlanes = new ArrayList<>();
196+
Mat mag = new Mat();
197+
// split the comples image in two planes
198+
Core.split(complexImage, newPlanes);
199+
// compute the magnitude
200+
Core.magnitude(newPlanes.get(0), newPlanes.get(1), mag);
201+
202+
// move to a logarithmic scale
203+
Core.add(mag, Scalar.all(1), mag);
204+
Core.log(mag, mag);
205+
// optionally reorder the 4 quadrants of the magnitude image
206+
this.shiftDFT(mag);
207+
// normalize the magnitude image for the visualization since both JavaFX
208+
// and OpenCV need images with value between 0 and 255
209+
Core.normalize(mag, mag, 0, 255, Core.NORM_MINMAX);
210+
211+
// you can also write on disk the resulting image...
212+
// Highgui.imwrite("../magnitude.png", mag);
213+
214+
return mag;
215+
}
216+
217+
/**
218+
* Reorder the 4 quadrants of the image representing the magnitude, after
219+
* the DFT
220+
*
221+
* @param image
222+
* the {@link Mat} object whose quadrants are to reorder
223+
*/
224+
private void shiftDFT(Mat image)
225+
{
226+
image = image.submat(new Rect(0, 0, image.cols() & -2, image.rows() & -2));
227+
int cx = image.cols() / 2;
228+
int cy = image.rows() / 2;
229+
230+
Mat q0 = new Mat(image, new Rect(0, 0, cx, cy));
231+
Mat q1 = new Mat(image, new Rect(cx, 0, cx, cy));
232+
Mat q2 = new Mat(image, new Rect(0, cy, cx, cy));
233+
Mat q3 = new Mat(image, new Rect(cx, cy, cx, cy));
234+
235+
Mat tmp = new Mat();
236+
q0.copyTo(tmp);
237+
q3.copyTo(q0);
238+
tmp.copyTo(q3);
239+
240+
q1.copyTo(tmp);
241+
q2.copyTo(q1);
242+
tmp.copyTo(q2);
243+
}
244+
245+
/**
246+
* Set the current stage (needed for the FileChooser modal window)
247+
*
248+
* @param stage
249+
* the stage
250+
*/
251+
public void setStage(Stage stage)
252+
{
253+
this.stage = stage;
254+
}
255+
256+
/**
257+
* Convert a Mat object (OpenCV) in the corresponding Image for JavaFX
258+
*
259+
* @param frame
260+
* the {@link Mat} representing the current frame
261+
* @return the {@link Image} to show
262+
*/
263+
private Image mat2Image(Mat frame)
264+
{
265+
// create a temporary buffer
266+
MatOfByte buffer = new MatOfByte();
267+
// encode the frame in the buffer, according to the PNG format
268+
Highgui.imencode(".png", frame, buffer);
269+
// build and return an Image created from the image encoded in the
270+
// buffer
271+
return new Image(new ByteArrayInputStream(buffer.toArray()));
272+
}
273+
}

0 commit comments

Comments
 (0)