1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2023-06-01 09:37:38 +03:00
565 changed files with 84396 additions and 17589 deletions
+5
View File
@@ -1,3 +1,8 @@
if (NOT CMAKE_CROSSCOMPILING)
file(RELATIVE_PATH __loc_relative "${OpenCV_BINARY_DIR}" "${CMAKE_CURRENT_LIST_DIR}/pattern_tools\n")
file(APPEND "${OpenCV_BINARY_DIR}/opencv_apps_python_tests.cfg" "${__loc_relative}")
endif()
if(NOT BUILD_DOCS)
return()
endif()
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

@@ -41,7 +41,7 @@
<script src="utils.js" type="text/javascript"></script>
<script id="codeSnippet" type="text/code-snippet">
let src = cv.imread('canvasInput');
let dst = cv.Mat.zeros(src.cols, src.rows, cv.CV_8UC3);
let dst = cv.Mat.zeros(src.rows, src.cols, cv.CV_8UC3);
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
cv.threshold(src, src, 120, 200, cv.THRESH_BINARY);
let contours = new cv.MatVector();
@@ -147,7 +147,7 @@ if (dataset === 'COCO') {
["Neck", "LShoulder"], ["RShoulder", "RElbow"],
["RElbow", "RWrist"], ["LShoulder", "LElbow"],
["LElbow", "LWrist"], ["Nose", "REye"],
["REye", "REar"], ["Neck", "LEye"],
["REye", "REar"], ["Nose", "LEye"],
["LEye", "LEar"], ["Neck", "MidHip"],
["MidHip", "RHip"], ["RHip", "RKnee"],
["RKnee", "RAnkle"], ["RAnkle", "RBigToe"],
@@ -83,6 +83,9 @@ Building OpenCV.js from Source
It requires `python` and `cmake` installed in your development environment.
-# The build script builds asm.js version by default. To build WebAssembly version, append `--build_wasm` switch.
By default everything is bundled into one JavaScript file by `base64` encoding the WebAssembly code. For production
builds you can add `--disable_single_file` which will reduce total size by writing the WebAssembly code
to a dedicated `.wasm` file which the generated JavaScript file will automatically load.
For example, to build wasm version in `build_wasm` directory:
@code{.bash}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+85 -7
View File
@@ -6,7 +6,7 @@ python gen_pattern.py -o out.svg -r 11 -c 8 -T circles -s 20.0 -R 5.0 -u mm -w 2
-o, --output - output file (default out.svg)
-r, --rows - pattern rows (default 11)
-c, --columns - pattern columns (default 8)
-T, --type - type of pattern, circles, acircles, checkerboard, radon_checkerboard (default circles)
-T, --type - type of pattern: circles, acircles, checkerboard, radon_checkerboard, charuco_board. default circles.
-s, --square_size - size of squares in pattern (default 20.0)
-R, --radius_rate - circles_radius = square_size/radius_rate (default 5.0)
-u, --units - mm, inches, px, m (default mm)
@@ -14,16 +14,20 @@ python gen_pattern.py -o out.svg -r 11 -c 8 -T circles -s 20.0 -R 5.0 -u mm -w 2
-h, --page_height - page height in units (default 279)
-a, --page_size - page size (default A4), supersedes -h -w arguments
-m, --markers - list of cells with markers for the radon checkerboard
-p, --aruco_marker_size - aruco markers size for ChAruco pattern (default 10.0)
-f, --dict_file - file name of custom aruco dictionary for ChAruco pattern
-H, --help - show help
"""
import argparse
import numpy as np
import json
import gzip
from svgfig import *
class PatternMaker:
def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height, markers):
def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height, markers, aruco_marker_size, dict_file):
self.cols = cols
self.rows = rows
self.output = output
@@ -33,6 +37,9 @@ class PatternMaker:
self.width = page_width
self.height = page_height
self.markers = markers
self.aruco_marker_size = aruco_marker_size #for charuco boards only
self.dict_file = dict_file
self.g = SVG("g") # the svg group container
def make_circles_pattern(self):
@@ -124,7 +131,7 @@ class PatternMaker:
height=spacing, fill="black", stroke="none")
else:
square = SVG("path", d=self._make_round_rect(x * spacing + xspacing, y * spacing + yspacing,
spacing, corner_types), fill="black", stroke="none")
spacing, corner_types), fill="black", stroke="none")
self.g.append(square)
if self.markers is not None:
r = self.square_size * 0.17
@@ -140,6 +147,69 @@ class PatternMaker:
cy=(y * spacing) + y_spacing + r, r=r, fill=color, stroke="none")
self.g.append(dot)
@staticmethod
def _create_marker_bits(markerSize_bits, byteList):
marker = np.zeros((markerSize_bits+2, markerSize_bits+2))
bits = marker[1:markerSize_bits+1, 1:markerSize_bits+1]
for i in range(markerSize_bits):
for j in range(markerSize_bits):
bits[i][j] = int(byteList[i*markerSize_bits+j])
return marker
def make_charuco_board(self):
if (self.aruco_marker_size>self.square_size):
print("Error: Aruco marker cannot be lager than chessboard square!")
return
if (self.dict_file.split(".")[-1] == "gz"):
with gzip.open(self.dict_file, 'r') as fin:
json_bytes = fin.read()
json_str = json_bytes.decode('utf-8')
dictionary = json.loads(json_str)
else:
f = open(self.dict_file)
dictionary = json.load(f)
if (dictionary["nmarkers"] < int(self.cols*self.rows/2)):
print("Error: Aruco dictionary contains less markers than it needs for chosen board. Please choose another dictionary or use smaller board than required for chosen board")
return
markerSize_bits = dictionary["markersize"]
side = self.aruco_marker_size / (markerSize_bits+2)
spacing = self.square_size
xspacing = (self.width - self.cols * self.square_size) / 2.0
yspacing = (self.height - self.rows * self.square_size) / 2.0
ch_ar_border = (self.square_size - self.aruco_marker_size)/2
marker_id = 0
for y in range(0, self.rows):
for x in range(0, self.cols):
if x % 2 == y % 2:
square = SVG("rect", x=x * spacing + xspacing, y=y * spacing + yspacing, width=spacing,
height=spacing, fill="black", stroke="none")
self.g.append(square)
else:
img_mark = self._create_marker_bits(markerSize_bits, dictionary["marker_"+str(marker_id)])
marker_id +=1
x_pos = x * spacing + xspacing
y_pos = y * spacing + yspacing
square = SVG("rect", x=x_pos+ch_ar_border, y=y_pos+ch_ar_border, width=self.aruco_marker_size,
height=self.aruco_marker_size, fill="black", stroke="none")
self.g.append(square)
for x_ in range(len(img_mark[0])):
for y_ in range(len(img_mark)):
if (img_mark[y_][x_] != 0):
square = SVG("rect", x=x_pos+ch_ar_border+(x_)*side, y=y_pos+ch_ar_border+(y_)*side, width=side,
height=side, fill="white", stroke="white", stroke_width = spacing*0.01)
self.g.append(square)
def save(self):
c = canvas(self.g, width="%d%s" % (self.width, self.units), height="%d%s" % (self.height, self.units),
viewBox="0 0 %d %d" % (self.width, self.height))
@@ -155,7 +225,7 @@ def main():
type=int)
parser.add_argument("-r", "--rows", help="pattern rows", default="11", action="store", dest="rows", type=int)
parser.add_argument("-T", "--type", help="type of pattern", default="circles", action="store", dest="p_type",
choices=["circles", "acircles", "checkerboard", "radon_checkerboard"])
choices=["circles", "acircles", "checkerboard", "radon_checkerboard", "charuco_board"])
parser.add_argument("-u", "--units", help="length unit", default="mm", action="store", dest="units",
choices=["mm", "inches", "px", "m"])
parser.add_argument("-s", "--square_size", help="size of squares in pattern", default="20.0", action="store",
@@ -172,6 +242,10 @@ def main():
"coordinates as list of numbers: -m 1 2 3 4 means markers in cells "
"[1, 2] and [3, 4]",
default=argparse.SUPPRESS, action="store", dest="markers", nargs="+", type=int)
parser.add_argument("-p", "--marker_size", help="aruco markers size for ChAruco pattern (default 10.0)", default="10.0",
action="store", dest="aruco_marker_size", type=float)
parser.add_argument("-f", "--dict_file", help="file name of custom aruco dictionary for ChAruco pattern", default="DICT_ARUCO_ORIGINAL.json",
action="store", dest="dict_file", type=str)
args = parser.parse_args()
show_help = args.show_help
@@ -185,6 +259,9 @@ def main():
units = args.units
square_size = args.square_size
radius_rate = args.radius_rate
aruco_marker_size = args.aruco_marker_size
dict_file = args.dict_file
if 'page_width' and 'page_height' in args:
page_width = args.page_width
page_height = args.page_height
@@ -206,10 +283,11 @@ def main():
else:
raise ValueError("The marker {},{} is outside the checkerboard".format(x, y))
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height, markers)
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height, markers, aruco_marker_size, dict_file)
# dict for easy lookup of pattern type
mp = {"circles": pm.make_circles_pattern, "acircles": pm.make_acircles_pattern,
"checkerboard": pm.make_checkerboard_pattern, "radon_checkerboard": pm.make_radon_checkerboard_pattern}
"checkerboard": pm.make_checkerboard_pattern, "radon_checkerboard": pm.make_radon_checkerboard_pattern,
"charuco_board": pm.make_charuco_board}
mp[p_type]()
# this should save pattern to output
pm.save()
+118
View File
@@ -0,0 +1,118 @@
from __future__ import print_function
import os, tempfile, numpy as np
import sys
import cv2 as cv
from tests_common import NewOpenCVTests
import gen_pattern
class aruco_objdetect_test(NewOpenCVTests):
def test_aruco_dicts(self):
try:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
except:
raise self.skipTest("libraies svglib and reportlab not found")
else:
cols = 3
rows = 5
square_size = 100
aruco_type = [cv.aruco.DICT_4X4_1000, cv.aruco.DICT_5X5_1000, cv.aruco.DICT_6X6_1000,
cv.aruco.DICT_7X7_1000, cv.aruco.DICT_ARUCO_ORIGINAL, cv.aruco.DICT_APRILTAG_16h5,
cv.aruco.DICT_APRILTAG_25h9, cv.aruco.DICT_APRILTAG_36h10, cv.aruco.DICT_APRILTAG_36h11]
aruco_type_str = ['DICT_4X4_1000','DICT_5X5_1000', 'DICT_6X6_1000',
'DICT_7X7_1000', 'DICT_ARUCO_ORIGINAL', 'DICT_APRILTAG_16h5',
'DICT_APRILTAG_25h9', 'DICT_APRILTAG_36h10', 'DICT_APRILTAG_36h11']
marker_size = 0.8*square_size
board_width = cols*square_size
board_height = rows*square_size
for aruco_type_i in range(len(aruco_type)):
#draw desk using opencv
aruco_dict = cv.aruco.getPredefinedDictionary(aruco_type[aruco_type_i])
board = cv.aruco.CharucoBoard((cols, rows), square_size, marker_size, aruco_dict)
charuco_detector = cv.aruco.CharucoDetector(board)
from_cv_img = board.generateImage((cols*square_size*10, rows*square_size*10))
#draw desk using svg
fd1, filesvg = tempfile.mkstemp(prefix="out", suffix=".svg")
os.close(fd1)
fd2, filepng = tempfile.mkstemp(prefix="svg_marker", suffix=".png")
os.close(fd2)
try:
basedir = os.path.abspath(os.path.dirname(__file__))
pm = gen_pattern.PatternMaker(cols, rows, filesvg, "px", square_size, 0, board_width,
board_height, "charuco_checkboard", marker_size,
os.path.join(basedir, aruco_type_str[aruco_type_i]+'.json.gz'))
pm.make_charuco_board()
pm.save()
drawing = svg2rlg(filesvg)
renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=720)
from_svg_img = cv.imread(filepng)
#test
_charucoCorners, _charucoIds, markerCorners_svg, markerIds_svg = charuco_detector.detectBoard(from_svg_img)
_charucoCorners, _charucoIds, markerCorners_cv, markerIds_cv = charuco_detector.detectBoard(from_cv_img)
np.testing.assert_allclose(markerCorners_svg, markerCorners_cv, 0.1, 0.1)
np.testing.assert_allclose(markerIds_svg, markerIds_cv, 0.1, 0.1)
finally:
if os.path.exists(filesvg):
os.remove(filesvg)
if os.path.exists(filepng):
os.remove(filepng)
def test_aruco_marker_sizes(self):
try:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
except:
raise self.skipTest("libraies svglib and reportlab not found")
else:
cols = 3
rows = 5
square_size = 100
aruco_type = cv.aruco.DICT_5X5_1000
aruco_type_str = 'DICT_5X5_1000'
marker_sizes_rate = [0.25, 0.5, 0.75, 0.9]
board_width = cols*square_size
board_height = rows*square_size
for marker_s_rate in marker_sizes_rate:
marker_size = marker_s_rate*square_size
#draw desk using opencv
aruco_dict = cv.aruco.getPredefinedDictionary(aruco_type)
board = cv.aruco.CharucoBoard((cols, rows), square_size, marker_size, aruco_dict)
charuco_detector = cv.aruco.CharucoDetector(board)
from_cv_img = board.generateImage((cols*square_size*10, rows*square_size*10))
#draw desk using svg
fd1, filesvg = tempfile.mkstemp(prefix="out", suffix=".svg")
os.close(fd1)
fd2, filepng = tempfile.mkstemp(prefix="svg_marker", suffix=".png")
os.close(fd2)
try:
basedir = os.path.abspath(os.path.dirname(__file__))
pm = gen_pattern.PatternMaker(cols, rows, filesvg, "px", square_size, 0, board_width,
board_height, "charuco_checkboard", marker_size, os.path.join(basedir, aruco_type_str+'.json.gz'))
pm.make_charuco_board()
pm.save()
drawing = svg2rlg(filesvg)
renderPM.drawToFile(drawing, filepng, fmt='PNG', dpi=720)
from_svg_img = cv.imread(filepng)
#test
_charucoCorners, _charucoIds, markerCorners_svg, markerIds_svg = charuco_detector.detectBoard(from_svg_img)
_charucoCorners, _charucoIds, markerCorners_cv, markerIds_cv = charuco_detector.detectBoard(from_cv_img)
np.testing.assert_allclose(markerCorners_svg, markerCorners_cv, 0.1, 0.1)
np.testing.assert_allclose(markerIds_svg, markerIds_cv, 0.1, 0.1)
finally:
if os.path.exists(filesvg):
os.remove(filesvg)
if os.path.exists(filepng):
os.remove(filepng)
+2
View File
@@ -0,0 +1,2 @@
svglib>=1.5.1
reportlab>=4.0.0
@@ -44,7 +44,7 @@ from matplotlib import pyplot as plt
imgL = cv.imread('tsukuba_l.png', cv.IMREAD_GRAYSCALE)
imgR = cv.imread('tsukuba_r.png', cv.IMREAD_GRAYSCALE)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
stereo = cv.StereoBM.create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imgL,imgR)
plt.imshow(disparity,'gray')
plt.show()
@@ -63,6 +63,9 @@ There are some parameters when you get familiar with StereoBM, and you may need
- uniqueness_ratio: Another post-filtering step. If the best matching disparity is not sufficiently better than every other disparity in the search range, the pixel is filtered out. You can try tweaking this if texture_threshold and the speckle filtering are still letting through spurious matches.
- prefilter_size and prefilter_cap: The pre-filtering phase, which normalizes image brightness and enhances texture in preparation for block matching. Normally you should not need to adjust these.
These parameters are set with dedicated setters and getters after the algoritm
initialization, such as `setTextureThreshold`, `setSpeckleRange`, `setUniquenessRatio`,
and more. See cv::StereoBM documentation for details.
Additional Resources
--------------------
@@ -30,7 +30,7 @@ programmer to express ideas in fewer lines of code without reducing readability.
Compared to languages like C/C++, Python is slower. That said, Python can be easily extended with
C/C++, which allows us to write computationally intensive code in C/C++ and create Python wrappers
that can be used as Python modules. This gives us two advantages: first, the code is as fast as the
original C/C++ code (since it is the actual C++ code working in background) and second, it easier to
original C/C++ code (since it is the actual C++ code working in background) and second, it is easier to
code in Python than C/C++. OpenCV-Python is a Python wrapper for the original OpenCV C++
implementation.
@@ -79,8 +79,9 @@ Below is the list of contributors who submitted tutorials to OpenCV-Python.
Additional Resources
--------------------
-# A Quick guide to Python - [A Byte of Python](http://swaroopch.com/notes/python/)
2. [NumPy Quickstart tutorial](https://numpy.org/devdocs/user/quickstart.html)
3. [NumPy Reference](https://numpy.org/devdocs/reference/index.html#reference)
4. [OpenCV Documentation](http://docs.opencv.org/)
-# A Quick guide to Python - [A Byte of Python](https://python.swaroopch.com/)
1. [A Quick guide to Python](https://www.freecodecamp.org/news/the-python-guide-for-beginners/)
2. [NumPy Quickstart tutorial](https://numpy.org/doc/stable/user/quickstart.html)
3. [NumPy Reference](https://numpy.org/doc/stable/reference/index.html)
4. [OpenCV Documentation](https://docs.opencv.org/)
5. [OpenCV Forum](https://forum.opencv.org/)
@@ -33,6 +33,8 @@ Installing OpenCV from prebuilt binaries
-# Copy **cv2.pyd** to **C:/Python27/lib/site-packages**.
-# Copy the **opencv_world.dll** file to **C:/Python27/lib/site-packages**
-# Open Python IDLE and type following codes in Python terminal.
@code
>>> import cv2 as cv
@@ -60,6 +60,7 @@ done through basic geometrical equations. The equations used depend on the chose
objects. Currently OpenCV supports three types of objects for calibration:
- Classical black-white chessboard
- ChArUco board pattern
- Symmetrical circle pattern
- Asymmetrical circle pattern
@@ -88,7 +89,8 @@ Source code
You may also find the source code in the `samples/cpp/tutorial_code/calib3d/camera_calibration/`
folder of the OpenCV source library or [download it from here
](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp). For the usage of the program, run it with `-h` argument. The program has an
](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp).
For the usage of the program, run it with `-h` argument. The program has an
essential argument: the name of its configuration file. If none is given then it will try to open the
one named "default.xml". [Here's a sample configuration file
](https://github.com/opencv/opencv/tree/5.x/samples/cpp/tutorial_code/calib3d/camera_calibration/in_VID5.xml) in XML format. In the
@@ -128,14 +130,23 @@ Explanation
The formation of the equations I mentioned above aims
to finding major patterns in the input: in case of the chessboard this are corners of the
squares and for the circles, well, the circles themselves. The position of these will form the
squares and for the circles, well, the circles themselves. ChArUco board is equivalent to
chessboard, but corners are mached by ArUco markers. The position of these will form the
result which will be written into the *pointBuf* vector.
@snippet samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp find_pattern
Depending on the type of the input pattern you use either the @ref cv::findChessboardCorners or
the @ref cv::findCirclesGrid function. For both of them you pass the current image and the size
of the board and you'll get the positions of the patterns. Furthermore, they return a boolean
variable which states if the pattern was found in the input (we only need to take into account
those images where this is true!).
the @ref cv::findCirclesGrid function or @ref cv::aruco::CharucoDetector::detectBoard method.
For all of them you pass the current image and the size of the board and you'll get the positions
of the patterns. cv::findChessboardCorners and cv::findCirclesGrid return a boolean variable
which states if the pattern was found in the input (we only need to take into account
those images where this is true!). `CharucoDetector::detectBoard` may detect partially visible
pattern and returns coordunates and ids of visible inner corners.
@note Board size and amount of matched points is different for chessboard, circles grid and ChArUco.
All chessboard related algorithm expects amount of inner corners as board width and height.
Board size of circles grid is just amount of circles by both grid dimentions. ChArUco board size
is defined in squares, but detection result is list of inner corners and that's why is smaller
by 1 in both dimentions.
Then again in case of cameras we only take camera images when an input delay time is passed.
This is done in order to allow user moving the chessboard around and getting different images.
@@ -17,6 +17,9 @@ You can find a chessboard pattern in https://github.com/opencv/opencv/blob/5.x/d
You can find a circleboard pattern in https://github.com/opencv/opencv/blob/5.x/doc/acircles_pattern.png
You can find a ChAruco board pattern in https://github.com/opencv/opencv/blob/5.x/doc/charuco_board_pattern.png
(7X5 ChAruco board, square size: 30 mm , marker size: 15 mm, aruco dict: DICT_5X5_100, page width: 210 mm, page height: 297 mm)
Create your own pattern
---------------
@@ -28,7 +31,7 @@ create a checkerboard pattern in file chessboard.svg with 9 rows, 6 columns and
python gen_pattern.py -o chessboard.svg --rows 9 --columns 6 --type checkerboard --square_size 20
create a circle board pattern in file circleboard.svg with 7 rows, 5 columns and a radius of 15mm:
create a circle board pattern in file circleboard.svg with 7 rows, 5 columns and a radius of 15 mm:
python gen_pattern.py -o circleboard.svg --rows 7 --columns 5 --type circles --square_size 15
@@ -40,13 +43,18 @@ create a radon checkerboard for findChessboardCornersSB() with markers in (7 4),
python gen_pattern.py -o radon_checkerboard.svg --rows 10 --columns 15 --type radon_checkerboard -s 12.1 -m 7 4 7 5 8 5
create a ChAruco board pattern in charuco_board.svg with 7 rows, 5 columns, square size 30 mm, aruco marker size 15 mm and using DICT_5X5_100 as dictionary for aruco markers (it contains in DICT_ARUCO.json file):
python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board --square_size 30 --marker_size 15 -f DICT_5X5_100.json.gz
If you want to change unit use -u option (mm inches, px, m)
If you want to change page size use -w and -h options
@cond HAVE_opencv_aruco
If you want to create a ChArUco board read @ref tutorial_charuco_detection "tutorial Detection of ChArUco Corners" in opencv_contrib tutorial.
@endcond
@cond !HAVE_opencv_aruco
If you want to create a ChArUco board read tutorial Detection of ChArUco Corners in opencv_contrib tutorial.
@endcond
If you want to use your own dictionary for ChAruco board your should write name of file with your dictionary. For example
python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board -f my_dictionary.json
You can generate your dictionary in my_dictionary.json file with number of markers 30 and markers size 5 bits by using opencv/samples/cpp/aruco_dict_utils.cpp.
bin/example_cpp_aruco_dict_utils.exe my_dict.json -nMarkers=30 -markerSize=5
@@ -11,29 +11,30 @@
| Compatibility | OpenCV >= 3.4.1 |
## Introduction
Deep learning is a fast growing area. The new approaches to build neural networks
usually introduce new types of layers. They could be modifications of existing
ones or implement outstanding researching ideas.
Deep learning is a fast-growing area. New approaches to building neural networks
usually introduce new types of layers. These could be modifications of existing
ones or implementation of outstanding research ideas.
OpenCV gives an opportunity to import and run networks from different deep learning
frameworks. There are a number of the most popular layers. However you can face
a problem that your network cannot be imported using OpenCV because of unimplemented layers.
OpenCV allows importing and running networks from different deep learning frameworks.
There is a number of the most popular layers. However, you can face a problem that
your network cannot be imported using OpenCV because some layers of your network
can be not implemented in the deep learning engine of OpenCV.
The first solution is to create a feature request at https://github.com/opencv/opencv/issues
mentioning details such a source of model and type of new layer. A new layer could
be implemented if OpenCV community shares this need.
mentioning details such as a source of a model and a type of new layer.
The new layer could be implemented if the OpenCV community shares this need.
The second way is to define a **custom layer** so OpenCV's deep learning engine
The second way is to define a **custom layer** so that OpenCV's deep learning engine
will know how to use it. This tutorial is dedicated to show you a process of deep
learning models import customization.
learning model's import customization.
## Define a custom layer in C++
Deep learning layer is a building block of network's pipeline.
It has connections to **input blobs** and produces results to **output blobs**.
There are trained **weights** and **hyper-parameters**.
Layers' names, types, weights and hyper-parameters are stored in files are generated by
native frameworks during training. If OpenCV mets unknown layer type it throws an
exception trying to read a model:
Layers' names, types, weights and hyper-parameters are stored in files are
generated by native frameworks during training. If OpenCV encounters unknown
layer type it throws an exception while trying to read a model:
```
Unspecified error: Can't create layer "layer_name" of type "MyType" in function getLayerInstance
@@ -69,7 +70,7 @@ This method should create an instance of you layer and return cv::Ptr with it.
@snippet dnn/custom_layers.hpp MyLayer::getMemoryShapes
Returns layer's output shapes depends on input shapes. You may request an extra
Returns layer's output shapes depending on input shapes. You may request an extra
memory using `internals`.
- Run a layer
@@ -79,20 +80,20 @@ memory using `internals`.
Implement a layer's logic here. Compute outputs for given inputs.
@note OpenCV manages memory allocated for layers. In the most cases the same memory
can be reused between layers. So your `forward` implementation should not rely that
the second invocation of `forward` will has the same data at `outputs` and `internals`.
can be reused between layers. So your `forward` implementation should not rely on that
the second invocation of `forward` will have the same data at `outputs` and `internals`.
- Optional `finalize` method
@snippet dnn/custom_layers.hpp MyLayer::finalize
The chain of methods are the following: OpenCV deep learning engine calls `create`
method once then it calls `getMemoryShapes` for an every created layer then you
can make some preparations depends on known input dimensions at cv::dnn::Layer::finalize.
After network was initialized only `forward` method is called for an every network's input.
The chain of methods is the following: OpenCV deep learning engine calls `create`
method once, then it calls `getMemoryShapes` for every created layer, then you
can make some preparations depend on known input dimensions at cv::dnn::Layer::finalize.
After network was initialized only `forward` method is called for every network's input.
@note Varying input blobs' sizes such height or width or batch size you make OpenCV
reallocate all the internal memory. That leads efficiency gaps. Try to initialize
@note Varying input blobs' sizes such height, width or batch size make OpenCV
reallocate all the internal memory. That leads to efficiency gaps. Try to initialize
and deploy models using a fixed batch size and image's dimensions.
## Example: custom layer from Caffe
@@ -209,7 +210,7 @@ deep learning model. That was trained with one and only difference comparing to
a current version of [Caffe framework](http://caffe.berkeleyvision.org/). `Crop`
layers that receive two input blobs and crop the first one to match spatial dimensions
of the second one used to crop from the center. Nowadays Caffe's layer does it
from the top-left corner. So using the latest version of Caffe or OpenCV you'll
from the top-left corner. So using the latest version of Caffe or OpenCV you will
get shifted results with filled borders.
Next we're going to replace OpenCV's `Crop` layer that makes top-left cropping by
@@ -225,7 +226,7 @@ a centric one.
@snippet dnn/edge_detection.py Register
That's it! We've replaced an implemented OpenCV's layer to a custom one.
That's it! We have replaced an implemented OpenCV's layer to a custom one.
You may find a full script in the [source code](https://github.com/opencv/opencv/tree/5.x/samples/dnn/edge_detection.py).
<table border="0">