├── pypackage ├── MANIFEST.in ├── setup.cfg ├── edcc │ ├── constants.py │ ├── code.py │ ├── config.py │ ├── exceptions.py │ ├── encoder.py │ ├── __init__.py │ └── adapter.py └── setup.py ├── palmprint_data ├── downloads.all.lst ├── downloads.lst ├── download.sh └── README.md ├── test ├── main.cpp ├── test_base.h ├── test_core │ ├── test_gabor_filter.cpp │ ├── test_encoder.cpp │ └── test_comparer.cpp ├── test_controller │ ├── test_c_api.cpp │ └── test_facade.cpp ├── CMakeLists.txt └── test_config │ └── test_reader.cpp ├── examples ├── c_example │ ├── CMakeLists.txt │ └── example.c ├── cpp_example │ ├── CMakeLists.txt │ └── example.cpp └── py_example │ └── example.py ├── edcc-config-version.cmake.in ├── thirdparty └── gtest │ └── CMakeLists.txt.in ├── .travis.yml ├── src ├── core │ ├── comparer.h │ ├── code.h │ ├── encoder.h │ ├── gabor_filter.h │ ├── comparer.cpp │ ├── encoder.cpp │ └── gabor_filter.cpp ├── config │ ├── config.h │ ├── reader.h │ └── reader.cpp └── controller │ ├── c_api.cpp │ └── facade.cpp ├── edcc-config.cmake.in ├── .codecov.yml ├── LICENSE ├── include └── edcc │ ├── facade.h │ ├── c_api.h │ └── status.h ├── CONTRIBUTING.md ├── .clang-format ├── manage.sh ├── CMakeLists.txt ├── README.md └── .gitignore /pypackage/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include ../README.md ../LICENSE -------------------------------------------------------------------------------- /palmprint_data/downloads.all.lst: -------------------------------------------------------------------------------- 1 | PolyU.tar.gz 2 | Tongji.tar.gz -------------------------------------------------------------------------------- /palmprint_data/downloads.lst: -------------------------------------------------------------------------------- 1 | a_01.bmp 2 | a_02.bmp 3 | b_01.bmp 4 | b_02.bmp -------------------------------------------------------------------------------- /pypackage/setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [flake8] 5 | max-line-length = 100 6 | ignore = F821 -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "gtest/gtest.h" 6 | 7 | int main(int argc, char** argv) { 8 | ::testing::InitGoogleTest(&argc, argv); 9 | ::testing::GTEST_FLAG(filter) = "*"; 10 | return RUN_ALL_TESTS(); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /examples/c_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | 3 | SET(PROJECT_NAME run_c_sample) 4 | PROJECT(${PROJECT_NAME}) 5 | SET(SAMPLES_ROOT ${CMAKE_BINARY_DIR}/../..) 6 | 7 | FIND_PACKAGE(edcc REQUIRED) 8 | INCLUDE_DIRECTORIES(${EDCC_INCLUDE_DIR}) 9 | 10 | ADD_EXECUTABLE(${PROJECT_NAME} example.c) 11 | 12 | ADD_DEPENDENCIES(${PROJECT_NAME} ${EDCC_LIBRARIES}) 13 | TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${EDCC_LIBRARIES}) -------------------------------------------------------------------------------- /edcc-config-version.cmake.in: -------------------------------------------------------------------------------- 1 | SET(PACKAGE_VERSION "@EDCC_VERSION@") 2 | 3 | # Check whether the requested PACKAGE_FIND_VERSION is compatible 4 | IF("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") 5 | SET(PACKAGE_VERSION_COMPATIBLE FALSE) 6 | ELSE() 7 | SET(PACKAGE_VERSION_COMPATIBLE TRUE) 8 | IF("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") 9 | SET(PACKAGE_VERSION_EXACT TRUE) 10 | ENDIF() 11 | ENDIF() 12 | -------------------------------------------------------------------------------- /pypackage/edcc/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | 6 | import enum 7 | 8 | 9 | @enum.unique 10 | class EdccErrorCode(enum.IntEnum): 11 | InvalidArgument = 1 12 | LackingCodeBuffer = 2 13 | CodeCfgNEWhenComparing = 3 14 | 15 | 16 | ReversedEdccErrorCode = {f.value: f.name for f in EdccErrorCode} 17 | -------------------------------------------------------------------------------- /examples/cpp_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | 3 | SET(PROJECT_NAME run_cpp_sample) 4 | PROJECT(${PROJECT_NAME}) 5 | SET(SAMPLES_ROOT ${CMAKE_BINARY_DIR}/../..) 6 | 7 | ADD_DEFINITIONS(-std=c++11) 8 | 9 | FIND_PACKAGE(edcc REQUIRED) 10 | INCLUDE_DIRECTORIES(${EDCC_INCLUDE_DIR}) 11 | 12 | ADD_EXECUTABLE(${PROJECT_NAME} example.cpp) 13 | 14 | ADD_DEPENDENCIES(${PROJECT_NAME} ${EDCC_LIBRARIES}) 15 | TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${EDCC_LIBRARIES}) -------------------------------------------------------------------------------- /thirdparty/gtest/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG master 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: required 4 | 5 | services: 6 | - docker 7 | 8 | cache: 9 | directories: 10 | - $HOME/docker 11 | 12 | before_cache: 13 | - ./manage.sh save_images 14 | 15 | stages: 16 | - tests_and_lints 17 | 18 | jobs: 19 | include: 20 | - stage: tests_and_lints 21 | name: test 22 | script: 23 | - ./manage.sh test 24 | - ./manage.sh upload_codecov 25 | - stage: tests_and_lints 26 | name: lint 27 | script: 28 | - ./manage.sh lint 29 | -------------------------------------------------------------------------------- /src/core/comparer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_SRC_CORE_COMPARER_H_ 6 | #define EDCC_SRC_CORE_COMPARER_H_ 7 | 8 | #include "core/code.h" 9 | #include "edcc/status.h" 10 | 11 | namespace edcc { 12 | class Comparer { 13 | public: 14 | static Status Compare(const PalmprintCode& lhs_code, const PalmprintCode& rhs_code, double* score); 15 | }; 16 | } // namespace edcc 17 | 18 | #endif // EDCC_SRC_CORE_COMPARER_H_ -------------------------------------------------------------------------------- /pypackage/edcc/code.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | 6 | from .adapter import EdccAdapter 7 | 8 | 9 | class PalmprintCode(object): 10 | """Store code, compare to another code.""" 11 | 12 | def __init__(self, code_bytes, adapter: EdccAdapter): 13 | self._code = code_bytes 14 | self._adapter = adapter 15 | 16 | def compare_to(self, another): 17 | return self._adapter.calc_score(self._code, another._code) 18 | -------------------------------------------------------------------------------- /edcc-config.cmake.in: -------------------------------------------------------------------------------- 1 | # - Config file for the edcc package 2 | # It defines the following variables 3 | # EDCC_INCLUDE_DIR - include directory 4 | # EDCC_LIBRARIES - libraries to link against 5 | 6 | # Compute paths 7 | GET_FILENAME_COMPONENT(EDCC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 8 | SET(EDCC_INCLUDE_DIR "@CONFIG_INCLUDE_DIRS@") 9 | 10 | # Our library dependencies (contains definitions for IMPORTED targets) 11 | INCLUDE("${EDCC_CMAKE_DIR}/edcc-targets.cmake") 12 | 13 | # These are IMPORTED targets created by edcc-targets.cmake 14 | SET(EDCC_LIBRARIES "@EXPORT_TARGETS@") 15 | -------------------------------------------------------------------------------- /pypackage/edcc/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | 6 | class EncoderConfig(object): 7 | """Config which is used to initialize `Encoder`""" 8 | 9 | def __init__( 10 | self, image_size, gabor_kernel_size, laplace_kernel_size, gabor_directions 11 | ): 12 | self.image_size = image_size 13 | self.gabor_kernel_size = gabor_kernel_size 14 | self.laplace_kernel_size = laplace_kernel_size 15 | self.gabor_directions = gabor_directions 16 | 17 | 18 | default_encoder_config = EncoderConfig(29, 5, 5, 10) 19 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: nearest 4 | range: "90...95" 5 | status: 6 | project: 7 | default: off 8 | test: 9 | target: 90% 10 | paths: 11 | - "test/" 12 | app: 13 | target: 95% 14 | paths: 15 | - "include/" 16 | - "src/" 17 | patch: 18 | default: off 19 | tests: 20 | target: 95% 21 | paths: 22 | - "test/" 23 | app: 24 | target: 98% 25 | paths: 26 | - "include/" 27 | - "src/" 28 | ignore: 29 | - "thirdparty" 30 | 31 | flags: 32 | app: 33 | paths: 34 | - "include/" 35 | - "src/" 36 | test: 37 | paths: 38 | - "test/" 39 | 40 | codecov: 41 | branch: master 42 | 43 | comment: 44 | layout: "reach, diff, flags, files" 45 | -------------------------------------------------------------------------------- /src/core/code.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_SRC_CORE_CODE_H_ 6 | #define EDCC_SRC_CORE_CODE_H_ 7 | 8 | #include 9 | #include "config/config.h" 10 | 11 | namespace edcc { 12 | 13 | typedef struct { 14 | // `d` means `gabor direction` which range is [0, 15], so pow(2, 4) - 1 = 15 15 | // can cover direction. 16 | uint8_t d : 4; 17 | // `s` means `direction side` which value is left(1) or right(0), so range 18 | // [0,1] can cover side. 19 | uint8_t s : 1; 20 | uint8_t _pad : 3; 21 | } PalmprintCodeMetadata; 22 | 23 | typedef struct { 24 | EncoderConfig cfg; 25 | uint32_t len; // num of metadata 26 | PalmprintCodeMetadata data[0]; 27 | } PalmprintCode; 28 | 29 | } // namespace edcc 30 | 31 | #endif // EDCC_SRC_CORE_CODE_H_ -------------------------------------------------------------------------------- /pypackage/edcc/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | from .constants import EdccErrorCode, ReversedEdccErrorCode 6 | 7 | 8 | class EdccExceptionBase(Exception): 9 | """Base exception for edcc""" 10 | 11 | def __init__(self, errcode, errmsg): 12 | """ 13 | :param errcode: Error code 14 | :param errmsg: Error message 15 | """ 16 | self.errcode = errcode 17 | self.errmsg = errmsg 18 | 19 | def __str__(self): 20 | return "Error code: {code}, means: {code_name}. Error message: {msg}".format( 21 | code=self.errcode, 22 | code_name=ReversedEdccErrorCode[self.errcode], 23 | msg=self.errmsg, 24 | ) 25 | 26 | def __repr__(self): 27 | return self.__str__() 28 | -------------------------------------------------------------------------------- /src/config/config.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_SRC_CONFIG_CONFIG_H_ 6 | #define EDCC_SRC_CONFIG_CONFIG_H_ 7 | 8 | #include 9 | 10 | namespace edcc { 11 | 12 | typedef struct { 13 | uint8_t image_size; // resize original palm image to (x, x) 14 | uint8_t gabor_kernel_size; 15 | uint8_t laplace_kernel_size; 16 | uint8_t gabor_directions; 17 | } EncoderConfig; 18 | 19 | namespace limit { 20 | static const uint8_t kMinImageSize = 29; 21 | static const uint8_t kMaxLaplaceKernelSize = 31; 22 | static const uint8_t kMinGaborDirections = 4; 23 | static const uint8_t kMaxGaborDirections = 16; 24 | } // namespace limit 25 | 26 | static const EncoderConfig kDefaultEncoderConfig = {29, 5, 5, 10}; 27 | 28 | } // namespace edcc 29 | 30 | #endif // EDCC_SRC_CONFIG_CONFIG_H_ -------------------------------------------------------------------------------- /pypackage/edcc/encoder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | from .adapter import EdccAdapter 6 | from .config import EncoderConfig 7 | from .code import PalmprintCode 8 | 9 | 10 | class Encoder(object): 11 | """Encode palmprint image to `Palmprint code`.""" 12 | 13 | def __init__(self, config: EncoderConfig, adapter: EdccAdapter): 14 | self._cfg = config 15 | self._adapter = adapter 16 | self._id = self._adapter.new_encoder(self._cfg) 17 | 18 | def encode_using_file(self, filepath): 19 | with open(filepath, "rb") as palmprint: 20 | return self.encode_using_bytes(palmprint.read()) 21 | 22 | def encode_using_bytes(self, image_bytes): 23 | code_bytes = self._adapter.do_encode(self._id, image_bytes) 24 | return PalmprintCode(code_bytes, self._adapter) 25 | -------------------------------------------------------------------------------- /palmprint_data/download.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | CurDir="$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)" 4 | OSS_EDCC_PART_BASE_DIR=https://blog-images-1257621236.cos.ap-shanghai.myqcloud.com/edcc_data/for_test_and_example 5 | OSS_EDCC_ALL_BASE_DIR=https://blog-images-1257621236.cos.ap-shanghai.myqcloud.com/edcc_data/all 6 | 7 | do_download() { 8 | url=$1 9 | dst=$2 10 | echo "Downloading ${url}......" 11 | curl -o ${dst} ${url} 1>/dev/null 2>&1 12 | echo "Download ${url} success!" 13 | } 14 | 15 | download_type=$1 16 | 17 | if [ "_${download_type}" = "_all" ]; then 18 | download_list=$(cat ${CurDir}/downloads.all.lst) 19 | url_base_dir=${OSS_EDCC_ALL_BASE_DIR} 20 | else 21 | download_list=$(cat ${CurDir}/downloads.lst) 22 | url_base_dir=${OSS_EDCC_PART_BASE_DIR} 23 | fi 24 | 25 | for item in ${download_list[@]}; do 26 | if [ ! -f ${CurDir}/${item} ]; then 27 | do_download ${url_base_dir}/${item} ${CurDir}/${item} 28 | fi 29 | done 30 | -------------------------------------------------------------------------------- /src/core/encoder.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_SRC_CORE_ENCODER_H_ 6 | #define EDCC_SRC_CORE_ENCODER_H_ 7 | 8 | #include 9 | #include 10 | #include "config/config.h" 11 | #include "core/code.h" 12 | #include "core/gabor_filter.h" 13 | #include "edcc/status.h" 14 | 15 | namespace edcc { 16 | 17 | class Encoder { 18 | public: 19 | explicit Encoder(const EncoderConfig& cfg); 20 | 21 | size_t GetCodeBufferSize() const; 22 | Status Encode(const cv::Mat& palmprint, PalmprintCode* code, size_t buffer_size) const; 23 | 24 | private: 25 | uint8_t GetDirectionOfMaxResponse(const std::vector& gabor_filter_result, uint8_t x, uint8_t y) const; 26 | 27 | const EncoderConfig cfg_; 28 | std::unique_ptr gabor_filter_; 29 | }; 30 | 31 | } // namespace edcc 32 | 33 | #endif // EDCC_SRC_CORE_ENCODER_H_ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Leosocy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/core/gabor_filter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_SRC_CORE_GABOR_FILTER_H_ 6 | #define EDCC_SRC_CORE_GABOR_FILTER_H_ 7 | 8 | #include 9 | #include 10 | #include "config/config.h" 11 | 12 | namespace edcc { 13 | class GaborFilter { 14 | public: 15 | explicit GaborFilter(const EncoderConfig &config); 16 | void Handle(const cv::Mat &src, std::vector *result); 17 | 18 | private: 19 | // init gabor filter kernels in different directions. 20 | void InitKernels(); 21 | void GetKernelReal(cv::Mat *kernel, int width, int height, int dimension, int direction, double kmax = CV_PI / 2, 22 | double f = sqrt(2.0), double sigma = 2 * CV_PI) const; 23 | void PreProcessImage(const cv::Mat &src, cv::Mat *result) const; 24 | void EnhanceImage(const cv::Mat &src, cv::Mat *result) const; 25 | const EncoderConfig &cfg_; 26 | std::vector kernels_; 27 | }; 28 | } // namespace edcc 29 | 30 | #endif // EDCC_SRC_CORE_GABOR_FILTER_H_ -------------------------------------------------------------------------------- /examples/py_example/example.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | import os 6 | import edcc 7 | 8 | TEST_PALMPRINT_DATA_DIR = "../../palmprint_data" 9 | TEST_A_01_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "a_01.bmp") 10 | TEST_A_02_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "a_02.bmp") 11 | TEST_B_01_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "b_01.bmp") 12 | TEST_B_02_PALMPRINT_IMAGE = os.path.join(TEST_PALMPRINT_DATA_DIR, "b_02.bmp") 13 | 14 | 15 | if __name__ == "__main__": 16 | config = edcc.EncoderConfig(29, 5, 5, 10) 17 | encoder = edcc.create_encoder(config) 18 | one_palmprint_code = encoder.encode_using_file(TEST_A_01_PALMPRINT_IMAGE) 19 | another_palmprint_code = encoder.encode_using_file(TEST_B_01_PALMPRINT_IMAGE) 20 | similarity_score = one_palmprint_code.compare_to(another_palmprint_code) 21 | print( 22 | "{} <-> {} similarity score:{}".format( 23 | TEST_A_01_PALMPRINT_IMAGE, TEST_B_01_PALMPRINT_IMAGE, similarity_score 24 | ) 25 | ) 26 | -------------------------------------------------------------------------------- /src/config/reader.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_SRC_CONFIG_READER_H_ 6 | #define EDCC_SRC_CONFIG_READER_H_ 7 | 8 | #include 9 | #include "config/config.h" 10 | #include "edcc/status.h" 11 | 12 | namespace edcc { 13 | 14 | class ConfigReader { 15 | public: 16 | virtual ~ConfigReader() = default;; 17 | virtual Status LoadAndValidate() = 0; 18 | const EncoderConfig& GetEncoderConfig() { return encoder_cfg_; } 19 | 20 | // we'll validate when set config value. 21 | Status SetImageSize(uint8_t size); 22 | Status SetGaborKernelSize(uint8_t size); 23 | Status SetLaplaceKernelSize(uint8_t size); 24 | Status SetGaborDirections(uint8_t num); 25 | 26 | protected: 27 | EncoderConfig encoder_cfg_; 28 | }; 29 | 30 | class SimpleConfigReader : public ConfigReader { 31 | public: 32 | explicit SimpleConfigReader(const EncoderConfig& config); 33 | Status LoadAndValidate() override; 34 | }; 35 | 36 | // TODO: YamlConfigReader/JsonConfigReader 37 | 38 | } // namespace edcc 39 | 40 | #endif // EDCC_SRC_CONFIG_READER_H_ -------------------------------------------------------------------------------- /test/test_base.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #define TEST_PALMPRINT_DATA_DIR "../../palmprint_data" 11 | 12 | #define TEST_A_01_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/a_01.bmp" 13 | #define TEST_A_02_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/a_02.bmp" 14 | #define TEST_B_01_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/b_01.bmp" 15 | #define TEST_B_02_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/b_02.bmp" 16 | 17 | class EdccTestFixtureBase : public testing::Test { 18 | public: 19 | EdccTestFixtureBase() {} 20 | virtual void SetUp() { 21 | a_01_ = cv::imread(TEST_A_01_PALMPRINT_IMAGE); 22 | a_02_ = cv::imread(TEST_A_02_PALMPRINT_IMAGE); 23 | b_01_ = cv::imread(TEST_B_01_PALMPRINT_IMAGE); 24 | b_02_ = cv::imread(TEST_B_02_PALMPRINT_IMAGE); 25 | } 26 | virtual void TearDown() { 27 | a_01_.release(); 28 | a_02_.release(); 29 | b_01_.release(); 30 | b_02_.release(); 31 | } 32 | 33 | protected: 34 | cv::Mat a_01_; 35 | cv::Mat a_02_; 36 | cv::Mat b_01_; 37 | cv::Mat b_02_; 38 | }; -------------------------------------------------------------------------------- /pypackage/edcc/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | """ 6 | An efficient and accurate algorithm for palmprint-recognition. 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | usage: 10 | 11 | >>> import edcc 12 | >>> config = edcc.EncoderConfig(29, 5, 5 ,10) 13 | >>> encoder = edcc.create_encoder(config) 14 | >>> one_palmprint_code = encoder.encode_using_file("./palmprint_one.bmp") 15 | >>> another_palmprint_code = encoder.encode_using_file("./palmprint_another.bmp") 16 | >>> one_palmprint_code.compare_to(another_palmprint_code) 17 | 0.24910820451843044 18 | 19 | """ 20 | 21 | __author__ = "Leosocy " 22 | __version__ = "0.2.1" 23 | 24 | from .adapter import EdccAdapter 25 | from .config import EncoderConfig, default_encoder_config 26 | from .encoder import Encoder 27 | from .code import PalmprintCode 28 | from .exceptions import EdccExceptionBase 29 | 30 | 31 | adapter = EdccAdapter() 32 | 33 | 34 | def create_encoder(config): 35 | return Encoder(config, adapter) 36 | 37 | 38 | default_encoder = create_encoder(default_encoder_config) 39 | 40 | -------------------------------------------------------------------------------- /src/core/comparer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "core/comparer.h" 6 | #include "config/config.h" 7 | 8 | namespace edcc { 9 | Status Comparer::Compare(const PalmprintCode& lhs_code, const PalmprintCode& rhs_code, double* score) { 10 | *score = .0; 11 | if (memcmp(&lhs_code.cfg, &rhs_code.cfg, sizeof(EncoderConfig)) != 0 || lhs_code.len != rhs_code.len) { 12 | return Status::CodeCfgNEWhenComparing("The two palmprint codes have different config."); 13 | } 14 | uint32_t acc = 0; 15 | const PalmprintCodeMetadata* lhs_md_cur_ptr = lhs_code.data; 16 | const PalmprintCodeMetadata* lhs_md_end_ptr = lhs_code.data + lhs_code.len; 17 | const PalmprintCodeMetadata* rhs_md_cur_ptr = rhs_code.data; 18 | while (lhs_md_cur_ptr < lhs_md_end_ptr) { 19 | uint8_t distance = *(uint8_t*)lhs_md_cur_ptr ^ *(uint8_t*)rhs_md_cur_ptr; 20 | if (distance == 0x00) { // same d, s. 21 | acc += 2; 22 | } else if (distance < 0x10) { // same d, diff s. 23 | acc += 0; 24 | } 25 | ++lhs_md_cur_ptr, ++rhs_md_cur_ptr; 26 | } 27 | *score = acc / (2.0 * lhs_code.len); 28 | return Status::Ok(); 29 | } 30 | } // namespace edcc -------------------------------------------------------------------------------- /test/test_core/test_gabor_filter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include 6 | #include "config/config.h" 7 | #include "config/reader.h" 8 | #include "core/gabor_filter.h" 9 | #include "test_base.h" 10 | 11 | namespace { 12 | 13 | using edcc::ConfigReader; 14 | using edcc::GaborFilter; 15 | using edcc::kDefaultEncoderConfig; 16 | using edcc::SimpleConfigReader; 17 | 18 | class GaborFilterTestFixture : public EdccTestFixtureBase { 19 | public: 20 | virtual void SetUp() { 21 | EdccTestFixtureBase::SetUp(); 22 | config_reader_ = std::unique_ptr(new edcc::SimpleConfigReader(kDefaultEncoderConfig)); 23 | config_reader_->LoadAndValidate(); 24 | gabor_filter_ = std::unique_ptr(new GaborFilter(config_reader_->GetEncoderConfig())); 25 | } 26 | 27 | protected: 28 | std::unique_ptr gabor_filter_; 29 | std::unique_ptr config_reader_; 30 | }; 31 | 32 | TEST_F(GaborFilterTestFixture, handle_palmprint_image) { 33 | std::vector result; 34 | auto cfg = config_reader_->GetEncoderConfig(); 35 | gabor_filter_->Handle(a_01_, &result); 36 | EXPECT_EQ(result.size(), cfg.gabor_directions); 37 | auto d1 = result[0]; 38 | EXPECT_EQ(d1.cols, cfg.image_size); // has been resized 39 | EXPECT_EQ(d1.channels(), 1); // has been converted to gray image. 40 | } 41 | } // namespace -------------------------------------------------------------------------------- /palmprint_data/README.md: -------------------------------------------------------------------------------- 1 | # ROI of palmprint 2 | 3 | There are only four palmprint images from two people for testing and example. 4 | 5 | For full data, run the following command 6 | 7 | ```shell 8 | sh download.sh all 9 | ``` 10 | 11 | ## PolyU Palmprint Database 12 | 13 | The multi-spectral palmprint database contains four spectra (red, green, blue, and near-infrared NIR). 14 | 15 | A database of each spectrum was collected from 250 subjects (195 males and 55 females). 16 | 17 | The age distribution ranged from 20 to 60 years old. The whole collection process was divided into two times. A total of 12 images of two palms were collected at a time. 18 | 19 | Therefore, 24 images from 2 palms were collected for each volunteer under each spectrum. In total, the database contains 4 spectra, each of which includes 6000 images of 500 different palms. 20 | 21 | ## Tongji Palmprint Database 22 | 23 | Tongji University palmprint database: based on non-contact device acquisition. 24 | 25 | Contains images of palms from 300 individuals (192 men and 108 women), of whom 235 were 20 to 30 years old and the rest were 30 to 50 years old. 26 | 27 | The whole collection process is divided into two times. The average time interval between the first and second time is about 61 days. The maximum and the shortest time intervals are 106 days and 21 days respectively. Each time, 20 pictures of two palms are collected. 28 | 29 | Therefore, each subject collected 40 images from 2 palms, which contained a total of 12,000 image samples. 30 | -------------------------------------------------------------------------------- /test/test_controller/test_c_api.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "edcc/c_api.h" 6 | #include "test_base.h" 7 | 8 | namespace { 9 | 10 | class CApiTestFixture : public EdccTestFixtureBase { 11 | public: 12 | virtual void SetUp() { 13 | EdccTestFixtureBase::SetUp(); 14 | cv::imencode(".png", a_01_, a_01_bytes_); 15 | cv::imencode(".png", b_01_, b_01_bytes_); 16 | } 17 | 18 | protected: 19 | std::vector a_01_bytes_; 20 | std::vector b_01_bytes_; 21 | }; 22 | 23 | TEST_F(CApiTestFixture, compare_two_palmprint) { 24 | char status[128]; 25 | int eid = new_encoder_with_config(29, 5, 5, 10, status); 26 | EXPECT_EQ(status[0], '\0'); 27 | EXPECT_EQ(eid, 0); // is first at encoders. 28 | status[0] = 1; 29 | unsigned long code_buffer_size = get_size_of_code_buffer_required(eid); 30 | char* a_01_code_bytes = new char[code_buffer_size]; 31 | char* b_01_code_bytes = new char[code_buffer_size]; 32 | encode_palmprint_using_bytes(eid, (char*)a_01_bytes_.data(), a_01_bytes_.size(), a_01_code_bytes, code_buffer_size, 33 | status); 34 | EXPECT_EQ(status[0], 0); 35 | status[0] = 1; 36 | encode_palmprint_using_file(eid, TEST_B_01_PALMPRINT_IMAGE, b_01_code_bytes, code_buffer_size, status); 37 | EXPECT_EQ(status[0], 0); 38 | status[0] = 1; 39 | double score = .0; 40 | score = calculate_codes_similarity(a_01_code_bytes, b_01_code_bytes, status); 41 | EXPECT_EQ(status[0], 0); 42 | EXPECT_GT(score, 0.0); 43 | delete[] a_01_code_bytes; 44 | delete[] b_01_code_bytes; 45 | } 46 | 47 | } // namespace 48 | -------------------------------------------------------------------------------- /include/edcc/facade.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_INCLUDE_EDCC_FACADE_H_ 6 | #define EDCC_INCLUDE_EDCC_FACADE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "edcc/status.h" 13 | 14 | namespace edcc { 15 | 16 | // Forward Declare 17 | class Encoder; 18 | 19 | class EdccFacade { 20 | public: 21 | typedef int eid; // encoder id represents the position in the vector. 22 | 23 | EdccFacade(const EdccFacade&) = delete; 24 | EdccFacade& operator=(const EdccFacade&) = delete; 25 | EdccFacade(const EdccFacade&&) = delete; 26 | EdccFacade& operator=(const EdccFacade&&) = delete; 27 | 28 | static EdccFacade* Instance(); 29 | void ClearEncoders(); 30 | 31 | EdccFacade::eid NewEncoderWithDefaultConfig(Status* s); 32 | // will return -1 if config invalid. 33 | EdccFacade::eid NewEncoderWithConfig(uint8_t image_size, uint8_t gabor_kernel_size, uint8_t laplace_kernel_size, 34 | uint8_t gabor_directions, Status* s); 35 | 36 | size_t GetSizeOfCodeBufferRequired(EdccFacade::eid id); 37 | void EncodePalmprint(EdccFacade::eid id, const cv::Mat& palmprint, char* code_buffer, size_t buffer_size, Status* s); 38 | void EncodePalmprint(EdccFacade::eid id, const std::string& filename, char* code_buffer, size_t buffer_size, 39 | Status* s); 40 | 41 | static double CalcCodeSimilarity(const char* lhs_code_buffer, const char* rhs_code_buffer, Status* s); 42 | 43 | private: 44 | EdccFacade(); 45 | 46 | std::vector> encoders_; 47 | }; 48 | } // namespace edcc 49 | 50 | #endif // EDCC_INCLUDE_EDCC_FACADE_H_ -------------------------------------------------------------------------------- /pypackage/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | import io 6 | import os 7 | 8 | from setuptools import setup 9 | 10 | import edcc 11 | 12 | # Package meta-data. 13 | NAME = "edcc" 14 | DESCRIPTION = "EDCC: An efficient and accurate algorithm for palmprint recognition." 15 | URL = "https://github.com/Leosocy/EDCC-Palmprint-Recognition" 16 | EMAIL = "leosocy@gmail.com" 17 | AUTHOR = "Leosocy" 18 | VERSION = edcc.__version__ 19 | 20 | root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 21 | 22 | edcc_classifiers = [ 23 | # Trove classifiers 24 | # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers 25 | "Development Status :: 2 - Pre-Alpha", 26 | "Programming Language :: Python", 27 | "Programming Language :: Python :: 3", 28 | "Programming Language :: Python :: 3.4", 29 | "Programming Language :: Python :: 3.5", 30 | "Programming Language :: Python :: 3.6", 31 | "Intended Audience :: Developers", 32 | "License :: OSI Approved :: MIT License", 33 | "Topic :: Software Development :: Libraries", 34 | ] 35 | 36 | try: 37 | with io.open(os.path.join(root, "README.md"), encoding="utf-8") as f: 38 | long_description = "\n" + f.read() 39 | except FileNotFoundError: 40 | long_description = DESCRIPTION 41 | 42 | setup( 43 | name=NAME, 44 | version=VERSION, 45 | description=DESCRIPTION, 46 | long_description=long_description, 47 | long_description_content_type="text/markdown", 48 | author=AUTHOR, 49 | author_email=EMAIL, 50 | python_requires=">=3", 51 | url=URL, 52 | packages=["edcc"], 53 | package_dir={"edcc": "edcc"}, 54 | include_package_data=True, 55 | license="MIT", 56 | classifiers=edcc_classifiers, 57 | ) 58 | -------------------------------------------------------------------------------- /examples/c_example/example.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define TEST_PALMPRINT_DATA_DIR "../../../palmprint_data" 10 | 11 | #define TEST_A_01_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/a_01.bmp" 12 | #define TEST_A_02_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/a_02.bmp" 13 | #define TEST_B_01_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/b_01.bmp" 14 | #define TEST_B_02_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/b_02.bmp" 15 | 16 | #define ASSERT_STATUS_OK(s) \ 17 | do { \ 18 | if (s[0] != '\0') { \ 19 | perror(s + 1); \ 20 | return -1; \ 21 | } \ 22 | } while (0) 23 | 24 | int main() { 25 | // create a new encoder. 26 | char status[128]; 27 | int encoder_id = new_encoder_with_config(29, 5, 5, 10, status); 28 | ASSERT_STATUS_OK(status); 29 | // encode palmprints to code buffer. 30 | unsigned long buffer_size = get_size_of_code_buffer_required(encoder_id); 31 | char* code_buffer_one = (char*)malloc(buffer_size); 32 | char* code_buffer_another = (char*)malloc(buffer_size); 33 | encode_palmprint_using_file(encoder_id, TEST_A_01_PALMPRINT_IMAGE, code_buffer_one, buffer_size, status); 34 | ASSERT_STATUS_OK(status); 35 | encode_palmprint_using_file(encoder_id, TEST_B_01_PALMPRINT_IMAGE, code_buffer_another, buffer_size, status); 36 | ASSERT_STATUS_OK(status); 37 | // calculate the similarity score of two codes. 38 | double score = calculate_codes_similarity(code_buffer_one, code_buffer_another, status); 39 | ASSERT_STATUS_OK(status); 40 | printf("%s <-> %s similarity score:%lf\n", TEST_A_01_PALMPRINT_IMAGE, TEST_B_01_PALMPRINT_IMAGE, score); 41 | free(code_buffer_one); 42 | free(code_buffer_another); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /examples/cpp_example/example.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | using edcc::EdccFacade; 9 | using edcc::Status; 10 | 11 | #define TEST_PALMPRINT_DATA_DIR "../../../palmprint_data" 12 | 13 | #define TEST_A_01_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/a_01.bmp" 14 | #define TEST_A_02_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/a_02.bmp" 15 | #define TEST_B_01_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/b_01.bmp" 16 | #define TEST_B_02_PALMPRINT_IMAGE TEST_PALMPRINT_DATA_DIR "/b_02.bmp" 17 | 18 | #define ASSERT_STATUS_OK(s) \ 19 | do { \ 20 | if (!s.IsOk()) { \ 21 | perror(s.msg()); \ 22 | return -1; \ 23 | } \ 24 | } while (0) 25 | 26 | int main() { 27 | Status s; 28 | // create a new encoder. 29 | auto inst = EdccFacade::Instance(); 30 | auto encoder_id = inst->NewEncoderWithConfig(29, 5, 5, 10, &s); 31 | ASSERT_STATUS_OK(s); 32 | // encode palmprints to code buffer. 33 | size_t buffer_size = inst->GetSizeOfCodeBufferRequired(encoder_id); 34 | char* code_buffer_one = new char[buffer_size]; 35 | char* code_buffer_another = new char[buffer_size]; 36 | inst->EncodePalmprint(encoder_id, TEST_B_01_PALMPRINT_IMAGE, code_buffer_one, buffer_size, &s); 37 | ASSERT_STATUS_OK(s); 38 | inst->EncodePalmprint(encoder_id, TEST_B_02_PALMPRINT_IMAGE, code_buffer_another, buffer_size, &s); 39 | ASSERT_STATUS_OK(s); 40 | // calculate the similarity score of two codes. 41 | double score = EdccFacade::CalcCodeSimilarity(code_buffer_one, code_buffer_another, &s); 42 | ASSERT_STATUS_OK(s); 43 | printf("%s <-> %s similarity score:%lf\n", TEST_B_01_PALMPRINT_IMAGE, TEST_B_02_PALMPRINT_IMAGE, score); 44 | delete []code_buffer_one; 45 | delete []code_buffer_another; 46 | return 0; 47 | } -------------------------------------------------------------------------------- /src/controller/c_api.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "edcc/c_api.h" 6 | #include "edcc/facade.h" 7 | 8 | using edcc::EdccFacade; 9 | using edcc::Status; 10 | 11 | int new_encoder_with_config(unsigned char image_size, unsigned char gabor_kernel_size, 12 | unsigned char laplace_kernel_size, unsigned char gabor_directions, char* status_ptr) { 13 | Status s; 14 | int eid = EdccFacade::Instance()->NewEncoderWithConfig(image_size, gabor_kernel_size, laplace_kernel_size, 15 | gabor_directions, &s); 16 | s.CopyToBuffer(status_ptr); 17 | return eid; 18 | } 19 | 20 | unsigned long get_size_of_code_buffer_required(int eid) { 21 | return EdccFacade::Instance()->GetSizeOfCodeBufferRequired(eid); 22 | } 23 | 24 | void encode_palmprint_using_file(int eid, const char* filepath, char* code_bytes, unsigned long code_bytes_size, 25 | char* status_ptr) { 26 | Status s; 27 | EdccFacade::Instance()->EncodePalmprint(eid, filepath, code_bytes, code_bytes_size, &s); 28 | s.CopyToBuffer(status_ptr); 29 | } 30 | 31 | void encode_palmprint_using_bytes(int eid, const char* palmprint_bytes, unsigned long palmprint_bytes_size, 32 | char* code_bytes, unsigned long code_bytes_size, char* status_ptr) { 33 | Status s; 34 | std::vector vp_bytes(palmprint_bytes_size); 35 | memcpy(vp_bytes.data(), palmprint_bytes, palmprint_bytes_size); 36 | cv::Mat palmprint(cv::imdecode(vp_bytes, 1)); 37 | EdccFacade::Instance()->EncodePalmprint(eid, palmprint, code_bytes, code_bytes_size, &s); 38 | s.CopyToBuffer(status_ptr); 39 | } 40 | 41 | double calculate_codes_similarity(char* lhs_code_bytes, char* rhs_code_bytes, char* status_ptr) { 42 | Status s; 43 | double score; 44 | score = EdccFacade::CalcCodeSimilarity(lhs_code_bytes, rhs_code_bytes, &s); 45 | s.CopyToBuffer(status_ptr); 46 | return score; 47 | } 48 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################### 2 | # EDCC test cmake configuration # 3 | ################################### 4 | 5 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 6 | 7 | SET(LIBRARY_NAME edcc) 8 | SET(PROJECT_NAME test-${LIBRARY_NAME}) 9 | PROJECT(${PROJECT_NAME}) 10 | 11 | SET(PROJECT_ROOT ${CMAKE_BINARY_DIR}/..) 12 | SET(TEST_ROOT ${PROJECT_ROOT}/test) 13 | 14 | 15 | # Download and unpack googletest at configure time 16 | CONFIGURE_FILE(${PROJECT_ROOT}/thirdparty/gtest/CMakeLists.txt.in 17 | ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) 18 | EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 19 | RESULT_VARIABLE result 20 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 21 | IF(result) 22 | MESSAGE(FATAL_ERROR "CMake step for googletest failed: ${result}") 23 | ENDIF() 24 | EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . 25 | RESULT_VARIABLE result 26 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 27 | IF(result) 28 | MESSAGE(FATAL_ERROR "Build step for googletest failed: ${result}") 29 | ENDIF() 30 | SET(BUILD_SHARED_LIBS ON CACHE BOOL "build gtest shared libs") 31 | ADD_SUBDIRECTORY(${CMAKE_BINARY_DIR}/googletest-src 32 | ${CMAKE_BINARY_DIR}/googletest-build 33 | EXCLUDE_FROM_ALL ) 34 | IF(CMAKE_VERSION VERSION_LESS 2.8.11) 35 | INCLUDE_DIRECTORIES("${gtest_SOURCE_DIR}/include") 36 | ENDIF() 37 | 38 | 39 | INCLUDE_DIRECTORIES(${TEST_ROOT}) 40 | INCLUDE_DIRECTORIES(${PROJECT_ROOT}/include) 41 | INCLUDE_DIRECTORIES(${PROJECT_ROOT}/src) 42 | 43 | 44 | FILE(GLOB_RECURSE TEST_HEADER_FILES ${TEST_ROOT}/*.h) 45 | FILE(GLOB_RECURSE TEST_SRC_FILES ${TEST_ROOT}/*.cpp) 46 | ADD_EXECUTABLE(${PROJECT_NAME} ${TEST_HEADER_FILES} ${TEST_SRC_FILES}) 47 | 48 | SET(LINK_LIBRARIES 49 | ${LIBRARY_NAME} 50 | gtest_main ) 51 | TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LINK_LIBRARIES}) 52 | 53 | EXECUTE_PROCESS(COMMAND ${PROJECT_ROOT}/palmprint_data/download.sh) 54 | 55 | ADD_TEST(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) 56 | ADD_CUSTOM_TARGET(build_and_test 57 | COMMAND ${PROJECT_NAME} 58 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) 59 | -------------------------------------------------------------------------------- /test/test_core/test_encoder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include 6 | #include "config/config.h" 7 | #include "core/code.h" 8 | #include "core/encoder.h" 9 | #include "edcc/status.h" 10 | #include "test_base.h" 11 | 12 | namespace { 13 | 14 | using edcc::Encoder; 15 | using edcc::kDefaultEncoderConfig; 16 | using edcc::PalmprintCode; 17 | using edcc::PalmprintCodeMetadata; 18 | using edcc::Status; 19 | 20 | class EncoderTestFixture : public EdccTestFixtureBase { 21 | public: 22 | EncoderTestFixture() : encoder_(kDefaultEncoderConfig) {} 23 | virtual void SetUp() { EdccTestFixtureBase::SetUp(); } 24 | 25 | protected: 26 | Encoder encoder_; 27 | }; 28 | 29 | TEST_F(EncoderTestFixture, get_code_buffer_size) { 30 | const Encoder& encoder = encoder_; 31 | EXPECT_EQ(encoder.GetCodeBufferSize(), sizeof(PalmprintCode) + kDefaultEncoderConfig.image_size * 32 | kDefaultEncoderConfig.image_size * 33 | sizeof(PalmprintCodeMetadata)); 34 | } 35 | 36 | TEST_F(EncoderTestFixture, lacking_code_buffer_size) { 37 | const Encoder& encoder = encoder_; 38 | PalmprintCode* code = (PalmprintCode*)malloc(encoder.GetCodeBufferSize()); 39 | auto status = encoder.Encode(a_01_, code, 1); 40 | EXPECT_EQ(status.code(), Status::kLackingCodeBuffer); 41 | } 42 | 43 | TEST_F(EncoderTestFixture, correctly_encode) { 44 | const Encoder& encoder = encoder_; 45 | size_t buffer_size = encoder.GetCodeBufferSize(); 46 | PalmprintCode* code = (PalmprintCode*)malloc(buffer_size); 47 | auto status = encoder.Encode(a_01_, code, buffer_size); 48 | EXPECT_EQ(status.code(), Status::kOk); 49 | EXPECT_TRUE(memcmp(&code->cfg, &kDefaultEncoderConfig, sizeof(code->cfg)) == 0); 50 | EXPECT_EQ(code->len, kDefaultEncoderConfig.image_size * kDefaultEncoderConfig.image_size); 51 | for (uint32_t offset = 0; offset < code->len; ++offset) { 52 | PalmprintCodeMetadata* metadata = code->data + offset; 53 | EXPECT_TRUE(metadata->d >= 0 && metadata->d < kDefaultEncoderConfig.gabor_directions); 54 | EXPECT_TRUE(metadata->s == 0 || metadata->s == 1); 55 | EXPECT_TRUE(metadata->_pad == 0); 56 | } 57 | } 58 | 59 | } // namespace -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. 4 | 5 | You can contribute in many ways: 6 | 7 | ## Types of Contributions 8 | 9 | ### Report Bugs 10 | 11 | Report bugs at [issues](https://github.com/Leosocy/EDCC-Palmprint-Recognition/issues). 12 | 13 | If you are reporting a bug, please include: 14 | 15 | - Your operating system name and version. 16 | - Any details about your local setup that might be helpful in troubleshooting. 17 | - Detailed steps to reproduce the bug. 18 | 19 | ### Submit Feedback 20 | 21 | The best way to send feedback is to file an issue at [issues](https://github.com/Leosocy/EDCC-Palmprint-Recognition/issues). 22 | 23 | If you are proposing a feature: 24 | 25 | - Explain in detail how it would work. 26 | - Keep the scope as narrow as possible, to make it easier to implement. 27 | - Remember that this is a volunteer-driven project, and that contributions are welcome :) 28 | 29 | ## Get Started! 30 | 31 | Ready to contribute? Here's how to set up `EDCC` for local development. 32 | 33 | 1. Fork the [EDCC-Palmprint-Recognition](https://github.com/Leosocy/EDCC-Palmprint-Recognition) repo on Github. 34 | 1. Clone your fork locally: 35 | ```shell 36 | git clone git@github.com:your_name_here/EDCC-Palmprint-Recognition.git 37 | ``` 38 | 1. Create a branch for local development: 39 | ```shell 40 | git checkout -b name-of-your-bugfix-or-feature 41 | ``` 42 | Now you can make your changes locally. 43 | 1. When you're done making changes, check that your changes pass the tests and lints: 44 | ``` 45 | ./manage.sh test_and_lint 46 | ``` 47 | and then it will pull docker image, and run tests and lints in container. 48 | 1. Commit your changes and push your branch to GitHub: 49 | ```shell 50 | git add . 51 | git commit -m "Your detailed description of your changes." 52 | git push origin name-of-your-bugfix-or-feature 53 | ``` 54 | 1. Submit a pull request through the GitHub website. 55 | 56 | ## Pull Request Guidelines 57 | 58 | Before you submit a pull request, check that it meets these guidelines: 59 | 60 | 1. The pull request should include tests and reach the codecov requirement(see in [.codecov.yml](https://github.com/Leosocy/EDCC-Palmprint-Recognition/blob/master/.codecov.yml)). 61 | 1. Please follow `Clean Code` standards, and it would be nicer to follow the [Google C++ Code Style](https://leosocy.top/Google%20C++%20Code%20Style/). 62 | -------------------------------------------------------------------------------- /src/core/encoder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "core/encoder.h" 6 | #include 7 | #include 8 | 9 | namespace edcc { 10 | 11 | Encoder::Encoder(const EncoderConfig &cfg) : cfg_(cfg) { 12 | gabor_filter_ = std::unique_ptr(new GaborFilter(cfg_)); 13 | } 14 | 15 | size_t Encoder::GetCodeBufferSize() const { 16 | uint32_t metadata_len = cfg_.image_size * cfg_.image_size; 17 | return sizeof(PalmprintCode) + metadata_len * sizeof(PalmprintCodeMetadata); 18 | } 19 | 20 | Status Encoder::Encode(const cv::Mat &palmprint, PalmprintCode *code, size_t buffer_size) const { 21 | assert(code != nullptr); 22 | if (buffer_size < GetCodeBufferSize()) { 23 | return Status::LackingCodeBuffer("Buffer size:%lu is lacking.", buffer_size); 24 | } 25 | memset(code, 0, buffer_size); 26 | code->cfg = cfg_; 27 | code->len = cfg_.image_size * cfg_.image_size; 28 | 29 | std::vector gabor_filter_result; 30 | gabor_filter_->Handle(palmprint, &gabor_filter_result); 31 | 32 | PalmprintCodeMetadata *metadata_ptr = code->data; 33 | for (uint8_t h = 0; h < cfg_.image_size; ++h) { 34 | for (uint8_t w = 0; w < cfg_.image_size; ++w) { 35 | uint8_t direction = GetDirectionOfMaxResponse(gabor_filter_result, h, w); 36 | metadata_ptr->d = direction; 37 | 38 | uint8_t s_left = direction == cfg_.gabor_directions - 1 ? 0 : direction + 1; 39 | uint8_t s_right = direction == 0 ? cfg_.gabor_directions - 1 : direction - 1; 40 | double left_direction_response = gabor_filter_result[s_left].ptr(h)[w]; 41 | double right_direction_response = gabor_filter_result[s_right].ptr(h)[w]; 42 | metadata_ptr->s = left_direction_response >= right_direction_response ? 1 : 0; 43 | ++metadata_ptr; 44 | } 45 | } 46 | return Status::Ok(); 47 | } 48 | 49 | inline uint8_t Encoder::GetDirectionOfMaxResponse(const std::vector &gabor_filter_result, uint8_t x, 50 | uint8_t y) const { 51 | double max_response = -DBL_MAX; 52 | uint8_t direction_of = UINT8_MAX; 53 | for (uint8_t d = 0; d < cfg_.gabor_directions; ++d) { 54 | double response = gabor_filter_result[d].ptr(x)[y]; 55 | if (response > max_response) { 56 | max_response = response; 57 | direction_of = d; 58 | } 59 | } 60 | return direction_of; 61 | } 62 | 63 | } // namespace edcc -------------------------------------------------------------------------------- /test/test_controller/test_facade.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "edcc/facade.h" 6 | #include "test_base.h" 7 | 8 | namespace { 9 | 10 | using edcc::EdccFacade; 11 | using edcc::Status; 12 | 13 | class EdccFacadeTestFixture : public EdccTestFixtureBase { 14 | public: 15 | virtual void TearDown() { EdccFacade::Instance()->ClearEncoders(); } 16 | }; 17 | 18 | TEST_F(EdccFacadeTestFixture, new_encoder_with_invalid_config) { 19 | Status s; 20 | EdccFacade::eid id = EdccFacade::Instance()->NewEncoderWithConfig(29, 5, 5, 66, &s); 21 | EXPECT_EQ(id, -1); 22 | EXPECT_EQ(s.code(), Status::kInvalidArgument); 23 | } 24 | 25 | TEST_F(EdccFacadeTestFixture, compare_two_palmprint_with_same_encoder) { 26 | Status s; 27 | EdccFacade::eid id = EdccFacade::Instance()->NewEncoderWithDefaultConfig(&s); 28 | ASSERT_TRUE(s.IsOk()); 29 | size_t buffer_size = EdccFacade::Instance()->GetSizeOfCodeBufferRequired(id); 30 | char* code_a_01_buffer = new char[buffer_size]; 31 | char* code_a_02_buffer = new char[buffer_size]; 32 | EdccFacade::Instance()->EncodePalmprint(id, a_01_, code_a_01_buffer, buffer_size, &s); 33 | ASSERT_TRUE(s.IsOk()); 34 | EdccFacade::Instance()->EncodePalmprint(id, TEST_A_02_PALMPRINT_IMAGE, code_a_02_buffer, buffer_size, &s); 35 | ASSERT_TRUE(s.IsOk()); 36 | double score; 37 | score = EdccFacade::Instance()->CalcCodeSimilarity(code_a_01_buffer, code_a_02_buffer, &s); 38 | ASSERT_TRUE(s.IsOk()); 39 | EXPECT_GT(score, 0.0); 40 | } 41 | 42 | TEST_F(EdccFacadeTestFixture, compare_two_palmprint_with_different_encoder) { 43 | Status s, s1, s2; 44 | EdccFacade::eid id1 = EdccFacade::Instance()->NewEncoderWithDefaultConfig(&s1); 45 | EdccFacade::eid id2 = EdccFacade::Instance()->NewEncoderWithConfig(29, 5, 5, 12, &s2); 46 | size_t buffer_size1 = EdccFacade::Instance()->GetSizeOfCodeBufferRequired(id1); 47 | char* code_a_01_buffer = new char[buffer_size1]; 48 | size_t buffer_size2 = EdccFacade::Instance()->GetSizeOfCodeBufferRequired(id2); 49 | char* code_a_02_buffer = new char[buffer_size2]; 50 | EdccFacade::Instance()->EncodePalmprint(id1, a_01_, code_a_01_buffer, buffer_size1, &s1); 51 | EdccFacade::Instance()->EncodePalmprint(id2, TEST_A_02_PALMPRINT_IMAGE, code_a_02_buffer, buffer_size2, &s2); 52 | double score; 53 | score = EdccFacade::Instance()->CalcCodeSimilarity(code_a_01_buffer, code_a_02_buffer, &s); 54 | EXPECT_EQ(s.code(), Status::kCodeCfgNEWhenComparing); 55 | } 56 | 57 | } // namespace -------------------------------------------------------------------------------- /src/core/gabor_filter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "core/gabor_filter.h" 6 | 7 | namespace edcc { 8 | 9 | GaborFilter::GaborFilter(const EncoderConfig &config) : cfg_(config) { InitKernels(); } 10 | 11 | void GaborFilter::Handle(const cv::Mat &src, std::vector *result) { 12 | assert(result); 13 | cv::Mat enhanced_image; 14 | PreProcessImage(src, &enhanced_image); 15 | EnhanceImage(enhanced_image, &enhanced_image); 16 | for (uint8_t d = 0; d < cfg_.gabor_directions; ++d) { 17 | cv::Mat directional_filter_result; 18 | filter2D(enhanced_image, directional_filter_result, CV_64F, kernels_[d]); 19 | result->push_back(directional_filter_result.clone()); 20 | } 21 | } 22 | 23 | void GaborFilter::InitKernels() { 24 | for (uint8_t d = 0; d < cfg_.gabor_directions; ++d) { 25 | cv::Mat directional_kernel; 26 | GetKernelReal(&directional_kernel, cfg_.gabor_kernel_size, cfg_.gabor_kernel_size, 0, d); 27 | kernels_.push_back(directional_kernel.clone()); 28 | } 29 | } 30 | 31 | void GaborFilter::GetKernelReal(cv::Mat *kernel, int width, int height, int dimension, int direction, double kmax, 32 | double f, double sigma) const { 33 | int half_width = width / 2; 34 | int half_height = height / 2; 35 | double Qu = CV_PI * direction / cfg_.gabor_directions; 36 | double sqsigma = sigma * sigma; 37 | double Kv = kmax / pow(f, dimension); 38 | double postmean = exp(-sqsigma / 2); 39 | kernel->create(width, height, CV_64F); 40 | double tmp1, tmp2; 41 | for (int i = -half_height; i <= half_height; ++i) { 42 | for (int j = -half_width; j <= half_width; ++j) { 43 | tmp1 = exp(-(Kv * Kv * (i * i + j * j)) / (2 * sqsigma)); 44 | tmp2 = cos(Kv * cos(Qu) * j + Kv * sin(Qu) * i) - postmean; 45 | kernel->ptr(i + half_height)[j + half_width] = Kv * Kv * tmp1 * tmp2 / sqsigma; 46 | } 47 | } 48 | } 49 | 50 | void GaborFilter::PreProcessImage(const cv::Mat &src, cv::Mat *result) const { 51 | assert(result); 52 | cv::Mat resized; 53 | cv::resize(src, resized, cv::Size(cfg_.image_size, cfg_.image_size)); 54 | cvtColor(resized, *result, cv::COLOR_BGR2GRAY); 55 | } 56 | 57 | void GaborFilter::EnhanceImage(const cv::Mat &src, cv::Mat *result) const { 58 | assert(result); 59 | cv::Mat gaussian; 60 | GaussianBlur(src, gaussian, cv::Size(5, 5), 0, 0, cv::BORDER_DEFAULT); 61 | Laplacian(gaussian, *result, CV_64F, cfg_.laplace_kernel_size); 62 | } 63 | 64 | } // namespace edcc -------------------------------------------------------------------------------- /include/edcc/c_api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_INCLUDE_EDCC_C_API_H_ 6 | #define EDCC_INCLUDE_EDCC_C_API_H_ 7 | 8 | #define EDCC_API 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** 15 | * Create a edcc encoder. 16 | * @param status_ptr: a pointer of status, status[0] stands for code, status[1:] stands for msg. 17 | * @return: id of encoder. return -1 if config is invalid. 18 | */ 19 | EDCC_API int new_encoder_with_config(unsigned char image_size, unsigned char gabor_kernel_size, 20 | unsigned char laplace_kernel_size, unsigned char gabor_directions, 21 | char* status_ptr); 22 | 23 | /** 24 | * Get the length used to initialize code buffer. 25 | * @param eid: encoder id returned from `new_encoder_with_config`. 26 | * @return: size of code buffer which calculate by encoder config. 27 | */ 28 | EDCC_API unsigned long get_size_of_code_buffer_required(int eid); 29 | 30 | /** 31 | * Encode a palmprint image to code buffer. 32 | * @param eid: encoder id returned from `new_encoder_with_config`. 33 | * @param filepath: path of palmprint image. 34 | * @param code_bytes: a bytes of palmprint code buffer. 35 | * @param code_bytes_size: the size of code_bytes buffer. 36 | */ 37 | EDCC_API void encode_palmprint_using_file(int eid, const char* filepath, char* code_bytes, 38 | unsigned long code_bytes_size, char* status_ptr); 39 | 40 | /** 41 | * Encode a palmprint bytes to code buffer. 42 | * @param eid: encoder id returned from `new_encoder_with_config`. 43 | * @param palmprint_bytes: a bytes of palmprint image. 44 | * @param palmprint_bytes_size: the size of palmprint_bytes. 45 | * @param code_bytes: a bytes of palmprint code buffer. 46 | * @param code_bytes_size: the size of code_bytes buffer. 47 | */ 48 | EDCC_API void encode_palmprint_using_bytes(int eid, const char* palmprint_bytes, unsigned long palmprint_bytes_size, 49 | char* code_bytes, unsigned long code_bytes_size, char* status_ptr); 50 | 51 | /** 52 | * Calculate similarity of two codes. 53 | * @param lhs_code_bytes: one code_bytes encoded by `encode_palmprint_bytes`. 54 | * @param rhs_code_bytes: another code_bytes encoded by `encode_palmprint_bytes`. 55 | * @return: the similarity score between two codes. 56 | */ 57 | EDCC_API double calculate_codes_similarity(char* lhs_code_bytes, char* rhs_code_bytes, char* status_ptr); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif // EDCC_INCLUDE_EDCC_C_API_H_ -------------------------------------------------------------------------------- /src/config/reader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "config/reader.h" 6 | 7 | namespace edcc { 8 | 9 | #define INVOKE_SETTER_WITH_STATUS_CHECKING(setter) \ 10 | do { \ 11 | auto status = setter; \ 12 | if (!status.IsOk()) { \ 13 | return status; \ 14 | } \ 15 | } while (0) 16 | 17 | Status ConfigReader::SetImageSize(uint8_t size) { 18 | if (size < limit::kMinImageSize) { 19 | return Status::InvalidArgument("Image size less than minimum limit."); 20 | } 21 | encoder_cfg_.image_size = size; 22 | return Status::Ok(); 23 | } 24 | 25 | Status ConfigReader::SetGaborKernelSize(uint8_t size) { 26 | if (size > encoder_cfg_.image_size) { 27 | return Status::InvalidArgument("Gabor kernel size larger than image size."); 28 | } 29 | if ((size & 0x01) == 0) { 30 | return Status::InvalidArgument("Gabor kernel size not odd."); 31 | } 32 | encoder_cfg_.gabor_kernel_size = size; 33 | return Status::Ok(); 34 | } 35 | 36 | Status ConfigReader::SetLaplaceKernelSize(uint8_t size) { 37 | if (size > encoder_cfg_.image_size || size > limit::kMaxLaplaceKernelSize) { 38 | return Status::InvalidArgument("Laplace kernel size larger than image size or %d.", limit::kMaxLaplaceKernelSize); 39 | } 40 | if ((size & 0x01) == 0) { 41 | return Status::InvalidArgument("Laplace kernel size not odd."); 42 | } 43 | encoder_cfg_.laplace_kernel_size = size; 44 | return Status::Ok(); 45 | } 46 | 47 | Status ConfigReader::SetGaborDirections(uint8_t num) { 48 | if (num > limit::kMaxGaborDirections || num < limit::kMinGaborDirections) { 49 | return Status::InvalidArgument("Gabor directions not in range [%d, %d].", limit::kMinGaborDirections, 50 | limit::kMaxGaborDirections); 51 | } 52 | encoder_cfg_.gabor_directions = num; 53 | return Status::Ok(); 54 | } 55 | 56 | SimpleConfigReader::SimpleConfigReader(const EncoderConfig& config) { encoder_cfg_ = config; } 57 | 58 | Status SimpleConfigReader::LoadAndValidate() { 59 | INVOKE_SETTER_WITH_STATUS_CHECKING(SetImageSize(encoder_cfg_.image_size)); 60 | INVOKE_SETTER_WITH_STATUS_CHECKING(SetGaborKernelSize(encoder_cfg_.gabor_kernel_size)); 61 | INVOKE_SETTER_WITH_STATUS_CHECKING(SetLaplaceKernelSize(encoder_cfg_.laplace_kernel_size)); 62 | INVOKE_SETTER_WITH_STATUS_CHECKING(SetGaborDirections(encoder_cfg_.gabor_directions)); 63 | return Status::Ok(); 64 | } 65 | 66 | } // namespace edcc -------------------------------------------------------------------------------- /include/edcc/status.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #ifndef EDCC_INCLUDE_EDCC_STATUS_H_ 6 | #define EDCC_INCLUDE_EDCC_STATUS_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace edcc { 14 | 15 | #define DECL_STATUS_CODE_FUNC(name, code) \ 16 | static Status name(const char* fmt = "", ...) { \ 17 | va_list args; \ 18 | va_start(args, fmt); \ 19 | Status status = Status(code, fmt, args); \ 20 | va_end(args); \ 21 | return status; \ 22 | } 23 | 24 | class Status { 25 | public: 26 | enum Code { 27 | kOk = 0, 28 | kInvalidArgument = 1, 29 | kLackingCodeBuffer = 2, 30 | kCodeCfgNEWhenComparing = 3, 31 | }; 32 | DECL_STATUS_CODE_FUNC(Ok, kOk); 33 | DECL_STATUS_CODE_FUNC(InvalidArgument, kInvalidArgument); 34 | DECL_STATUS_CODE_FUNC(LackingCodeBuffer, kLackingCodeBuffer); 35 | DECL_STATUS_CODE_FUNC(CodeCfgNEWhenComparing, kCodeCfgNEWhenComparing); 36 | 37 | Status() : state_(nullptr) {} 38 | Status(const Status& s) = delete; 39 | Status& operator=(const Status& s) = delete; 40 | Status(Status&& s) : state_(s.state_) { s.state_ = nullptr; } 41 | Status& operator=(Status&& s) noexcept { 42 | if (this == &s) { 43 | return *this; 44 | } 45 | DeleteState(); 46 | state_ = s.state_; 47 | s.state_ = nullptr; 48 | return *this; 49 | } 50 | ~Status() { DeleteState(); } 51 | Code code() { 52 | assert(state_ != nullptr); 53 | return static_cast(state_[0]); 54 | } 55 | const char* msg() { 56 | assert(state_ != nullptr); 57 | return state_ + 1; 58 | } 59 | bool IsOk() { return code() == kOk; } 60 | void CopyToBuffer(char* buffer) { 61 | if (state_ != nullptr) { 62 | *buffer++ = state_[0]; 63 | memcpy(buffer, state_ + 1, strlen(state_ + 1) + 1); 64 | } 65 | } 66 | 67 | private: 68 | Status(Code code, const char* fmt, va_list args); 69 | void DeleteState(); 70 | const char* state_; 71 | }; 72 | 73 | inline Status::Status(Code code, const char* fmt, va_list args) { 74 | assert(fmt != nullptr); 75 | size_t msg_len = strlen(fmt); 76 | char* result = new char[msg_len + 2]; 77 | result[0] = static_cast(code); 78 | vsnprintf(result + 1, msg_len, fmt, args); 79 | result[msg_len + 1] = '\0'; 80 | state_ = result; 81 | } 82 | 83 | inline void Status::DeleteState() { 84 | if (state_ != nullptr) { 85 | delete[] state_; 86 | state_ = nullptr; 87 | } 88 | } 89 | 90 | } // namespace edcc 91 | 92 | #endif // EDCC_INCLUDE_EDCC_STATUS_H_ -------------------------------------------------------------------------------- /src/controller/facade.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include "edcc/facade.h" 6 | #include "config/config.h" 7 | #include "config/reader.h" 8 | #include "core/comparer.h" 9 | #include "core/encoder.h" 10 | 11 | namespace edcc { 12 | 13 | EdccFacade::EdccFacade() = default; 14 | 15 | EdccFacade* EdccFacade::Instance() { 16 | static EdccFacade instance; 17 | return &instance; 18 | } 19 | 20 | void EdccFacade::ClearEncoders() { encoders_.clear(); } 21 | 22 | EdccFacade::eid EdccFacade::NewEncoderWithDefaultConfig(Status* s) { 23 | const EncoderConfig& cfg = kDefaultEncoderConfig; 24 | return NewEncoderWithConfig(cfg.image_size, cfg.gabor_kernel_size, cfg.laplace_kernel_size, cfg.gabor_directions, s); 25 | } 26 | 27 | EdccFacade::eid EdccFacade::NewEncoderWithConfig(uint8_t image_size, uint8_t gabor_kernel_size, 28 | uint8_t laplace_kernel_size, uint8_t gabor_directions, Status* s) { 29 | EncoderConfig cfg; 30 | cfg.image_size = image_size; 31 | cfg.gabor_kernel_size = gabor_kernel_size; 32 | cfg.laplace_kernel_size = laplace_kernel_size; 33 | cfg.gabor_directions = gabor_directions; 34 | std::unique_ptr reader = std::unique_ptr(new SimpleConfigReader(cfg)); 35 | *s = reader->LoadAndValidate(); 36 | if (s->IsOk()) { 37 | std::unique_ptr encoder = std::unique_ptr(new Encoder(reader->GetEncoderConfig())); 38 | encoders_.emplace_back(std::move(encoder)); 39 | return encoders_.size() - 1; 40 | } else { 41 | return -1; 42 | } 43 | } 44 | 45 | size_t EdccFacade::GetSizeOfCodeBufferRequired(EdccFacade::eid id) { return encoders_.at(id)->GetCodeBufferSize(); } 46 | 47 | void EdccFacade::EncodePalmprint(EdccFacade::eid id, const cv::Mat& palmprint, char* code_buffer, size_t buffer_size, 48 | Status* s) { 49 | auto* code = reinterpret_cast(code_buffer); 50 | *s = encoders_.at(id)->Encode(palmprint, code, buffer_size); 51 | } 52 | 53 | void EdccFacade::EncodePalmprint(EdccFacade::eid id, const std::string& filename, char* code_buffer, size_t buffer_size, 54 | Status* s) { 55 | cv::Mat palmprint = cv::imread(filename); 56 | EncodePalmprint(id, palmprint, code_buffer, buffer_size, s); 57 | } 58 | 59 | double EdccFacade::CalcCodeSimilarity(const char* lhs_code_buffer, const char* rhs_code_buffer, Status* s) { 60 | const auto* lhs_code = reinterpret_cast(lhs_code_buffer); 61 | const auto* rhs_code = reinterpret_cast(rhs_code_buffer); 62 | double score = .0; 63 | *s = Comparer::Compare(*lhs_code, *rhs_code, &score); 64 | return score; 65 | } 66 | 67 | } // namespace edcc -------------------------------------------------------------------------------- /test/test_config/test_reader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include 6 | #include "config/reader.h" 7 | 8 | namespace { 9 | 10 | using edcc::EncoderConfig; 11 | using edcc::kDefaultEncoderConfig; 12 | using edcc::SimpleConfigReader; 13 | using edcc::Status; 14 | 15 | TEST(SimpleConfigReaderTest, all_correct) { 16 | auto reader = SimpleConfigReader(edcc::kDefaultEncoderConfig); 17 | auto status = reader.LoadAndValidate(); 18 | EXPECT_TRUE(status.IsOk()); 19 | auto config = reader.GetEncoderConfig(); 20 | EXPECT_EQ(config.image_size, edcc::kDefaultEncoderConfig.image_size); 21 | EXPECT_EQ(config.gabor_kernel_size, edcc::kDefaultEncoderConfig.gabor_kernel_size); 22 | EXPECT_EQ(config.laplace_kernel_size, edcc::kDefaultEncoderConfig.laplace_kernel_size); 23 | EXPECT_EQ(config.gabor_directions, edcc::kDefaultEncoderConfig.gabor_directions); 24 | } 25 | 26 | TEST(SimpleConfigReaderTest, invalid_image_size) { 27 | EncoderConfig config = kDefaultEncoderConfig; 28 | config.image_size = edcc::limit::kMinImageSize - 1; 29 | auto reader = SimpleConfigReader(config); 30 | auto status = reader.LoadAndValidate(); 31 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 32 | } 33 | 34 | TEST(SimpleConfigReaderTest, invalid_gabor_kernel_size) { 35 | EncoderConfig config = kDefaultEncoderConfig; 36 | config.gabor_kernel_size = kDefaultEncoderConfig.image_size + 1; 37 | auto reader = SimpleConfigReader(config); 38 | auto status = reader.LoadAndValidate(); 39 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 40 | 41 | config.gabor_kernel_size = 4; 42 | reader = SimpleConfigReader(config); 43 | status = reader.LoadAndValidate(); 44 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 45 | } 46 | 47 | TEST(SimpleConfigReaderTest, invalid_laplace_kernel_size) { 48 | EncoderConfig config = kDefaultEncoderConfig; 49 | config.laplace_kernel_size = kDefaultEncoderConfig.image_size + 1; 50 | auto reader = SimpleConfigReader(config); 51 | auto status = reader.LoadAndValidate(); 52 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 53 | 54 | config.laplace_kernel_size = 4; 55 | reader = SimpleConfigReader(config); 56 | status = reader.LoadAndValidate(); 57 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 58 | 59 | config.laplace_kernel_size = edcc::limit::kMaxLaplaceKernelSize + 1; 60 | reader = SimpleConfigReader(config); 61 | status = reader.LoadAndValidate(); 62 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 63 | } 64 | 65 | TEST(SimpleConfigReaderTest, invalid_gabor_directions) { 66 | EncoderConfig config = kDefaultEncoderConfig; 67 | config.gabor_directions = edcc::limit::kMaxGaborDirections + 1; 68 | auto reader = SimpleConfigReader(config); 69 | auto status = reader.LoadAndValidate(); 70 | EXPECT_EQ(status.code(), Status::kInvalidArgument); 71 | } 72 | 73 | } // namespace -------------------------------------------------------------------------------- /test/test_core/test_comparer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 leosocy. All rights reserved. 2 | // Use of this source code is governed by a MIT-style license 3 | // that can be found in the LICENSE file. 4 | 5 | #include 6 | #include "config/config.h" 7 | #include "config/reader.h" 8 | #include "core/code.h" 9 | #include "core/comparer.h" 10 | #include "core/encoder.h" 11 | #include "edcc/status.h" 12 | #include "test_base.h" 13 | 14 | namespace { 15 | 16 | using edcc::Comparer; 17 | using edcc::ConfigReader; 18 | using edcc::Encoder; 19 | using edcc::kDefaultEncoderConfig; 20 | using edcc::PalmprintCode; 21 | using edcc::PalmprintCodeMetadata; 22 | using edcc::Status; 23 | 24 | class ComparerTestFixture : public EdccTestFixtureBase { 25 | public: 26 | virtual void SetUp() { 27 | EdccTestFixtureBase::SetUp(); 28 | config_reader_ = std::unique_ptr(new edcc::SimpleConfigReader(kDefaultEncoderConfig)); 29 | config_reader_->LoadAndValidate(); 30 | Encoder encoder(config_reader_->GetEncoderConfig()); 31 | size_t buffer_size = encoder.GetCodeBufferSize(); 32 | a_01_code_ = (PalmprintCode*)malloc(buffer_size); 33 | a_02_code_ = (PalmprintCode*)malloc(buffer_size); 34 | b_01_code_ = (PalmprintCode*)malloc(buffer_size); 35 | b_02_code_ = (PalmprintCode*)malloc(buffer_size); 36 | encoder.Encode(a_01_, a_01_code_, buffer_size); 37 | encoder.Encode(a_02_, a_02_code_, buffer_size); 38 | encoder.Encode(b_01_, b_01_code_, buffer_size); 39 | config_reader_->SetGaborDirections(15); 40 | Encoder encoder1(config_reader_->GetEncoderConfig()); 41 | encoder1.Encode(b_02_, b_02_code_, buffer_size); 42 | } 43 | virtual void TearDown() { 44 | free(a_01_code_); 45 | free(a_02_code_); 46 | free(b_01_code_); 47 | free(b_02_code_); 48 | EdccTestFixtureBase::TearDown(); 49 | } 50 | 51 | protected: 52 | std::unique_ptr config_reader_; 53 | PalmprintCode* a_01_code_; 54 | PalmprintCode* a_02_code_; 55 | PalmprintCode* b_01_code_; 56 | PalmprintCode* b_02_code_; 57 | }; 58 | 59 | TEST_F(ComparerTestFixture, two_codes_have_differect_cfg) { 60 | double score = 0; 61 | auto status = Comparer::Compare(*a_01_code_, *b_02_code_, &score); 62 | EXPECT_EQ(status.code(), Status::kCodeCfgNEWhenComparing); 63 | } 64 | 65 | TEST_F(ComparerTestFixture, correctly_compare) { 66 | double score_a1a1 = 0, score_a1a2 = 0, score_a2a1 = 0, score_a1b1 = 0; 67 | auto status = Comparer::Compare(*a_01_code_, *a_01_code_, &score_a1a1); 68 | EXPECT_EQ(status.code(), Status::kOk); 69 | status = Comparer::Compare(*a_01_code_, *a_02_code_, &score_a1a2); 70 | EXPECT_EQ(status.code(), Status::kOk); 71 | status = Comparer::Compare(*a_02_code_, *a_01_code_, &score_a2a1); 72 | EXPECT_EQ(status.code(), Status::kOk); 73 | status = Comparer::Compare(*a_01_code_, *b_01_code_, &score_a1b1); 74 | EXPECT_EQ(status.code(), Status::kOk); 75 | EXPECT_EQ(score_a1a1, 1.0); 76 | EXPECT_GT(score_a1b1, 0.0); 77 | EXPECT_LT(score_a1b1, score_a2a1); 78 | EXPECT_EQ(score_a1a2, score_a2a1); 79 | } 80 | 81 | } // namespace -------------------------------------------------------------------------------- /pypackage/edcc/adapter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 leosocy. All rights reserved. 2 | # Use of this source code is governed by a MIT-style license 3 | # that can be found in the LICENSE file. 4 | 5 | import platform 6 | import ctypes 7 | 8 | from .exceptions import EdccExceptionBase 9 | from .config import EncoderConfig 10 | 11 | 12 | class EdccAdapter(object): 13 | """An adapter for c apis in edcc library.""" 14 | 15 | LIB_NAME = "libedcc.{suffix}".format( 16 | suffix="so" if platform.uname()[0] == "Linux" else "dylib" 17 | ) 18 | LIB_INSTALLATION_URL = ( 19 | "https://github.com/Leosocy/EDCC-Palmprint-Recognition#install-library" 20 | ) 21 | 22 | def __init__(self): 23 | try: 24 | self._lib = ctypes.cdll.LoadLibrary(self.LIB_NAME) 25 | except OSError: 26 | raise OSError( 27 | "Library [{:s}] not found.\nPlease see {:s}".format( 28 | self.LIB_NAME, self.LIB_INSTALLATION_URL 29 | ) 30 | ) 31 | 32 | # init edcc lib apis. 33 | self._lib.new_encoder_with_config.argtypes = [ 34 | ctypes.c_uint8, 35 | ctypes.c_uint8, 36 | ctypes.c_uint8, 37 | ctypes.c_uint8, 38 | ctypes.c_char_p, 39 | ] 40 | self._lib.new_encoder_with_config.restype = ctypes.c_int 41 | 42 | self._lib.get_size_of_code_buffer_required.argtypes = [ctypes.c_int] 43 | self._lib.get_size_of_code_buffer_required.restype = ctypes.c_ulong 44 | 45 | self._lib.encode_palmprint_using_bytes.argtypes = [ 46 | ctypes.c_int, 47 | ctypes.c_char_p, 48 | ctypes.c_ulong, 49 | ctypes.c_char_p, 50 | ctypes.c_ulong, 51 | ctypes.c_char_p, 52 | ] 53 | self._lib.encode_palmprint_using_bytes.restype = None 54 | 55 | self._lib.calculate_codes_similarity.argtypes = [ 56 | ctypes.c_char_p, 57 | ctypes.c_char_p, 58 | ctypes.c_char_p, 59 | ] 60 | self._lib.calculate_codes_similarity.restype = ctypes.c_double 61 | 62 | def new_encoder(self, config: EncoderConfig): 63 | """Create a new encoder and return it's id.""" 64 | status = ctypes.create_string_buffer(128) 65 | encoder_id = self._lib.new_encoder_with_config( 66 | config.image_size, 67 | config.gabor_kernel_size, 68 | config.laplace_kernel_size, 69 | config.gabor_directions, 70 | status, 71 | ) 72 | self._check_status(status) 73 | return encoder_id 74 | 75 | def do_encode(self, encoder_id, palmprint_bytes): 76 | """Encode palmprint image bytes to code bytes.""" 77 | status = ctypes.create_string_buffer(128) 78 | code_bytes_size = self._lib.get_size_of_code_buffer_required(encoder_id) 79 | code_bytes = ctypes.create_string_buffer(code_bytes_size) 80 | self._lib.encode_palmprint_using_bytes( 81 | encoder_id, 82 | palmprint_bytes, 83 | len(palmprint_bytes), 84 | code_bytes, 85 | code_bytes_size, 86 | status, 87 | ) 88 | self._check_status(status) 89 | return code_bytes 90 | 91 | def calc_score(self, lhs_code_bytes, rhs_code_bytes): 92 | """Calculate two codes similarity score.""" 93 | status = ctypes.create_string_buffer(128) 94 | score = self._lib.calculate_codes_similarity( 95 | lhs_code_bytes, rhs_code_bytes, status 96 | ) 97 | self._check_status(status) 98 | return score 99 | 100 | @staticmethod 101 | def _check_status(status): 102 | code = status.raw[0] 103 | if code != 0: 104 | raise EdccExceptionBase(errcode=code, errmsg=status.raw[1:].decode()) 105 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: Yes 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializersBeforeComma: false 45 | BreakConstructorInitializers: BeforeColon 46 | BreakAfterJavaFieldAnnotations: false 47 | BreakStringLiterals: true 48 | ColumnLimit: 120 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 52 | ConstructorInitializerIndentWidth: 4 53 | ContinuationIndentWidth: 4 54 | Cpp11BracedListStyle: true 55 | DerivePointerAlignment: true 56 | DisableFormat: false 57 | ExperimentalAutoDetectBinPacking: false 58 | FixNamespaceComments: true 59 | ForEachMacros: 60 | - foreach 61 | - Q_FOREACH 62 | - BOOST_FOREACH 63 | IncludeBlocks: Preserve 64 | IncludeCategories: 65 | - Regex: '^' 66 | Priority: 2 67 | - Regex: '^<.*\.h>' 68 | Priority: 1 69 | - Regex: '^<.*' 70 | Priority: 2 71 | - Regex: '.*' 72 | Priority: 3 73 | IncludeIsMainRegex: '([-_](test|unittest))?$' 74 | IndentCaseLabels: true 75 | IndentPPDirectives: None 76 | IndentWidth: 2 77 | IndentWrappedFunctionNames: false 78 | JavaScriptQuotes: Leave 79 | JavaScriptWrapImports: true 80 | KeepEmptyLinesAtTheStartOfBlocks: false 81 | MacroBlockBegin: '' 82 | MacroBlockEnd: '' 83 | MaxEmptyLinesToKeep: 1 84 | NamespaceIndentation: None 85 | ObjCBinPackProtocolList: Never 86 | ObjCBlockIndentWidth: 2 87 | ObjCSpaceAfterProperty: false 88 | ObjCSpaceBeforeProtocolList: true 89 | PenaltyBreakAssignment: 2 90 | PenaltyBreakBeforeFirstCallParameter: 1 91 | PenaltyBreakComment: 300 92 | PenaltyBreakFirstLessLess: 120 93 | PenaltyBreakString: 1000 94 | PenaltyBreakTemplateDeclaration: 10 95 | PenaltyExcessCharacter: 1000000 96 | PenaltyReturnTypeOnItsOwnLine: 200 97 | PointerAlignment: Left 98 | RawStringFormats: 99 | - Language: Cpp 100 | Delimiters: 101 | - cc 102 | - CC 103 | - cpp 104 | - Cpp 105 | - CPP 106 | - 'c++' 107 | - 'C++' 108 | CanonicalDelimiter: '' 109 | BasedOnStyle: google 110 | - Language: TextProto 111 | Delimiters: 112 | - pb 113 | - PB 114 | - proto 115 | - PROTO 116 | EnclosingFunctions: 117 | - EqualsProto 118 | - EquivToProto 119 | - PARSE_PARTIAL_TEXT_PROTO 120 | - PARSE_TEST_PROTO 121 | - PARSE_TEXT_PROTO 122 | - ParseTextOrDie 123 | - ParseTextProtoOrDie 124 | CanonicalDelimiter: '' 125 | BasedOnStyle: google 126 | ReflowComments: true 127 | SortIncludes: true 128 | SortUsingDeclarations: true 129 | SpaceAfterCStyleCast: false 130 | SpaceAfterTemplateKeyword: true 131 | SpaceBeforeAssignmentOperators: true 132 | SpaceBeforeCpp11BracedList: false 133 | SpaceBeforeCtorInitializerColon: true 134 | SpaceBeforeInheritanceColon: true 135 | SpaceBeforeParens: ControlStatements 136 | SpaceBeforeRangeBasedForLoopColon: true 137 | SpaceInEmptyParentheses: false 138 | SpacesBeforeTrailingComments: 2 139 | SpacesInAngles: false 140 | SpacesInContainerLiterals: true 141 | SpacesInCStyleCastParentheses: false 142 | SpacesInParentheses: false 143 | SpacesInSquareBrackets: false 144 | Standard: Auto 145 | StatementMacros: 146 | - Q_UNUSED 147 | - QT_REQUIRE_VERSION 148 | TabWidth: 8 149 | UseTab: Never 150 | ... 151 | 152 | -------------------------------------------------------------------------------- /manage.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | CurDir="$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)" 4 | 5 | ######## envs ######## 6 | 7 | DOCKER_REGISTRY=registry.cn-hangzhou.aliyuncs.com/leosocy 8 | OPENCV_CI_IMAGE=${DOCKER_REGISTRY}/opencv:ci 9 | CPPCHECK_CI_IMAGE=${DOCKER_REGISTRY}/cppcheck:1.83 10 | 11 | CACHED_IMAGES=(${OPENCV_CI_IMAGE} ${CPPCHECK_CI_IMAGE}) 12 | 13 | APP_TEST_NAME=test-edcc 14 | 15 | ######## functions ######## 16 | 17 | check_exec_success() { 18 | if [ "$1" != "0" ]; then 19 | echo "[ERROR] $2 failed!" 20 | exit 1 21 | else 22 | echo "[INFO] $2 success!" 23 | fi 24 | } 25 | 26 | 27 | test() { 28 | load_images ${OPENCV_CI_IMAGE} 29 | image_exist ${OPENCV_CI_IMAGE} 30 | if [ $? -ne 0 ]; then 31 | docker pull ${OPENCV_CI_IMAGE} > /dev/null 32 | fi 33 | check_exec_success "$?" "pulling ${OPENCV_CI_IMAGE} image" 34 | docker run -it --rm -v ${CurDir}:/app -w /app ${OPENCV_CI_IMAGE} /bin/sh -ec """ 35 | mkdir -p build_test; cd build_test; 36 | cmake .. -DEDCC_BUILD_TESTS=ON; make -j2 build_and_test; 37 | lcov -b . -d . -c -o cov_info.lst > /dev/null; 38 | lcov -r cov_info.lst \"/usr/*\" \"*/thirdparty/*\" \"*/test/*\" \"*/build_test/*\" -o cov_info.lst -q; 39 | lcov -l cov_info.lst; 40 | genhtml -o cov_result cov_info.lst > /dev/null; rm -rf ../cov_result; mv cov_result ..; 41 | echo "" 42 | echo "" 43 | echo \"==========Generated code coverage report under ./cov_result directory.==========\" 44 | """ 45 | check_exec_success "$?" "run test" 46 | } 47 | 48 | 49 | gdbtest() { 50 | load_images ${OPENCV_CI_IMAGE} 51 | image_exist ${OPENCV_CI_IMAGE} 52 | if [ $? -ne 0 ]; then 53 | docker pull ${OPENCV_CI_IMAGE} > /dev/null 54 | fi 55 | check_exec_success "$?" "pulling ${OPENCV_CI_IMAGE} image" 56 | docker run -it --rm -v ${CurDir}:/app -w /app \ 57 | --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ 58 | ${OPENCV_CI_IMAGE} /bin/sh -ec """ 59 | mkdir -p build_test; cd build_test; 60 | cmake .. -DEDCC_BUILD_TESTS=ON; make -j2; gdb ${APP_TEST_NAME} 61 | """ 62 | check_exec_success "$?" "gdb test" 63 | } 64 | 65 | 66 | lint() { 67 | load_images ${CPPCHECK_CI_IMAGE} 68 | if [ $? -ne 0 ]; then 69 | docker pull ${CPPCHECK_CI_IMAGE} > /dev/null 70 | fi 71 | check_exec_success "$?" "pulling ${CPPCHECK_CI_IMAGE} image" 72 | docker run -it --rm -v ${CurDir}:/app -w /app ${CPPCHECK_CI_IMAGE} /bin/sh -ec """ 73 | cppcheck --enable=warning,performance --error-exitcode=1 include src 74 | """ 75 | check_exec_success "$?" "run lint" 76 | } 77 | 78 | 79 | test_and_lint() { 80 | test 81 | lint 82 | } 83 | 84 | 85 | runenv() { 86 | load_images ${OPENCV_CI_IMAGE} 87 | image_exist ${OPENCV_CI_IMAGE} 88 | if [ $? -ne 0 ]; then 89 | docker pull ${OPENCV_CI_IMAGE} > /dev/null 90 | fi 91 | check_exec_success "$?" "pulling ${OPENCV_CI_IMAGE} image" 92 | docker run -it --rm -v ${CurDir}:/app -w /app ${OPENCV_CI_IMAGE} /bin/sh -ec """ 93 | mkdir -p build_install; cd build_install; 94 | cmake .. -DEDCC_BUILD_TESTS=ON; make install; 95 | cd ../pypackage; python setup.py install; cd .. 96 | bash 97 | """ 98 | } 99 | 100 | ######## below functions are used for travis-ci ######## 101 | 102 | upload_codecov() { 103 | if [ -z ${CODECOV_TOKEN} ]; then 104 | echo "Please set CODECOV_TOKEN value" 105 | exit 1 106 | fi 107 | docker run -it --rm -v ${CurDir}:/app -w /app/build_test \ 108 | -e CODECOV_TOKEN=${CODECOV_TOKEN} \ 109 | ${OPENCV_CI_IMAGE} /bin/bash -ec "$(curl -s https://codecov.io/bash)" 110 | check_exec_success "$?" "upload codecov" 111 | } 112 | 113 | image_exist() { 114 | docker images -a --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}' | grep $1 &> /dev/null 115 | return $? 116 | } 117 | 118 | save_images() { 119 | for image in "${CACHED_IMAGES[@]}"; do 120 | image_exist ${image} 121 | if [ $? -eq 0 ]; then 122 | mkdir -p ${HOME}/docker && docker images -a \ 123 | --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}' | grep ${image} \ 124 | | xargs -n 2 -t sh -c 'test -e $HOME/docker/$1.tar.gz || docker save $0 | gzip -2 > ${HOME}/docker/$1.tar.gz' 125 | fi 126 | done 127 | } 128 | 129 | load_images() { 130 | image_exist $1 131 | if [ $? -eq 0 ]; then 132 | return 0 133 | fi 134 | if [[ -d ${HOME}/docker ]]; then 135 | ls ${HOME}/docker/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load"; 136 | fi 137 | } 138 | 139 | ######## script start ######## 140 | 141 | case "$1" in 142 | test) test ;; 143 | gdbtest) gdbtest ;; 144 | lint) lint ;; 145 | test_and_lint) test_and_lint ;; 146 | env) runenv ;; 147 | upload_codecov) upload_codecov ;; 148 | save_images) save_images ;; 149 | load_images) load_images $2 ;; 150 | *) 151 | echo "Usage:" 152 | echo "./manage.sh test | gdbtest" 153 | echo "./manage.sh lint" 154 | echo "./manage.sh test_and_lint" 155 | echo "./manage.sh env" 156 | exit 1 157 | ;; 158 | esac 159 | 160 | exit 0 161 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ######################## 2 | # EDCC library cmake # 3 | ######################## 4 | 5 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 6 | 7 | PROJECT(EDCC) 8 | SET(CMAKE_MACOSX_RPATH 1) 9 | SET(PROJECT_ROOT ${CMAKE_SOURCE_DIR}) 10 | SET(LIBRARY_NAME edcc) 11 | 12 | SET(${PROJECT_NAME}_VERSION_MAJOR "0") 13 | SET(${PROJECT_NAME}_VERSION_MINOR "2") 14 | SET(${PROJECT_NAME}_VERSION_PATCH "1") 15 | SET(${PROJECT_NAME}_VERSION 16 | "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}") 17 | 18 | 19 | # Project Options 20 | OPTION(${PROJECT_NAME}_BUILD_TESTS "Enable testing" OFF) 21 | OPTION(${PROJECT_NAME}_BUILD_DEV "Enable developing" OFF) 22 | OPTION(${PROJECT_NAME}_BUILD_SHARED_LIBS "Build Shared Libraries" ON) 23 | 24 | 25 | # Required libraries 26 | FIND_PACKAGE(OpenCV REQUIRED) 27 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 28 | 29 | IF(${PROJECT_NAME}_BUILD_TESTS) 30 | SET(GCC_COVERAGE_COMPILE_FLAGS "-ggdb -coverage -fprofile-arcs -ftest-coverage") 31 | SET(GCC_COVERAGE_LINK_FLAGS "-coverage -lgcov") 32 | ENDIF() 33 | SET(CMAKE_CXX_FLAGS "--std=c++11 ${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}") 34 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}") 35 | SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) 36 | 37 | 38 | # Sources, headers, directories and libs 39 | SET(HEADER_DIRECTORY "include/edcc/") 40 | 41 | FILE(GLOB_RECURSE SRC_FILES "src/[a-zA-Z_]*.cpp") 42 | FILE(GLOB_RECURSE PUBLIC_HEADER_FILES "${HEADER_DIRECTORY}[a-zA-Z_]*.h") 43 | FILE(GLOB_RECURSE PRIVATE_HEADER_FILES "src/[a-zA-Z_]*.h") 44 | 45 | SET(LIBRARY_SOURCES 46 | ${SRC_FILES} 47 | ${PUBLIC_HEADER_FILES} 48 | ${PRIVATE_HEADER_FILES} 49 | ) 50 | 51 | IF (CMAKE_VERSION VERSION_LESS 2.8.12) 52 | INCLUDE_DIRECTORIES(${PROJECT_ROOT}/include) 53 | INCLUDE_DIRECTORIES(${PROJECT_ROOT}/src) 54 | ENDIF() 55 | 56 | 57 | # General compilation settings 58 | SET(${PROJECT_NAME}_C_FLAGS ${CMAKE_C_FLAGS}) 59 | SET(${PROJECT_NAME}_CXX_FLAGS ${CMAKE_CXX_FLAGS}) 60 | 61 | IF(${PROJECT_NAME}_BUILD_SHARED_LIBS) 62 | SET(LABEL_SUFFIX "SHARED") 63 | ELSE() 64 | SET(LABEL_SUFFIX "STATIC") 65 | ENDIF() 66 | 67 | 68 | # General install settings 69 | SET(INCLUDE_INSTALL_ROOT_DIR include) 70 | 71 | SET(INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_ROOT_DIR}/${LIBRARY_NAME}) 72 | SET(LIB_INSTALL_DIR "lib${LIB_SUFFIX}") 73 | 74 | SET(_INSTALL_DESTINATIONS 75 | RUNTIME DESTINATION bin 76 | LIBRARY DESTINATION ${LIB_INSTALL_DIR} 77 | ARCHIVE DESTINATION ${LIB_INSTALL_DIR} 78 | ) 79 | 80 | 81 | # Library 82 | ADD_LIBRARY(${LIBRARY_NAME} ${LABEL_SUFFIX} ${LIBRARY_SOURCES}) 83 | SET(LINK_LIBRARIES ${OpenCV_LIBS}) 84 | TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${LINK_LIBRARIES}) 85 | 86 | IF(NOT CMAKE_VERSION VERSION_LESS 2.8.12) 87 | TARGET_INCLUDE_DIRECTORIES(${LIBRARY_NAME} 88 | PUBLIC $ 89 | $ 90 | PRIVATE $ 91 | ) 92 | ENDIF() 93 | 94 | SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES 95 | COMPILE_FLAGS "${${PROJECT_NAME}_C_FLAGS} ${${PROJECT_NAME}_CXX_FLAGS}" 96 | ) 97 | 98 | SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES 99 | VERSION "${${PROJECT_NAME}_VERSION}" 100 | SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}" 101 | PROJECT_LABEL "${LIBRARY_NAME} ${LABEL_SUFFIX}" 102 | ) 103 | 104 | INSTALL(TARGETS ${LIBRARY_NAME} EXPORT ${LIBRARY_NAME}-targets ${_INSTALL_DESTINATIONS}) 105 | INSTALL( 106 | DIRECTORY ${HEADER_DIRECTORY} 107 | DESTINATION ${INCLUDE_INSTALL_DIR} 108 | FILES_MATCHING PATTERN "*.h" 109 | ) 110 | 111 | EXPORT( 112 | TARGETS ${LIBRARY_NAME} 113 | FILE "${PROJECT_BINARY_DIR}/${LIBRARY_NAME}-targets.cmake" 114 | ) 115 | EXPORT(PACKAGE EDCC) # EDCCConfig.cmake 116 | SET(EXPORT_TARGETS ${LIBRARY_NAME} CACHE INTERNAL "export targets") 117 | 118 | SET(CONFIG_INCLUDE_DIRS "${PROJECT_ROOT}/include") 119 | CONFIGURE_FILE(${PROJECT_ROOT}/${LIBRARY_NAME}-config.cmake.in 120 | "${PROJECT_BINARY_DIR}/${LIBRARY_NAME}.cmake" @ONLY) 121 | 122 | SET(INSTALL_CMAKE_DIR ${LIB_INSTALL_DIR}/cmake/${LIBRARY_NAME}) 123 | 124 | FILE(RELATIVE_PATH REL_INCLUDE_DIR 125 | "${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}" 126 | "${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_ROOT_DIR}") 127 | SET(CONFIG_INCLUDE_DIRS "\${${PROJECT_NAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}") 128 | CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${LIBRARY_NAME}-config.cmake.in 129 | "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LIBRARY_NAME}-config.cmake" @ONLY) 130 | 131 | CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${LIBRARY_NAME}-config-version.cmake.in 132 | "${PROJECT_BINARY_DIR}/${LIBRARY_NAME}-config-version.cmake" @ONLY) 133 | 134 | INSTALL(FILES 135 | "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LIBRARY_NAME}-config.cmake" 136 | "${PROJECT_BINARY_DIR}/${LIBRARY_NAME}-config-version.cmake" 137 | DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) 138 | INSTALL(EXPORT ${LIBRARY_NAME}-targets DESTINATION ${INSTALL_CMAKE_DIR}) 139 | 140 | 141 | # Extras 142 | IF(${PROJECT_NAME}_BUILD_TESTS) 143 | ENABLE_TESTING() 144 | ADD_SUBDIRECTORY(test) 145 | ELSEIF(${PROJECT_NAME}_BUILD_DEV) 146 | ADD_SUBDIRECTORY(test/) 147 | ADD_SUBDIRECTORY(examples/c_example) 148 | ADD_SUBDIRECTORY(examples/cpp_example) 149 | ENDIF() 150 | 151 | 152 | # Formatting 153 | ADD_CUSTOM_TARGET(fmt 154 | COMMAND clang-format --style=file -i ${LIBRARY_SOURCES} 155 | COMMENT "Running clang-format" 156 | VERBATIM 157 | ) 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EDCC: An efficient and accurate algorithm for palmprint-recognition 2 | 3 | [![Build Status](https://travis-ci.org/Leosocy/EDCC-Palmprint-Recognition.svg?branch=master)](https://travis-ci.org/Leosocy/EDCC-Palmprint-Recognition) 4 | [![codecov](https://codecov.io/gh/Leosocy/EDCC-Palmprint-Recognition/branch/master/graph/badge.svg)](https://codecov.io/gh/Leosocy/EDCC-Palmprint-Recognition) 5 | [![MIT licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://raw.githubusercontent.com/Leosocy/EDCC-Palmprint-Recognition/master/LICENSE) 6 | 7 | `EDCC(Enhanced and Discriminative Competitive Code)`, which is used for palmprint-recognition. 8 | 9 | Use the `EDCC` algorithm with [default config](https://github.com/Leosocy/EDCC-Palmprint-Recognition/blob/master/pypackage/edcc/config.py#L18) to validate on several published palmprint databases(`multispectral`, `tongji`), the first `N(N = 2, 4, 6, 8)` palmprint images of each palm are employed as training samples and the remaining palmprint images form the test sample set. Each sample in the test sample set is compared with all samples of each class in the training set to calculate the matching score. The class that produces the highest matching score is treated as the class of the test sample. 10 | 11 | | Database | N=2 | N=4 | N=6 | N=8 | 12 | | :---------------: | :------: | :------: | :------: | :------: | 13 | | `Multispectral_B` | 98.6800% | 99.8750% | 99.9667% | 99.9800% | 14 | | `Multispectral_G` | 98.8400% | 99.8500% | 99.9333% | 99.9500% | 15 | | `Multispectral_I` | 98.9200% | 99.9000% | 99.9000% | 99.9000% | 16 | | `Multispectral_R` | 98.8400% | 99.7500% | 99.8667% | 99.9000% | 17 | | `Tongji` | 98.8056% | 99.6979% | 99.9881% | 99.9861% | 18 | 19 | Advantages of `EDCC` algorithm: 20 | 21 | 1. **Less training samples.** 22 | 1. **Faster recognition speed.** 23 | 1. **Higher recognition accuracy.** 24 | 25 | [**More details about `EDCC`**](https://blog.leosocy.top/posts/4354/) 26 | 27 | ## Installation 28 | 29 | ### Install library 30 | 31 | There are ***some requirements*** if you want to install `EDCC` library: 32 | 33 | - OS *nix like. 34 | - [`OpenCV 3.4`](https://docs.opencv.org/3.4/d7/d9f/tutorial_linux_install.html) or [`OpenCV 4.5`](https://docs.opencv.org/4.5.1/d7/d9f/tutorial_linux_install.html) installed. 35 | 36 | *Steps:* 37 | 38 | 1. `git clone https://github.com/Leosocy/EDCC-Palmprint-Recognition.git` 39 | 2. `cd EDCC-Palmprint-Recognition && mkdir -p build && cd build` 40 | 3. `cmake .. && sudo make install` 41 | 42 | ### Install Python Package 43 | 44 | Please make sure that the ***edcc library has been successfully installed*** by following the steps above. 45 | 46 | **Python3.x** required. 47 | 48 | *Steps:* 49 | 50 | 1. `cd pypackage` 51 | 2. `python setup.py install` 52 | 53 | ## QuickStart 54 | 55 | The project provides a Docker container runtime environment with edcc library and python package installed. 56 | 57 | You can quick start accord to the following commands: 58 | 59 | ```shell 60 | # bootstrap a docker container with edcc library installed 61 | ./manage.sh env 62 | 63 | # run c example 64 | cd /app/examples/c_example && mkdir -p build && cd build && cmake .. && make && ./run_c_sample 65 | 66 | # run cpp example 67 | cd /app/examples/cpp_example && mkdir -p build && cd build && cmake .. && make && ./run_cpp_sample 68 | 69 | # run python example 70 | cd /app/examples/py_example && python example.py 71 | ``` 72 | 73 | ## Usage 74 | 75 | Make sure you have installed [library](#install-library) and [Python package](#install-python-package) before using edcc. 76 | 77 | And you can see more usage details under [examples](./examples) directory about usage. 78 | 79 | ### C/C++ 80 | 81 | In your CMakeLists.txt, add these lines: 82 | 83 | ```cmake 84 | find_package(edcc REQUIRED) 85 | include_directories(${EDCC_INCLUDE_DIR}) 86 | ... 87 | add_dependencies(${YOUR_PROJECT} ${EDCC_LIBRARIES}) 88 | target_link_libraries(${YOUR_PROJECT} ${EDCC_LIBRARIES}) 89 | ``` 90 | 91 | Then you can use it in your source code(C or C++) like this: 92 | 93 | C 94 | 95 | ```c 96 | #include 97 | 98 | #define ASSERT_STATUS_OK(s) \ 99 | do { \ 100 | if (s[0] != '\0') { \ 101 | perror(s + 1); \ 102 | return -1; \ 103 | } \ 104 | } while (0) 105 | 106 | int main() { 107 | // create a new encoder. 108 | char status[128]; 109 | int encoder_id = new_encoder_with_config(29, 5, 5, 10, status); 110 | ASSERT_STATUS_OK(status); 111 | // encode palmprints to code buffer. 112 | unsigned long buffer_size = get_size_of_code_buffer_required(encoder_id); 113 | char* code_buffer_one = (char*)malloc(buffer_size); 114 | char* code_buffer_another = (char*)malloc(buffer_size); 115 | encode_palmprint_using_file(encoder_id, one_image_file_path, code_buffer_one, buffer_size, status); 116 | ASSERT_STATUS_OK(status); 117 | encode_palmprint_using_file(encoder_id, another_image_file_path, code_buffer_another, buffer_size, status); 118 | ASSERT_STATUS_OK(status); 119 | // calculate the similarity score of two codes. 120 | double score = calculate_codes_similarity(code_buffer_one, code_buffer_another, status); 121 | ASSERT_STATUS_OK(status); 122 | return 0; 123 | } 124 | ``` 125 | 126 | C++ 127 | 128 | ```c++ 129 | #include 130 | #include 131 | 132 | #define ASSERT_STATUS_OK(s) \ 133 | do { \ 134 | if (!s.IsOk()) { \ 135 | perror(s.msg()); \ 136 | return -1; \ 137 | } \ 138 | } while (0) 139 | 140 | using edcc::EdccFacade; 141 | using edcc::Status; 142 | 143 | int main() { 144 | Status s; 145 | // create a new encoder. 146 | auto inst = EdccFacade::Instance(); 147 | auto encoder_id = inst->NewEncoderWithConfig(29, 5, 5, 10, &s); 148 | ASSERT_STATUS_OK(s); 149 | // encode palmprints to code buffer. 150 | size_t buffer_size = inst->GetSizeOfCodeBufferRequired(encoder_id); 151 | char* code_buffer_one = new char[buffer_size]; 152 | char* code_buffer_another = new char[buffer_size]; 153 | inst->EncodePalmprint(encoder_id, one_image_file_path, code_buffer_one, buffer_size, &s); 154 | ASSERT_STATUS_OK(s); 155 | inst->EncodePalmprint(encoder_id, another_image_file_path, code_buffer_another, buffer_size, &s); 156 | ASSERT_STATUS_OK(s); 157 | // calculate the similarity score of two codes. 158 | double score = inst->CalcCodeSimilarity(code_buffer_one, code_buffer_another, &s); 159 | ASSERT_STATUS_OK(s); 160 | return 0; 161 | } 162 | ``` 163 | 164 | ### Python 165 | 166 | ```Python 167 | import edcc 168 | 169 | config = edcc.EncoderConfig(29, 5, 5 ,10) 170 | encoder = edcc.create_encoder(config) 171 | one_palmprint_code = encoder.encode_using_filename("./palmprint_one.bmp") 172 | another_palmprint_code = encoder.encode_using_filename("./palmprint_another.bmp") 173 | similarity_score = one_palmprint_code.compare_to(another_palmprint_code) 174 | ``` 175 | 176 | ## Contributing 177 | 178 | Please see [CONTRIBUTING.md](./CONTRIBUTING.md) 179 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/c,c++,cmake,python,visualstudio,visualstudiocode 3 | # Edit at https://www.gitignore.io/?templates=c,c++,cmake,python,visualstudio,visualstudiocode 4 | 5 | **/.DS_Store 6 | cov_result 7 | build* 8 | **/*.bmp 9 | **/*.jpg 10 | .vscode 11 | .idea/* 12 | **/cmake-build-debug 13 | 14 | ### C ### 15 | # Prerequisites 16 | *.d 17 | 18 | # Object files 19 | *.o 20 | *.ko 21 | *.obj 22 | *.elf 23 | 24 | # Linker output 25 | *.ilk 26 | *.map 27 | *.exp 28 | 29 | # Precompiled Headers 30 | *.gch 31 | *.pch 32 | 33 | # Libraries 34 | *.lib 35 | *.a 36 | *.la 37 | *.lo 38 | 39 | # Shared objects (inc. Windows DLLs) 40 | *.dll 41 | *.so 42 | *.so.* 43 | *.dylib 44 | 45 | # Executables 46 | *.exe 47 | *.out 48 | *.app 49 | *.i*86 50 | *.x86_64 51 | *.hex 52 | 53 | # Debug files 54 | *.dSYM/ 55 | *.su 56 | *.idb 57 | *.pdb 58 | 59 | # Kernel Module Compile Results 60 | *.mod* 61 | *.cmd 62 | .tmp_versions/ 63 | modules.order 64 | Module.symvers 65 | Mkfile.old 66 | dkms.conf 67 | 68 | ### C++ ### 69 | # Prerequisites 70 | 71 | # Compiled Object files 72 | *.slo 73 | 74 | # Precompiled Headers 75 | 76 | # Compiled Dynamic libraries 77 | 78 | # Fortran module files 79 | *.mod 80 | *.smod 81 | 82 | # Compiled Static libraries 83 | *.lai 84 | 85 | # Executables 86 | 87 | ### CMake ### 88 | CMakeLists.txt.user 89 | CMakeCache.txt 90 | CMakeFiles 91 | CMakeScripts 92 | Testing 93 | Makefile 94 | cmake_install.cmake 95 | install_manifest.txt 96 | compile_commands.json 97 | CTestTestfile.cmake 98 | _deps 99 | 100 | ### CMake Patch ### 101 | # External projects 102 | *-prefix/ 103 | 104 | ### Python ### 105 | # Byte-compiled / optimized / DLL files 106 | __pycache__/ 107 | *.py[cod] 108 | *$py.class 109 | 110 | # C extensions 111 | 112 | # Distribution / packaging 113 | .Python 114 | build/ 115 | develop-eggs/ 116 | dist/ 117 | downloads/ 118 | eggs/ 119 | .eggs/ 120 | lib/ 121 | lib64/ 122 | parts/ 123 | sdist/ 124 | var/ 125 | wheels/ 126 | pip-wheel-metadata/ 127 | share/python-wheels/ 128 | *.egg-info/ 129 | .installed.cfg 130 | *.egg 131 | MANIFEST 132 | 133 | # PyInstaller 134 | # Usually these files are written by a python script from a template 135 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 136 | *.manifest 137 | *.spec 138 | 139 | # Installer logs 140 | pip-log.txt 141 | pip-delete-this-directory.txt 142 | 143 | # Unit test / coverage reports 144 | htmlcov/ 145 | .tox/ 146 | .nox/ 147 | .coverage 148 | .coverage.* 149 | .cache 150 | nosetests.xml 151 | coverage.xml 152 | *.cover 153 | .hypothesis/ 154 | .pytest_cache/ 155 | 156 | # Translations 157 | *.mo 158 | *.pot 159 | 160 | # Django stuff: 161 | *.log 162 | local_settings.py 163 | db.sqlite3 164 | 165 | # Flask stuff: 166 | instance/ 167 | .webassets-cache 168 | 169 | # Scrapy stuff: 170 | .scrapy 171 | 172 | # Sphinx documentation 173 | docs/_build/ 174 | 175 | # PyBuilder 176 | target/ 177 | 178 | # Jupyter Notebook 179 | .ipynb_checkpoints 180 | 181 | # IPython 182 | profile_default/ 183 | ipython_config.py 184 | 185 | # pyenv 186 | .python-version 187 | 188 | # pipenv 189 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 190 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 191 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 192 | # install all needed dependencies. 193 | #Pipfile.lock 194 | 195 | # celery beat schedule file 196 | celerybeat-schedule 197 | 198 | # SageMath parsed files 199 | *.sage.py 200 | 201 | # Environments 202 | .env 203 | .venv 204 | env/ 205 | venv/ 206 | ENV/ 207 | env.bak/ 208 | venv.bak/ 209 | 210 | # Spyder project settings 211 | .spyderproject 212 | .spyproject 213 | 214 | # Rope project settings 215 | .ropeproject 216 | 217 | # mkdocs documentation 218 | /site 219 | 220 | # mypy 221 | .mypy_cache/ 222 | .dmypy.json 223 | dmypy.json 224 | 225 | # Pyre type checker 226 | .pyre/ 227 | 228 | ### VisualStudioCode ### 229 | .vscode/* 230 | !.vscode/settings.json 231 | !.vscode/tasks.json 232 | !.vscode/launch.json 233 | !.vscode/extensions.json 234 | 235 | ### VisualStudioCode Patch ### 236 | # Ignore all local history of files 237 | .history 238 | 239 | ### VisualStudio ### 240 | ## Ignore Visual Studio temporary files, build results, and 241 | ## files generated by popular Visual Studio add-ons. 242 | ## 243 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 244 | 245 | # User-specific files 246 | *.rsuser 247 | *.suo 248 | *.user 249 | *.userosscache 250 | *.sln.docstates 251 | 252 | # User-specific files (MonoDevelop/Xamarin Studio) 253 | *.userprefs 254 | 255 | # Mono auto generated files 256 | mono_crash.* 257 | 258 | # Build results 259 | [Dd]ebug/ 260 | [Dd]ebugPublic/ 261 | [Rr]elease/ 262 | [Rr]eleases/ 263 | x64/ 264 | x86/ 265 | [Aa][Rr][Mm]/ 266 | [Aa][Rr][Mm]64/ 267 | bld/ 268 | [Bb]in/ 269 | [Oo]bj/ 270 | [Ll]og/ 271 | 272 | # Visual Studio 2015/2017 cache/options directory 273 | .vs/ 274 | # Uncomment if you have tasks that create the project's static files in wwwroot 275 | #wwwroot/ 276 | 277 | # Visual Studio 2017 auto generated files 278 | Generated\ Files/ 279 | 280 | # MSTest test Results 281 | [Tt]est[Rr]esult*/ 282 | [Bb]uild[Ll]og.* 283 | 284 | # NUNIT 285 | *.VisualState.xml 286 | TestResult.xml 287 | 288 | # Build Results of an ATL Project 289 | [Dd]ebugPS/ 290 | [Rr]eleasePS/ 291 | dlldata.c 292 | 293 | # Benchmark Results 294 | BenchmarkDotNet.Artifacts/ 295 | 296 | # .NET Core 297 | project.lock.json 298 | project.fragment.lock.json 299 | artifacts/ 300 | 301 | # StyleCop 302 | StyleCopReport.xml 303 | 304 | # Files built by Visual Studio 305 | *_i.c 306 | *_p.c 307 | *_h.h 308 | *.meta 309 | *.iobj 310 | *.ipdb 311 | *.pgc 312 | *.pgd 313 | *.rsp 314 | *.sbr 315 | *.tlb 316 | *.tli 317 | *.tlh 318 | *.tmp 319 | *.tmp_proj 320 | *_wpftmp.csproj 321 | *.vspscc 322 | *.vssscc 323 | .builds 324 | *.pidb 325 | *.svclog 326 | *.scc 327 | 328 | # Chutzpah Test files 329 | _Chutzpah* 330 | 331 | # Visual C++ cache files 332 | ipch/ 333 | *.aps 334 | *.ncb 335 | *.opendb 336 | *.opensdf 337 | *.sdf 338 | *.cachefile 339 | *.VC.db 340 | *.VC.VC.opendb 341 | 342 | # Visual Studio profiler 343 | *.psess 344 | *.vsp 345 | *.vspx 346 | *.sap 347 | 348 | # Visual Studio Trace Files 349 | *.e2e 350 | 351 | # TFS 2012 Local Workspace 352 | $tf/ 353 | 354 | # Guidance Automation Toolkit 355 | *.gpState 356 | 357 | # ReSharper is a .NET coding add-in 358 | _ReSharper*/ 359 | *.[Rr]e[Ss]harper 360 | *.DotSettings.user 361 | 362 | # JustCode is a .NET coding add-in 363 | .JustCode 364 | 365 | # TeamCity is a build add-in 366 | _TeamCity* 367 | 368 | # DotCover is a Code Coverage Tool 369 | *.dotCover 370 | 371 | # AxoCover is a Code Coverage Tool 372 | .axoCover/* 373 | !.axoCover/settings.json 374 | 375 | # Visual Studio code coverage results 376 | *.coverage 377 | *.coveragexml 378 | 379 | # NCrunch 380 | _NCrunch_* 381 | .*crunch*.local.xml 382 | nCrunchTemp_* 383 | 384 | # MightyMoose 385 | *.mm.* 386 | AutoTest.Net/ 387 | 388 | # Web workbench (sass) 389 | .sass-cache/ 390 | 391 | # Installshield output folder 392 | [Ee]xpress/ 393 | 394 | # DocProject is a documentation generator add-in 395 | DocProject/buildhelp/ 396 | DocProject/Help/*.HxT 397 | DocProject/Help/*.HxC 398 | DocProject/Help/*.hhc 399 | DocProject/Help/*.hhk 400 | DocProject/Help/*.hhp 401 | DocProject/Help/Html2 402 | DocProject/Help/html 403 | 404 | # Click-Once directory 405 | publish/ 406 | 407 | # Publish Web Output 408 | *.[Pp]ublish.xml 409 | *.azurePubxml 410 | # Note: Comment the next line if you want to checkin your web deploy settings, 411 | # but database connection strings (with potential passwords) will be unencrypted 412 | *.pubxml 413 | *.publishproj 414 | 415 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 416 | # checkin your Azure Web App publish settings, but sensitive information contained 417 | # in these scripts will be unencrypted 418 | PublishScripts/ 419 | 420 | # NuGet Packages 421 | *.nupkg 422 | # The packages folder can be ignored because of Package Restore 423 | **/[Pp]ackages/* 424 | # except build/, which is used as an MSBuild target. 425 | !**/[Pp]ackages/build/ 426 | # Uncomment if necessary however generally it will be regenerated when needed 427 | #!**/[Pp]ackages/repositories.config 428 | # NuGet v3's project.json files produces more ignorable files 429 | *.nuget.props 430 | *.nuget.targets 431 | 432 | # Microsoft Azure Build Output 433 | csx/ 434 | *.build.csdef 435 | 436 | # Microsoft Azure Emulator 437 | ecf/ 438 | rcf/ 439 | 440 | # Windows Store app package directories and files 441 | AppPackages/ 442 | BundleArtifacts/ 443 | Package.StoreAssociation.xml 444 | _pkginfo.txt 445 | *.appx 446 | *.appxbundle 447 | *.appxupload 448 | 449 | # Visual Studio cache files 450 | # files ending in .cache can be ignored 451 | *.[Cc]ache 452 | # but keep track of directories ending in .cache 453 | !?*.[Cc]ache/ 454 | 455 | # Others 456 | ClientBin/ 457 | ~$* 458 | *~ 459 | *.dbmdl 460 | *.dbproj.schemaview 461 | *.jfm 462 | *.pfx 463 | *.publishsettings 464 | orleans.codegen.cs 465 | 466 | # Including strong name files can present a security risk 467 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 468 | #*.snk 469 | 470 | # Since there are multiple workflows, uncomment next line to ignore bower_components 471 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 472 | #bower_components/ 473 | 474 | # RIA/Silverlight projects 475 | Generated_Code/ 476 | 477 | # Backup & report files from converting an old project file 478 | # to a newer Visual Studio version. Backup files are not needed, 479 | # because we have git ;-) 480 | _UpgradeReport_Files/ 481 | Backup*/ 482 | UpgradeLog*.XML 483 | UpgradeLog*.htm 484 | ServiceFabricBackup/ 485 | *.rptproj.bak 486 | 487 | # SQL Server files 488 | *.mdf 489 | *.ldf 490 | *.ndf 491 | 492 | # Business Intelligence projects 493 | *.rdl.data 494 | *.bim.layout 495 | *.bim_*.settings 496 | *.rptproj.rsuser 497 | *- Backup*.rdl 498 | 499 | # Microsoft Fakes 500 | FakesAssemblies/ 501 | 502 | # GhostDoc plugin setting file 503 | *.GhostDoc.xml 504 | 505 | # Node.js Tools for Visual Studio 506 | .ntvs_analysis.dat 507 | node_modules/ 508 | 509 | # Visual Studio 6 build log 510 | *.plg 511 | 512 | # Visual Studio 6 workspace options file 513 | *.opt 514 | 515 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 516 | *.vbw 517 | 518 | # Visual Studio LightSwitch build output 519 | **/*.HTMLClient/GeneratedArtifacts 520 | **/*.DesktopClient/GeneratedArtifacts 521 | **/*.DesktopClient/ModelManifest.xml 522 | **/*.Server/GeneratedArtifacts 523 | **/*.Server/ModelManifest.xml 524 | _Pvt_Extensions 525 | 526 | # Paket dependency manager 527 | .paket/paket.exe 528 | paket-files/ 529 | 530 | # FAKE - F# Make 531 | .fake/ 532 | 533 | # CodeRush personal settings 534 | .cr/personal 535 | 536 | # Python Tools for Visual Studio (PTVS) 537 | *.pyc 538 | 539 | # Cake - Uncomment if you are using it 540 | # tools/** 541 | # !tools/packages.config 542 | 543 | # Tabs Studio 544 | *.tss 545 | 546 | # Telerik's JustMock configuration file 547 | *.jmconfig 548 | 549 | # BizTalk build output 550 | *.btp.cs 551 | *.btm.cs 552 | *.odx.cs 553 | *.xsd.cs 554 | 555 | # OpenCover UI analysis results 556 | OpenCover/ 557 | 558 | # Azure Stream Analytics local run output 559 | ASALocalRun/ 560 | 561 | # MSBuild Binary and Structured Log 562 | *.binlog 563 | 564 | # NVidia Nsight GPU debugger configuration file 565 | *.nvuser 566 | 567 | # MFractors (Xamarin productivity tool) working folder 568 | .mfractor/ 569 | 570 | # Local History for Visual Studio 571 | .localhistory/ 572 | 573 | # BeatPulse healthcheck temp database 574 | healthchecksdb 575 | 576 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 577 | MigrationBackup/ 578 | 579 | # End of https://www.gitignore.io/api/c,c++,cmake,python,visualstudio,visualstudiocode --------------------------------------------------------------------------------