mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 15:53:03 +04:00
Merge pull request #23363 from vovka643:4.x_generate_charuco
Added charuco board generation to gen_pattern.py #23363 added charuco board generation in gen_pattern.py moved aruco_dict_utils.cpp to samples from opencv_contrib (https://github.com/opencv/opencv_contrib/pull/3464) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
committed by
GitHub
parent
f2311d1bfd
commit
9931da772d
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user