Установка и использование opencv в windows

Documentation for opencv-python

CI build process

The project is structured like a normal Python package with a standard file.
The build process for a single entry in the build matrices is as follows (see for example file):

  1. In Linux and MacOS build: get OpenCV’s optional C dependencies that we compile against

  2. Checkout repository and submodules

    • OpenCV is included as submodule and the version is updated
      manually by maintainers when a new OpenCV release has been made
    • Contrib modules are also included as a submodule
  3. Find OpenCV version from the sources

  4. Build OpenCV

    • tests are disabled, otherwise build time increases too much
    • there are 4 build matrix entries for each build combination: with and without contrib modules, with and without GUI (headless)
    • Linux builds run in manylinux Docker containers (CentOS 5)
    • source distributions are separate entries in the build matrix
  5. Rearrange OpenCV’s build result, add our custom files and generate wheel

  6. Linux and macOS wheels are transformed with auditwheel and delocate, correspondingly

  7. Install the generated wheel

  8. Test that Python can import the library and run some sanity checks

  9. Use twine to upload the generated wheel to PyPI (only in release builds)

Steps 1—4 are handled by .

The build can be customized with environment variables. In addition to any variables that OpenCV’s build accepts, we recognize:

  • . Set to to emulate the CI environment build behaviour. Used only in CI builds to force certain build flags on in . Do not use this unless you know what you are doing.
  • and . Set to to build the contrib and/or headless version
  • , Set to to enable the Java client build. This is disabled by default.
  • . Additional arguments for OpenCV’s CMake invocation. You can use this to make a custom build.

See the next section for more info about manual builds outside the CI environment.

Manual builds

If some dependency is not enabled in the pre-built wheels, you can also run the build locally to create a custom wheel.

  1. Clone this repository:
  2. Add custom Cmake flags if needed, for example: (in Windows you need to set environment variables differently depending on Command Line or PowerShell)
  3. Select the package flavor which you wish to build with and : i.e. if you wish to build
  4. Run . NOTE: make sure you have the latest version, the command replaces the old command which does not support .
  5. You’ll have the wheel file in the folder and you can do with that whatever you wish
    • Optional: on Linux use some of the images as a build hosts if maximum portability is needed and run for the wheel after build
    • Optional: on macOS use (same as but for macOS) for better portability

Source distributions

Since OpenCV version 4.3.0, also source distributions are provided in PyPI. This means that if your system is not compatible with any of the wheels in PyPI, will attempt to build OpenCV from sources. If you need a OpenCV version which is not available in PyPI as a source distribution, please follow the manual build guidance above instead of this one.

You can also force to build the wheels from the source distribution. Some examples:

If you need contrib modules or headless version, just change the package name (step 4 in the previous section is not needed). However, any additional CMake flags can be provided via environment variables as described in step 3 of the manual build section. If none are provided, OpenCV’s CMake scripts will attempt to find and enable any suitable dependencies. Headless distributions have hard coded CMake flags which disable all possible GUI dependencies.

On slow systems such as Raspberry Pi the full build may take several hours. On a 8-core Ryzen 7 3700X the build takes about 6 minutes.

Versioning

script searches for the version information from OpenCV sources and appends also a revision number specific to this repository to the version string. It saves the version information to file under in addition to some other flags.

Releases

A release is made and uploaded to PyPI when a new tag is pushed to master branch. These tags differentiate packages (this repo might have modifications but OpenCV version stays same) and should be incremented sequentially. In practice, release version numbers look like this:

e.g.

The master branch follows OpenCV master branch releases. 3.4 branch follows OpenCV 3.4 bugfix releases.

Development builds

Every commit to the master branch of this repo will be built. Possible build artifacts use local version identifiers:

e.g.

These artifacts can’t be and will not be uploaded to PyPI.

Supported Python versions

Python 3.x compatible pre-built wheels are provided for the officially supported Python versions (not in EOL):

  • 3.6
  • 3.7
  • 3.8
  • 3.9

Starting from 4.2.0 and 3.4.9 builds the macOS Travis build environment was updated to XCode 9.4. The change effectively dropped support for older than 10.13 macOS versions.

Участки кода, привлекшие внимание при рассмотрении отчета анализатора

WriteableBitmapConverter

  • V3005 The ‘optimumChannels’ variable is assigned to itself. WriteableBitmapConverter.cs 22
  • V3005 The ‘optimumChannels’ variable is assigned to itself. WriteableBitmapConverter.cs 23
  • V3005 The ‘optimumTypes’ variable is assigned to itself. WriteableBitmapConverter.cs 50
  • V3005 The ‘optimumTypes’ variable is assigned to itself. WriteableBitmapConverter.cs 51

PixelFormats System.Windows.MediaWriteableBitmapConverter optimumChannelsoptimumChannels

Прочувствуйте мощь и силу статического анализа.Предупреждение PVS-StudioV3021

if

  • если истинно первое условное выражение, будет осуществлён выход из метода;
  • если первое условие ложно, второе также будет ложно, так как проверяемая переменная — t — между условными выражениями не изменяется.

Vec2s Предупреждение PVS-StudioV3010

RotatedRectangleIntersection Point2fintersectingRegionintersectingRegion ToString(). Предупреждения PVS-Studio

  • V3021 There are two ‘if’ statements with identical conditional expressions. The first ‘if’ statement contains method return. This means that the second ‘if’ statement is senseless Cv2_calib3d.cs 1370
  • V3022 Expression ‘objectPoints == null’ is always false. Cv2_calib3d.cs 1372

ifthenif

Милый Copy-Paste.

  • V3021 There are two ‘if’ statements with identical conditional expressions. The first ‘if’ statement contains method return. This means that the second ‘if’ statement is senseless Cv2_calib3d.cs 1444
  • V3022 Expression ‘objectPoints == null’ is always false. Cv2_calib3d.cs 1446

Предупреждение PVS-StudioV3022

labellabel Предупреждение PVS-StudioV3038

photo_fastNlMeansDenoisingMulti

templateWindowSizetemporalWindowSizetemporalWindowSizephoto_fastNlMeansDenoisingMulti

  • V3038 The argument was passed to method several times. It is possible that other argument should be passed instead. Cv2_photo.cs 149
  • V3038 The argument was passed to method several times. It is possible that other argument should be passed instead. Cv2_photo.cs 180
  • V3038 The argument was passed to method several times. It is possible that other argument should be passed instead. Cv2_photo.cs 205

Предупреждение PVS-StudioV3066

calib3d_Rodrigues_MatToVec

calib3d_Rodrigues_MatToVecmatrixM.CvPtr vectorM.CvPtrПредупреждение PVS-StudioV3063

data == nulltruedata null

Понимаю, что вы уже устали, но осталось совсем немного.Предупреждение PVS-StudioV3127

nullwindow nullwindowsrc2

Предупреждение PVS-StudioV3142

returnSubMat

Предупреждение PVS-StudioV3022

Предупреждение PVS-StudioV3022

Предупреждение PVS-StudioV3022

V3022string

Load an Image and Add it to the Stream¶

The next step is to add another check box which, if checked, will trigger the display of an image over the camera stream.
Let’s start by adding the image to the project; create a new folder in the root directory of your project and put the image in there.
In my project I have a folder with a image.
Go back to Eclipse and refresh your project (you should have the new folder in it).
Let’s open the fxml file with Scene Builder and add a new checkbox below the one that controls the stream colors; we have to set the text, the name of the method in the field and a id.
In the code we will have for example:

<CheckBox fx:id="logoCheckBox" text="Show logo" onAction="#loadLogo" />

In the controller file we have to define a new variable associated with the checkbox, the method set on the 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;

metheod:
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 return 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 = Highgui.imread("resources/Poli.png");
}

Adapt the code.

We are going to add some variants to the code in order to display our logo in a specific region of the stream. This means that for each frame capture, before the image could be converted into 1 or 3 channels, we have to set a ROI (region of interest) in which we want to place the logo.
Usually a ROI of an Image is a portion of it, we can define the roi as a Rect object.
Rect is a template class for 2D rectangles, described by the following parameters:

Rect roi = new Rect(frame.cols()-logo.cols(), frame.rows()-logo.rows(), logo.cols(), logo.rows());

Then we have to take control of our Mat’s 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: or .
The function calculates the weighted sum of two arrays as follows:

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:

Note

Saturation is not applied when the output array has the depth . 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 we’ll have:

Core.addWeighted(imageROI, 1.0, logo, 0.7, 0.0, imageROI);

The second method () simply copies a Mat into the other. We’ll 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:

Getting Started¶

Create a new JavaFX project (e.g. “CameraCalibration”) with the usual OpenCV user library.
Open Scene Builder and add a Border Pane with:

on TOP we need to have the possibility to set the number of samples for the calibration, the number of horizontal corners we have in the test image, the number of vertical corners we have in the test image and a button to update this data. To make things cleaner let’s put all these elements inside a HBox.

<HBox alignment="CENTER" spacing="10">

Let’s also add some labels before each text fields.
Each text field is going to need an id, and let’s 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.

<Button fx:id="cameraButton" alignment="center" text="Start camera" onAction="#startCamera" disable="true" />
<Button fx:id="snapshotButton" alignment="center" text="Take snapshot" onAction="#takeSnapshot" disable="true" />

Your GUI will look something like this:

Draw the Histogram¶

Next step is to draw the calculated histogram in our GUI.
Open the fxml file with Scene Builder and add an ImageView above the “Controls” text in the right of the BP and set its id:

<ImageView fx:id="histogram" />

Now back to the Controller class. Let’s add a global variable to control the just added image view:

@FXML
private ImageView histogram;

and continue to write the method.
First thing first, let’s create an image to display the histogram:

int hist_w = 150;
int hist_h = 150;
int bin_w = (int) Math.round(hist_w  histSize.get(, )[]);
Mat histImage = new Mat(hist_h, hist_w, CvType.CV_8UC3, new Scalar(, , ));

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, , histImage.rows(), Core.NORM_MINMAX, -1, new Mat());
if (!gray){
   Core.normalize(hist_g, hist_g, , histImage.rows(), Core.NORM_MINMAX, -1, new Mat());
   Core.normalize(hist_r, hist_r, , histImage.rows(), Core.NORM_MINMAX, -1, new Mat());
}

Now we can draw the histogram in our Mat:

for (int i = 1; i < histSize.get(, )[]; i++){
   Imgproc.line(histImage, new Point(bin_w * (i - 1), hist_h - Math.round(hist_b.get(i - 1, )[])), new Point(bin_w * (i), hist_h - Math.round(hist_b.get(i, )[])), new Scalar(255, , ), 2, 8, );
   if (!gray){
      Imgproc.line(histImage, new Point(bin_w * (i - 1), hist_h - Math.round(hist_g.get(i - 1, )[])),new Point(bin_w * (i), hist_h - Math.round(hist_g.get(i, )[])), new Scalar(, 255, ), 2, 8, );
      Imgproc.line(histImage, new Point(bin_w * (i - 1), hist_h - Math.round(hist_r.get(i - 1, )[])),Math.round(hist_r.get(i, )[])), new Scalar(, , 255), 2, 8, );
   }
}

Let’s convert the obtained Mat to an Image with our method and update the ImageView with the returned Image:

histo = mat2Image(histImage);
histogram.setImage(histo);

The source code of the entire tutorial is available on GitHub.

Доступ к пикселям и манипулирование ими

Для того, чтобы узнать высоту, ширину и количество каналов у изображения можно использовать атрибут shape:

Важно помнить, что у изображений в оттенках серого img.shape будет недоступно, так как данные изображения представлены в виде 2D массива. Чтобы получить доступ к значению пикселя, нам просто нужно указать координаты x и y пикселя, который нас интересует

Также важно помнить, что библиотека OpenCV хранит каналы формата RGB в обратном порядке, в то время как мы думаем в терминах красного, зеленого и синего, то OpenCV хранит их в порядке синего, зеленого и красного цветов:

Чтобы получить доступ к значению пикселя, нам просто нужно указать координаты x и y пикселя, который нас интересует

Также важно помнить, что библиотека OpenCV хранит каналы формата RGB в обратном порядке, в то время как мы думаем в терминах красного, зеленого и синего, то OpenCV хранит их в порядке синего, зеленого и красного цветов:. Cначала мы берём пиксель, который расположен в точке (0,0)

Данный пиксель, да и любой другой пиксель, представлены в виде кортежа. Заметьте, что название переменных расположены в порядке b, g и r. В следующей строке выводим значение каждого канала на экран. Как можно увидеть, доступ к значениям пикселей довольно прост, также просто можно и манипулировать значениями пикселей:

Cначала мы берём пиксель, который расположен в точке (0,0). Данный пиксель, да и любой другой пиксель, представлены в виде кортежа. Заметьте, что название переменных расположены в порядке b, g и r. В следующей строке выводим значение каждого канала на экран. Как можно увидеть, доступ к значениям пикселей довольно прост, также просто можно и манипулировать значениями пикселей:

В первой строке мы устанавливаем значение пикселя (0, 0) равным (255, 0, 0), затем мы снова берём значение данного пикселя и выводим его на экран, в результате мне на консоль вывелось следующее:

Contributors

Big thanks to everybody who contributed (here is the incomplete list of patch authors; please report if you contributed but do not see your name here):

opencv

Alexander Alekhin, Liubov Batanina, Dmitry Kurtaev, Maksim Shabunin, YashasSamaga, Vadim Pisarevsky, Alexander Smorkalov, Anton Potapov, ashishiva3@gmail.com, Anatoliy Talamanov, Hannes Achleitner, Maxim Pashchenkov, Gourav Roy, Vadim Levin, Yashas Samaga B L, Alexander Duda, Chip Kerchner, Collin Brake, Ganesh Kathiresan, Julien, Peter Würtz, Sayed Adel, Andrey Golubev, Brian Wignall, Dizhenin Vlad, Dmitry Matveev, Ilya Lavrenov, Moritz Fischer-Gundlach, NesQl, Pavel Rojtberg, Polina Smolnikova, Rajkiran Natarajan, StefanBruens, berak, catree, cyy, hannesa2, Aditya Kumar, Alexey Smirnov, Andrei-Florin BENCSIK, Andrew Bruce, Arnaud Brejeon, Augusto Fraga Giachero, Christoph Schmidt-Hieber, Eduard Trulls, Elizarov Ilya, Gagandeep Singh, Ianaré Sévi, Igor Murzov, Ivan Galanin, Jan Solanti, Janusz Lisiecki, Jed, Julien Maille, Karl Liu, Khem Raj, Manoj Gupta, Miguel Pari Soto, MoonChasing, Muhammad Taha, Niklas Hambüchen, Nuzhny007, Omar Hassan, Paul E. Murphy, Pierre Letessier, Pratik Raj, RAJKIRAN NATARAJAN, Samuel Thibault, Smirnov Alexey, Suleyman TURKMEN, Talamanov, Anatoliy, Tomoaki Teshima, Yuhel Tanaka, Yuriy Obukh, Zach Lowry, ankit6979, atinfinity, baka-gori, cudawarped, firebladed, gapry, h6197627, ihsan314, iteal, jshiwam, keeper121, midjji, olramde, rayonnant14, sajarindider

opencv_contrib

Alexander Alekhin, Pavel Rojtberg, Alexander Smorkalov, Ayush Garg, catree, David Geldreich, Gagandeep Singh, Igor Murzov, Kunal Tyagi, Lim, Maksim Shabunin, Miloš Komarčević, RAJKIRAN NATARAJAN, Rostislav Vasilikhin, Tobias Senst, Vishal Chiluka, Xavier Weber, akashsharma02, atinfinity, berak, cudawarped, jshiwam, raanyild, shimat, sunitanyk

Detection and Tracking¶

Once we’ve loaded the classifiers we are ready to start the detection; we are going to implement the detection in the method.
First of all we need to convert the frame in grayscale and equalize the histogram to improve the results:

Imgproc.cvtColor(frame, grayFrame, Imgproc.COLOR_BGR2GRAY);
Imgproc.equalizeHist(grayFrame, grayFrame);

Then we have to set the minimum size of the face to be detected (this required is need in the actual detection function). Let’s set the minimum size as the 20% of the frame height:

if (this.absoluteFaceSize == )
{
    int height = grayFrame.rows();
    if (Math.round(height * 0.2f) > )
    {
            this.absoluteFaceSize = Math.round(height * 0.2f);
    }
}

Now we can start the detection:

this.faceCascade.detectMultiScale(grayFrame, faces, 1.1, 2,  | Objdetect.CASCADE_SCALE_IMAGE, new Size(this.absoluteFaceSize, this.absoluteFaceSize), new Size());

The function detects objects of different sizes in the input image. The detected objects are returned as a list of rectangles.
The parameters are:

So the result of the detection is going to be in the objects parameter or in our case .

Let’s 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 = ; i < facesArray.length; i++)
    Imgproc.rectangle(frame, facesArrayitl(), facesArrayibr(), new Scalar(, 255, , 255), 3);

As you can see we selected the color green with a transparent background: .
and stand for top-left and bottom-right and they represents the two opposite vertexes.
The last parameter just set the thickness of the rectangle’s border.

The tracking part can be implemented by calling the method for each frame.

The source code of the entire tutorial is available on GitHub.

Installing e(fx)clipse plugin and Scene Builder¶

In Eclipse, install the plugin, by following the guide at .
If you choose not to install such a plugin, you have to create a traditional Java project, only.
Download and install JavaFX Scene Builder 2.0 from http://www.oracle.com/technetwork/java/javafxscenebuilder-1x-archive-2199384.html.

Now you can create a new JavaFX project. and select .

Choose a name for your project and click .

Now add your OpenCV user library to your project and click .

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 GUI’s components.

Преобразование изображений

Последней, но одной из самых важных тем, поднятых нами в данном обзоре библиотеки OpenCV, является преобразование изображений. Эта тема находит применение в самых разных приложениях, но отдельно следует упомянуть задачу аугментации данных для моделей машинного обучения. Речь идет о ситуациях, когда в нашем датасете для полноценного обучения недостаточно данных, и мы, дополняя и видоизменяя существующие картинки, увеличиваем первоначальный датасет до нужного размера. Это помогает нам серьезно увеличить точность работы обучаемой модели.

Список возможных преобразований весьма велик и включает в себя масштабирование, афинное преобразование изображений, вращение, транспонирование и многое другое. Мы кратко расскажем только про масштабирование и вращение, но в библиотеке OpenСV есть поддержка всех возможных преобразований. Начнем с масштабирования.

Масштабирование

Попросту говоря, масштабирование — это не что иное как изменение размеров изображения, его увеличение либо уменьшение. В библиотеке OpenCV для этого существует функция resize. У этой функции, в свою очередь, есть три метода: ,  и . Давайте на примере конкретного кода разберем, как это все работает. Пожалуйста, внимательно изучите код, комментарии к нему и описание ниже.

import cv2
import numpy as np
import matplotlib.pyplot as plt

image = cv2.imread('my_bike.jpg')

# Увеличиваем масштаб/расширяем в 2 раза по ширине и высоте
result_1 = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)

# Уменьшаем масштаб/сжимаем в 2 раза по ширине и высоте
result_2 = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_AREA)

# Выводим на экран получившиеся изображения
plt.imshow(result_1)
plt.imshow(result_2)
plt.show()

Здесь в функции параметр определяет масштаб изменений по ширине, — по высоте, а параметр отвечает за сам способ изменений (то есть расширение или сжатие).

Вращение

Вращение позволяет нам перемещать изображение вокруг определенной оси на заданный угол.

Перед тем как мы научимся вращать наши изображения при помощи библиотеки OpenСV, давайте вспомним, что существует линейный оператор под названием матрица поворота, который как раз и осуществляет преобразования такого рода. Мы не будем вдаваться в математические детали, так как в библиотеке OpenCV эта матрица вычисляется при помощи одного вызова функции. Вы это увидите в следующем коде:

import cv2
import matplotlib.pyplot as plt

# Загружаем изображение велосипеда
image = cv2.imread('my_bike.jpg',0)

# ряды и колонки
r, c = image.shape

matrix = cv2.getRotationMatrix2D((cols/2,rows/2), 180, 1)
result = cv2.warpAffine(image,matrix,(c,r))

# Выводим на экран повернутое изображение
plt.imshow(result)
plt.show()

В функции — это угол, на который наше изображение должно быть повернуто, — это масштабный коэффициент. Данная функция возвращает матрицу поворота, которая записывается в переменную .

Далее функция , используя вычисленную на предыдущем шаге матрицу поворота, поворачивает наше изображение в соответствии с заданной спецификацией.

Working with Scene Builder¶

If you have installed Scene Builder you can now right click on your FXML file in Eclipse and select .
Scene Builder can help construct you gui by interacting with a graphic interface; this allows you to see a real time preview of your window and modify your components and their position just by editing the graphic preview. Let’s take a look at what I’m talking about.
At fist the FXML file will have just an AnchorPane.
An AnchorPane allows the edges of child nodes to be anchored to an offset from the anchorpane’s edges. If the anchorpane has a border and/or padding set, the offsets will be measured from the inside edge of those insets.
The anchorpane lays out each managed child regardless of the child’s visible property value; unmanaged children are ignored for all layout calculations.
You can go ahead and delete the anchorpane and add a BorderPane instaed.
A BorderPane lays out children in top, left, right, bottom, and center positions.

You can add a BorderPane by dragging from the menu a borderpane and then drop it in the menu.
Now we can add the button that will allow us to start and stop the stream. Take a button component from the 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 field under the menu and the id of the button (e.g. “start_btn”) in the field under the menu.

We are going to need the id of the button later, in order to edit the button properties from our Controller‘s 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 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 under the menu.

Now, we shall add an ImageView component from the menu into the CENTER field of our BP. Let’s 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 field under the menu located in the bottom left corner of the window.

Изменение размера изображения

Первый метод, который мы изучим — это как поменять высоту и ширину у изображения. Для этого в opencv есть такая функция как resize():

Данная функция первым аргументом принимает изображение, размер которого мы хотим изменить, вторым — кортеж, который должен содержать в себе ширину и высоту для нового изображения, третьим — метод интерполяции(необязательный). Интерполяция — это алгоритм, который находит неизвестные промежуточные значения по имеющемуся набору известных значений. Фактически, это то, как будут заполняться новые пиксели при модификации размера изображения. К примеру, интерполяция методом ближайшего соседа (cv2.INTER_NEAREST) просто берёт для каждого пикселя итогового изображения один пиксель исходного, который наиболее близкий к его положению — это самый простой и быстрый способ. Кроме этого метода в opencv существуют следующие: cv2.INTER_AREA, cv2.INTER_LINEAR( используется по умолчанию), cv2.INTER_CUBIC и cv2.INTER_LANCZOS4. Наиболее предпочтительным методом интерполяции для сжатия изображения является cv2.INTER_AREA, для увелечения — cv2.INTER_LINEAR

От данного метода зависит качество конечного изображения, но как показывает практика, если мы уменьшаем/увеличиваем изображение меньше, чем в 1.5 раза, то не важно каким методом интерполяции мы воспользовались — качество будет схожим. Данное утверждение можно проверить на практике

Напишем следующий код:

Слева — изображение с интерполяцией методом ближайшего соседа, справа — изображение с билинейной интерполяцией:

Очень важно при изменении размера изображения учитывать соотношение сторон. Соотношение сторон — это пропорциональное соотношение ширины и высоты изображения.Если мы забудем о данном понятии, то получим изображение такого плана:

Поэтому текущую функцию для изменения размера необходимо модифицировать:

Соотношение сторон мы вычисляем в переменной ratio. В зависимости от того, какой параметр не равен None, мы берём установленную нами новую высоту/ширину и делим на старую высоту/ширину. Далее, в переменной dimension мы определяем новые размеры изображения и передаём в функцию cv2.resize().

Release highlights

  • OpenCV license has been changed to Apache 2 (OpenCV 3.x will keep using BSD)
  • GSoC is over, all projects were success and most of them have already been merged. Optimizations for RISC-V, bindings for Julia language, real-time single object tracking, improved SIFT and others
  • OpenJPEG is now used by default for JPEG2000
  • Supported multiple OpenCL contexts
  • Improvements in dnn module:
    • Support latest OpenVINO 2021.1 release
    • Tengine lite support for inference on ARM
    • Many fixes and optimizations in CUDA backend
  • Added Python bindings for G-API module
  • Multiple fixes and improvements in flann module
  • Added Robot-World/Hand-Eye calibration function

More details can be found in the .

Most of bugfixes and improvements have made their way to both 3.4 and master branches.

Source Code¶

public class Main extends Application {
        @Override
        public void start(Stage primaryStage) {
                try {
                        // load the FXML resource
                        FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFirstJFX.fxml"));
                        // store the root element so that the controllers can use it
                        BorderPane root = (BorderPane) loader.load();
                        // create and style a scene
                        Scene scene = new Scene(root);
                        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
                        // create the stage with the given title and the previously created scene
                        primaryStage.setTitle("JavaFX meets OpenCV");
                        primaryStage.setScene(scene);
                        // show the GUI
                        primaryStage.show();
                        // set a reference of this class for its controller
                        FXController controller = loader.getController();
                        controller.setRootElement(root);

                } catch(Exception e) {
                        e.printStackTrace();
                }
        }

        public static void main(String[] args) {
                // load the native OpenCV library
                System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
                launch(args);
        }
}

FXController.java

public class FXController {

    @FXML
    private Button start_btn;
    @FXML
    private ImageView currentFrame;

    private Pane rootElement;
    private Timer timer;
    private VideoCapture capture = new VideoCapture();

    @FXML
    protected void startCamera(ActionEvent event)
    {
            // check: the main class is accessible?
            if (this.rootElement != null)
            {
                    // get the ImageView object for showing the video stream
                    final ImageView frameView = currentFrame;
                    // check if the capture stream is opened
                    if (!this.capture.isOpened())
                    {
                            // start the video capture
                            this.capture.open();
                            // grab a frame every 33 ms (30 frames/sec)
                            TimerTask frameGrabber = new TimerTask() {
                                    @Override
                                    public void run()
                                    {
                                            Image tmp = grabFrame();
                                            Platform.runLater(new Runnable() {
                                                    @Override
                                        public void run()
                                                    {
                                                            frameView.setImage(tmp);
                                        }
                                            });

                                    }
                            };
                            this.timer = new Timer();
                            //set the timer scheduling, this allow you to perform frameGrabber every 33ms;
                            this.timer.schedule(frameGrabber, , 33);
                            this.start_btn.setText("Stop Camera");
                    }
                    else
                    {
                            this.start_btn.setText("Start Camera");
                            // stop the timer
                            if (this.timer != null)
                            {
                                    this.timer.cancel();
                                    this.timer = null;
                            }
                            // release the camera
                            this.capture.release();
                            // clear the image container
                            frameView.setImage(null);
                    }
            }
    }

    private Image grabFrame()
    {
            //init
            Image imageToShow = null;
            Mat frame = new Mat();
            // check if the capture is open
            if (this.capture.isOpened())
            {
                    try
                    {
                            // read the current frame
                            this.capture.read(frame);
                            // if the frame is not empty, process it
                            if (!frame.empty())
                            {
                                    // convert the image to gray scale
                                    Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
                                    // convert the Mat object (OpenCV) to Image (JavaFX)
                                    imageToShow = mat2Image(frame);
                            }
                    }
                    catch (Exception e)
                    {
                            // log the error
                            System.err.println("ERROR: " + e.getMessage());
                    }
            }
            return imageToShow;
    }

    private Image mat2Image(Mat frame)
    {
            // create a temporary buffer
            MatOfByte buffer = new MatOfByte();
            // encode the frame in the buffer
            Highgui.imencode(".png", frame, buffer);
            // build and return an Image created from the image encoded in the buffer
            return new Image(new ByteArrayInputStream(buffer.toArray()));
    }

    public void setRootElement(Pane root)
    {
            this.rootElement = root;
    }

}

MyFirstJFX.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.FXController">
   <bottom>
      <Button fx:id="start_btn" mnemonicParsing="false" onAction="#startCamera" text="Start Camera" BorderPane.alignment="CENTER">
         <BorderPane.margin>
            <Insets bottom="10.0" />
         </BorderPane.margin></Button>
   </bottom>
   <center>
      <ImageView fx:id="currentFrame" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER">
         <BorderPane.margin>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
         </BorderPane.margin></ImageView>
   </center>
</BorderPane>

Managing GUI interactions with the Controller class¶

For our application we need to do basically two thing: control the button push and the refreshment of the image view.
To do so we have to create a reference between the gui components and a variable used in our controller class:

@FXML
private Button start_btn;
@FXML
private ImageView currentFrame;

The 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 tag is used with the same meaning for the Actions set under the Code menu in a specific element.

for:

<Button fx:id="start_btn" mnemonicParsing="false" onAction="#startCamera" text="Start Camera" BorderPane.alignment="CENTER">

we set:

Оцените статью
Рейтинг автора
5
Материал подготовил
Андрей Измаилов
Наш эксперт
Написано статей
116
Добавить комментарий