Opencv Java Tutorials Documentation: Release 1.0alpha
Opencv Java Tutorials Documentation: Release 1.0alpha
Release 1.0alpha
Contents
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
3
3
3
3
4
6
8
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
9
9
10
11
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
13
13
13
16
20
20
20
OpenCV Basics
4.1 What we will do in this tutorial . . . .
4.2 Getting started . . . . . . . . . . . . .
4.3 Color channel checkbox . . . . . . . .
4.4 Load an Image and Add it to the Stream
4.5 Calculate a Histogram . . . . . . . . .
4.6 Draw the Histogram . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
23
23
23
23
25
26
27
Fourier Transform
5.1 Goal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 What is the Fourier Transform? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
31
31
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5.3
5.4
5.5
5.6
5.7
5.8
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
31
32
32
33
34
34
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
37
37
37
37
37
38
39
Image Segmentation
7.1 Goal . . . . . . . . . . . . . .
7.2 Canny edge detector . . . . . .
7.3 Dilatation and Erosion . . . . .
7.4 What we will do in this tutorial
7.5 Getting Started . . . . . . . . .
7.6 Using the Canny edge detection
7.7 Canny Result . . . . . . . . . .
7.8 Using the Background Removal
7.9 Background Removal Result . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
43
44
44
44
45
46
46
48
Object Detection
8.1 Goal . . . . . . . . . . . . . . .
8.2 Morphological Image Processing
8.3 What we will do in this tutorial .
8.4 Getting Started . . . . . . . . . .
8.5 Image processing . . . . . . . . .
8.6 Morphological Operators . . . . .
8.7 Object tracking . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
49
49
49
50
51
52
52
Camera Calibration
9.1 Goal . . . . . . . . . . . . . .
9.2 What is the camera calibration?
9.3 Calibration Pattern . . . . . . .
9.4 What we will do in this tutorial
9.5 Getting Started . . . . . . . . .
9.6 Pattern Recognition . . . . . .
9.7 Saving Data . . . . . . . . . . .
9.8 Camera Calibration . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
55
55
55
55
56
56
57
60
60
ii
.
.
.
.
.
.
.
.
63
Note: We are in the process to update these tutorials to use Java 8, only.
Contents:
Contents
Contents
CHAPTER 1
To install OpenCV (with Java support) through Homebrew, you need to add the science tap to Homebrew: brew tap homebrew/science and effectively install OpenCV: brew install opencv3 --HEAD
--with-contrib --with-java
After the installation of OpenCV, the needed jar file and the dylib library will be located at
/usr/local/opt/opencv3/share/OpenCV/java/.
Now press Configure and use the default compilers for Unix Makefiles. Please, be sure to have installed
a C/C++ compiler. In the Ungrouped Entries group, insert the path to the Apache Ant executable (e.g.,
/apache-ant-1.9.6/bin/ant). In the BUILD group, unselect:
BUILD_PERF_TESTS
BUILD_SHARED_LIBRARY to make the Java bindings dynamic library all-sufficient
BUILD_TESTS
BUILD_opencv_python
In the CMAKE group, set to Debug (or Release) the CMAKE_BUILD_TYPE
In the JAVA group:
insert the Java AWT include path (e.g., /usr/lib/jvm/java-1.8.0/include/)
insert the Java AWT library path (e.g., /usr/lib/jvm/java-1.8.0/include/jawt.h)
insert the Java include path (e.g., /usr/lib/jvm/java-1.8.0/include/)
insert the alternative Java include path (e.g., /usr/lib/jvm/java-1.8.0/include/linux)
insert the JVM library path (e.g., /usr/lib/jvm/java-1.8.0/include/jni.h)
Press Configure twice, and the CMake window should appear with a white background. Now, press Generate
and close CMake.
Now open the terminal, go to the build folder of OpenCV and compile everything with the command: make
-j. Notice that the -j flag tells make to run in parallel with the maximum number of allowed job threads,
which makes the build theoretically faster. Wait for the process to be completed... If everything went well you
should have opencv-3xx.jar in the /opencv/build/bin directory and libopencv_java3xx.so in the
/opencv/build/lib directory. The 3xx suffix of each file is a shortcut for the current OpenCV version, e.g., it
will be 300 for OpenCV 3.0 and 310 for OpenCV 3.1. This is everything you need.
From the menu navigate under Java > Build Path > User Libraries and choose New.... Enter a
name for the library (e.g., opencv) and select the newly created user library. Choose Add External JARs...,
browse to select opencv-3xx.jar from your computer. After adding the jar, extend it, select Native library
location and press Edit....
Select External Folder... and browse to select the folder containing the OpenCV libraries (e.g.,
C:\opencv\build\java\x64 under Windows).
In case of MacOS, if you installed OpenCV without Homebrew, you need to create a soft link with .dylib extension for
the .so file. E.g., from the terminal, type: ln -s libopencv_java300.so libopencv_java300.dylib
CHAPTER 2
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
In the New Java Project dialog write the name of your project and click on Finish.
Select User Libraries and click on Next, check the checkbox of the OpenCV library and click Finish.
10
First of all we need to load the OpenCV Native Library previously set on our project.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
The Mat.eye represents a identity matrix, we set the dimensions of it (3x3) and the type of its elements.
As you can notice, if you leave the code just like this, you will get some error; this is due to the fact that eclipse cant
resolve some variables. You can locate your mouse cursor on the words that seem to be errors and wait for a dialog to
pop up and click on the voice Import.... If you do that for all the variables we have added to the code the following
rows:
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
We can now try to build and run our application by clicking on the Run button. You should have the following output:
11
12
CHAPTER 3
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
13
14
Now add your OpenCV user library to your project and click Next.
15
Choose a name for your package, for the FXML file and for the Controller Class. The FXML file will contain the
description of your GUI in FXML language, while the Controller Class will handle all the method and event which
have to be called and managed when the user interacts with the GUIs components.
16
You can add a BorderPane by dragging from the Container menu a borderpane and then drop it in the Hierarchy
menu. Now we can add the button that will allow us to start and stop the stream. Take a button component from the
Controls menu and drop it on the BOTTOM field of our BP. As we can see, on the right we will get three menus
(Properties, Layout, Code) which are used to customize our selected component. For example we can change text of
our button in Start Camera in the Text field under the Properties menu and the id of the button (e.g. start_btn)
in the fx:id field under the Code menu.
17
We are going to need the id of the button later, in order to edit the button properties from our Controllers methods.
As you can see our button is too close to the edge of the windows, so we should add some bottom margin to it; to do
so we can add this information in the Layout menu. In order to make the button work, we have to set the name of the
method (e.g. startCamera) that will execute the action we want to preform in the field OnAction under the Code
menu.
18
Now, we shall add an ImageView component from the Controls menu into the CENTER field of our BP. Lets also
edit the id of the image view (e.g. currentFrame), and add some margin to it.
Finally we have to tell which Controller class will mange the GUI, we can do so by adding our controller class name
in the Controller class field under the Controller menu located in the bottom left corner of the window.
We just created our first GUI by using Scene Builder, if you save the file and return to Eclipse you will notice that
some FXML code has been generated automatically.
19
and load the fxml file that will populate our stage, the root element of the scene and the controller class:
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXHelloCV.fxml"));
BorderPane root = (BorderPane) loader.load();
FXController controller = loader.getController();
The @FXML tag means that we are linking our variable to an element of the fxml file and the value used to declare the
variable has to equal to the id set for that specific element.
The @FXML tag is used with the same meaning for the Actions set under the Code menu in a specific element.
for:
we set:
@FXML
protected void startCamera(ActionEvent event) { ...
This on itself builds on the FFmpeg open source library. A video is composed of a succession of images, we refer to these in the literature as frames. In case of a video file there is a frame rate specifying just how long is
between two frames. While for the video cameras usually there is a limit of just how many frames they can digitalize per second. In our case we set as frame rate 30 frames per sec. To do so we initialize a timer (i.e., a
ScheduledExecutorService) that will open a background task every 33 milliseconds.
Runnable frameGrabber = new Runnable() { ... }
this.timer = Executors.newSingleThreadScheduledExecutor();
this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
20
To check if the binding of the class to a video source was successful or not use the isOpened function:
if (this.capture.isOpened()) { ... }
Closing the video is automatic when the objects destructor is called. However, if you want to close it before this you
need to call its release function.
this.capture.release();
The frames of the video are just simple images. Therefore, we just need to extract them from the VideoCapture object
and put them inside a Mat one.
Mat frame = new Mat();
The video streams are sequential. You may get the frames one after another by the read or the overloaded >> operator.
this.capture.read(frame);
Now we are going to convert our image from BGR to Grayscale format. OpenCV has a really nice function to do this
kind of transformations:
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
Then we can put the frame into the buffer by using the imencode function:
Imgcodecs.imencode(".png", frame, buffer);
This encodes an image into a memory buffer. The function compresses the image and stores it in the memory buffer
that is resized to fit the result.
Note: imencode returns single-row matrix of type CV_8UC1 that contains encoded image as array of bytes.
It takes three parameters:
(.png) File extension that defines the output format.
(frame) Image to be written.
(buffer) Output buffer resized to fit the compressed image.
Once we filled the buffer we have to stream it into an Image by using ByteArrayInputStream:
new Image(new ByteArrayInputStream(buffer.toArray()));
Now we can put the new image in the ImageView. With Java 1.8 we cannot perform an update of a GUI element in a
thread that differs from the main thread; so we need to get the new frame in a second thread and refresh our ImageView
in the main thread:
21
22
CHAPTER 4
OpenCV Basics
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
will be layed out within those insets. Also it will resize children (if resizable) to their preferred heights and uses its
fillWidth property to determine whether to resize their widths to fill its own width or keep their widths to their
preferred (fillWidth defaults to true). A HBox works just like a VBox but it lays out its children horizontally instead
of vertically.
Now we can put inside the VBox a new checkbox, change its text to Show in gray scale, and set an id (e.g.,
grayscale).
<CheckBox fx:id="grayscale" text="Show in gray scale" />
Lets also add a title to this section by putting a text before our new checkbox, but still inside the VBox. Then, set its
text to Controls (we can find the text element under the Shapes menu).
<Text text="Controls" />
The graphic interface is complete for the first task, now we need to work on the controller; in the previous tutorial we
could control the number of channels displayed on screen with the line:
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
In order to control this conversion with the check box, we have to link the check box with a FXML variable:
@FXML
private CheckBox grayscale;
Now we can implement the control by adding a simple if condition which will perform the conversion only if our
check box is checked:
24
if (grayscale.isSelected())
{
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
}
In the controller file we have to define a new variable associated with the checkbox, the method set on the OnAction
field and adapt the code so that it will display the logo when the checkbox is checked and the stream is on. Variable:
@FXML
private CheckBox logoCheckBox;
loadLogo method: In this method we are going to load the image whenever the logoCheckBox id selected (checked).
In order to load the image we have to use a basic OpenCV function: imread. It returns a Mat and takes the path of the
image and a flag (> 0 RGB image, =0 grayscale, <0 with the alpha channel).
@FXML
protected void loadLogo()
{
if (logoCheckBox.isSelected())
this.logo = Imgcodecs.imread("resources/Poli.png");
}
Then we have to take control of our Mats ROI, by doing so we are able to add our logo in the disired area of the
frame defined by the ROI.
Mat imageROI = frame.submat(roi);
We had to make this operation because we can only add Mats with the same sizes; but how can we add two Mat
together? We have to keep in mind that our logo could have 4 channels (RGB + alpha). So we could use two functions:
addWeighted or copyTo. The addWeighted function calculates the weighted sum of two arrays as follows:
dst(I)= saturate(src1(I) alpha + src2(I)* beta + gamma)*
25
where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each channel is processed
independently. The function can be replaced with a matrix expression:
dst = src1*alpha + src2*beta + gamma
Note: Saturation is not applied when the output array has the depth CV_32S. You may even get result of an incorrect
sign in the case of overflow.
Parameters:
src1 first input array.
alpha weight of the first array elements.
src2 second input array of the same size and channel number as src1.
beta weight of the second array elements.
gamma scalar added to each sum.
dst output array that has the same size and number of channels as the input arrays.
So well have:
Core.addWeighted(imageROI, 1.0, logo, 0.7, 0.0, imageROI);
The second method (copyTo) simply copies a Mat into the other. Well have:
Mat mask = logo.clone();
logo.copyTo(imageROI, mask);
Everything we have done so far to add the logo to the image has to perform only IF our checkbox is check and the
image loading process has ended successfully. So we have to add an if condition:
Since we know that the range of information value, we can segment our range in subparts (called bins); lets identify some parts
1. dims: The number of parameters you want to collect data of.
2. bins: It is the number of subdivisions in each dim. In our example, bins = 256
3. range: The limits for the values to be measured. In this case: range = [0,255]
26
Our last goal is to display the histogram of the video stream for either RGB or in grayscale. For this task we are going
to define a method in our controller class that takes a Mat (our current frame) and a boolean that will flag if the frame
is in RGB or in grayscale, for example:
First thing we need to do is to divide the frame into other n frames, where n represents the number of channels of which
our frame is composed. To do so we need to use the Core.split function; it needs a source Mat and a List<Mat>
where to put the different channels. Obviously if the frame is in grayscale the list will have just one element.
Before we could calculate the histogram of each channel we have to prepare all the inputs that the calcHist function
needs. The functions calcHist calculates the histogram of one or more arrays. The elements of a tuple used to increment
a histogram bin are taken from the corresponding input arrays at the same location. Parameters:
images Source arrays. They all should have the same depth, CV_8U or CV_32F, and the same size. Each of
them can have an arbitrary number of channels.
channels List of the dims channels used to compute the histogram. The first array channels are numerated
from 0 to images[0].channels()-1, the second array channels are counted from images[0].channels() to images[0].channels() + images[1].channels()-1, and so on.
mask Optional mask. If the matrix is not empty, it must be an 8-bit array of the same size as images[i]. The
non-zero mask elements mark the array elements counted in the histogram.
hist Output histogram, which is a dense or sparse dims -dimensional array.
histSize Array of histogram sizes in each dimension.
ranges Array of the dims arrays of the histogram bin boundaries in each dimension. When the histogram
is uniform (uniform =true), then for each dimension i it is enough to specify the lower (inclusive) boundary
L_0 of the 0-th histogram bin and the upper (exclusive) boundary U_(histSize[i]-1) for the last histogram bin
histSize[i]-1. That is, in case of a uniform histogram each of ranges[i] is an array of 2 elements. When the histogram is not uniform (uniform=false), then each of ranges[i] contains histSize[i]+1 elements: L_0, U_0=L_1,
U_1=L_2,..., U_(histSize[i]-2)=L_(histSize[i]-1), U_(histSize[i]-1). The array elements, that are not between
L_0 and U_(histSize[i]-1), are not counted in the histogram.
accumulate Accumulation flag. If it is set, the histogram is not cleared in the beginning when it is allocated.
This feature enables you to compute a single histogram from several sets of arrays, or to update the histogram
in time.
The image will be our frame, we dont need a mask and the last flag will be false; thus we need to define the channels,
the hist, the histSize and the ranges:
In the RGB case we will need all of the hist defined, in the grayscale case instead we will use just the hist_b one.
We are now ready to do the histogram calculation:
where gray is the flag we passed to the showHistogram method.
Now back to the Controller class. Lets add a global variable to control the just added image view:
@FXML
private ImageView histogram;
and continue to write the showHistogram method. First thing first, lets create an image to display the histogram:
27
int
int
int
Mat
hist_w = 150;
hist_h = 150;
bin_w = (int) Math.round(hist_w / histSize.get(0, 0)[0]);
histImage = new Mat(hist_h, hist_w, CvType.CV_8UC3, new Scalar(0, 0, 0));
before drawing, we first normalize the histogram so its values fall in the range indicated by the parameters entered:
Core.normalize(hist_b, hist_b, 0, histImage.rows(), Core.NORM_MINMAX, -1, new Mat());
if (!gray){
Core.normalize(hist_g, hist_g, 0, histImage.rows(), Core.NORM_MINMAX, -1, new Mat());
Core.normalize(hist_r, hist_r, 0, histImage.rows(), Core.NORM_MINMAX, -1, new Mat());
}
Lets convert the obtained Mat to an Image with our method mat2Image and update the ImageView with the returned
Image:
histo = mat2Image(histImage);
histogram.setImage(histo);
28
29
30
CHAPTER 5
Fourier Transform
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
5.1 Goal
In this tutorial we are going to create a JavaFX application where we can load a picture from our file system and apply
to it the DFT and the inverse DFT.
31
on the RIGHT two ImageViews one over the other to display the DFT and the iDFT;
<ImageView fx:id="transformedImage" />
<ImageView fx:id="antitransformedImage" />
on the BOTTOM three buttons, the first one to load the picture, the second one to apply the DFT and show it,
and the last one to apply the anti-transform and show it.
When we click the load button we have to set the initial directory of the FC and open the dialog. The FC will return
the selected file:
File file = new File("./resources/");
this.fileChooser.setInitialDirectory(file);
file = this.fileChooser.showOpenDialog(this.main.getStage());
32
Once weve loaded the file we have to make sure that its going to be in grayscale and display the image into the image
view:
this.image = Imgcodecs.imread(file.getAbsolutePath(), Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
this.originalImage.setImage(this.mat2Image(this.image));
Now we can apply the DFT and then get the real and the imaginary part from the complex image:
Core.dft(this.complexImage, this.complexImage);
Core.split(complexImage, newPlanes);
Core.magnitude(newPlanes.get(0), newPlanes.get(1), mag);
Unfortunately the dynamic range of the Fourier coefficients is too large to be displayed on the screen. To use the gray
scale values for visualization we can transform our linear scale to a logarithmic one:
Core.add(Mat.ones(mag.size(), CVType.CV_32F), mag);
Core.log(mag, mag);
Remember, that at the first step, we expanded the image? Well, its time to throw away the newly introduced values.
For visualization purposes we may also rearrange the quadrants of the result, so that the origin (zero, zero) corresponds
with the image center:
image = image.submat(new Rect(0, 0, image.cols() & -2, image.rows() & -2));
int cx = image.cols() / 2;
int cy = image.rows() / 2;
Mat
Mat
Mat
Mat
q0
q1
q2
q3
=
=
=
=
new
new
new
new
Mat(image,
Mat(image,
Mat(image,
Mat(image,
new
new
new
new
33
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
Now we have to normalize our values by using the normalize() function in order to transform the matrix with
float values into a viewable image form:
Core.normalize(mag, mag, 0, 255, Core.NORM_MINMAX);
34
The image is a horizontal sine of 4 cycles. Notice that the DFT just has a single component, represented by 2 bright
spots symmetrically placed about the center of the DFT image. The center of the image is the origin of the frequency
coordinate system. The x-axis runs left to right through the center and represents the horizontal component of frequency. The y-axis runs bottom to top through the center and represents the vertical component of frequency. There
is a dot at the center that represents the (0,0) frequency term or average value of the image. Images usually have
a large average value (like 128) and lots of low frequency information so FT images usually have a bright blob of
components near the center. High frequencies in the horizontal direction will cause bright dots away from the center
in the horizontal direction.
circle.png
35
In this case we have a circular aperture, and what is the Fourier transform of a circular aperture? The diffraction disk
and rings. A large aperture produces a compact transform, instead a small one produces a larger Airy pattern; thus the
disk is greater if aperture is smaller; according to Fourier properties, from the center to the middle of the first dark ring
the distance is (1.22 x N) / d; in this case N is the size of the image, and d is the diameter of the circle. An Airy disk is
the bright center of the diffraction pattern created from a circular aperture ideal optical system; nearly half of the light
is contained in a diameter of 1.02 x lamba x f_number.
The source code of the entire tutorial is available on GitHub.
36
CHAPTER 6
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
6.1 Goal
In this tutorial we are going to use well-known classifiers that have been already trained and distributed by OpenCV
in order to detect and track a moving face into a video stream.
37
on TOP a VBox a HBox and a separator. In the HBox we are goning to need two checkboxes, the first one is to
select the Haar Classifier and the second one is to select the LBP Classifier.
<CheckBox fx:id="haarClassifier" onAction="#haarSelected" text="Haar Classifier"/>
<CheckBox fx:id="lbpClassifier" onAction="#lbpSelected" text="LBP Classifier"/>
in the CENTRE we are going to put an ImageView for the web cam stream.
<ImageView fx:id="originalFrame" />
on the BOTTOM we can add the usual button to start/stop the stream
<Button fx:id="cameraButton" alignment="center" text="Start camera" onAction="#startCamera"
lbpSelected for the LPB we can use the same method and change the path of the classifier to be loaded:
38
this.checkboxSelection("resources/lbpcascades/lbpcascade_frontalface.xml");
Then we have to set the minimum size of the face to be detected (this required is need in the actual detection function).
Lets set the minimum size as the 20% of the frame height:
if (this.absoluteFaceSize == 0)
{
int height = grayFrame.rows();
if (Math.round(height * 0.2f) > 0)
{
this.absoluteFaceSize = Math.round(height * 0.2f);
}
}
The detectMultiScale function detects objects of different sizes in the input image. The detected objects are
returned as a list of rectangles. The parameters are:
image Matrix of the type CV_8U containing an image where objects are detected.
objects Vector of rectangles where each rectangle contains the detected object.
scaleFactor Parameter specifying how much the image size is reduced at each image scale.
minNeighbors Parameter specifying how many neighbors each candidate rectangle should have to retain it.
flags Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used
for a new cascade.
minSize Minimum possible object size. Objects smaller than that are ignored.
maxSize Maximum possible object size. Objects larger than that are ignored.
So the result of the detection is going to be in the objects parameter or in our case faces.
Lets put this result in an array of rects and draw them on the frame, by doing so we can display the detected face are:
Rect[] facesArray = faces.toArray();
for (int i = 0; i < facesArray.length; i++)
Imgproc.rectangle(frame, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3);
As you can see we selected the color green with a transparent background: Scalar(0, 255, 0, 255). .tl()
and .br() stand for top-left and bottom-right and they represents the two opposite vertexes. The last parameter just
set the thickness of the rectangles border.
The tracking part can be implemented by calling the detectAndDisplay method for each frame.
39
40
41
42
CHAPTER 7
Image Segmentation
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
7.1 Goal
In this tutorial we are going to create a JavaFX application where we can decide to apply to video stream captured
from our web cam either a Canny edge detector or a trivial background removal using the two basic morphological
operations: dilatation and erosion.
The Canny algorithm contains a number of adjustable parameters, which can affect the computation time and effectiveness of t
The size of the Gaussian filter: the smoothing filter used in the first stage directly affects the results of the
Canny algorithm. Smaller filters cause less blurring, and allow detection of small, sharp lines. A larger
filter causes more blurring, smearing out the value of a given pixel over a larger area of the image.
Thresholds: A threshold set too high can miss important information. On the other hand, a threshold set
too low will falsely identify irrelevant information (such as noise) as important. It is difficult to give a
generic threshold that works well on all images. No tried and tested approach to this problem yet exists.
For our purpose we are going to set the filter size to 3 and the threshold editable by a Slider.
43
In the second HBox we need two checkboxes, the first one to select the Backgrond removal mode and the second
one to say if we want to invert the algorithm (a sort of foreground removal).
<CheckBox fx:id="dilateErode" onAction="#dilateErodeSelected" text="Background removal"/>
<CheckBox fx:id="inverse" text="Invert" disable="true"/>
in the CENTRE we are going to put an ImageView for the web cam stream.
<ImageView fx:id="originalFrame" />
on the BOTTOM we can add the usual button to start/stop the stream
44
doCanny is a method that we define to execute the edge detection. First, we convert the image into a grayscale one
and blur it with a filter of kernel size 3:
Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
Imgproc.blur(grayImage, detectedEdges, new Size(3, 3));
45
Finally, we will use the function copyTo to map only the areas of the image that are identified as edges (on a black
background).
frame.copyTo(dest, detectedEdges);
copyTo copies the src image onto dest. However, it will only copy the pixels in the locations where they have
non-zero values.
If the background is uniform and fills most of the frame, its value should be close to mean just calculated. Then we
can use the mean as the threshold to separate the background from the foreground, depending on the invert checkbox
we need to perform a back(fore)ground removal:
46
if (this.inverse.isSelected())
Imgproc.threshold(hsvPlanes.get(0), thresholdImg, threshValue, 179.0, Imgproc.THRESH_BINARY_INV)
else
Imgproc.threshold(hsvPlanes.get(0), thresholdImg, threshValue, 179.0, Imgproc.THRESH_BINARY);
Now we apply a low pass filter (blur) with a 5x5 kernel mask to enhance the result:
Imgproc.blur(thresholdImg, thresholdImg, new Size(5, 5));
Finally apply the dilatation then the erosion (closing) to the image:
Imgproc.dilate(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 1);
Imgproc.erode(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 3);
At last, we can apply the image weve just obtained as a mask to the original frame:
Mat foreground = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
frame.copyTo(foreground, thresholdImg);
47
48
CHAPTER 8
Object Detection
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
8.1 Goal
In this tutorial we are going to identify and track one or more tennis balls. It performs the detection of the tennis balls
upon a webcam video stream by using the color range of the balls, erosion and dilation, and the findContours method.
49
in the CENTER. we are going to put three ImageViews, the first one shows normal image from the web cam
stream, the second one will show mask image and the last one will show morph image. The HBox is used to
normal image and VBox to put the other ones.
<HBox alignment="CENTER" spacing="5">
<padding>
<Insets right="10" left="10" />
</padding>
<ImageView fx:id="originalFrame" />
<VBox alignment="CENTER" spacing="5">
<ImageView fx:id="maskImage" />
<ImageView fx:id="morphImage" />
</VBox>
</HBox>
on the BOTTOM we can add the usual button to start/stop the stream and the current values HSV selected with
the sliders.
<Button fx:id="cameraButton" alignment="center" text="Start camera" onAction="#startCamera"
<Separator />
<Label fx:id="hsvCurrentValues" />
50
Values of HSV image With the sliders we can modify the values of the HSV Image, the image will be
updtated in real time, that allows to increase or decrease the capactity to recognize object into the image. .
// get thresholding values from the UI
// remember: H ranges 0-180, S and V range 0-255
Scalar minValues = new Scalar(this.hueStart.getValue(), this.saturationStart.getValue(),
this.valueStart.getValue());
Scalar maxValues = new Scalar(this.hueStop.getValue(), this.saturationStop.getValue(),
51
this.valueStop.getValue());
// show the current selected HSV range
String valuesToPrint = "Hue range: " + minValues.val[0] + "-" + maxValues.val[0]
+ "\tSaturation range: " + minValues.val[1] + "-" + maxValues.val[1] + "\tValue range: "
+ minValues.val[2] + "-" + maxValues.val[2];
this.onFXThread(this.hsvValuesProp, valuesToPrint);
// threshold HSV image to select tennis balls
Core.inRange(hsvImage, minValues, maxValues, mask);
// show the partial output
this.onFXThread(maskProp, this.mat2Image(mask));
// find contours
Imgproc.findContours(maskedImage, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_
// if any contour exist...
if (hierarchy.size().height > 0 && hierarchy.size().width > 0)
{
// for each contour, display it in blue
for (int idx = 0; idx >= 0; idx = (int) hierarchy.get(0, idx)[0])
{
Imgproc.drawContours(frame, contours, idx, new Scalar(250, 0, 0));
}
}
52
53
54
CHAPTER 9
Camera Calibration
Note: We assume that by now you have already read the previous tutorials. If not, please check previous tutorials at
http://opencv-java-tutorials.readthedocs.org/en/latest/index.html. You can also find the source code and resources at
https://github.com/opencv-java/
Warning: This tutorial is not updated to OpenCV 3.0.
9.1 Goal
The goal of this tutorial is to learn how to calibrate a camera given a set of chessboard images.
55
The reason why we use this image is because there are some OpenCV functions that can recognize this pattern and
draw a scheme which highlights the intersections between each block. To make the calibration work you need to print
the chessboard image and show it to the cam; it is important to maintain the sheet still, better if stick to a surface. In
order to make a good calibration, we need to have about 20 samples of the pattern taken from different angles and
distances.
56
Lets also add some labels before each text fields. Each text field is going to need an id, and lets put a standard value
for them already.
<Label text="Boards #" />
<TextField fx:id="numBoards" text="20" maxWidth="50" />
<Label text="Horizontal corners #" />
<TextField fx:id="numHorCorners" text="9" maxWidth="50" />
<Label text="Vertical corners #" />
<TextField fx:id="numVertCorners" text="6" maxWidth="50" />
For the button instead, set the id and a method for the onAction field:
<Button fx:id="applyButton" alignment="center" text="Apply" onAction="#updateSettings" />
on the LEFT add an ImageView inside a VBox for the normal cam stream; set an id for it.
<ImageView fx:id="originalFrame" />
on the RIGHT add an ImageView inside a VBox for the calibrated cam stream; set an id for it.
<ImageView fx:id="originalFrame" />
in the BOTTOM add a start/stop cam stream button and a snapshot button inside a HBox; set an id and a action
method for each one.
57
some reference systems 3D point where the chessboard is located (lets assume that the Z axe is always 0):
The findChessboardCorners function attempts to determine whether the input image is a view of the chessboard
pattern and locate the internal chessboard corners. Its parameters are:
image Source chessboard view. It must be an 8-bit grayscale or color image.
patternSize Number of inner corners per a chessboard row and column
corners Output array of detected corners.
flags Various operation flags that can be zero or a combination of the following values:
CV_CALIB_CB_ADAPTIVE_THRESH Use adaptive thresholding to convert the image to black and
white, rather than a fixed threshold level (computed from the average image brightness).
CV_CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with equalizeHist before applying fixed or adaptive thresholding.
CV_CALIB_CB_FILTER_QUADS Use additional criteria (like contour area, perimeter, square-like
shape) to filter out false quads extracted at the contour retrieval stage.
CALIB_CB_FAST_CHECK Run a fast check on the image that looks for chessboard corners, and
shortcut the call if none is found. This can drastically speed up the call in the degenerate condition
when no chessboard is observed.
Warning: Before doing the findChessboardCorners convert the image to grayscale and save the board
size into a Size variable:
Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
Size boardSize = new Size(this.numCornersHor, this.numCornersVer);
The function draws individual chessboard corners detected either as red circles if the board was not found, or as
colored corners connected with lines if the board was found.
Its parameters are:
image Destination image. It must be an 8-bit color image.
patternSize Number of inner corners per a chessboard row and column.
corners Array of detected corners, the output of findChessboardCorners.
58
patternWasFound Parameter indicating whether the complete board was found or not. The return value of
findChessboardCorners should be passed here.
Now we can activate the Snapshot button to save the data.
this.snapshotButton.setDisable(false);
59
We should take the set number of snapshots from different angles and depth, in order to make the calibration.
Note: We dont actually save the image but just the data we need.
60
The calibrateCamera function estimates the intrinsic camera parameters and extrinsic parameters for each of the
views. The algorithm is based on [Zhang2000] and [BouguetMCT]. The coordinates of 3D object points and their
corresponding 2D projections in each view must be specified. Its parameters are:
objectPoints In the new interface it is a vector of vectors of calibration pattern points in the calibration pattern
coordinate space. The outer vector contains as many elements as the number of the pattern views. The points
are 3D, but since they are in a pattern coordinate system, then, if the rig is planar, it may make sense to put the
model to a XY coordinate plane so that Z-coordinate of each input object point is 0.
imagePoints It is a vector of vectors of the projections of calibration pattern points.
imageSize Size of the image used only to initialize the intrinsic camera matrix.
cameraMatrix Output 3x3 floating-point camera matrix A = |fx 0 cx| |0 fy cy| |0 0 1|.
If
CV_CALIB_USE_INTRINSIC_GUESS and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or
all of fx, fy, cx, cy must be initialized before calling the function.
distCoeffs Output vector of distortion coefficients of 4, 5, or 8 elements.
rvecs Output vector of rotation vectors estimated for each pattern view. That is, each k-th rotation vector together
with the corresponding k-th translation vector.
tvecs Output vector of translation vectors estimated for each pattern view.
We ran calibration and got cameras matrix with the distortion coefficients we may want to correct the image using
undistort function:
if (this.isCalibrated)
{
// prepare the undistored image
Mat undistored = new Mat();
Imgproc.undistort(frame, undistored, intrinsic, distCoeffs);
undistoredImage = mat2Image(undistored);
}
The undistort function transforms an image to compensate radial and tangential lens distortion.
The source code of the entire tutorial is available on GitHub.
61
62
CHAPTER 10
genindex
modindex
search
63