diff --git a/.gitignore b/.gitignore index 79e16989..089c84da 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build command +tmp diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100755 index 34a7b5a8..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/build/cfsd18-perception-detectcone", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": ["--cid=48","--id=112","--name=cam0", - "--width=1344", "--height=376", "--bpp=24", "--threshold=0.8", - "--timeDiffMilliseconds=20", "--separationTimeMs=10", "--checkLidarMilliseconds=1000", - "--senderStamp=118", "--attentionSenderStamp=116", - "--offline=1", "--annotate=1", "--stateMachineId=1401", "--readyStateMachine=1", - "--forwardDetection=0", "--verbose"], - "externalConsole": true, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - ] -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bddbc508..1def182f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,9 +19,9 @@ project(cfsd18-perception-detectcone) ################################################################################ # Defining the relevant versions of OpenDLV Standard Message Set and libcluon. -set(OPENDLV_STANDARD_MESSAGE_SET opendlv-standard-message-set-v0.9.1.odvd) -set(CLUON_COMPLETE cluon-complete-v0.0.102.hpp) - +set(OPENDLV_STANDARD_MESSAGE_SET opendlv-standard-message-set-v0.9.5.odvd) +set(CLUON_COMPLETE cluon-complete-v0.0.114.hpp) +set(CLUON_PATH ${CMAKE_CURRENT_SOURCE_DIR}) ################################################################################ # This project requires C++14 or newer. set(CMAKE_CXX_STANDARD 14) @@ -53,41 +53,47 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ # Threads are necessary for linking the resulting binaries as UDPReceiver is running in parallel. set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) + +SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake.Modules") + ################################################################################ # Extract cluon-msc from cluon-complete.hpp. + +# Downloads a file, populates the root dir (needs to discussed if we want this...) +# execute_process(COMMAND wget -P ${CLUON_PATH} https://raw.githubusercontent.com/chrberger/libcluon/gh-pages/headeronly/${CLUON_COMPLETE}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cluon-msc WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE} ${CMAKE_BINARY_DIR}/cluon-complete.hpp + COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/include/${CLUON_COMPLETE} ${CMAKE_BINARY_DIR}/cluon-complete.hpp COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/cluon-complete.hpp ${CMAKE_BINARY_DIR}/cluon-complete.cpp COMMAND ${CMAKE_CXX_COMPILER} -o ${CMAKE_BINARY_DIR}/cluon-msc ${CMAKE_BINARY_DIR}/cluon-complete.cpp -std=c++14 -pthread -D HAVE_CLUON_MSC - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE}) + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/include/${CLUON_COMPLETE}) ################################################################################ # Generate opendlv-standard-message-set.{hpp,cpp} from ${OPENDLV_STANDARD_MESSAGE_SET} file. -add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.cpp +add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_BINARY_DIR}/cluon-msc --cpp-sources --cpp-add-include-file=opendlv-standard-message-set.hpp --out=${CMAKE_BINARY_DIR}/opendlv-standard-message-set.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} - COMMAND ${CMAKE_BINARY_DIR}/cluon-msc --cpp-headers --out=${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} ${CMAKE_BINARY_DIR}/cluon-msc) + COMMAND ${CMAKE_BINARY_DIR}/cluon-msc --cpp --out=${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/${OPENDLV_STANDARD_MESSAGE_SET} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/include/${OPENDLV_STANDARD_MESSAGE_SET} ${CMAKE_BINARY_DIR}/cluon-msc) # Add current build directory as include directory as it contains generated files. include_directories(SYSTEM ${CMAKE_BINARY_DIR}) -include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty) ################################################################################ # Gather all object code first to avoid double compilation. This needs to be done for all added objects, can become very long? Why is this object and not static which works +add_library(${PROJECT_NAME}-core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/detectcone.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/collector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/cone.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp) +add_custom_target(generate_opendlv_standard_message_set_hpp DEPENDS ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp) +add_dependencies(${PROJECT_NAME}-core generate_opendlv_standard_message_set_hpp) ################################################################################ # OpenCV -#set(CMAKE_PREFIX_PATH "/usr/local/opt/opencv3/share/OpenCV") find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) - ################################################################################ # Eigen -find_package(Eigen3 REQUIRED NO_MODULE) -include_directories(${Eigen3_INCLUDE_DIRS}) +find_package(Eigen3 3.3 REQUIRED NO_MODULE) +include_directories(${Eigen_INCLUDE_DIRS}) ############################################################################### # OpenMP @@ -101,39 +107,31 @@ IF(USE_OpenMP) ENDIF() ############################################################################### -#TinyDnn +# TinyDnn find_package(TinyDNN REQUIRED) ############################################################################### -#TBB +# TBB find_package(TBB REQUIRED) include_directories(${TBB_INCLUDE_DIRS}) -add_definitions(${TBB_DEFINITIONS}) -SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") +################################################################################ +# LibRT find_package(LibRT REQUIRED) set(LIBRARIES ${LIBRARIES} ${LIBRT_LIBRARIES}) include_directories(SYSTEM ${LIBRT_INCLUDE_DIR}) -################################################################################ -# Link libs -#set(LIBRARIES Threads::Threads opencv_core opencv_imgcodecs opencv_features2d opencv_imgproc opencv_highgui opencv_calib3d rt libgio-2.0) include_directories(SYSTEM /usr/include) include_directories(SYSTEM /usr/local/include) -link_directories(SYSTEM /usr/lib) #Access g2o -link_directories(SYSTEM /usr/local/lib) #Access g2o +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) -add_library(${PROJECT_NAME}-core STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/detectcone.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/collector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/cone.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.cpp) - -# Add custom libs h -set(LIBRARIES ${LIBRARIES} Threads::Threads ${OpenCV_LIBS} ${Eigen3_LIBS} ${TinyDNN_LIBS} ${TBB_LIBRARIES} rt) +################################################################################ +# Link libs +set(LIBRARIES ${LIBRARIES} Threads::Threads ${OpenCV_LIBS} ${Eigen3_LIBS} ${TinyDNN_LIBS} ${TBB_LIBRARIES} ) -MESSAGE(STATUS "heej") MESSAGE(STATUS ${LIBRARIES}) -#${TBB_LIBRARIES} ################################################################################ # Create executable. diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index 21a9431d..fedfe55e 100755 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -13,47 +13,46 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Part to build opendlv-perception-vision-orbslam2. -FROM alpine:edge as builder -MAINTAINER Christian Berger "christian.berger@gu.se" +# Part to build opendlv-perception-vision-orbslam2. +FROM alpine:3.8 as builder +# Deprecated, use label (see docker docs) +# MAINTAINER Christian Berger "christian.berger@gu.se" #Get OS stuff RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories -RUN apk update && \ +RUN apk update && apk upgrade && \ apk --no-cache add \ cmake \ - #bash \ g++ \ make \ upx \ git \ - mercurial \ + # mercurial \ libmount \ libtbb@testing \ libtbb-dev@testing \ - opencv-dev@testing + opencv@testing \ + opencv-dev@testing \ + libgomp \ + libtbb@testing \ + libtbb-dev@testing \ + openblas \ + openblas-dev WORKDIR /tmp -ADD ./config.h /opt/sources/config.h -RUN git clone https://github.com/justusc/FindTBB.git && \ - cp FindTBB/FindTBB.cmake /usr/share/cmake/Modules/ - -RUN git clone https://github.com/tiny-dnn/tiny-dnn.git && \ - mkdir -p tiny-dnn/build && \ - cp /opt/sources/config.h tiny-dnn/tiny_dnn/config.h && \ - cd tiny-dnn/build && \ - cmake .. && \ - make install - -RUN git clone https://github.com/eigenteam/eigen-git-mirror.git && \ +RUN git clone --depth 1 https://github.com/eigenteam/eigen-git-mirror.git && \ mkdir -p eigen-git-mirror/build && \ cd eigen-git-mirror/build && \ cmake .. && \ make install -RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories -RUN apk update && \ - apk --no-cache add \ -opencv@testing + +RUN git clone --depth 10 https://github.com/tiny-dnn/tiny-dnn.git && \ + mkdir -p tiny-dnn/build && \ + cd tiny-dnn/build && \ + # Temp fix for the recent compilation error + git checkout 1aec6a1ece0ba7a5e018a070bd52e045d49d1411 && \ + cmake -DUSE_TBB=ON -DUSE_OMP=ON .. && \ + make install ADD . /opt/sources/ @@ -62,17 +61,98 @@ WORKDIR /opt/sources RUN mkdir -p build && \ cd build && \ cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/tmp/cfsd18-perception-detectcone-dest .. && \ - make -j3 && make test && make install && upx -9 /tmp/cfsd18-perception-detectcone-dest/bin/cfsd18-perception-detectcone + make -j3 && make test && make install + # mkdir -p /usr/lib/dependency && \ + # cp $(ldd cfsd18-perception-detectcone | awk '/\/usr\/bin/ {print $3}') /usr/lib/dependency/ + # && upx -9 /tmp/cfsd18-perception-detectcone-dest/bin/cfsd18-perception-detectcone # Part to deploy opendlv-perception-vision-orbslam2 FROM alpine:edge -MAINTAINER Christian Berger "christian.berger@gu.se" +# Deprecated, use label (see docker docs) +# MAINTAINER Christian Berger "christian.berger@gu.se" +RUN apk update && \ + apk --no-cache add \ + glib #Start microservice WORKDIR /usr/bin -COPY --from=builder /usr/local/include/ /usr/include/ -COPY --from=builder /usr/local/lib/ /usr/lib/ -COPY --from=builder /usr/lib/ /usr/lib/ +# This will make the image heavy... Do we need all of them? +# This could be slimmed down +# Nothing in the builder's /usr/local/lib +# COPY --from=builder /usr/local/lib/ /usr/lib/ +# COPY --from=builder /usr/lib/ /usr/lib/ + +# COPY --from=builder /usr/lib/dependency/ /usr/lib/ +# Tried to copy from /usr/lib/dependency/ +# but " ldd cfsd18-perception-detectcone | awk '{print $3}' " output nothing (i.e. line 66 failed) +# instead, use the manual and tedious way +# image size: 393MB -> 79.1MB +COPY --from=builder /usr/lib/libopencv_calib3d.so.3.2 \ + /usr/lib/libopencv_features2d.so.3.2 \ + /usr/lib/libopencv_flann.so.3.2 \ + /usr/lib/libopencv_highgui.so.3.2 \ + /usr/lib/libopencv_imgcodecs.so.3.2 \ + /usr/lib/libopencv_imgproc.so.3.2 \ + /usr/lib/libopencv_core.so.3.2 \ + /usr/lib/libstdc++.so.6 \ + /usr/lib/libgcc_s.so.1 \ + /usr/lib/libgtk-3.so.0 \ + /usr/lib/libgdk-3.so.0 \ + /usr/lib/libcairo.so.2 \ + /usr/lib/libgdk_pixbuf-2.0.so.0 \ + /usr/lib/libgobject-2.0.so.0 \ + /usr/lib/libglib-2.0.so.0 \ + /usr/lib/libjpeg.so.8 \ + /usr/lib/libwebp.so.7 \ + /usr/lib/libpng16.so.16 \ + /usr/lib/libtiff.so.5 \ + /usr/lib/libjasper.so.4 \ + /usr/lib/libIlmImf-2_2.so.23 \ + /usr/lib/libHalf.so.12 \ + /usr/lib/libgomp.so.1 \ + /usr/lib/libgmodule-2.0.so.0 \ + /usr/lib/libpangocairo-1.0.so.0 \ + /usr/lib/libX11.so.6 \ + /usr/lib/libXi.so.6 \ + /usr/lib/libXfixes.so.3 \ + /usr/lib/libcairo-gobject.so.2 \ + /usr/lib/libatk-1.0.so.0 \ + /usr/lib/libatk-bridge-2.0.so.0 \ + /usr/lib/libepoxy.so.0 \ + /usr/lib/libpangoft2-1.0.so.0 \ + /usr/lib/libpango-1.0.so.0 \ + /usr/lib/libfontconfig.so.1 \ + /usr/lib/libgio-2.0.so.0 \ + /usr/lib/libintl.so.8 \ + /usr/lib/libXinerama.so.1 \ + /usr/lib/libXrandr.so.2 \ + /usr/lib/libXcursor.so.1 \ + /usr/lib/libXcomposite.so.1 \ + /usr/lib/libXdamage.so.1 \ + /usr/lib/libxkbcommon.so.0 \ + /usr/lib/libwayland-cursor.so.0 \ + /usr/lib/libwayland-egl.so.1 \ + /usr/lib/libwayland-client.so.0 \ + /usr/lib/libXext.so.6 \ + /usr/lib/libpixman-1.so.0 \ + /usr/lib/libfreetype.so.6 \ + /usr/lib/libxcb-shm.so.0 \ + /usr/lib/libxcb.so.1 \ + /usr/lib/libxcb-render.so.0 \ + /usr/lib/libXrender.so.1 \ + /usr/lib/libffi.so.6 \ + /usr/lib/libpcre.so.1 \ + /usr/lib/libIex-2_2.so.12 \ + /usr/lib/libIlmThread-2_2.so.12 \ + /usr/lib/libatspi.so.0 \ + /usr/lib/libdbus-1.so.3 \ + /usr/lib/libharfbuzz.so.0 \ + /usr/lib/libexpat.so.1 \ + /usr/lib/libbz2.so.1 \ + /usr/lib/libXau.so.6 \ + /usr/lib/libXdmcp.so.6 \ + /usr/lib/libgraphite2.so.3 \ + /usr/lib/libbsd.so.0 \ + /usr/lib/ COPY --from=builder /tmp/cfsd18-perception-detectcone-dest/bin/cfsd18-perception-detectcone . -RUN apk update && apk add glib CMD ["/usr/bin/cfsd18-perception-detectcone"] diff --git a/FindOpenCV.cmake b/FindOpenCV.cmake deleted file mode 100755 index 160a1fa9..00000000 --- a/FindOpenCV.cmake +++ /dev/null @@ -1,243 +0,0 @@ -# - Try to find OpenCV library installation -# See http://sourceforge.net/projects/opencvlibrary/ -# -# The follwoing variables are optionally searched for defaults -# OpenCV_ROOT_DIR: Base directory of OpenCv tree to use. -# OpenCV_FIND_REQUIRED_COMPONENTS : FIND_PACKAGE(OpenCV COMPONENTS ..) -# compatible interface. typically CV CXCORE CVAUX HIGHGUI CVCAM .. etc. -# -# The following are set after configuration is done: -# OpenCV_FOUND -# OpenCV_INCLUDE_DIR -# OpenCV_LIBRARIES -# OpenCV_LINK_DIRECTORIES -# -# 2004/05 Jan Woetzel, Friso, Daniel Grest -# 2006/01 complete rewrite by Jan Woetzel -# 1006/09 2nd rewrite introducing ROOT_DIR and PATH_SUFFIXES -# to handle multiple installed versions gracefully by Jan Woetzel -# -# tested with: -# -OpenCV 0.97 (beta5a): MSVS 7.1, gcc 3.3, gcc 4.1 -# -OpenCV 0.99 (1.0rc1): MSVS 7.1 -# -# www.mip.informatik.uni-kiel.de/~jw -# -# --------------------- -# -# $Id: FindOpenCV.cmake 8 2009-01-04 21:13:48Z adeguet1 $ -# ERC-CISST version: -# -# - Removed deprecated code starting with cap. OPENCV -# - Removed debugging code and messages -# - Removed path and options specifics to previous authors setups -# -# This file should be removed when CMake will provide an equivalent - - -# required cv components with header and library if COMPONENTS unspecified -IF(NOT OpenCV_FIND_COMPONENTS) - # default - - - - # - # fix for OpenCV 2.4.6.1 on Mac OSX - # - #SET(OpenCV_FIND_REQUIRED_COMPONENTS CV CXCORE HIGHGUI) - SET(OpenCV_FIND_REQUIRED_COMPONENTS CORE HIGHGUI) - - - -# IF(WIN32) -# LIST(APPEND OpenCV_FIND_REQUIRED_COMPONENTS CVCAM ) # WIN32 only actually -# ENDIF(WIN32) -ENDIF(NOT OpenCV_FIND_COMPONENTS) - - -# typical root dirs of installations, exactly one of them is used -SET(OpenCV_POSSIBLE_ROOT_DIRS - "${OpenCV_ROOT_DIR}" - "$ENV{OpenCV_ROOT_DIR}" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Intel(R) Open Source Computer Vision Library_is1;Inno Setup: App Path]" - "$ENV{ProgramFiles}/OpenCV" - "/usr/local" - "/usr" - "/opt/local" - ) - -# -# select exactly ONE OpenCV base directory/tree -# to avoid mixing different version headers and libs -# -FIND_PATH(OpenCV_ROOT_DIR - NAMES - cv/include/cv.h # windows - include/opencv/cv.h # linux /opt/net - include/cv/cv.h - include/cv.h - PATHS ${OpenCV_POSSIBLE_ROOT_DIRS}) - -# header include dir suffixes appended to OpenCV_ROOT_DIR -SET(OpenCV_INCDIR_SUFFIXES - include - include/cv - include/opencv - cv/include - cxcore/include - cvaux/include - otherlibs/cvcam - otherlibs/highgui - ) - -# library linkdir suffixes appended to OpenCV_ROOT_DIR -SET(OpenCV_LIBDIR_SUFFIXES - lib - OpenCV/lib - ) - -# find incdir for each lib -FIND_PATH(OpenCV_CV_INCLUDE_DIR - NAMES cv.h - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES}) - -FIND_PATH(OpenCV_CXCORE_INCLUDE_DIR - NAMES cxcore.h - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES}) -FIND_PATH(OpenCV_CVAUX_INCLUDE_DIR - NAMES cvaux.h - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES}) -FIND_PATH(OpenCV_HIGHGUI_INCLUDE_DIR - NAMES highgui.h - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES}) -FIND_PATH(OpenCV_CVCAM_INCLUDE_DIR - NAMES cvcam.h - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES}) - - - -# -# fix for OpenCV 2.4.6.1 on Mac OSX -# -FIND_PATH(OpenCV_CORE_INCLUDE_DIR - NAMES cxcore.h - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_INCDIR_SUFFIXES}) - - - -# -# find sbsolute path to all libraries -# some are optionally, some may not exist on Linux -# -FIND_LIBRARY(OpenCV_CV_LIBRARY - NAMES cv opencv - PATHS ${OpenCV_ROOT_DIR} - PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_CVAUX_LIBRARY - NAMES cvaux - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_CVCAM_LIBRARY - NAMES cvcam - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_CVHAARTRAINING_LIBRARY - NAMES cvhaartraining - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_CXCORE_LIBRARY - NAMES cxcore - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_CXTS_LIBRARY - NAMES cxts - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_HIGHGUI_LIBRARY - NAMES highgui - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) -FIND_LIBRARY(OpenCV_TRS_LIBRARY - NAMES trs - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) - - - -FIND_LIBRARY(OpenCV_CORE_LIBRARY - NAMES opencv_core - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) - -FIND_LIBRARY(OpenCV_HIGHGUI_LIBRARY - NAMES opencv_highgui - PATHS ${OpenCV_ROOT_DIR} PATH_SUFFIXES ${OpenCV_LIBDIR_SUFFIXES}) - - - -# Logic selecting required libs and headers -SET(OpenCV_FOUND ON) -FOREACH(NAME ${OpenCV_FIND_REQUIRED_COMPONENTS}) - # only good if header and library both found - IF(OpenCV_${NAME}_INCLUDE_DIR AND OpenCV_${NAME}_LIBRARY) - LIST(APPEND OpenCV_INCLUDE_DIRS "${OpenCV_${NAME}_INCLUDE_DIR}") - LIST(APPEND OpenCV_LIBRARIES "${OpenCV_${NAME}_LIBRARY}") - ELSE(OpenCV_${NAME}_INCLUDE_DIR AND OpenCV_${NAME}_LIBRARY) - message("Could not find OpenCV_${NAME}_INCLUDE_DIR") - SET(OpenCV_FOUND OFF) - ENDIF(OpenCV_${NAME}_INCLUDE_DIR AND OpenCV_${NAME}_LIBRARY) -ENDFOREACH(NAME) - -message("Include dir: ${OpenCV_FOUND}") - -# get the link directory for rpath to be used with LINK_DIRECTORIES: -IF(OpenCV_CV_LIBRARY) - GET_FILENAME_COMPONENT(OpenCV_LINK_DIRECTORIES ${OpenCV_CV_LIBRARY} PATH) -ENDIF(OpenCV_CV_LIBRARY) - -MARK_AS_ADVANCED( - OpenCV_ROOT_DIR - OpenCV_INCLUDE_DIRS - OpenCV_CV_INCLUDE_DIR - OpenCV_CXCORE_INCLUDE_DIR - OpenCV_CVAUX_INCLUDE_DIR - OpenCV_CVCAM_INCLUDE_DIR - OpenCV_HIGHGUI_INCLUDE_DIR - OpenCV_LIBRARIES - OpenCV_CV_LIBRARY - OpenCV_CXCORE_LIBRARY - OpenCV_CVAUX_LIBRARY - OpenCV_CVCAM_LIBRARY - OpenCV_CVHAARTRAINING_LIBRARY - OpenCV_CXTS_LIBRARY - OpenCV_HIGHGUI_LIBRARY - OpenCV_TRS_LIBRARY - ) - - -# display help message -IF(NOT OpenCV_FOUND) - # make FIND_PACKAGE friendly - IF(OpenCV_FIND_REQUIRED) - MESSAGE(FATAL_ERROR - "OpenCV required but some headers or libs not found. Please specify it's location with OpenCV_ROOT_DIR env. variable.") - ELSE(OpenCV_FIND_REQUIRED) - MESSAGE(STATUS - "ERROR: OpenCV was not found.") - ENDIF(OpenCV_FIND_REQUIRED) -ELSE(NOT OpenCV_FOUND) - MESSAGE(STATUS - "OpenCV found at ${OpenCV_ROOT_DIR}") -ENDIF(NOT OpenCV_FOUND) - - -# -# $Log: FindOpenCV.cmake,v $ -# Revision 1.2 2008/11/03 16:40:34 vagvoba -# cmake: Proper cvcam and highgui directories added to FindOpenCV.cmake -# -# Revision 1.1 2007/07/12 00:22:25 anton -# cmake utilities: Added FindOpenCV.cmake based on example found all over the -# web. Will have to be removed when CMake provides one. -# -# - - diff --git a/README.md b/README.md index f43ebc31..3ffdddf4 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,147 @@ # cfsd18-perception-detectcone [![Build Status](https://travis-ci.org/cfsd/cfsd18-perception-detectcone.svg?branch=master)](https://travis-ci.org/cfsd/cfsd18-perception-detectcone) + +### How it works +This microservice reads image from shared memory and extract RoI (region of interest, i.e. cone position) from image. Either ORB or Lidar+ORB is applied to find RoI. ORB (an OpenCV keypoint detector) detects keypoints/cones in the image and return back the 2D positions which are later triangulated to 3D ones. The 3D points are filtered, and then clustered via k-d tree. Median point of each cluster/group is computed, representing position of a cone. 3D points are project back to 2D positions, and we can extract RoI which is centered at the corresponding median point. However, with Lidar+ORB, lidar RoI is matched to ORB RoI, providing higher accuracy of position. With either one of the two methods, we can obtain RoI and do classification on it with CNN model. At the end, this microservice will send out messages including cone position and cone color. + + +### OD4Session message in and out +- recieve: + - opendlv.logic.perception.ObjectDirection + - opendlv.logic.perception.ObjectDistance + - sender: (sensation-attention, 116) + - opendlv.proxy.SwitchStateReading + - sender: (?, 1401) +- send: + - opendlv.system.SignalStatusMessage + - opendlv.logic.perception.ObjectDirection + - opendlv.logic.perception.ObjectDistance + - opendlv.logic.perception.ObjectType + - opendlv.logic.perception.Object + - sender: (perception-detectcone, 118) + + +### Command line arguments +| parameter | comments | +| ----- | ----- | +| cid | OpenDaVINCI v4 session identifier [1..254] | +| name | name of the shared memory area; must start with '/' and be no longer than 255, if not it will be adjusted accordingly | +| width | image width, image is from camera | +| height | image height | +| bpp | bits per pixel, should be either 24 or 8 | +| threshold | if the predicted probability is bigger than the threshold, then the corresponding cone can be labeled | +| timeDiffMilliseconds | time limit of collecting a specific number of lidar cones | +| separationTimeMs | time limit of collecting a lidar cone in CollectCones function | +| checkLidarMilliseconds | time limit of recieving lidar data | +| senderStamp | sender stamp of this microservice (118) | +| attentionSenderStamp | sender stamp of sensation-attention (116) | +| offline | if true, will not save data from shared memory as png images, but will use the already existed png images for further processing | +| annotate | if ture, make annotation | +| stateMachineId | opendlv.proxy.SwitchStateReading envelope's senderstamp | +| readyStateMachine | if ture, will set the drivingState true | +| forwardDetection | if ture, will call forwardDetectionORB | +| fastThreshold | size of the border where the features are not detected, set by ORB detector in backwardDetection | +| matchDistance | [not found usage in code] | +- example usage: + + cfsd18-perception-detectcone --cid=${CID} --name=cam0 --width=2560 --height=720 --bpp=24 --threshold=0.6 --timeDiffMilliseconds=40 --separationTimeMs=20 --checkLidarMilliseconds=1000 --senderStamp=118 --attentionSenderStamp=116 --offline=0 --annotate=0 --stateMachineId=1401 --readyStateMachine=0 --forwardDetection=0 --fastThreshold=20 --orbPatchSize=31 --matchDistance=1.5 + + +### Function call graph +- main() + - detectcone.setTimeStamp(std::pair) [pass timestamp and image to detectcone] + - detectcone.checkLidarState() + - **detectcone.forwardDetectionORB(cv::Mat image)** + - od4.dataTrigger -> envelopeRecieved [recieve message sent by sensation-attention] + - collector.CollectorCones(cluon::data::Envelope) + - [independent thread] collector.InitializeCollection() + - collector.SendFrame() + - detectcone.recieveCombinedMessage(cluon::data::TimeStamp, std::map) + - detectcone.SendCollectorCones(Eigen::MatrixXd) + - **detectcone.backwardDetection(cv::Mat image, Eigen::MatrixXd lidarCones, int64_t minValue)** + + +### main function +- parse command line arguments +- create folders and files: + - /opt/yyyy-mm-dd-hh-mm-ss/ (e.g. 2018-07-13-14-58-45) + - /opt/yyyy-mm-dd-hh-mm-ss/timestamp/timestamps.txt + - /opt/yyyy-mm-dd-hh-mm-ss/images/n.png (n = 0,1,2,...) + - /opt/yyyy-mm-dd-hh-mm-ss/results/log.txt +- attach to shared memory, create image header and read data from shared memory +- if in drivingState, call function detectcone.checkLidarState; and save frame image as n.png (n = 0,1,2,...) +- register lambda function for handling recieved message, if recieve cone direction or distance, call function collector.CollectCones +- send out opendlv.system.SignalStatusMessage + + +### forwardDetectionORB: ORB + CNN +- take image frame as input +- create an OpenCV ORB detector, and set nfeatures = 100 (maximum number of featrues to retain) +- ORB detects keypoints within a specific row range of the image +- 2D position from keypoints, and triangulate these points to 3D world +- discard the 3D keypoints that are outside the effective region, and then filter the rest 3D keypoints further +- group 3D points by kd-tree, compute median point for each group +- project back the 3D points to 2D points, and extract RoI (cv::Rect) which is centered at the median point +- patch the image with the RoI +- convert the patched image to CNN inputs +- given the inputs, predict the probability of each color with pre-trained CNN model +- find each cone's maxProb and the corresponding position +- if annotate is true, make annotation +- save the processed image with results as n.png (n = 0,1,2,...) via an independent thread +- call SendMatchedContainer function, send out direction, distance and type of cones (camera cone) via od4.send + + +### backwardDetection: Lidar + ORB + CNN +- take image frame and lidar frame as input +- match image frame and lidar frame by timestamp +- create an OpenCV ORB detector, set nfeatures = 200, also set some threshold and patchsize +- detect keypoints +- triangulate to 3D world +- filter out points that are outside interest region +- group 3D points and compute median for each group +- project 3D points back to image +- match lidar cone (obtained by using Lidar and processing pointcloud, cone data would be more precise) and camera cone (obtained by ORB detector, cone data would be less precise than lidar one) by computing the 3D distance between lidar-cone position and camera-cone position, i.e. for each lidar cone, find the closest camera cone +- re-calibrate the calibration between lidar and camera +- take position from lidar cone, and extract RoI (centered at the median point) from the matched camera cone +- classify the RoI by CNN, find color label based on probability +- call SendMatchedContainer, send out direction, distance and type of cones (lidar-camera matched cone) via od4.send +- if offline is true, save the processed images as n_minValue.png (n = 0,1,2,..., minValue is min timestamp difference) + + +### labels of CNN classification +| label name | label | comments | +| :-----: | :-----: | :-----: | +| "background" | 666 | none | +| "blue" | 1 | | +| "yellow" | 2 | | +| "orange" | 3 | small orange cone | +| "orange" | 4 | big orange cone | + +According to FSG rules: +- the left borders of the track are marked with small blue cones +- the right borders of the track are marked with small yellow cones +- exit and entry lanes are marked with small orange cones +- big orange cones will be placed before and after start, finish and timekeeping lines + +### forwardDetection versus backardDetection +- if lidar is working, forwardDetection will not be utilized; but the collector will keep collecting lidar cones sent by microservice sensation-attention, and backwardDetection will be called +- if lidar is not working, use forwardDetectioin +- however, only using ORB detection (forwardDetection) will have problems with delay and uneven ground and cause RoI off the cone center +- lidar provides solution to these: + - fix delay: match between lidar and camera frame by timestamp, and between match lidar and camera cone (or RoI) by 3D distance + - fix uneven ground: real time calibration between lidar and camera + - provide accurate position: position from lidar rather than ORB, classification from camera + + +### Questions +- the image used in ORB detection, is from left camera or right camera or a merged one (since we are using stereo camera)? +- what does "delay" mean when not using lidar? +- why not using lidar directly (I guess we are able to use lidar RoI to make CNN classification)? +- ... + + +### To-do +- Refactor the name of command arguments, e.g. the "sender stamp" in perception-detectcone is named senderStamp, in sensation-attention is named "id" +- Refactor function names, as some functions start with uppercase letter while others start with lowercase letter +- ... \ No newline at end of file diff --git a/FindLibRT.cmake b/cmake.Modules/FindLibRT.cmake similarity index 100% rename from FindLibRT.cmake rename to cmake.Modules/FindLibRT.cmake diff --git a/config.h b/config.h deleted file mode 100755 index c5448cc0..00000000 --- a/config.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright (c) 2013, Taiga Nomi and the respective contributors - All rights reserved. - - Use of this source code is governed by a BSD-style license that can be found - in the LICENSE file. -*/ -#pragma once - -#include -#include - -/** - * define if you want to use intel TBB library - */ -#define CNN_USE_TBB - -/** - * define to enable avx vectorization - */ -//#define CNN_USE_AVX - -/** - * define to enable sse2 vectorization - */ -// #define CNN_USE_SSE - -/** - * define to enable OMP parallelization - */ -#define CNN_USE_OMP - -/** - * define to enable Grand Central Dispatch parallelization - */ -// #define CNN_USE_GCD - -/** - * define to use exceptions - */ -#define CNN_USE_EXCEPTIONS - -/** - * comment out if you want tiny-dnn to be quiet - */ -#define CNN_USE_STDOUT - -// #define CNN_SINGLE_THREAD - -/** - * disable serialization/deserialization function - * You can uncomment this to speedup compilation & linking time, - * if you don't use network::save / network::load functions. - **/ -// #define CNN_NO_SERIALIZATION - -/** - * Enable Image API support. - * Currently we use stb by default. - **/ -// #define DNN_USE_IMAGE_API - -/** - * Enable Gemmlowp support. - **/ -#ifdef USE_GEMMLOWP -#if !defined(_MSC_VER) && !defined(_WIN32) && !defined(WIN32) -#define CNN_USE_GEMMLOWP // gemmlowp doesn't support MSVC/mingw -#endif -#endif // USE_GEMMLOWP - -/** - * number of task in batch-gradient-descent. - * @todo automatic optimization - */ -#ifdef CNN_USE_OMP -#define CNN_TASK_SIZE 100 -#else -#define CNN_TASK_SIZE 8 -#endif - -namespace tiny_dnn { - -/** - * calculation data type - * you can change it to float, or user defined class (fixed point,etc) - **/ -#ifdef CNN_USE_DOUBLE -typedef double float_t; -#else -typedef float float_t; -#endif - -} // namespace tiny_dnn diff --git a/src/cluon-complete-v0.0.102.hpp b/include/cluon-complete-v0.0.114.hpp old mode 100755 new mode 100644 similarity index 83% rename from src/cluon-complete-v0.0.102.hpp rename to include/cluon-complete-v0.0.114.hpp index a1480953..17d44efb --- a/src/cluon-complete-v0.0.102.hpp +++ b/include/cluon-complete-v0.0.114.hpp @@ -1,6 +1,6 @@ // This is an auto-generated header-only single-file distribution of libcluon. -// Date: Mon, 25 Jun 2018 16:13:25 +0200 -// Version: 0.0.102 +// Date: Thu, 04 Oct 2018 20:58:35 +0200 +// Version: 0.0.114 // // // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. @@ -466,7 +466,7 @@ namespace std // // peglib.h // -// Copyright (c) 2015-17 Yuji Hirose. All rights reserved. +// Copyright (c) 2015-18 Yuji Hirose. All rights reserved. // MIT License // @@ -506,8 +506,8 @@ namespace std namespace peg { -#if __clang__ == 1 && __clang_major__ == 5 && __clang_minor__ == 0 && __clang_patchlevel__ == 0 -static void* enabler = nullptr; // workaround for Clang 5.0.0 +#if __clang__ == 1 && __clang_major__ <= 5 +static void* enabler = nullptr; // workaround for Clang version <= 5.0.0 #else extern void* enabler; #endif @@ -670,6 +670,213 @@ auto make_scope_exit(EF&& exit_function) -> scope_exit { return scope_exit::type>(std::forward(exit_function)); } +/*----------------------------------------------------------------------------- + * UTF8 functions + *---------------------------------------------------------------------------*/ + +inline size_t codepoint_length(const char *s8, size_t l) { + if (l) { + auto b = static_cast(s8[0]); + if ((b & 0x80) == 0) { + return 1; + } else if ((b & 0xE0) == 0xC0) { + return 2; + } else if ((b & 0xF0) == 0xE0) { + return 3; + } else if ((b & 0xF8) == 0xF0) { + return 4; + } + } + return 0; +} + +inline size_t encode_codepoint(char32_t cp, char *buff) { + if (cp < 0x0080) { + buff[0] = static_cast(cp & 0x7F); + return 1; + } else if (cp < 0x0800) { + buff[0] = static_cast(0xC0 | ((cp >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (cp & 0x3F)); + return 2; + } else if (cp < 0xD800) { + buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (cp & 0x3F)); + return 3; + } else if (cp < 0xE000) { + // D800 - DFFF is invalid... + return 0; + } else if (cp < 0x10000) { + buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (cp & 0x3F)); + return 3; + } else if (cp < 0x110000) { + buff[0] = static_cast(0xF0 | ((cp >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((cp >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (cp & 0x3F)); + return 4; + } + return 0; +} + +inline std::string encode_codepoint(char32_t cp) { + char buff[4]; + auto l = encode_codepoint(cp, buff); + return std::string(buff, l); +} + +inline bool decode_codepoint(const char *s8, size_t l, size_t &bytes, + char32_t &cp) { + if (l) { + auto b = static_cast(s8[0]); + if ((b & 0x80) == 0) { + bytes = 1; + cp = b; + return true; + } else if ((b & 0xE0) == 0xC0) { + if (l >= 2) { + bytes = 2; + cp = ((static_cast(s8[0] & 0x1F)) << 6) | + (static_cast(s8[1] & 0x3F)); + return true; + } + } else if ((b & 0xF0) == 0xE0) { + if (l >= 3) { + bytes = 3; + cp = ((static_cast(s8[0] & 0x0F)) << 12) | + ((static_cast(s8[1] & 0x3F)) << 6) | + (static_cast(s8[2] & 0x3F)); + return true; + } + } else if ((b & 0xF8) == 0xF0) { + if (l >= 4) { + bytes = 4; + cp = ((static_cast(s8[0] & 0x07)) << 18) | + ((static_cast(s8[1] & 0x3F)) << 12) | + ((static_cast(s8[2] & 0x3F)) << 6) | + (static_cast(s8[3] & 0x3F)); + return true; + } + } + } + return false; +} + +inline size_t decode_codepoint(const char *s8, size_t l, char32_t &out) { + size_t bytes; + if (decode_codepoint(s8, l, bytes, out)) { + return bytes; + } + return 0; +} + +inline char32_t decode_codepoint(const char *s8, size_t l) { + char32_t out = 0; + decode_codepoint(s8, l, out); + return out; +} + +inline std::u32string decode(const char *s8, size_t l) { + std::u32string out; + size_t i = 0; + while (i < l) { + auto beg = i++; + while (i < l && (s8[i] & 0xc0) == 0x80) { + i++; + } + out += decode_codepoint(&s8[beg], (i - beg)); + } + return out; +} + +/*----------------------------------------------------------------------------- + * resolve_escape_sequence + *---------------------------------------------------------------------------*/ + +inline bool is_hex(char c, int& v) { + if ('0' <= c && c <= '9') { + v = c - '0'; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } + return false; +} + +inline bool is_digit(char c, int& v) { + if ('0' <= c && c <= '9') { + v = c - '0'; + return true; + } + return false; +} + +inline std::pair parse_hex_number(const char* s, size_t n, size_t i) { + int ret = 0; + int val; + while (i < n && is_hex(s[i], val)) { + ret = static_cast(ret * 16 + val); + i++; + } + return std::make_pair(ret, i); +} + +inline std::pair parse_octal_number(const char* s, size_t n, size_t i) { + int ret = 0; + int val; + while (i < n && is_digit(s[i], val)) { + ret = static_cast(ret * 8 + val); + i++; + } + return std::make_pair(ret, i); +} + +inline std::string resolve_escape_sequence(const char* s, size_t n) { + std::string r; + r.reserve(n); + + size_t i = 0; + while (i < n) { + auto ch = s[i]; + if (ch == '\\') { + i++; + switch (s[i]) { + case 'n': r += '\n'; i++; break; + case 'r': r += '\r'; i++; break; + case 't': r += '\t'; i++; break; + case '\'': r += '\''; i++; break; + case '"': r += '"'; i++; break; + case '[': r += '['; i++; break; + case ']': r += ']'; i++; break; + case '\\': r += '\\'; i++; break; + case 'x': + case 'u': { + char32_t cp; + std::tie(cp, i) = parse_hex_number(s, n, i + 1); + r += encode_codepoint(cp); + break; + } + default: { + char32_t cp; + std::tie(cp, i) = parse_octal_number(s, n, i); + r += encode_codepoint(cp); + break; + } + } + } else { + r += ch; + i++; + } + } + return r; +} + /*----------------------------------------------------------------------------- * PEG *---------------------------------------------------------------------------*/ @@ -929,11 +1136,6 @@ struct parse_error { std::string s_; }; -/* - * Match action - */ -typedef std::function MatchAction; - /* * Result */ @@ -948,8 +1150,8 @@ inline bool fail(size_t len) { /* * Context */ -class Ope; class Context; +class Ope; class Definition; typedef std::function Tracer; @@ -967,6 +1169,7 @@ class Context std::vector> value_stack; size_t value_stack_size; + std::vector>> args_stack; size_t nest_level; @@ -975,6 +1178,10 @@ class Context std::shared_ptr whitespaceOpe; bool in_whitespace; + std::shared_ptr wordOpe; + + std::vector> capture_scope_stack; + const size_t def_count; const bool enablePackratParsing; std::vector cache_registered; @@ -990,6 +1197,7 @@ class Context size_t a_l, size_t a_def_count, std::shared_ptr a_whitespaceOpe, + std::shared_ptr a_wordOpe, bool a_enablePackratParsing, Tracer a_tracer) : path(a_path) @@ -1002,12 +1210,15 @@ class Context , in_token(false) , whitespaceOpe(a_whitespaceOpe) , in_whitespace(false) + , wordOpe(a_wordOpe) , def_count(a_def_count) , enablePackratParsing(a_enablePackratParsing) , cache_registered(enablePackratParsing ? def_count * (l + 1) : 0) , cache_success(enablePackratParsing ? def_count * (l + 1) : 0) , tracer(a_tracer) { + args_stack.resize(1); + capture_scope_stack.resize(1); } template @@ -1062,6 +1273,35 @@ class Context value_stack_size--; } + void push_args(const std::vector>& args) { + args_stack.push_back(args); + } + + void pop_args() { + args_stack.pop_back(); + } + + const std::vector>& top_args() const { + return args_stack[args_stack.size() - 1]; + } + + void push_capture_scope() { + capture_scope_stack.resize(capture_scope_stack.size() + 1); + } + + void pop_capture_scope() { + capture_scope_stack.pop_back(); + } + + void shift_capture_values() { + assert(capture_scope_stack.size() >= 2); + auto it = capture_scope_stack.rbegin(); + auto it_prev = it + 1; + for (const auto& kv: *it) { + (*it_prev)[kv.first] = kv.second; + } + } + void set_error_pos(const char* a_s) { if (error_pos < a_s) error_pos = a_s; } @@ -1157,9 +1397,11 @@ class PrioritizedChoice : public Ope for (const auto& ope : opes_) { c.nest_level++; auto& chldsv = c.push(); + c.push_capture_scope(); auto se = make_scope_exit([&]() { c.nest_level--; c.pop(); + c.pop_capture_scope(); }); const auto& rule = *ope; auto len = rule.parse(s, n, chldsv, c, dt); @@ -1171,6 +1413,8 @@ class PrioritizedChoice : public Ope sv.n_ = chldsv.length(); sv.choice_ = id; sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); + + c.shift_capture_values(); return len; } id++; @@ -1196,12 +1440,18 @@ class ZeroOrMore : public Ope size_t i = 0; while (n - i > 0) { c.nest_level++; - auto se = make_scope_exit([&]() { c.nest_level--; }); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); auto save_sv_size = sv.size(); auto save_tok_size = sv.tokens.size(); const auto& rule = *ope_; auto len = rule.parse(s + i, n - i, sv, c, dt); - if (fail(len)) { + if (success(len)) { + c.shift_capture_values(); + } else { if (sv.size() != save_sv_size) { sv.erase(sv.begin() + static_cast(save_sv_size)); } @@ -1231,10 +1481,16 @@ class OneOrMore : public Ope size_t len = 0; { c.nest_level++; - auto se = make_scope_exit([&]() { c.nest_level--; }); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); const auto& rule = *ope_; len = rule.parse(s, n, sv, c, dt); - if (fail(len)) { + if (success(len)) { + c.shift_capture_values(); + } else { return static_cast(-1); } } @@ -1242,12 +1498,18 @@ class OneOrMore : public Ope auto i = len; while (n - i > 0) { c.nest_level++; - auto se = make_scope_exit([&]() { c.nest_level--; }); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); auto save_sv_size = sv.size(); auto save_tok_size = sv.tokens.size(); const auto& rule = *ope_; len = rule.parse(s + i, n - i, sv, c, dt); - if (fail(len)) { + if (success(len)) { + c.shift_capture_values(); + } else { if (sv.size() != save_sv_size) { sv.erase(sv.begin() + static_cast(save_sv_size)); } @@ -1278,10 +1540,15 @@ class Option : public Ope c.nest_level++; auto save_sv_size = sv.size(); auto save_tok_size = sv.tokens.size(); - auto se = make_scope_exit([&]() { c.nest_level--; }); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop_capture_scope(); + }); const auto& rule = *ope_; auto len = rule.parse(s, n, sv, c, dt); if (success(len)) { + c.shift_capture_values(); return len; } else { if (sv.size() != save_sv_size) { @@ -1309,9 +1576,11 @@ class AndPredicate : public Ope c.trace("AndPredicate", s, n, sv, dt); c.nest_level++; auto& chldsv = c.push(); + c.push_capture_scope(); auto se = make_scope_exit([&]() { c.nest_level--; c.pop(); + c.pop_capture_scope(); }); const auto& rule = *ope_; auto len = rule.parse(s, n, chldsv, c, dt); @@ -1337,9 +1606,11 @@ class NotPredicate : public Ope auto save_error_pos = c.error_pos; c.nest_level++; auto& chldsv = c.push(); + c.push_capture_scope(); auto se = make_scope_exit([&]() { c.nest_level--; c.pop(); + c.pop_capture_scope(); }); const auto& rule = *ope_; auto len = rule.parse(s, n, chldsv, c, dt); @@ -1358,61 +1629,83 @@ class NotPredicate : public Ope }; class LiteralString : public Ope + , public std::enable_shared_from_this { public: - LiteralString(const std::string& s) : lit_(s) {} + LiteralString(const std::string& s) + : lit_(s) + , init_is_word_(false) + , is_word_(false) + {} size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; void accept(Visitor& v) override; std::string lit_; + mutable bool init_is_word_; + mutable bool is_word_; }; class CharacterClass : public Ope + , public std::enable_shared_from_this { public: - CharacterClass(const std::string& chars) : chars_(chars) {} + CharacterClass(const std::string& s) { + auto chars = decode(s.c_str(), s.length()); + auto i = 0u; + while (i < chars.size()) { + if (i + 2 < chars.size() && chars[i + 1] == '-') { + auto cp1 = chars[i]; + auto cp2 = chars[i + 2]; + ranges_.emplace_back(std::make_pair(cp1, cp2)); + i += 3; + } else { + auto cp = chars[i]; + ranges_.emplace_back(std::make_pair(cp, cp)); + i += 1; + } + } + } + + CharacterClass(const std::vector>& ranges) : ranges_(ranges) {} size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { c.trace("CharacterClass", s, n, sv, dt); - // TODO: UTF8 support + if (n < 1) { c.set_error_pos(s); return static_cast(-1); } - auto ch = s[0]; - auto i = 0u; - while (i < chars_.size()) { - if (i + 2 < chars_.size() && chars_[i + 1] == '-') { - if (chars_[i] <= ch && ch <= chars_[i + 2]) { - return 1; - } - i += 3; - } else { - if (chars_[i] == ch) { - return 1; + + char32_t cp; + auto len = decode_codepoint(s, n, cp); + + if (!ranges_.empty()) { + for (const auto& range: ranges_) { + if (range.first <= cp && cp <= range.second) { + return len; } - i += 1; } } + c.set_error_pos(s); return static_cast(-1); } void accept(Visitor& v) override; - std::string chars_; + std::vector> ranges_; }; class Character : public Ope + , public std::enable_shared_from_this { public: Character(char ch) : ch_(ch) {} size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { c.trace("Character", s, n, sv, dt); - // TODO: UTF8 support if (n < 1 || s[0] != ch_) { c.set_error_pos(s); return static_cast(-1); @@ -1426,32 +1719,56 @@ class Character : public Ope }; class AnyCharacter : public Ope + , public std::enable_shared_from_this { public: size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { c.trace("AnyCharacter", s, n, sv, dt); - // TODO: UTF8 support - if (n < 1) { + auto len = codepoint_length(s, n); + if (len < 1) { c.set_error_pos(s); return static_cast(-1); } - return 1; + return len; + } + + void accept(Visitor& v) override; +}; + +class CaptureScope : public Ope +{ +public: + CaptureScope(const std::shared_ptr& ope) + : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.pop_capture_scope(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + return len; } void accept(Visitor& v) override; + + std::shared_ptr ope_; }; class Capture : public Ope { public: - Capture(const std::shared_ptr& ope, MatchAction ma, size_t id, const std::string& name) - : ope_(ope), match_action_(ma), id_(id), name_(name) {} + typedef std::function MatchAction; + + Capture(const std::shared_ptr& ope, MatchAction ma) + : ope_(ope), match_action_(ma) {} size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { const auto& rule = *ope_; auto len = rule.parse(s, n, sv, c, dt); if (success(len) && match_action_) { - match_action_(s, len, id_, name_); + match_action_(s, len, c); } return len; } @@ -1459,11 +1776,7 @@ class Capture : public Ope void accept(Visitor& v) override; std::shared_ptr ope_; - -private: MatchAction match_action_; - size_t id_; - std::string name_; }; class TokenBoundary : public Ope @@ -1499,6 +1812,19 @@ class Ignore : public Ope typedef std::function Parser; +class User : public Ope +{ +public: + User(Parser fn) : fn_(fn) {} + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("User", s, n, sv, dt); + assert(fn_); + return fn_(s, n, sv, dt); + } + void accept(Visitor& v) override; + std::function fn_; +}; + class WeakHolder : public Ope { public: @@ -1534,28 +1860,42 @@ class Holder : public Ope friend class Definition; }; -class DefinitionReference : public Ope +typedef std::unordered_map Grammar; + +class Reference : public Ope + , public std::enable_shared_from_this { public: - DefinitionReference( - const std::unordered_map& grammar, const std::string& name, const char* s) + Reference( + const Grammar& grammar, + const std::string& name, + const char* s, + bool is_macro, + const std::vector>& args) : grammar_(grammar) , name_(name) - , s_(s) {} + , s_(s) + , is_macro_(is_macro) + , args_(args) + , rule_(nullptr) + , iarg_(0) + {} size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; void accept(Visitor& v) override; - std::shared_ptr get_rule() const; + std::shared_ptr get_core_operator() const; - const std::unordered_map& grammar_; - const std::string name_; - const char* s_; + const Grammar& grammar_; + const std::string name_; + const char* s_; -private: - mutable std::once_flag init_; - mutable std::shared_ptr rule_; + const bool is_macro_; + const std::vector> args_; + + Definition* rule_; + size_t iarg_; }; class Whitespace : public Ope @@ -1578,6 +1918,103 @@ class Whitespace : public Ope std::shared_ptr ope_; }; +class BackReference : public Ope +{ +public: + BackReference(const std::string& name) : name_(name) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::string name_; +}; + +/* + * Factories + */ +template +std::shared_ptr seq(Args&& ...args) { + return std::make_shared(static_cast>(args)...); +} + +template +std::shared_ptr cho(Args&& ...args) { + return std::make_shared(static_cast>(args)...); +} + +inline std::shared_ptr zom(const std::shared_ptr& ope) { + return std::make_shared(ope); +} + +inline std::shared_ptr oom(const std::shared_ptr& ope) { + return std::make_shared(ope); +} + +inline std::shared_ptr opt(const std::shared_ptr& ope) { + return std::make_shared