mirror of
https://github.com/opencv/opencv.git
synced 2025-08-06 06:26:29 +08:00
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
This commit is contained in:
parent
8fa8d471af
commit
982745fb83
@ -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.
|
||||

|
||||
*/
|
||||
CV_EXPORTS_AS(findChessboardCornersSBWithMeta)
|
||||
|
Loading…
Reference in New Issue
Block a user