Merge pull request #20735 from AleksandrPanov:radon_checkerboard

generate radon checkerboard

* added _make_round_rect

* added round rect to make_checkerboard_pattern

* added markers

* update docs

* removed links to findChessboardCornersSB() and added checks to markers
pull/20771/head
Alexander Panov 3 years ago committed by GitHub
parent 8fa8d471af
commit 982745fb83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 97
      doc/pattern_tools/gen_pattern.py
  2. 4
      doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown
  3. 4
      modules/calib3d/include/opencv2/calib3d.hpp

@ -6,13 +6,14 @@ 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 (default circles)
-T, --type - type of pattern, circles, acircles, checkerboard, radon_checkerboard (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)
-w, --page_width - page width in units (default 216)
-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
-H, --help - show help
"""
@ -22,7 +23,7 @@ from svgfig import *
class PatternMaker:
def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height):
def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height, markers):
self.cols = cols
self.rows = rows
self.output = output
@ -31,6 +32,7 @@ class PatternMaker:
self.radius_rate = radius_rate
self.width = page_width
self.height = page_height
self.markers = markers
self.g = SVG("g") # the svg group container
def make_circles_pattern(self):
@ -70,6 +72,74 @@ class PatternMaker:
height=spacing, fill="black", stroke="none")
self.g.append(square)
@staticmethod
def _make_round_rect(x, y, diam, corners=("right", "right", "right", "right")):
rad = diam / 2
cw_point = ((0, 0), (diam, 0), (diam, diam), (0, diam))
mid_cw_point = ((0, rad), (rad, 0), (diam, rad), (rad, diam))
res_str = "M{},{} ".format(x + mid_cw_point[0][0], y + mid_cw_point[0][1])
n = len(cw_point)
for i in range(n):
if corners[i] == "right":
res_str += "L{},{} L{},{} ".format(x + cw_point[i][0], y + cw_point[i][1],
x + mid_cw_point[(i + 1) % n][0], y + mid_cw_point[(i + 1) % n][1])
elif corners[i] == "round":
res_str += "A{},{} 0,0,1 {},{} ".format(rad, rad, x + mid_cw_point[(i + 1) % n][0],
y + mid_cw_point[(i + 1) % n][1])
else:
raise TypeError("unknown corner type")
return res_str
def _get_type(self, x, y):
corners = ["right", "right", "right", "right"]
is_inside = True
if x == 0:
corners[0] = "round"
corners[3] = "round"
is_inside = False
if y == 0:
corners[0] = "round"
corners[1] = "round"
is_inside = False
if x == self.cols - 1:
corners[1] = "round"
corners[2] = "round"
is_inside = False
if y == self.rows - 1:
corners[2] = "round"
corners[3] = "round"
is_inside = False
return corners, is_inside
def make_radon_checkerboard_pattern(self):
spacing = self.square_size
xspacing = (self.width - self.cols * self.square_size) / 2.0
yspacing = (self.height - self.rows * self.square_size) / 2.0
for x in range(0, self.cols):
for y in range(0, self.rows):
if x % 2 == y % 2:
corner_types, is_inside = self._get_type(x, y)
if is_inside:
square = SVG("rect", x=x * spacing + xspacing, y=y * spacing + yspacing, width=spacing,
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")
self.g.append(square)
if self.markers is not None:
r = self.square_size * 0.17
pattern_width = ((self.cols - 1.0) * spacing) + (2.0 * r)
pattern_height = ((self.rows - 1.0) * spacing) + (2.0 * r)
x_spacing = (self.width - pattern_width) / 2.0
y_spacing = (self.height - pattern_height) / 2.0
for x, y in self.markers:
color = "black"
if x % 2 == y % 2:
color = "white"
dot = SVG("circle", cx=(x * spacing) + x_spacing + r,
cy=(y * spacing) + y_spacing + r, r=r, fill=color, stroke="none")
self.g.append(dot)
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))
@ -85,7 +155,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"])
choices=["circles", "acircles", "checkerboard", "radon_checkerboard"])
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",
@ -96,8 +166,12 @@ def main():
dest="page_width", type=float)
parser.add_argument("-h", "--page_height", help="page height in units", default=argparse.SUPPRESS, action="store",
dest="page_height", type=float)
parser.add_argument("-a", "--page_size", help="page size, superseded if -h and -w are set", default="A4", action="store",
dest="page_size", choices=["A0", "A1", "A2", "A3", "A4", "A5"])
parser.add_argument("-a", "--page_size", help="page size, superseded if -h and -w are set", default="A4",
action="store", dest="page_size", choices=["A0", "A1", "A2", "A3", "A4", "A5"])
parser.add_argument("-m", "--markers", help="list of cells with markers for the radon checkerboard. Marker "
"coordinates as list of numbers: -m 1 2 3 4 means markers in cells "
"[1, 2] and [3, 4]",
action="store", dest="markers", nargs="+", type=int)
args = parser.parse_args()
show_help = args.show_help
@ -121,10 +195,19 @@ def main():
"A5": [148, 210]}
page_width = page_sizes[page_size][0]
page_height = page_sizes[page_size][1]
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height)
if len(args.markers) % 2 == 1:
raise ValueError("The length of the markers array={} must be even".format(len(args.markers)))
markers = set()
for x, y in zip(args.markers[::2], args.markers[1::2]):
if x in range(0, columns) and y in range(0, rows):
markers.add((x, y))
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)
# dict for easy lookup of pattern type
mp = {"circles": pm.make_circles_pattern, "acircles": pm.make_acircles_pattern,
"checkerboard": pm.make_checkerboard_pattern}
"checkerboard": pm.make_checkerboard_pattern, "radon_checkerboard": pm.make_radon_checkerboard_pattern}
mp[p_type]()
# this should save pattern to output
pm.save()

@ -36,6 +36,10 @@ create a circle board pattern in file acircleboard.svg with 7 rows, 5 columns an
python gen_pattern.py -o acircleboard.svg --rows 7 --columns 5 --type acircles --square_size 10 --radius_rate 2
create a radon checkerboard for findChessboardCornersSB() with markers in (7 4), (7 5), (8 5) cells:
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
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

@ -1489,6 +1489,8 @@ Sample usage of detecting and drawing chessboard corners: :
the board to make the detection more robust in various environments. Otherwise, if there is no
border and the background is dark, the outer black squares cannot be segmented properly and so the
square grouping and ordering algorithm fails.
Use gen_pattern.py (@ref tutorial_camera_calibration_pattern) to create checkerboard.
*/
CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );
@ -1545,6 +1547,8 @@ transformation it is beneficial to use round corners for the field corners
which are located on the outside of the board. The following figure illustrates
a sample checkerboard optimized for the detection. However, any other checkerboard
can be used as well.
Use gen_pattern.py (@ref tutorial_camera_calibration_pattern) to create checkerboard.
![Checkerboard](pics/checkerboard_radon.png)
*/
CV_EXPORTS_AS(findChessboardCornersSBWithMeta)

Loading…
Cancel
Save