mirror of
https://github.com/opencv/opencv.git
synced 2024-12-15 18:09:11 +08:00
d296d29a1c
In-house QR codes decoding #24299 ### Pull Request Readiness Checklist QR codes decoding pipeline without 3rdparty dependency (Quirc library). Implemented according to standard https://github.com/yansikeim/QR-Code/blob/master/ISO%20IEC%2018004%202015%20Standard.pdf **Merge with extra**: https://github.com/opencv/opencv_extra/pull/1124 resolves https://github.com/opencv/opencv/issues/24225 resolves https://github.com/opencv/opencv/issues/17290 resolves https://github.com/opencv/opencv/issues/24318 https://github.com/opencv/opencv/issues/24346 Resources: * https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders * https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Massey_algorithm ``` Geometric mean (ms) Name of Test quirc new2 new2 vs quirc (x-factor) decode::Perf_Objdetect_Not_QRCode::("chessboard", 640x480) 9.151 9.157 1.00 decode::Perf_Objdetect_Not_QRCode::("chessboard", 1280x720) 21.609 21.609 1.00 decode::Perf_Objdetect_Not_QRCode::("chessboard", 1920x1080) 42.088 41.924 1.00 decode::Perf_Objdetect_Not_QRCode::("chessboard", 3840x2160) 169.737 169.050 1.00 decode::Perf_Objdetect_Not_QRCode::("random", 640x480) 8.552 8.611 0.99 decode::Perf_Objdetect_Not_QRCode::("random", 1280x720) 21.264 21.581 0.99 decode::Perf_Objdetect_Not_QRCode::("random", 1920x1080) 42.415 43.468 0.98 decode::Perf_Objdetect_Not_QRCode::("random", 3840x2160) 175.003 174.294 1.00 decode::Perf_Objdetect_Not_QRCode::("zero", 640x480) 8.528 8.421 1.01 decode::Perf_Objdetect_Not_QRCode::("zero", 1280x720) 21.548 21.209 1.02 decode::Perf_Objdetect_Not_QRCode::("zero", 1920x1080) 42.581 42.529 1.00 decode::Perf_Objdetect_Not_QRCode::("zero", 3840x2160) 176.231 174.410 1.01 decode::Perf_Objdetect_QRCode::"kanji.jpg" 6.105 6.072 1.01 decode::Perf_Objdetect_QRCode::"link_github_ocv.jpg" 6.069 6.076 1.00 decode::Perf_Objdetect_QRCode::"link_ocv.jpg" 6.143 6.240 0.98 decode::Perf_Objdetect_QRCode::"link_wiki_cv.jpg" 6.369 6.420 0.99 decode::Perf_Objdetect_QRCode::"russian.jpg" 6.558 6.549 1.00 decode::Perf_Objdetect_QRCode::"version_1_down.jpg" 5.634 5.621 1.00 decode::Perf_Objdetect_QRCode::"version_1_left.jpg" 5.560 5.609 0.99 decode::Perf_Objdetect_QRCode::"version_1_right.jpg" 5.539 5.631 0.98 decode::Perf_Objdetect_QRCode::"version_1_top.jpg" 5.622 5.566 1.01 decode::Perf_Objdetect_QRCode::"version_1_up.jpg" 5.569 5.534 1.01 decode::Perf_Objdetect_QRCode::"version_5_down.jpg" 6.514 6.436 1.01 decode::Perf_Objdetect_QRCode::"version_5_left.jpg" 6.668 6.479 1.03 decode::Perf_Objdetect_QRCode::"version_5_top.jpg" 6.481 6.484 1.00 decode::Perf_Objdetect_QRCode::"version_5_up.jpg" 7.011 6.513 1.08 decodeMulti::Perf_Objdetect_QRCode_Multi::("2_qrcodes.png", "aruco_based") 14.885 15.089 0.99 decodeMulti::Perf_Objdetect_QRCode_Multi::("2_qrcodes.png", "contours_based") 14.896 14.906 1.00 decodeMulti::Perf_Objdetect_QRCode_Multi::("3_close_qrcodes.png", "aruco_based") 6.661 6.663 1.00 decodeMulti::Perf_Objdetect_QRCode_Multi::("3_close_qrcodes.png", "contours_based") 6.614 6.592 1.00 decodeMulti::Perf_Objdetect_QRCode_Multi::("3_qrcodes.png", "aruco_based") 14.814 14.592 1.02 decodeMulti::Perf_Objdetect_QRCode_Multi::("3_qrcodes.png", "contours_based") 15.245 15.135 1.01 decodeMulti::Perf_Objdetect_QRCode_Multi::("4_qrcodes.png", "aruco_based") 10.923 10.881 1.00 decodeMulti::Perf_Objdetect_QRCode_Multi::("4_qrcodes.png", "contours_based") 10.680 10.128 1.05 decodeMulti::Perf_Objdetect_QRCode_Multi::("5_qrcodes.png", "contours_based") 11.788 11.576 1.02 decodeMulti::Perf_Objdetect_QRCode_Multi::("6_qrcodes.png", "aruco_based") 25.887 25.979 1.00 decodeMulti::Perf_Objdetect_QRCode_Multi::("6_qrcodes.png", "contours_based") 26.183 25.627 1.02 decodeMulti::Perf_Objdetect_QRCode_Multi::("7_qrcodes.png", "aruco_based") 32.786 32.253 1.02 decodeMulti::Perf_Objdetect_QRCode_Multi::("7_qrcodes.png", "contours_based") 24.290 24.435 0.99 decodeMulti::Perf_Objdetect_QRCode_Multi::("8_close_qrcodes.png", "aruco_based") 89.696 89.247 1.01 decodeMulti::Perf_Objdetect_QRCode_Multi::("8_close_qrcodes.png", "contours_based") 89.872 89.600 1.00 ```
353 lines
14 KiB
Python
353 lines
14 KiB
Python
#!/usr/bin/env python
|
|
|
|
import os, sys, subprocess, argparse, shutil, glob, re, multiprocessing
|
|
import logging as log
|
|
|
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
class Fail(Exception):
|
|
def __init__(self, text=None):
|
|
self.t = text
|
|
def __str__(self):
|
|
return "ERROR" if self.t is None else self.t
|
|
|
|
def execute(cmd, shell=False):
|
|
try:
|
|
log.info("Executing: %s" % cmd)
|
|
env = os.environ.copy()
|
|
env['VERBOSE'] = '1'
|
|
retcode = subprocess.call(cmd, shell=shell, env=env)
|
|
if retcode < 0:
|
|
raise Fail("Child was terminated by signal: %s" % -retcode)
|
|
elif retcode > 0:
|
|
raise Fail("Child returned: %s" % retcode)
|
|
except OSError as e:
|
|
raise Fail("Execution failed: %d / %s" % (e.errno, e.strerror))
|
|
|
|
def rm_one(d):
|
|
d = os.path.abspath(d)
|
|
if os.path.exists(d):
|
|
if os.path.isdir(d):
|
|
log.info("Removing dir: %s", d)
|
|
shutil.rmtree(d)
|
|
elif os.path.isfile(d):
|
|
log.info("Removing file: %s", d)
|
|
os.remove(d)
|
|
|
|
def check_dir(d, create=False, clean=False):
|
|
d = os.path.abspath(d)
|
|
log.info("Check dir %s (create: %s, clean: %s)", d, create, clean)
|
|
if os.path.exists(d):
|
|
if not os.path.isdir(d):
|
|
raise Fail("Not a directory: %s" % d)
|
|
if clean:
|
|
for x in glob.glob(os.path.join(d, "*")):
|
|
rm_one(x)
|
|
else:
|
|
if create:
|
|
os.makedirs(d)
|
|
return d
|
|
|
|
def check_file(d):
|
|
d = os.path.abspath(d)
|
|
if os.path.exists(d):
|
|
if os.path.isfile(d):
|
|
return True
|
|
else:
|
|
return False
|
|
return False
|
|
|
|
def find_file(name, path):
|
|
for root, dirs, files in os.walk(path):
|
|
if name in files:
|
|
return os.path.join(root, name)
|
|
|
|
class Builder:
|
|
def __init__(self, options):
|
|
self.options = options
|
|
self.build_dir = check_dir(options.build_dir, create=True)
|
|
self.opencv_dir = check_dir(options.opencv_dir)
|
|
print('-----------------------------------------------------------')
|
|
print('options.opencv_dir:', options.opencv_dir)
|
|
self.emscripten_dir = check_dir(options.emscripten_dir)
|
|
|
|
def get_toolchain_file(self):
|
|
return os.path.join(self.emscripten_dir, "cmake", "Modules", "Platform", "Emscripten.cmake")
|
|
|
|
def clean_build_dir(self):
|
|
for d in ["CMakeCache.txt", "CMakeFiles/", "bin/", "libs/", "lib/", "modules"]:
|
|
rm_one(d)
|
|
|
|
def get_cmake_cmd(self):
|
|
cmd = [
|
|
"cmake",
|
|
"-DPYTHON_DEFAULT_EXECUTABLE=%s" % sys.executable,
|
|
"-DENABLE_PIC=FALSE", # To workaround emscripten upstream backend issue https://github.com/emscripten-core/emscripten/issues/8761
|
|
"-DCMAKE_BUILD_TYPE=Release",
|
|
"-DCMAKE_TOOLCHAIN_FILE='%s'" % self.get_toolchain_file(),
|
|
"-DCPU_BASELINE=''",
|
|
"-DCMAKE_INSTALL_PREFIX=/usr/local",
|
|
"-DCPU_DISPATCH=''",
|
|
"-DCV_TRACE=OFF",
|
|
"-DBUILD_SHARED_LIBS=OFF",
|
|
"-DWITH_1394=OFF",
|
|
"-DWITH_ADE=OFF",
|
|
"-DWITH_VTK=OFF",
|
|
"-DWITH_EIGEN=OFF",
|
|
"-DWITH_FFMPEG=OFF",
|
|
"-DWITH_GSTREAMER=OFF",
|
|
"-DWITH_GTK=OFF",
|
|
"-DWITH_GTK_2_X=OFF",
|
|
"-DWITH_IPP=OFF",
|
|
"-DWITH_JASPER=OFF",
|
|
"-DWITH_JPEG=OFF",
|
|
"-DWITH_WEBP=OFF",
|
|
"-DWITH_OPENEXR=OFF",
|
|
"-DWITH_OPENGL=OFF",
|
|
"-DWITH_OPENVX=OFF",
|
|
"-DWITH_OPENNI=OFF",
|
|
"-DWITH_OPENNI2=OFF",
|
|
"-DWITH_PNG=OFF",
|
|
"-DWITH_TBB=OFF",
|
|
"-DWITH_TIFF=OFF",
|
|
"-DWITH_V4L=OFF",
|
|
"-DWITH_OPENCL=OFF",
|
|
"-DWITH_OPENCL_SVM=OFF",
|
|
"-DWITH_OPENCLAMDFFT=OFF",
|
|
"-DWITH_OPENCLAMDBLAS=OFF",
|
|
"-DWITH_GPHOTO2=OFF",
|
|
"-DWITH_LAPACK=OFF",
|
|
"-DWITH_ITT=OFF",
|
|
"-DWITH_QUIRC=OFF",
|
|
"-DBUILD_ZLIB=ON",
|
|
"-DBUILD_opencv_apps=OFF",
|
|
"-DBUILD_opencv_calib3d=ON",
|
|
"-DBUILD_opencv_dnn=ON",
|
|
"-DBUILD_opencv_features2d=ON",
|
|
"-DBUILD_opencv_flann=ON", # No bindings provided. This module is used as a dependency for other modules.
|
|
"-DBUILD_opencv_gapi=OFF",
|
|
"-DBUILD_opencv_ml=OFF",
|
|
"-DBUILD_opencv_photo=ON",
|
|
"-DBUILD_opencv_imgcodecs=OFF",
|
|
"-DBUILD_opencv_shape=OFF",
|
|
"-DBUILD_opencv_videoio=OFF",
|
|
"-DBUILD_opencv_videostab=OFF",
|
|
"-DBUILD_opencv_highgui=OFF",
|
|
"-DBUILD_opencv_superres=OFF",
|
|
"-DBUILD_opencv_stitching=OFF",
|
|
"-DBUILD_opencv_java=OFF",
|
|
"-DBUILD_opencv_js=ON",
|
|
"-DBUILD_opencv_python2=OFF",
|
|
"-DBUILD_opencv_python3=OFF",
|
|
"-DBUILD_EXAMPLES=ON",
|
|
"-DBUILD_PACKAGE=OFF",
|
|
"-DBUILD_TESTS=ON",
|
|
"-DBUILD_PERF_TESTS=ON"]
|
|
if self.options.cmake_option:
|
|
cmd += self.options.cmake_option
|
|
if self.options.build_doc:
|
|
cmd.append("-DBUILD_DOCS=ON")
|
|
else:
|
|
cmd.append("-DBUILD_DOCS=OFF")
|
|
|
|
if self.options.threads:
|
|
cmd.append("-DWITH_PTHREADS_PF=ON")
|
|
else:
|
|
cmd.append("-DWITH_PTHREADS_PF=OFF")
|
|
|
|
if self.options.simd:
|
|
cmd.append("-DCV_ENABLE_INTRINSICS=ON")
|
|
else:
|
|
cmd.append("-DCV_ENABLE_INTRINSICS=OFF")
|
|
|
|
if self.options.build_wasm_intrin_test:
|
|
cmd.append("-DBUILD_WASM_INTRIN_TESTS=ON")
|
|
else:
|
|
cmd.append("-DBUILD_WASM_INTRIN_TESTS=OFF")
|
|
|
|
if self.options.webnn:
|
|
cmd.append("-DWITH_WEBNN=ON")
|
|
|
|
flags = self.get_build_flags()
|
|
if flags:
|
|
cmd += ["-DCMAKE_C_FLAGS='%s'" % flags,
|
|
"-DCMAKE_CXX_FLAGS='%s'" % flags]
|
|
return cmd
|
|
|
|
def get_build_flags(self):
|
|
flags = ""
|
|
if self.options.build_wasm:
|
|
flags += "-s WASM=1 "
|
|
elif self.options.disable_wasm:
|
|
flags += "-s WASM=0 "
|
|
if not self.options.disable_single_file:
|
|
flags += "-s SINGLE_FILE=1 "
|
|
if self.options.threads:
|
|
flags += "-s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=4 "
|
|
else:
|
|
flags += "-s USE_PTHREADS=0 "
|
|
if self.options.enable_exception:
|
|
flags += "-s DISABLE_EXCEPTION_CATCHING=0 "
|
|
if self.options.simd:
|
|
flags += "-msimd128 "
|
|
if self.options.build_flags:
|
|
flags += self.options.build_flags
|
|
if self.options.webnn:
|
|
flags += "-s USE_WEBNN=1 "
|
|
return flags
|
|
|
|
def config(self):
|
|
cmd = self.get_cmake_cmd()
|
|
cmd.append(self.opencv_dir)
|
|
execute(cmd)
|
|
|
|
def build_opencvjs(self):
|
|
execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv.js"])
|
|
|
|
def build_test(self):
|
|
execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv_js_test"])
|
|
|
|
def build_perf(self):
|
|
execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv_js_perf"])
|
|
|
|
def build_doc(self):
|
|
execute(["make", "-j", str(multiprocessing.cpu_count()), "doxygen"])
|
|
|
|
def build_loader(self):
|
|
execute(["make", "-j", str(multiprocessing.cpu_count()), "opencv_js_loader"])
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
if __name__ == "__main__":
|
|
log.basicConfig(format='%(message)s', level=log.DEBUG)
|
|
|
|
opencv_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '../..'))
|
|
emscripten_dir = None
|
|
if "EMSCRIPTEN" in os.environ:
|
|
emscripten_dir = os.environ["EMSCRIPTEN"]
|
|
else:
|
|
log.warning("EMSCRIPTEN environment variable is not available. Please properly activate Emscripten SDK and consider using 'emcmake' launcher")
|
|
|
|
parser = argparse.ArgumentParser(description='Build OpenCV.js by Emscripten')
|
|
parser.add_argument("build_dir", help="Building directory (and output)")
|
|
parser.add_argument('--opencv_dir', default=opencv_dir, help='Opencv source directory (default is "../.." relative to script location)')
|
|
parser.add_argument('--emscripten_dir', default=emscripten_dir, help="Path to Emscripten to use for build (deprecated in favor of 'emcmake' launcher)")
|
|
parser.add_argument('--build_wasm', action="store_true", help="Build OpenCV.js in WebAssembly format")
|
|
parser.add_argument('--disable_wasm', action="store_true", help="Build OpenCV.js in Asm.js format")
|
|
parser.add_argument('--disable_single_file', action="store_true", help="Do not merge JavaScript and WebAssembly into one single file")
|
|
parser.add_argument('--threads', action="store_true", help="Build OpenCV.js with threads optimization")
|
|
parser.add_argument('--simd', action="store_true", help="Build OpenCV.js with SIMD optimization")
|
|
parser.add_argument('--build_test', action="store_true", help="Build tests")
|
|
parser.add_argument('--build_perf', action="store_true", help="Build performance tests")
|
|
parser.add_argument('--build_doc', action="store_true", help="Build tutorials")
|
|
parser.add_argument('--build_loader', action="store_true", help="Build OpenCV.js loader")
|
|
parser.add_argument('--clean_build_dir', action="store_true", help="Clean build dir")
|
|
parser.add_argument('--skip_config', action="store_true", help="Skip cmake config")
|
|
parser.add_argument('--config_only', action="store_true", help="Only do cmake config")
|
|
parser.add_argument('--enable_exception', action="store_true", help="Enable exception handling")
|
|
# Use flag --cmake option="-D...=ON" only for one argument, if you would add more changes write new cmake_option flags
|
|
parser.add_argument('--cmake_option', action='append', help="Append CMake options")
|
|
# Use flag --build_flags="-s USE_PTHREADS=0 -Os" for one and more arguments as in the example
|
|
parser.add_argument('--build_flags', help="Append Emscripten build options")
|
|
parser.add_argument('--build_wasm_intrin_test', default=False, action="store_true", help="Build WASM intrin tests")
|
|
# Write a path to modify file like argument of this flag
|
|
parser.add_argument('--config', default=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'opencv_js.config.py'),
|
|
help="Specify configuration file with own list of exported into JS functions")
|
|
parser.add_argument('--webnn', action="store_true", help="Enable WebNN Backend")
|
|
|
|
args = parser.parse_args()
|
|
|
|
log.debug("Args: %s", args)
|
|
|
|
os.environ["OPENCV_JS_WHITELIST"] = os.path.abspath(args.config)
|
|
|
|
if 'EMMAKEN_JUST_CONFIGURE' in os.environ:
|
|
del os.environ['EMMAKEN_JUST_CONFIGURE'] # avoid linker errors with NODERAWFS message then using 'emcmake' launcher
|
|
|
|
if args.emscripten_dir is None:
|
|
log.error("Cannot get Emscripten path, please use 'emcmake' launcher or specify it either by EMSCRIPTEN environment variable or --emscripten_dir option.")
|
|
sys.exit(-1)
|
|
|
|
builder = Builder(args)
|
|
|
|
os.chdir(builder.build_dir)
|
|
|
|
if args.clean_build_dir:
|
|
log.info("=====")
|
|
log.info("===== Clean build dir %s", builder.build_dir)
|
|
log.info("=====")
|
|
builder.clean_build_dir()
|
|
|
|
if not args.skip_config:
|
|
target = "default target"
|
|
if args.build_wasm:
|
|
target = "wasm"
|
|
elif args.disable_wasm:
|
|
target = "asm.js"
|
|
log.info("=====")
|
|
log.info("===== Config OpenCV.js build for %s" % target)
|
|
log.info("=====")
|
|
builder.config()
|
|
|
|
if args.config_only:
|
|
sys.exit(0)
|
|
|
|
log.info("=====")
|
|
log.info("===== Building OpenCV.js")
|
|
log.info("=====")
|
|
builder.build_opencvjs()
|
|
|
|
if args.build_test:
|
|
log.info("=====")
|
|
log.info("===== Building OpenCV.js tests")
|
|
log.info("=====")
|
|
builder.build_test()
|
|
|
|
if args.build_perf:
|
|
log.info("=====")
|
|
log.info("===== Building OpenCV.js performance tests")
|
|
log.info("=====")
|
|
builder.build_perf()
|
|
|
|
if args.build_doc:
|
|
log.info("=====")
|
|
log.info("===== Building OpenCV.js tutorials")
|
|
log.info("=====")
|
|
builder.build_doc()
|
|
|
|
if args.build_loader:
|
|
log.info("=====")
|
|
log.info("===== Building OpenCV.js loader")
|
|
log.info("=====")
|
|
builder.build_loader()
|
|
|
|
log.info("=====")
|
|
log.info("===== Build finished")
|
|
log.info("=====")
|
|
|
|
opencvjs_path = os.path.join(builder.build_dir, "bin", "opencv.js")
|
|
if check_file(opencvjs_path):
|
|
log.info("OpenCV.js location: %s", opencvjs_path)
|
|
|
|
if args.build_test:
|
|
opencvjs_test_path = os.path.join(builder.build_dir, "bin", "tests.html")
|
|
if check_file(opencvjs_test_path):
|
|
log.info("OpenCV.js tests location: %s", opencvjs_test_path)
|
|
|
|
if args.build_perf:
|
|
opencvjs_perf_path = os.path.join(builder.build_dir, "bin", "perf")
|
|
opencvjs_perf_base_path = os.path.join(builder.build_dir, "bin", "perf", "base.js")
|
|
if check_file(opencvjs_perf_base_path):
|
|
log.info("OpenCV.js performance tests location: %s", opencvjs_perf_path)
|
|
|
|
if args.build_doc:
|
|
opencvjs_tutorial_path = find_file("tutorial_js_root.html", os.path.join(builder.build_dir, "doc", "doxygen", "html"))
|
|
if check_file(opencvjs_tutorial_path):
|
|
log.info("OpenCV.js tutorials location: %s", opencvjs_tutorial_path)
|
|
|
|
if args.build_loader:
|
|
opencvjs_loader_path = os.path.join(builder.build_dir, "bin", "loader.js")
|
|
if check_file(opencvjs_loader_path):
|
|
log.info("OpenCV.js loader location: %s", opencvjs_loader_path)
|