From f8a499e029f0c19fa73ab9491311dd6e16ff46d3 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Wed, 14 Dec 2016 16:44:37 +0800 Subject: [PATCH 1/5] Data transformer using OpenCV and multi-threads. --- .clang-format | 28 +++++ .gitignore | 1 + .pre-commit-config.yaml | 23 ++++ CMakeLists.txt | 17 +++ cmake/FindGlog.cmake | 24 ++++ cmake/FindNumPy.cmake | 38 ++++++ cmake/flags.cmake | 77 ++++++++++++ transformer/CMakeLists.txt | 29 +++++ transformer/DataTransformer.cpp | 216 ++++++++++++++++++++++++++++++++ transformer/DataTransformer.h | 148 ++++++++++++++++++++++ transformer/PyDecodejpeg.cpp | 176 ++++++++++++++++++++++++++ transformer/Queue.h | 88 +++++++++++++ transformer/ThreadPool.h | 108 ++++++++++++++++ transformer/main.cpp | 46 +++++++ 14 files changed, 1019 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 CMakeLists.txt create mode 100644 cmake/FindGlog.cmake create mode 100644 cmake/FindNumPy.cmake create mode 100644 cmake/flags.cmake create mode 100644 transformer/CMakeLists.txt create mode 100644 transformer/DataTransformer.cpp create mode 100644 transformer/DataTransformer.h create mode 100644 transformer/PyDecodejpeg.cpp create mode 100644 transformer/Queue.h create mode 100644 transformer/ThreadPool.h create mode 100644 transformer/main.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9ba433b --- /dev/null +++ b/.clang-format @@ -0,0 +1,28 @@ +# This file is used by clang-format to autoformat paddle source code +# +# The clang-format is part of llvm toolchain. +# It need to install llvm and clang to format source code style. +# +# The basic usage is, +# clang-format -i -style=file PATH/TO/SOURCE/CODE +# +# The -style=file implicit use ".clang-format" file located in one of +# parent directory. +# The -i means inplace change. +# +# The document of clang-format is +# http://clang.llvm.org/docs/ClangFormat.html +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +--- +Language: Cpp +BasedOnStyle: Google +IndentWidth: 2 +TabWidth: 2 +ContinuationIndentWidth: 4 +AccessModifierOffset: -2 # The private/protected/public has no indent in class +Standard: Cpp11 +AllowAllParametersOfDeclarationOnNextLine: true +BinPackParameters: false +BinPackArguments: false +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b9902a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +- repo: https://github.com/Lucas-C/pre-commit-hooks.git + sha: c25201a00e6b0514370501050cf2a8538ac12270 + hooks: + - id: remove-crlf + files: (?!.*warp-ctc)^.*$ +- repo: https://github.com/reyoung/mirrors-yapf.git + sha: v0.13.2 + hooks: + - id: yapf + files: (.*\.(py|bzl)|BUILD|.*\.BUILD|WORKSPACE)$ # Bazel BUILD files follow Python syntax. +- repo: https://github.com/pre-commit/pre-commit-hooks + sha: 7539d8bd1a00a3c1bfd34cdb606d3a6372e83469 + hooks: + - id: check-added-large-files + - id: check-merge-conflict + - id: check-symlinks + - id: detect-private-key + files: (?!.*warp-ctc)^.*$ + - id: end-of-file-fixer +- repo: https://github.com/PaddlePaddle/clang-format-pre-commit-hook.git + sha: 28c0ea8a67a3e2dbbf4822ef44e85b63a0080a29 + hooks: + - id: clang-formater diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a8e8b22 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") +set(PROJ_ROOT ${CMAKE_SOURCE_DIR}) + +include(flags) + +find_package(PythonLibs 2.7 REQUIRED) +find_package(PythonInterp 2.7 REQUIRED) +find_package(Glog) +find_package(NumPy REQUIRED) + +include_directories(${PYTHON_INCLUDE_DIR}) +include_directories(${LIBGLOG_INCLUDE_DIR}) +include_directories(${PYTHON_NUMPY_INCLUDE_DIR}) + +add_subdirectory(transformer) diff --git a/cmake/FindGlog.cmake b/cmake/FindGlog.cmake new file mode 100644 index 0000000..142e2ca --- /dev/null +++ b/cmake/FindGlog.cmake @@ -0,0 +1,24 @@ +# +# Find libglog +# +# LIBGLOG_INCLUDE_DIR - where to find glog/logging.h, etc. +# LIBGLOG_LIBRARY - List of libraries when using libglog. +# LIBGLOG_FOUND - True if libglog found. +# +# from https://github.com/facebook/hhvm/blob/master/CMake/FindGlog.cmake + +IF (LIBGLOG_INCLUDE_DIR) + # Already in cache, be silent + SET(LIBGLOG_FIND_QUIETLY TRUE) +ENDIF () + +FIND_PATH(LIBGLOG_INCLUDE_DIR glog/logging.h) + +FIND_LIBRARY(LIBGLOG_LIBRARY glog) + +# handle the QUIETLY and REQUIRED arguments and set LIBGLOG_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBGLOG DEFAULT_MSG LIBGLOG_LIBRARY LIBGLOG_INCLUDE_DIR) + +MARK_AS_ADVANCED(LIBGLOG_LIBRARY LIBGLOG_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/FindNumPy.cmake b/cmake/FindNumPy.cmake new file mode 100644 index 0000000..8cdd642 --- /dev/null +++ b/cmake/FindNumPy.cmake @@ -0,0 +1,38 @@ +# Find the Python NumPy package +# PYTHON_NUMPY_INCLUDE_DIR +# NUMPY_FOUND +# will be set by this script + +cmake_minimum_required(VERSION 2.6) + +if(NOT PYTHON_EXECUTABLE) + if(NumPy_FIND_QUIETLY) + find_package(PythonInterp QUIET) + else() + find_package(PythonInterp) + set(_numpy_out 1) + endif() +endif() + +if (PYTHON_EXECUTABLE) + # write a python script that finds the numpy path + file(WRITE ${PROJECT_BINARY_DIR}/FindNumpyPath.py + "try: import numpy; print(numpy.get_include())\nexcept:pass\n") + + # execute the find script + exec_program("${PYTHON_EXECUTABLE}" ${PROJECT_BINARY_DIR} + ARGS "FindNumpyPath.py" + OUTPUT_VARIABLE NUMPY_PATH) +elseif(_numpy_out) + message(STATUS "Python executable not found.") +endif(PYTHON_EXECUTABLE) + +find_path(PYTHON_NUMPY_INCLUDE_DIR numpy/arrayobject.h + HINTS "${NUMPY_PATH}" "${PYTHON_INCLUDE_PATH}") + +if(PYTHON_NUMPY_INCLUDE_DIR) + set(PYTHON_NUMPY_FOUND 1 CACHE INTERNAL "Python numpy found") +endif(PYTHON_NUMPY_INCLUDE_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NumPy DEFAULT_MSG PYTHON_NUMPY_INCLUDE_DIR) diff --git a/cmake/flags.cmake b/cmake/flags.cmake new file mode 100644 index 0000000..d77a910 --- /dev/null +++ b/cmake/flags.cmake @@ -0,0 +1,77 @@ +include(CheckCXXCompilerFlag) +include(CheckCCompilerFlag) +include(CheckCXXSymbolExists) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel" + FORCE) +endif() + +function(CheckCompilerCXX11Flag) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.8) + message(FATAL_ERROR "Unsupported GCC version. GCC >= 4.8 required.") + endif() + endif() +endfunction() + +CheckCompilerCXX11Flag() +LIST(APPEND CMAKE_CXX_FLAGS -std=c++11) + +# safe_set_flag +# +# Set a compile flag only if compiler is support +# is_c: is C flag or C++ flag, bool type. +# src_list: The list name which the flag name will be append to. +# flag_name: the flag name for compiler, such as '-Werror' '-Wall' etc +# rest arguments: not used. +function(safe_set_flag is_c src_list flag_name) + string(REPLACE "-" "_" safe_name ${flag_name}) + string(REPLACE "=" "_" safe_name ${safe_name}) + if(is_c) + CHECK_C_COMPILER_FLAG(${flag_name} C_COMPILER_SUPPORT_FLAG_${safe_name}) + set(safe_name C_COMPILER_SUPPORT_FLAG_${safe_name}) + else() + CHECK_CXX_COMPILER_FLAG(${flag_name} CXX_COMPILER_SUPPORT_FLAG_${safe_name}) + set(safe_name CXX_COMPILER_SUPPORT_FLAG_${safe_name}) + endif() + if(${safe_name}) + set(${src_list} "${${src_list}} ${flag_name}" PARENT_SCOPE) + endif() +endfunction() + +# helper macro to set cflag +macro(safe_set_cflag src_list flag_name) + safe_set_flag(ON ${src_list} ${flag_name}) +endmacro() + +# helper macro to set cxxflag +macro(safe_set_cxxflag src_list flag_name) + safe_set_flag(OFF ${src_list} ${flag_name}) +endmacro() + +# helper macro to set nvcc flag +macro(safe_set_nvflag flag_name) + string(REPLACE "-" "_" safe_name ${flag_name}) + string(REPLACE "=" "_" safe_name ${safe_name}) + CHECK_C_COMPILER_FLAG(${flag_name} C_COMPILER_SUPPORT_FLAG_${safe_name}) + set(safe_name C_COMPILER_SUPPORT_FLAG_${safe_name}) + if(${safe_name}) + LIST(APPEND CUDA_NVCC_FLAGS -Xcompiler ${flag_name}) + endif() +endmacro() + +set(COMMON_FLAGS + -fPIC + -fno-omit-frame-pointer + -Wextra + -Wno-unused-parameter + -Wno-unused-function + -Wno-error=literal-suffix + -Wno-error=unused-local-typedefs) + +foreach(flag ${COMMON_FLAGS}) + safe_set_cflag(CMAKE_C_FLAGS ${flag}) + safe_set_cxxflag(CMAKE_CXX_FLAGS ${flag}) +endforeach() diff --git a/transformer/CMakeLists.txt b/transformer/CMakeLists.txt new file mode 100644 index 0000000..68e8a98 --- /dev/null +++ b/transformer/CMakeLists.txt @@ -0,0 +1,29 @@ +project(DeJpeg CXX C) +set(DEJPEG_LINKER_LIBS "") + +# OpenCV +find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc) +include_directories(${OpenCV_INCLUDE_DIRS}) + +# Boost +set(Boost_NO_SYSTEM_PATHS ON) +if (Boost_NO_SYSTEM_PATHS) + set(BOOST_ROOT $ENV{BOOST_ROOT}) + set(Boost_DIR ${BOOST_ROOT}) + set(Boost_INCLUDE_DIR "${BOOST_ROOT}/include") + set(Boost_LIBRARIES "${BOOST_ROOT}/lib/") +endif (Boost_NO_SYSTEM_PATHS) +find_package(Boost 1.46 COMPONENTS python) +include_directories(${Boost_INCLUDE_DIR}) + +list(APPEND DEJPEG_LINKER_LIBS ${OpenCV_LIBS}) +list(APPEND DEJPEG_LINKER_LIBS ${Boost_LIBRARIES}) + +set(DEJPEG_SOURCES + DataTransformer.cpp + PyDecodejpeg.cpp) + +add_library(DeJpeg SHARED ${DEJPEG_SOURCES}) +target_compile_options(DeJpeg BEFORE PRIVATE ${BUILD_FLAGS}) +target_link_libraries(DeJpeg ${DEJPEG_LINKER_LIBS}) +set_target_properties(DeJpeg PROPERTIES PREFIX "") diff --git a/transformer/DataTransformer.cpp b/transformer/DataTransformer.cpp new file mode 100644 index 0000000..5515057 --- /dev/null +++ b/transformer/DataTransformer.cpp @@ -0,0 +1,216 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include +#include + +#include "DataTransformer.h" + +DataTransformer::DataTransformer(int threadNum, + int capacity, + bool isTest, + bool isColor, + int cropHeight, + int cropWidth, + int imgSize, + bool isEltMean, + bool isChannelMean, + float* meanValues) + : isTest_(isTest), + isColor_(isColor), + cropHeight_(cropHeight), + cropWidth_(cropWidth), + imgSize_(imgSize), + capacity_(capacity), + threadPool_(threadNum), + prefetchQueue_(capacity) { + fetchCount_ = -1; + scale_ = 1.0; + isChannelMean_ = isChannelMean; + isEltMean_ = isEltMean; + loadMean(meanValues); + + imgPixels_ = cropHeight * cropWidth * (isColor_ ? 3 : 1); + + prefetch_.reserve(capacity); + for (int i = 0; i < capacity; i++) { + auto d = std::make_shared(new float[imgPixels_ * 3], 0); + prefetch_.push_back(d); + memset(prefetch_[i]->first, 0, imgPixels_ * sizeof(float)); + prefetchQueue_.enqueue(prefetch_[i]); + } + numThreads_ = threadNum; +} + +void DataTransformer::loadMean(float* values) { + if (values) { + int c = isColor_ ? 3 : 1; + int sz = isChannelMean_ ? c : cropHeight_ * cropWidth_ * c; + meanValues_ = new float[sz]; + memcpy(meanValues_, values, sz * sizeof(float)); + } +} + +void DataTransformer::transfromFile(std::string imgFile, float* trg) { + int cvFlag = (isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE); + try { + cv::Mat im = cv::imread(imgFile, cvFlag); + if (!im.data) { + LOG(ERROR) << "Could not decode image"; + LOG(ERROR) << im.channels() << " " << im.rows << " " << im.cols; + } + this->transform(im, trg); + } catch (cv::Exception& e) { + LOG(ERROR) << "Caught exception in cv::imdecode " << e.msg; + } +} + +void DataTransformer::transfromString(const char* src, + const int size, + float* trg) { + try { + cv::_InputArray imbuf(src, size); + int cvFlag = (isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE); + cv::Mat im = cv::imdecode(imbuf, cvFlag); + if (!im.data) { + LOG(ERROR) << "Could not decode image"; + LOG(ERROR) << im.channels() << " " << im.rows << " " << im.cols; + } + this->transform(im, trg); + } catch (cv::Exception& e) { + LOG(ERROR) << "Caught exception in cv::imdecode " << e.msg; + } +} + +int DataTransformer::Rand(int min, int max) { + std::random_device source; + std::mt19937 rng(source()); + std::uniform_int_distribution dist(min, max); + return dist(rng); +} + +void DataTransformer::transform(cv::Mat& cvImgOri, float* target) { + const int imgChannels = cvImgOri.channels(); + const int imgHeight = cvImgOri.rows; + const int imgWidth = cvImgOri.cols; + const bool doMirror = (!isTest_) && Rand(0, 1); + int h_off = 0; + int w_off = 0; + int th = imgHeight; + int tw = imgWidth; + cv::Mat img; + if (imgSize_ > 0) { + if (imgHeight > imgWidth) { + tw = imgSize_; + th = int(double(imgHeight) / imgWidth * tw); + th = th > imgSize_ ? th : imgSize_; + } else { + th = imgSize_; + tw = int(double(imgWidth) / imgHeight * th); + tw = tw > imgSize_ ? tw : imgSize_; + } + cv::resize(cvImgOri, img, cv::Size(tw, th)); + } else { + cv::Mat img = cvImgOri; + } + + cv::Mat cv_cropped_img = img; + if (cropHeight_ && cropWidth_) { + if (!isTest_) { + h_off = Rand(0, th - cropHeight_); + w_off = Rand(0, tw - cropWidth_); + } else { + h_off = (th - cropHeight_) / 2; + w_off = (tw - cropWidth_) / 2; + } + cv::Rect roi(w_off, h_off, cropWidth_, cropHeight_); + cv_cropped_img = img(roi); + } else { + CHECK_EQ(cropHeight_, imgHeight); + CHECK_EQ(cropWidth_, imgWidth); + } + int height = cropHeight_; + int width = cropWidth_; + int top_index; + for (int h = 0; h < height; ++h) { + const uchar* ptr = cv_cropped_img.ptr(h); + int img_index = 0; + for (int w = 0; w < width; ++w) { + for (int c = 0; c < imgChannels; ++c) { + if (doMirror) { + top_index = (c * height + h) * width + width - 1 - w; + } else { + top_index = (c * height + h) * width + w; + } + float pixel = static_cast(ptr[img_index++]); + if (isEltMean_) { + int mean_index = (c * imgHeight + h) * imgWidth + w; + target[top_index] = (pixel - meanValues_[mean_index]) * scale_; + } else { + if (isChannelMean_) { + target[top_index] = (pixel - meanValues_[c]) * scale_; + } else { + target[top_index] = pixel * scale_; + } + } + } + } + } // target: BGR +} + +void DataTransformer::processImgString(std::vector& data, + int* labels) { + results_.clear(); + for (size_t i = 0; i < data.size(); ++i) { + results_.emplace_back(threadPool_.enqueue([this, &data, labels, i]() { + DataTypePtr ret = this->prefetchQueue_.dequeue(); + std::string buf = data[i]; + int size = buf.length(); + ret->second = labels[i]; + this->transfromString(buf.c_str(), size, ret->first); + return ret; + })); + } + fetchCount_ = data.size(); + fetchId_ = 0; +} + +void DataTransformer::processImgFile(std::vector& data, + int* labels) { + results_.clear(); + for (size_t i = 0; i < data.size(); ++i) { + results_.emplace_back(threadPool_.enqueue([this, &data, labels, i]() { + DataTypePtr ret = this->prefetchQueue_.dequeue(); + std::string file = data[i]; + ret->second = labels[i]; + this->transfromFile(file, ret->first); + return ret; + })); + } + fetchCount_ = data.size(); + fetchId_ = 0; +} + +void DataTransformer::obtain(float* data, int* label) { + if (fetchId_ >= fetchCount_) { + LOG(FATAL) << "Empty data"; + } + DataTypePtr ret = results_[fetchId_].get(); + *label = ret->second; + memcpy(data, ret->first, sizeof(float) * imgPixels_); + prefetchQueue_.enqueue(ret); + ++fetchId_; +} diff --git a/transformer/DataTransformer.h b/transformer/DataTransformer.h new file mode 100644 index 0000000..852cf0d --- /dev/null +++ b/transformer/DataTransformer.h @@ -0,0 +1,148 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#ifndef DATATRANSFORMER_H_ +#define DATATRANSFORMER_H_ + +#include +#include +#include +#include +#include +#include + +#include "ThreadPool.h" +#include "Queue.h" + +/** + * This is an image processing module with OpenCV, such as + * resizing, scaling, mirroring, substracting the image mean... + * + * This class has a double BlockQueue and they shared the same memory. + * It is used to avoid create memory each time. And it also can + * return the data even if the data are processing in multi-threads. + */ +class DataTransformer { +public: + DataTransformer(int threadNum, + int capacity, + bool isTest, + bool isColor, + int cropHeight, + int cropWidth, + int imgSize, + bool isEltMean, + bool isChannelMean, + float* meanValues); + virtual ~DataTransformer() { + if (meanValues_) { + free(meanValues_); + } + } + + /** + * @brief Start multi-threads to transform a list of input image string. + * This function reads an image from the specified buffer in the + * memory. + * @param data Data is the specified image buffer in the memory. + * @param label The label of input image. + */ + void processImgString(std::vector& data, int* labels); + + /** + * @brief Start multi-threads to transform a list of input image file. + * This function loads image from the the specified file. + * @param data Data is an list of image file. + * @param label The label of input image. + */ + void processImgFile(std::vector& data, int* labels); + + /** + * @brief Applies the transformation on one image Mat. + * + * @param img The input image Mat to be transformed. + * @param target target is used to save the transformed data. + */ + void transform(cv::Mat& img, float* target); + + /** + * @brief Save image Mat as file. + * + * @param filename The file name. + * @param im The image to be saved. + */ + void imsave(std::string filename, cv::Mat& im) { cv::imwrite(filename, im); } + + /** + * @brief Decode the image buffer, then calls transform() function. + * + * @param src The input image buffer. + * @param size The length of string buffer. + * @param trg trg is used to save the transformed data. + */ + void transfromString(const char* src, const int size, float* trg); + + /** + * @brief Load image form image file, then calls transform() function. + * + * @param src The input image file. + * @param trg trg is used to save the transformed data. + */ + void transfromFile(std::string imgFile, float* trg); + + /** + * @brief Return the transformed data and its label. + */ + void obtain(float* data, int* label); + +private: + int isTest_; + int isColor_; + int cropHeight_; + int cropWidth_; + int imgSize_; + int capacity_; + int fetchCount_; + int fetchId_; + bool isEltMean_; + bool isChannelMean_; + int numThreads_; + float scale_; + int imgPixels_; + float* meanValues_; + + /** + * Initialize the mean values. + */ + void loadMean(float* values); + + /** + * @brief Generates a random integer from Uniform({min, min + 1, ..., max}). + * @param min The lower bound (inclusive) value of the random number. + * @param max The upper bound (inclusive) value of the random number. + * + * @return + * A uniformly random integer value from ({min, min + 1, ..., max}). + */ + int Rand(int min, int max); + + typedef std::pair DataType; + typedef std::shared_ptr DataTypePtr; + std::vector prefetch_; + ThreadPool threadPool_; + std::vector> results_; + BlockingQueue prefetchQueue_; +}; // class DataTransformer + +#endif // DATATRANSFORMER_H_ diff --git a/transformer/PyDecodejpeg.cpp b/transformer/PyDecodejpeg.cpp new file mode 100644 index 0000000..032aee8 --- /dev/null +++ b/transformer/PyDecodejpeg.cpp @@ -0,0 +1,176 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataTransformer.h" + +/** + * DecodeJpeg is an image processing API for interfacing Python and + * DataTransformer, which is C++ code using OpenCV and multi-threads + * to accelerate image processing. + * The Boost Python Library is used to wrap C++ interfaces. + */ + +class DecodeJpeg { +public: + /** + * The constructor will create and initialize an object of DataTransformer. + */ + DecodeJpeg(int threadNum, + int capacity, + bool isTest, + bool isColor, + int resize_min_size, + int cropSizeH, + int cropSizeW, + PyObject* meanValues) { + int channel = isColor ? 3 : 1; + bool isEltMean = false; + bool isChannelMean = false; + float* mean = NULL; + if (meanValues || meanValues != Py_None) { + if (!PyArray_Check(meanValues)) { + LOG(FATAL) << "Object is not a numpy array"; + } + pyTypeCheck(meanValues); + int size = PyArray_SIZE(reinterpret_cast(meanValues)); + isChannelMean = (size == channel) ? true : false; + isEltMean = (size == channel * cropSizeH * cropSizeW) ? true : false; + CHECK(isChannelMean != isEltMean); + mean = (float*)PyArray_DATA(reinterpret_cast(meanValues)); + } + tfhandlerPtr_ = std::make_shared(threadNum, + capacity, + isTest, + isColor, + cropSizeH, + cropSizeW, + resize_min_size, + isEltMean, + isChannelMean, + mean); + } + + ~DecodeJpeg() {} + + /** + * @brief This function is used to parse the Python object and convert + * the data to C++ format. Then it called the function of + * DataTransformer to start image processing. + * @param pysrc The input image list with string type. + * @param pylabel The input label of image. + * It's type is numpy.array with int32. + */ + void start(boost::python::list& pysrc, PyObject* pylabel) { + input_.clear(); + int num = len(pysrc); + for (int t = 0; t < num; ++t) { + std::string src = boost::python::extract(pysrc[t]); + input_.push_back(src); + } + labels_ = (int*)PyArray_DATA(reinterpret_cast(pylabel)); + tfhandlerPtr_->processImgString(input_, labels_); + } + + /** + * @brief Return one processed data. + * @param pytrg The processed image. + * @param pylabel The label of processed image. + */ + void get(PyObject* pytrg, PyObject* pylab) { + pyWritableCheck(pytrg); + pyWritableCheck(pylab); + pyContinuousCheck(pytrg); + pyContinuousCheck(pylab); + float* data = (float*)PyArray_DATA(reinterpret_cast(pytrg)); + int* label = (int*)PyArray_DATA(reinterpret_cast(pylab)); + tfhandlerPtr_->obtain(data, label); + } + + /** + * @brief An object of DataTransformer, which is used to call + * the image processing funtions. + */ + std::shared_ptr tfhandlerPtr_; + +private: + /** + * @brief Check whether the type of PyObject is valid or not. + */ + void pyTypeCheck(PyObject* o) { + int typenum = PyArray_TYPE(reinterpret_cast(o)); + + // clang-format off + int type = + typenum == NPY_UBYTE ? CV_8U : + typenum == NPY_BYTE ? CV_8S : + typenum == NPY_USHORT ? CV_16U : + typenum == NPY_SHORT ? CV_16S : + typenum == NPY_INT || typenum == NPY_LONG ? CV_32S : + typenum == NPY_FLOAT ? CV_32F : + typenum == NPY_DOUBLE ? CV_64F : -1; + // clang-format on + + if (type < 0) { + LOG(FATAL) << "toMat: Data type = " << type << " is not supported"; + } + } + + /** + * @brief Check whether the PyObject is writable or not. + */ + void pyWritableCheck(PyObject* o) { + CHECK(PyArray_ISWRITEABLE(reinterpret_cast(o))); + } + + /** + * @brief Check whether the PyObject is c-contiguous or not. + */ + void pyContinuousCheck(PyObject* o) { + CHECK(PyArray_IS_C_CONTIGUOUS(reinterpret_cast(o))); + } + + std::vector input_; + int* labels_; +}; // DecodeJpeg + +/** + * @brief Initialize the Python interpreter and numpy. + */ +static void initPython() { + Py_Initialize(); + PyOS_sighandler_t sighandler = PyOS_getsig(SIGINT); + import_array(); + PyOS_setsig(SIGINT, sighandler); +} + +/** + * Use Boost.Python to expose C++ interface to Python. + */ +BOOST_PYTHON_MODULE(DeJpeg) { + initPython(); + boost::python::class_( + "DecodeJpeg", + boost::python::init()) + .def("start", &DecodeJpeg::start) + .def("get", &DecodeJpeg::get); +}; diff --git a/transformer/Queue.h b/transformer/Queue.h new file mode 100644 index 0000000..8626e43 --- /dev/null +++ b/transformer/Queue.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include +#include + +template +class BlockingQueue { +public: + /** + * @brief Construct Function. + * @param[in] capacity the max numer of elements the queue can have. + */ + explicit BlockingQueue(size_t capacity) : capacity_(capacity) {} + + /** + * @brief enqueue an element into Queue. + * @param[in] x The enqueue element, pass by reference . + * @note This method is thread-safe, and will wake up another thread + * who was blocked because of the queue is empty. + * @note If it's size() >= capacity before enqueue, + * this method will block and wait until size() < capacity. + */ + void enqueue(const T& x) { + std::unique_lock lock(mutex_); + notFull_.wait(lock, [&] { return queue_.size() < capacity_; }); + queue_.push_back(x); + notEmpty_.notify_one(); + } + + /** + * Dequeue from a queue and return a element. + * @note this method will be blocked until not empty. + * @note this method will wake up another thread who was blocked because + * of the queue is full. + */ + T dequeue() { + std::unique_lock lock(mutex_); + notEmpty_.wait(lock, [&] { return !queue_.empty(); }); + + T front(queue_.front()); + queue_.pop_front(); + notFull_.notify_one(); + return front; + } + + /** + * Return size of queue. + * + * @note This method is thread safe. + * The size of the queue won't change until the method return. + */ + size_t size() { + std::lock_guard guard(mutex_); + return queue_.size(); + } + + /** + * @brief is empty or not. + * @return true if empty. + * @note This method is thread safe. + */ + size_t empty() { + std::lock_guard guard(mutex_); + return queue_.empty(); + } + +private: + std::mutex mutex_; + std::condition_variable notEmpty_; + std::condition_variable notFull_; + std::deque queue_; + size_t capacity_; +}; diff --git a/transformer/ThreadPool.h b/transformer/ThreadPool.h new file mode 100644 index 0000000..c6e582d --- /dev/null +++ b/transformer/ThreadPool.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2012 Jakob Progsch, Václav Zeman + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. */ + +#ifndef THREAD_POOL_H +#define THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ThreadPool { +public: + ThreadPool(size_t); + template + auto enqueue(F&& f, Args&&... args) + -> std::future::type>; + ~ThreadPool(); + +private: + // need to keep track of threads so we can join them + std::vector workers; + // the task queue + std::queue> tasks; + + // synchronization + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; +}; + +// the constructor just launches some amount of workers +inline ThreadPool::ThreadPool(size_t threads) : stop(false) { + for (size_t i = 0; i < threads; ++i) + workers.emplace_back([this] { + for (;;) { + std::function task; + + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait( + lock, [this] { return this->stop || !this->tasks.empty(); }); + if (this->stop && this->tasks.empty()) return; + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + + task(); + } + }); +} + +// add new work item to the pool +template +auto ThreadPool::enqueue(F&& f, Args&&... args) + -> std::future::type> { + using return_type = typename std::result_of::type; + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...)); + + std::future res = task->get_future(); + { + std::unique_lock lock(queue_mutex); + + // don't allow enqueueing after stopping the pool + if (stop) throw std::runtime_error("enqueue on stopped ThreadPool"); + + tasks.emplace([task]() { (*task)(); }); + } + condition.notify_one(); + return res; +} + +// the destructor joins all threads +inline ThreadPool::~ThreadPool() { + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for (std::thread& worker : workers) worker.join(); +} + +#endif diff --git a/transformer/main.cpp b/transformer/main.cpp new file mode 100644 index 0000000..6ce61ab --- /dev/null +++ b/transformer/main.cpp @@ -0,0 +1,46 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include "DataTransformer.h" + +int main(int argc, char** argv) { + float mean[3] = {103.939, 116.779, 123.68}; + DataTransformer* trans = new DataTransformer( + 4, 1024, false, true, 224, 224, 256, false, true, mean); + std::string src = argv[1]; + std::vector files; + std::vector labels; + + std::ifstream infile(src.c_str()); + std::string line; + size_t pos; + int label; + while (std::getline(infile, line)) { + pos = line.find_last_of(' '); + label = atoi(line.substr(pos + 1).c_str()); + files.push_back(line.substr(0, pos)); + labels.push_back(label); + } + std::cout << files.size() << std::endl; + trans->processImgFile(files, labels.data()); + float* data = new float[3 * 224 * 224]; + int lab = 0; + for (size_t i = 0; i < labels.size(); ++i) { + trans->obtain(data, &lab); + std::cout << lab << std::endl; + } + return 0; +} From 16f7c9c278b413bee72937d2c50cff86b64010be Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Sun, 18 Dec 2016 14:05:12 +0800 Subject: [PATCH 2/5] Remove BlockQueue and Refine unit tests --- .pre-commit-config.yaml | 2 - .set_python_path.sh | 37 ++++++ CMakeLists.txt | 2 + transformer/CMakeLists.txt | 5 +- transformer/DataTransformer.cpp | 186 +++++++++---------------------- transformer/DataTransformer.h | 110 ++++++------------ transformer/Parallel.h | 157 ++++++++++++++++++++++++++ transformer/PyDecodejpeg.cpp | 138 +++++------------------ transformer/Queue.h | 88 --------------- transformer/main.cpp | 46 -------- transformer/tests/CMakeLists.txt | 4 + transformer/tests/cat.jpg | Bin 0 -> 12881 bytes transformer/tests/test_dejpeg.py | 54 +++++++++ 13 files changed, 371 insertions(+), 458 deletions(-) create mode 100755 .set_python_path.sh create mode 100644 transformer/Parallel.h delete mode 100644 transformer/Queue.h delete mode 100644 transformer/main.cpp create mode 100644 transformer/tests/CMakeLists.txt create mode 100644 transformer/tests/cat.jpg create mode 100644 transformer/tests/test_dejpeg.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b9902a8..c458bba 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,6 @@ sha: c25201a00e6b0514370501050cf2a8538ac12270 hooks: - id: remove-crlf - files: (?!.*warp-ctc)^.*$ - repo: https://github.com/reyoung/mirrors-yapf.git sha: v0.13.2 hooks: @@ -15,7 +14,6 @@ - id: check-merge-conflict - id: check-symlinks - id: detect-private-key - files: (?!.*warp-ctc)^.*$ - id: end-of-file-fixer - repo: https://github.com/PaddlePaddle/clang-format-pre-commit-hook.git sha: 28c0ea8a67a3e2dbbf4822ef44e85b63a0080a29 diff --git a/.set_python_path.sh b/.set_python_path.sh new file mode 100755 index 0000000..8e40a24 --- /dev/null +++ b/.set_python_path.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# A simple test driver for cmake. +# set PYTHONPATH before run command. +# Usage: +# ./.set_python_pash.sh -p YOUR_PYTHON_PATH {exec...} +# +# It same as PYTHONPATH=${YOUR_PYTHON_PATH}:$PYTHONPATH {exec...} +# + +PYPATH="" +set -x +while getopts "d:" opt; do + case $opt in + d) + PYPATH=$OPTARG + ;; + esac +done +shift $(($OPTIND - 1)) +echo $PYPATH +export PYTHONPATH=$PYPATH:${PYTHONPATH} +$@ diff --git a/CMakeLists.txt b/CMakeLists.txt index a8e8b22..5d78a20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,4 +14,6 @@ include_directories(${PYTHON_INCLUDE_DIR}) include_directories(${LIBGLOG_INCLUDE_DIR}) include_directories(${PYTHON_NUMPY_INCLUDE_DIR}) +enable_testing() + add_subdirectory(transformer) diff --git a/transformer/CMakeLists.txt b/transformer/CMakeLists.txt index 68e8a98..37826c4 100644 --- a/transformer/CMakeLists.txt +++ b/transformer/CMakeLists.txt @@ -13,11 +13,12 @@ if (Boost_NO_SYSTEM_PATHS) set(Boost_INCLUDE_DIR "${BOOST_ROOT}/include") set(Boost_LIBRARIES "${BOOST_ROOT}/lib/") endif (Boost_NO_SYSTEM_PATHS) -find_package(Boost 1.46 COMPONENTS python) +find_package(Boost 1.63 COMPONENTS python numpy) include_directories(${Boost_INCLUDE_DIR}) list(APPEND DEJPEG_LINKER_LIBS ${OpenCV_LIBS}) list(APPEND DEJPEG_LINKER_LIBS ${Boost_LIBRARIES}) +list(APPEND DEJPEG_LINKER_LIBS ${LIBGLOG_LIBRARY}) set(DEJPEG_SOURCES DataTransformer.cpp @@ -27,3 +28,5 @@ add_library(DeJpeg SHARED ${DEJPEG_SOURCES}) target_compile_options(DeJpeg BEFORE PRIVATE ${BUILD_FLAGS}) target_link_libraries(DeJpeg ${DEJPEG_LINKER_LIBS}) set_target_properties(DeJpeg PROPERTIES PREFIX "") + +add_subdirectory(tests) diff --git a/transformer/DataTransformer.cpp b/transformer/DataTransformer.cpp index 5515057..cefae57 100644 --- a/transformer/DataTransformer.cpp +++ b/transformer/DataTransformer.cpp @@ -12,60 +12,19 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +#include #include #include -#include -#include #include "DataTransformer.h" -DataTransformer::DataTransformer(int threadNum, - int capacity, - bool isTest, - bool isColor, - int cropHeight, - int cropWidth, - int imgSize, - bool isEltMean, - bool isChannelMean, - float* meanValues) - : isTest_(isTest), - isColor_(isColor), - cropHeight_(cropHeight), - cropWidth_(cropWidth), - imgSize_(imgSize), - capacity_(capacity), - threadPool_(threadNum), - prefetchQueue_(capacity) { - fetchCount_ = -1; - scale_ = 1.0; - isChannelMean_ = isChannelMean; - isEltMean_ = isEltMean; - loadMean(meanValues); - - imgPixels_ = cropHeight * cropWidth * (isColor_ ? 3 : 1); - - prefetch_.reserve(capacity); - for (int i = 0; i < capacity; i++) { - auto d = std::make_shared(new float[imgPixels_ * 3], 0); - prefetch_.push_back(d); - memset(prefetch_[i]->first, 0, imgPixels_ * sizeof(float)); - prefetchQueue_.enqueue(prefetch_[i]); - } - numThreads_ = threadNum; -} - -void DataTransformer::loadMean(float* values) { - if (values) { - int c = isColor_ ? 3 : 1; - int sz = isChannelMean_ ? c : cropHeight_ * cropWidth_ * c; - meanValues_ = new float[sz]; - memcpy(meanValues_, values, sz * sizeof(float)); - } -} +DataTransformer::DataTransformer( + std::unique_ptr&& config) + : config_(std::move(config)) {} -void DataTransformer::transfromFile(std::string imgFile, float* trg) { - int cvFlag = (isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE); +void DataTransformer::transfromFile(const char* imgFile, float* trg) const { + int cvFlag = + config_->isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE; try { cv::Mat im = cv::imread(imgFile, cvFlag); if (!im.data) { @@ -79,11 +38,12 @@ void DataTransformer::transfromFile(std::string imgFile, float* trg) { } void DataTransformer::transfromString(const char* src, - const int size, - float* trg) { + int size, + float* trg) const { try { cv::_InputArray imbuf(src, size); - int cvFlag = (isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE); + int cvFlag = + config_->isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE; cv::Mat im = cv::imdecode(imbuf, cvFlag); if (!im.data) { LOG(ERROR) << "Could not decode image"; @@ -95,56 +55,55 @@ void DataTransformer::transfromString(const char* src, } } -int DataTransformer::Rand(int min, int max) { - std::random_device source; - std::mt19937 rng(source()); +int DataTransformer::Rand(int min, int max) const { + std::mt19937 rng(time(0)); std::uniform_int_distribution dist(min, max); return dist(rng); } -void DataTransformer::transform(cv::Mat& cvImgOri, float* target) { +void DataTransformer::transform(cv::Mat& cvImgOri, float* target) const { const int imgChannels = cvImgOri.channels(); const int imgHeight = cvImgOri.rows; const int imgWidth = cvImgOri.cols; - const bool doMirror = (!isTest_) && Rand(0, 1); - int h_off = 0; - int w_off = 0; + const bool doMirror = (!config_->isTest_) && Rand(0, 1); + int hoff = 0; + int woff = 0; int th = imgHeight; int tw = imgWidth; cv::Mat img; - if (imgSize_ > 0) { - if (imgHeight > imgWidth) { - tw = imgSize_; - th = int(double(imgHeight) / imgWidth * tw); - th = th > imgSize_ ? th : imgSize_; - } else { - th = imgSize_; - tw = int(double(imgWidth) / imgHeight * th); - tw = tw > imgSize_ ? tw : imgSize_; - } + int imsz = config_->imgSize_; + if (imsz > 0) { + double ratio = imgHeight < imgWidth ? double(imsz) / double(imgHeight) + : double(imsz) / double(imgWidth); + th = int(double(imgHeight) * ratio); + tw = int(double(imgWidth) * ratio); cv::resize(cvImgOri, img, cv::Size(tw, th)); } else { - cv::Mat img = cvImgOri; + img = cvImgOri; } cv::Mat cv_cropped_img = img; - if (cropHeight_ && cropWidth_) { - if (!isTest_) { - h_off = Rand(0, th - cropHeight_); - w_off = Rand(0, tw - cropWidth_); + int cropH = config_->cropHeight_; + int cropW = config_->cropWidth_; + if (cropH && cropW) { + if (!config_->isTest_) { + hoff = Rand(0, th - cropH); + woff = Rand(0, tw - cropW); } else { - h_off = (th - cropHeight_) / 2; - w_off = (tw - cropWidth_) / 2; + hoff = (th - cropH) / 2; + woff = (tw - cropW) / 2; } - cv::Rect roi(w_off, h_off, cropWidth_, cropHeight_); + cv::Rect roi(woff, hoff, cropW, cropH); cv_cropped_img = img(roi); } else { - CHECK_EQ(cropHeight_, imgHeight); - CHECK_EQ(cropWidth_, imgWidth); + CHECK_EQ(cropH, imgHeight); + CHECK_EQ(cropW, imgWidth); } - int height = cropHeight_; - int width = cropWidth_; + int height = cropH; + int width = cropW; int top_index; + float scale = config_->scale_; + float* meanVal = config_->meanValues_; for (int h = 0; h < height; ++h) { const uchar* ptr = cv_cropped_img.ptr(h); int img_index = 0; @@ -156,61 +115,24 @@ void DataTransformer::transform(cv::Mat& cvImgOri, float* target) { top_index = (c * height + h) * width + w; } float pixel = static_cast(ptr[img_index++]); - if (isEltMean_) { - int mean_index = (c * imgHeight + h) * imgWidth + w; - target[top_index] = (pixel - meanValues_[mean_index]) * scale_; - } else { - if (isChannelMean_) { - target[top_index] = (pixel - meanValues_[c]) * scale_; - } else { - target[top_index] = pixel * scale_; + switch (config_->meanType_) { + case CHANNEL_MEAN: { + target[top_index] = (pixel - meanVal[c]) * scale; + break; } + case ELEMENT_MEAN: { + int mean_index = (c * height + h) * width + w; + target[top_index] = (pixel - meanVal[mean_index]) * scale; + break; + } + case NULL_MEAN: { + target[top_index] = pixel * scale; + break; + } + default: + LOG(FATAL) << "Unsupport type"; } } } } // target: BGR } - -void DataTransformer::processImgString(std::vector& data, - int* labels) { - results_.clear(); - for (size_t i = 0; i < data.size(); ++i) { - results_.emplace_back(threadPool_.enqueue([this, &data, labels, i]() { - DataTypePtr ret = this->prefetchQueue_.dequeue(); - std::string buf = data[i]; - int size = buf.length(); - ret->second = labels[i]; - this->transfromString(buf.c_str(), size, ret->first); - return ret; - })); - } - fetchCount_ = data.size(); - fetchId_ = 0; -} - -void DataTransformer::processImgFile(std::vector& data, - int* labels) { - results_.clear(); - for (size_t i = 0; i < data.size(); ++i) { - results_.emplace_back(threadPool_.enqueue([this, &data, labels, i]() { - DataTypePtr ret = this->prefetchQueue_.dequeue(); - std::string file = data[i]; - ret->second = labels[i]; - this->transfromFile(file, ret->first); - return ret; - })); - } - fetchCount_ = data.size(); - fetchId_ = 0; -} - -void DataTransformer::obtain(float* data, int* label) { - if (fetchId_ >= fetchCount_) { - LOG(FATAL) << "Empty data"; - } - DataTypePtr ret = results_[fetchId_].get(); - *label = ret->second; - memcpy(data, ret->first, sizeof(float) * imgPixels_); - prefetchQueue_.enqueue(ret); - ++fetchId_; -} diff --git a/transformer/DataTransformer.h b/transformer/DataTransformer.h index 852cf0d..aac517e 100644 --- a/transformer/DataTransformer.h +++ b/transformer/DataTransformer.h @@ -15,58 +15,43 @@ limitations under the License. */ #ifndef DATATRANSFORMER_H_ #define DATATRANSFORMER_H_ -#include +#include #include +#include +#include #include -#include #include -#include +#include -#include "ThreadPool.h" -#include "Queue.h" +#define DISABLE_COPY(T) \ + T(T&&) = delete; \ + T(T const&) = delete; \ + void operator=(T const& t) = delete + +typedef enum { CHANNEL_MEAN = 0, ELEMENT_MEAN = 1, NULL_MEAN = 2 } MeanType; + +struct DataTransformerConfig { + bool isTest_; + bool isColor_; + int cropHeight_; + int cropWidth_; + int imgSize_; // short side + MeanType meanType_; + float scale_; + int imgPixels_; // the total pixels of transformed image + float* meanValues_; +}; /** * This is an image processing module with OpenCV, such as * resizing, scaling, mirroring, substracting the image mean... - * - * This class has a double BlockQueue and they shared the same memory. - * It is used to avoid create memory each time. And it also can - * return the data even if the data are processing in multi-threads. */ class DataTransformer { public: - DataTransformer(int threadNum, - int capacity, - bool isTest, - bool isColor, - int cropHeight, - int cropWidth, - int imgSize, - bool isEltMean, - bool isChannelMean, - float* meanValues); - virtual ~DataTransformer() { - if (meanValues_) { - free(meanValues_); - } - } - - /** - * @brief Start multi-threads to transform a list of input image string. - * This function reads an image from the specified buffer in the - * memory. - * @param data Data is the specified image buffer in the memory. - * @param label The label of input image. - */ - void processImgString(std::vector& data, int* labels); + DISABLE_COPY(DataTransformer); - /** - * @brief Start multi-threads to transform a list of input image file. - * This function loads image from the the specified file. - * @param data Data is an list of image file. - * @param label The label of input image. - */ - void processImgFile(std::vector& data, int* labels); + DataTransformer(std::unique_ptr&& config); + virtual ~DataTransformer() {} /** * @brief Applies the transformation on one image Mat. @@ -74,7 +59,7 @@ class DataTransformer { * @param img The input image Mat to be transformed. * @param target target is used to save the transformed data. */ - void transform(cv::Mat& img, float* target); + void transform(cv::Mat& img, float* target) const; /** * @brief Save image Mat as file. @@ -82,7 +67,9 @@ class DataTransformer { * @param filename The file name. * @param im The image to be saved. */ - void imsave(std::string filename, cv::Mat& im) { cv::imwrite(filename, im); } + void imsave(std::string filename, cv::Mat& im) const { + cv::imwrite(filename, im); + } /** * @brief Decode the image buffer, then calls transform() function. @@ -91,7 +78,7 @@ class DataTransformer { * @param size The length of string buffer. * @param trg trg is used to save the transformed data. */ - void transfromString(const char* src, const int size, float* trg); + void transfromString(const char* src, int size, float* trg) const; /** * @brief Load image form image file, then calls transform() function. @@ -99,33 +86,10 @@ class DataTransformer { * @param src The input image file. * @param trg trg is used to save the transformed data. */ - void transfromFile(std::string imgFile, float* trg); - - /** - * @brief Return the transformed data and its label. - */ - void obtain(float* data, int* label); + void transfromFile(const char* imgFile, float* trg) const; private: - int isTest_; - int isColor_; - int cropHeight_; - int cropWidth_; - int imgSize_; - int capacity_; - int fetchCount_; - int fetchId_; - bool isEltMean_; - bool isChannelMean_; - int numThreads_; - float scale_; - int imgPixels_; - float* meanValues_; - - /** - * Initialize the mean values. - */ - void loadMean(float* values); + std::unique_ptr config_; /** * @brief Generates a random integer from Uniform({min, min + 1, ..., max}). @@ -135,14 +99,8 @@ class DataTransformer { * @return * A uniformly random integer value from ({min, min + 1, ..., max}). */ - int Rand(int min, int max); - - typedef std::pair DataType; - typedef std::shared_ptr DataTypePtr; - std::vector prefetch_; - ThreadPool threadPool_; - std::vector> results_; - BlockingQueue prefetchQueue_; + int Rand(int min, int max) const; + }; // class DataTransformer #endif // DATATRANSFORMER_H_ diff --git a/transformer/Parallel.h b/transformer/Parallel.h new file mode 100644 index 0000000..34414d9 --- /dev/null +++ b/transformer/Parallel.h @@ -0,0 +1,157 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include +#include +#include +#include + +#include "DataTransformer.h" +#include "ThreadPool.h" + +namespace bn = boost::python::numpy; + +class Parallel { +public: + Parallel(int threadNum, + bool isTest, + bool isColor, + int resizeMinSize, + int cropSizeH, + int cropSizeW, + PyObject* meanValues) + : threadPool_(threadNum) { + int channel = isColor ? 3 : 1; + MeanType meanType; + float* mean = NULL; + if (meanValues || meanValues != Py_None) { + if (!PyArray_Check(meanValues)) { + LOG(FATAL) << "Object is not a numpy array"; + } + pyTypeCheck(meanValues); + int size = PyArray_SIZE(reinterpret_cast(meanValues)); + mean = (float*)PyArray_DATA(reinterpret_cast(meanValues)); + meanType = (size == channel) ? CHANNEL_MEAN : NULL_MEAN; + meanType = + (size == channel * cropSizeH * cropSizeW) ? ELEMENT_MEAN : meanType; + } + + imgPixels_ = channel * cropSizeH * cropSizeW; + + DataTransformerConfig* conf = new DataTransformerConfig; + conf->isTest_ = isTest; + conf->isColor_ = isColor; + conf->cropHeight_ = cropSizeH; + conf->cropWidth_ = cropSizeW; + conf->imgSize_ = resizeMinSize; + conf->meanType_ = meanType; + conf->scale_ = 1.0; + conf->imgPixels_ = imgPixels_; + conf->meanValues_ = mean; + + transformerPtr_ = std::unique_ptr( + new DataTransformer(std::unique_ptr(conf))); + } + + ~Parallel() {} + + int start(boost::python::list& pysrc, PyObject* pylabel, int mode) { + int num = len(pysrc); + int* labels = (int*)PyArray_DATA(reinterpret_cast(pylabel)); + for (int i = 0; i < num; ++i) { + const char* buf = boost::python::extract(pysrc[i]); + int buflen = len(pysrc[i]); + int label = labels[i]; + Py_intptr_t shape[1] = {this->imgPixels_}; + DataTypePtr trg = std::make_shared( + boost::python::numpy::zeros( + 1, shape, boost::python::numpy::dtype::get_builtin()), + 0); + results_.emplace_back( + threadPool_.enqueue([this, buf, buflen, label, trg, mode]() { + trg->second = label; + float* data = (float*)((trg->first).get_data()); + if (mode == 0) { + this->transformerPtr_->transfromString(buf, buflen, data); + } else if (mode == 1) { + this->transformerPtr_->transfromFile(buf, data); + } else { + LOG(FATAL) << "Unsupport mode " << mode; + } + return trg; + })); + } + return 0; + } + + boost::python::tuple get() { + DataTypePtr ret = results_.front().get(); + results_.pop_front(); + return boost::python::make_tuple(ret->first, ret->second); + } + +private: + /** + * @brief Check whether the type of PyObject is valid or not. + */ + void pyTypeCheck(PyObject* o) { + int typenum = PyArray_TYPE(reinterpret_cast(o)); + + // clang-format off + int type = + typenum == NPY_UBYTE ? CV_8U : + typenum == NPY_BYTE ? CV_8S : + typenum == NPY_USHORT ? CV_16U : + typenum == NPY_SHORT ? CV_16S : + typenum == NPY_INT || typenum == NPY_LONG ? CV_32S : + typenum == NPY_FLOAT ? CV_32F : + typenum == NPY_DOUBLE ? CV_64F : -1; + // clang-format on + + if (type < 0) { + LOG(FATAL) << "toMat: Data type = " << type << " is not supported"; + } + } + + /** + * @brief Check whether the PyObject is writable or not. + */ + void pyWritableCheck(PyObject* o) { + CHECK(PyArray_ISWRITEABLE(reinterpret_cast(o))); + } + + /** + * @brief Check whether the PyObject is c-contiguous or not. + */ + void pyContinuousCheck(PyObject* o) { + CHECK(PyArray_IS_C_CONTIGUOUS(reinterpret_cast(o))); + } + + int imgPixels_; + + /** + * @brief An object of DataTransformer, which is used to call + * the image processing funtions. + */ + std::unique_ptr transformerPtr_; + + ThreadPool threadPool_; + + typedef std::pair DataType; + typedef std::shared_ptr DataTypePtr; + std::deque> results_; + +}; // Parallel diff --git a/transformer/PyDecodejpeg.cpp b/transformer/PyDecodejpeg.cpp index 032aee8..ea88ff9 100644 --- a/transformer/PyDecodejpeg.cpp +++ b/transformer/PyDecodejpeg.cpp @@ -12,145 +12,56 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DataTransformer.h" +#include "Parallel.h" /** * DecodeJpeg is an image processing API for interfacing Python and - * DataTransformer, which is C++ code using OpenCV and multi-threads - * to accelerate image processing. - * The Boost Python Library is used to wrap C++ interfaces. + * C++ code. The Boost Python Library is used to wrap C++ interfaces. + * This class is only an interface and there is no specific calculation. */ - class DecodeJpeg { public: - /** - * The constructor will create and initialize an object of DataTransformer. - */ DecodeJpeg(int threadNum, - int capacity, bool isTest, bool isColor, int resize_min_size, int cropSizeH, int cropSizeW, PyObject* meanValues) { - int channel = isColor ? 3 : 1; - bool isEltMean = false; - bool isChannelMean = false; - float* mean = NULL; - if (meanValues || meanValues != Py_None) { - if (!PyArray_Check(meanValues)) { - LOG(FATAL) << "Object is not a numpy array"; - } - pyTypeCheck(meanValues); - int size = PyArray_SIZE(reinterpret_cast(meanValues)); - isChannelMean = (size == channel) ? true : false; - isEltMean = (size == channel * cropSizeH * cropSizeW) ? true : false; - CHECK(isChannelMean != isEltMean); - mean = (float*)PyArray_DATA(reinterpret_cast(meanValues)); - } - tfhandlerPtr_ = std::make_shared(threadNum, - capacity, - isTest, - isColor, - cropSizeH, - cropSizeW, - resize_min_size, - isEltMean, - isChannelMean, - mean); + tfhandlerPtr_ = std::make_shared(threadNum, + isTest, + isColor, + resize_min_size, + cropSizeH, + cropSizeW, + meanValues); } ~DecodeJpeg() {} /** - * @brief This function is used to parse the Python object and convert - * the data to C++ format. Then it called the function of - * DataTransformer to start image processing. + * @brief This function calls the start function of Parallel + * to process image with multi-threads. * @param pysrc The input image list with string type. * @param pylabel The input label of image. - * It's type is numpy.array with int32. + * Its type is numpy.array with int32. + * @param mode Two mode: + * 0: the input is image buffer + * 1: the input is image file path. */ - void start(boost::python::list& pysrc, PyObject* pylabel) { - input_.clear(); - int num = len(pysrc); - for (int t = 0; t < num; ++t) { - std::string src = boost::python::extract(pysrc[t]); - input_.push_back(src); - } - labels_ = (int*)PyArray_DATA(reinterpret_cast(pylabel)); - tfhandlerPtr_->processImgString(input_, labels_); + int start(boost::python::list& pysrc, PyObject* pylabel, int mode) { + int ret = tfhandlerPtr_->start(pysrc, pylabel, mode); + return 0; } /** - * @brief Return one processed data. - * @param pytrg The processed image. - * @param pylabel The label of processed image. + * @brief Return a tuple: (image, label). + * The image is transformed image. */ - void get(PyObject* pytrg, PyObject* pylab) { - pyWritableCheck(pytrg); - pyWritableCheck(pylab); - pyContinuousCheck(pytrg); - pyContinuousCheck(pylab); - float* data = (float*)PyArray_DATA(reinterpret_cast(pytrg)); - int* label = (int*)PyArray_DATA(reinterpret_cast(pylab)); - tfhandlerPtr_->obtain(data, label); - } - - /** - * @brief An object of DataTransformer, which is used to call - * the image processing funtions. - */ - std::shared_ptr tfhandlerPtr_; + boost::python::tuple get() { return tfhandlerPtr_->get(); } private: - /** - * @brief Check whether the type of PyObject is valid or not. - */ - void pyTypeCheck(PyObject* o) { - int typenum = PyArray_TYPE(reinterpret_cast(o)); - - // clang-format off - int type = - typenum == NPY_UBYTE ? CV_8U : - typenum == NPY_BYTE ? CV_8S : - typenum == NPY_USHORT ? CV_16U : - typenum == NPY_SHORT ? CV_16S : - typenum == NPY_INT || typenum == NPY_LONG ? CV_32S : - typenum == NPY_FLOAT ? CV_32F : - typenum == NPY_DOUBLE ? CV_64F : -1; - // clang-format on - - if (type < 0) { - LOG(FATAL) << "toMat: Data type = " << type << " is not supported"; - } - } - - /** - * @brief Check whether the PyObject is writable or not. - */ - void pyWritableCheck(PyObject* o) { - CHECK(PyArray_ISWRITEABLE(reinterpret_cast(o))); - } - - /** - * @brief Check whether the PyObject is c-contiguous or not. - */ - void pyContinuousCheck(PyObject* o) { - CHECK(PyArray_IS_C_CONTIGUOUS(reinterpret_cast(o))); - } - - std::vector input_; - int* labels_; + std::shared_ptr tfhandlerPtr_; }; // DecodeJpeg /** @@ -168,9 +79,10 @@ static void initPython() { */ BOOST_PYTHON_MODULE(DeJpeg) { initPython(); + boost::python::numpy::initialize(); boost::python::class_( "DecodeJpeg", - boost::python::init()) + boost::python::init()) .def("start", &DecodeJpeg::start) .def("get", &DecodeJpeg::get); }; diff --git a/transformer/Queue.h b/transformer/Queue.h deleted file mode 100644 index 8626e43..0000000 --- a/transformer/Queue.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once - -#include -#include -#include - -template -class BlockingQueue { -public: - /** - * @brief Construct Function. - * @param[in] capacity the max numer of elements the queue can have. - */ - explicit BlockingQueue(size_t capacity) : capacity_(capacity) {} - - /** - * @brief enqueue an element into Queue. - * @param[in] x The enqueue element, pass by reference . - * @note This method is thread-safe, and will wake up another thread - * who was blocked because of the queue is empty. - * @note If it's size() >= capacity before enqueue, - * this method will block and wait until size() < capacity. - */ - void enqueue(const T& x) { - std::unique_lock lock(mutex_); - notFull_.wait(lock, [&] { return queue_.size() < capacity_; }); - queue_.push_back(x); - notEmpty_.notify_one(); - } - - /** - * Dequeue from a queue and return a element. - * @note this method will be blocked until not empty. - * @note this method will wake up another thread who was blocked because - * of the queue is full. - */ - T dequeue() { - std::unique_lock lock(mutex_); - notEmpty_.wait(lock, [&] { return !queue_.empty(); }); - - T front(queue_.front()); - queue_.pop_front(); - notFull_.notify_one(); - return front; - } - - /** - * Return size of queue. - * - * @note This method is thread safe. - * The size of the queue won't change until the method return. - */ - size_t size() { - std::lock_guard guard(mutex_); - return queue_.size(); - } - - /** - * @brief is empty or not. - * @return true if empty. - * @note This method is thread safe. - */ - size_t empty() { - std::lock_guard guard(mutex_); - return queue_.empty(); - } - -private: - std::mutex mutex_; - std::condition_variable notEmpty_; - std::condition_variable notFull_; - std::deque queue_; - size_t capacity_; -}; diff --git a/transformer/main.cpp b/transformer/main.cpp deleted file mode 100644 index 6ce61ab..0000000 --- a/transformer/main.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include -#include -#include "DataTransformer.h" - -int main(int argc, char** argv) { - float mean[3] = {103.939, 116.779, 123.68}; - DataTransformer* trans = new DataTransformer( - 4, 1024, false, true, 224, 224, 256, false, true, mean); - std::string src = argv[1]; - std::vector files; - std::vector labels; - - std::ifstream infile(src.c_str()); - std::string line; - size_t pos; - int label; - while (std::getline(infile, line)) { - pos = line.find_last_of(' '); - label = atoi(line.substr(pos + 1).c_str()); - files.push_back(line.substr(0, pos)); - labels.push_back(label); - } - std::cout << files.size() << std::endl; - trans->processImgFile(files, labels.data()); - float* data = new float[3 * 224 * 224]; - int lab = 0; - for (size_t i = 0; i < labels.size(); ++i) { - trans->obtain(data, &lab); - std::cout << lab << std::endl; - } - return 0; -} diff --git a/transformer/tests/CMakeLists.txt b/transformer/tests/CMakeLists.txt new file mode 100644 index 0000000..e37b151 --- /dev/null +++ b/transformer/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +add_test(NAME test_dejpeg + COMMAND ${PROJ_ROOT}/.set_python_path.sh -d ${CMAKE_CURRENT_BINARY_DIR}/../. + python ${PROJ_ROOT}/transformer/tests/test_dejpeg.py + WORKING_DIRECTORY ${PROJ_ROOT}) diff --git a/transformer/tests/cat.jpg b/transformer/tests/cat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..47b01db90eddc46ff845f10bc2accaf2364c272d GIT binary patch literal 12881 zcmbWdcTf{f`0yEeuTla8LbD zr~xR+$tlRmC@Cl?sHiBZX<6uKX=rFUnC~#KaC6?};pPN^c)^lFynN#PAdv6_5pgML zSy|b;LNFyLL{U;k7VF9uvd!T!e|IhNb7r;abSR>LQA>soNGZB$6 z5&azi-2JytGNS(_fd5m7h)GDv$SEkPsA>Kw;1~hKL?k4{q$Ffyq@@3}BmT_;NSVl( z?@6nW-!XEe;PYdFL}ivz@~bxWu^P|(0LwV}M^jO=u>(0c1q6kJMMP!gFi_4$CuCD)w ziwHpSzp?&}{~PT8!Nv5Ci*!#(o zU9y=$e_rDD-g^|(uaISk(zB$&sPQ1c_*YR@Q8nl(Vd*b!8N_0b-Hu7~$1ck0(#~Ij zPK+rNS@h|6S>!bo$ltU)m)&tp^8}85HcA!ske`pqQlm9LnSb)yP48Z^U(CPnJytcw&UEo4~VtxQi3grmTbubJdP(D5nrABz1{wVGxSH8Gj8)1#^BDv+rx#0{IFhADg}_A@_cK`o5CI2TE;JwK z@82)2bZD0wg=vqCR5*=fnAZ0GUeHiQgxp$xtNd&vZ)HY$rk)uGG8Rj@DBYn*+MgX* zb1+HDaIfIoPKwmBM>D^c){Y_p%`4N+=8lv^?C%7i&w)RL`HIR&FrJ*BG&BHtm1t-U zqdQjn72-IL^+%ZL_-p|qHP8wBtbvWAfYVq5uFr{?9277V*V3wtCtV0eu9RuQS7$&1|-GlgMK~$s*r3S zT20;s%|Vbl-zY!<)Q>zKeU{cF#XI>3M<+$X;I;snFDnHXLawfKPXt=YSU#NQHqR3b@WYwW^SS>L-N<8J2@>|DNcbvQk;6PjP1J6R0Y$)-z&1;|Yx(o!VS36E; zx|Mpjb1hgrex&k0;*zsYlVr9!-8|q|$&K?JOs#eo%YwLOk0W@lnR|TKD6KWx>u0Tc zhQgfDGKe!5=B1E(O2l*+(Sl?yC0>#*O}fXq`~&nChV$|`&MGDTE})FPqp* zOv64>C%{YBi9Y-5Ak7^YU!?g~bMP?BR}Q5pjh=Y5o-XrP^DBD|18+oAqY~l;(Mr!l z%OLc2ph>B>mEkJMkVyQfY7wmN&qG6?a}lnkl+Ei)N=~zrXxYF_J71?#t@&HbjH}wP zTET z>};FXMhlNr)=4>dntdJFGh4Zfn3XwZ6ZVGgsWu>H5|TWg1YuzxlXj&1w6_;B@GRdY z+oJUVD)M{M=G#ssOjhP|blXX7OLqmFsfM#%ml+g)*1ZKQCJ}BoSO{|dbXv^h9{u%P zEhJA$tMj}M^ld6!_eI${%n^H8sjUtgGLWpK%DFbJx)v$dgT=S;^YS)|OENLpShjmj z?tq^)z>Lru4tb*GdG##QvX4$xwwG09+=SUoW!Flr-+&c_`(w9P%{y8@tG7T4CNL;R z&D$&rIYvxY2V`!5dcoWl6lNalHwtl+TR*S8lcP=DjDED)R_E$rseBh?(yX&v3c%zB zv13v>I~bepwHPFs6qx!;K?k)-y|&3nF$vGkX}WSq&J46LP5{uts<#aIxld2s6E#{J zWq{vGSDDJVc|d<`(T;Aoa)}RzobQp!E0N$KI%vI*4CW}7#Vm6PD4bbG`_b>fK-zos zqB=+(u3@(#Ig%~8&nZc$5FX*_FYNN)#JDtbFGFiGTg=wAc8kfiOO;eA9GB!1AG(8< zlmc0DCgAj9)i9bPLv7MvVY640&QEDLYY&hZU;G3(zvh8^k6vJvw%0(l0HWwnUTn1H zb+%dl#6mJPnWU(|3m%VMl{7I{(pczLh0L>QK8y=V&w~kC4+UQGlm{w?t&bM`{L(}= z367Du2gCrdmzl$YRZ<)WFe?=dX2Q*gBy9@1N{h(d)lo>RinJDsVqAv3afeyRQcxaK{LM zj23D6FPEbaTdSLEB>VV?2tnmO(tjamr8eTv!0M#n50u`sHO{Jj?buZI06f2)ZGRye zXWm!&&ow?@LK=jII$AE4?pis({{k#y z8T?^)t7|jYOZ^3=`GMR>do<_KoYA*1sE%Qc_Ge`I8;(SNp%R}s8P zBeRo9;clQ;FbkDbe!j?O;eTlswJf%&T3?D1aIlxQG|raE@}j?0#hL-t3*f_ICT6g|A7` zY^WVLDefCkjizbNSneaixb#LDbpwFaIE3&rj|Xkl$jMIgaRG)@2%zDd^r<-mahjqjhfPf5 z&a~cW4TaBttft9$hC%(3Cx$<1GxalVD072y(>+rk?E=sEMw^pS==jD~=^w%)a4mw+ z&H5*OeEdMZMsLfn+sLGGJOeM(M7v)qHDi_c4!dJg$e*u6VWxilBQPvbLX9?R8tV+y zF;KP4X=>Kvsv@&AcdPKav>u#j0yr?cG*(5T+xkj}$lO9U?|}U}b@tOr6HNM`-;>UN zoEs;fD)`E@t{hvin;)i_tKZYw2s^&KHKToCSg0cC$SdlfQuR#e;}>(DRMJTqc+Rft zusD?DAZgDrP9pvjAmcx}cOa2#3)>3|g~F_+w)^Fs4B{#BXdtW(i)=EYI)+5}Lp&w5 z#`Y?uq%0l0k0z};TvDI7+>F&-1vEsMxNht*uh_<;^$$sfZf>&|dzf@VlVd&v{uH-5 ztz!4q4$Pz4I5=$T<(~kQmW^%#8SNm25KbVtWId-!340*3fe>`h%cB6ir2K=ZTJ=6Q zq^t-;hkhuC1f1W9-2XC(|9wC2RAdLR|5$V=n1YD&d~6 zN&juxK=|71!K>Q!1LkWF>s-Dh;#aBIr#B(iRF2&$d3uYl zsWSLkt6@=CIu@Wqo9}h{a2NAorMBfs{h%eu&EAiRS&`3utn+@!sCt>j{KxA`dsDEf zuji~>g}qTlqFxHwU(vD4N5=NV-&CsGj55``k-U?t#9v{_LzZ@ zZMP6BZ39=2h26m12rtrVKI1trvl@NYV4|W_h4K%fm@Ul^?bzwyb!a3{4ySCW{LDKo#|PT4ZGNWJqrFK+RBwIBiM}bhJhfC=53Ar6K4(IYRdI)|eG!M1 zWHRC4erWErvd!*j=G4KrU#PN1Y@$;Icvg@8^uD@GtRvCVhHAwwh_I$Y!k*y!Jdvf$u zSJ!YTR1-7IK@B~yXv_CjHkxkGqbpR#7|EolzCuhx4!Mi z6~g*M^GEt$fSH|8_-LlFUTgel;LgZu6v@GC&Z39a($56b;wNT$T3VD&RgQ`o2w|3i z5kqGDp@;lc(l;MzUk1RfY`iLC{L`_T#KJ_ttB8FS{zC%hV^rFgj{f4IDv5+NH zAht}!|8kI(wy6%eQb!XHt0MP@R!vT{-gy=_*y?j+bO)uO|8$41*4=Y^HXCnf#Y`(8 z`PH;R64qP6+%{vxnHa6nmE0i!)&QzyX#QvW@9Ds?%y==4^Nt79YrVjAGRVgG+&Hh2 zbj!ge;l_ongD{`c2nksxmF`*>$ zUR*M5yNWs_v0$60XFsGwGyC4qN5tfW5kF}%Q3BTKO}dQWpoePS4|+PY#NE^%er@?d zF0l=prWn%<$cv-s@I^Vd3Z;N|d@7Lqgo`>|A8SF^?;9<#RJ-w?mo~7Y0lZ?IZAh#B zndd8X`;fy?*0Q6WI>;i2X(;O66tcj)Wu&oyKwkct%jU>zn|?tAUe+AOi%SAiX)2oa zz=~i$s6<1cyEOo-w1vF`6Pv3yT;PVs)GLJwdfHJEmxa6b>5nXIf_MuH?p!oHA6`+H zwyU3MST1k>M4SKBHvBK32tA{Q1iw@2`tt5~`H7YD0P*@%ah-Ld_iBk_fkhJ>tMt22 zI;)1ucI!s)sPb3tlO~T@iw@&jRsS9nwa-eVYTgy%Zr^R6c6Ct!U#fA=|aOSvsK;Byzm|pY_Qy*5`t&PN723wZmNI z$u=rAtuUm~<>VpV4;!7XGJkD@?@U(g57}dn@)Q^An?W-0M`q8fxe;z(Bj6=hI+4t? z?)aJScHap$fqB@jmK-3!o#Uuro*jblEQ@)fv4!I5%7gr_!ngN5afH0qtHo6@As zxQyBZPx-Y;W1{r|4G)`RcS>7B-_~3Hm=|A~4me4OPVx6wItV=`8@%0wf7BHH#5%?8 z8t{wMh!|KPLsSrnE~{MsQ&KlTn-ca)1H8pLrXYVDz*!#Jrp=fYtzasZux+U3<@IMa zr0xMdPcId}!{@PTIRA@yW6FI^L|6C z1JeX`{|+x?QrB|oWS$S2pkT{noyByiHYJul>#l9d2%GfTXQyL*8#O<#{kuNDi?35B zv7pE!`j8`fw>H5gN9l@pOO9I9eHLO?;w=vbfi|Lg_py zAn9H!0x{vN^D`c>0M;fy2v#%#_2P$hYLQGHuarco7^O$5*vr=-8V@sfXNW9U$sG^| zV3W}=1@n=Pm-WUevcx^N#ptZVzkmS8sLScUfQCZ1?;h^Jf+NjQK+0}o#jGAn)IBx! zY-i(gLRQTAq^sU#A`9t1|ATV;zDh~~a#}8?N}q1(Hy`Fun?&L~`fIR{J*WKsqavKZ zM3<`?XU=HfW%WXLHCRB(_KU-XP|9v)n#gfJZ$2l)LYNS38VJV3abj#tZ%^b^iD`oU zqQ9J1)?`{t*mn#miFTJ@Hy9!CZo0LC>$#)ju@*dmgf|S+j}7Lq)>W}I&Nbh$8-Eq) zmATl!f^* zzxN|lYZy(c@yQ-jW@aV4J-wudPfgGrA?`_!hM!_%we%G@Mbw` zOOyl#B`%9rK6c@4Zq%7US*E6EeP~xjypyJkbr+`W?QO7{v0prOeX7EIs{2YSnG&aj zAJQ#+pKK=4K-?u{sBgN3yT6h;|9V{CBB|r_%>Me4qxAVh#T|L1F3fn;LiStu7raA+PWnm7iVXt!0lkhJo-jO{*0BupmAdDY8~y?m9q#5nLLdJQu|ApKc#~4xeHfNJ z=-FVaPudr2$TpIav;l4xkI=5Z_z$R| zNGxnIk)(#(mQ=X}TwebX75pj?^eCtmt~YM^iI{DVRg=ST_}W}xqmsMr(^|jw?Ue|| zW>v+1e=na+pXja_Z6Ij?IGFn-J>oY1i*9m1N^y)=@umm)-8N`(lv863TQLlCEpk{Z zV5D-0%xl;j(`X`^Gst#FeWB%Ssrcna_lTD-%iZmYoMEwzZ{1rsF`jD2lAexQKKQ}= z9C2_4f{o|bv?6C;$GW#{9{Zd|;&dYNp#n;== zU(?9yJzdm`N^}B{H4AH-AE?d93`ZbFhYClk7l*Prx1cL!X=qpXvyTO5mgX5bCan%( zzZc$UgnS_PQIXr}?Ebz1TWP|2>10g5CQjueMlWn>jD3Lt#(_rJg;=v5ucXps#Zh$u%HE3(8f^nMdidkz_jE99#r5K5}?cbDLsp!fGqt3BN)6_`u@}hkQ zYU0-ZVzu!MaR=hBj&_bybwZ~8}C$MqNL$#!_^ zioNdv+KOnDqUJB)QTD+oL4L8{%JpHTYkyX&R< zt%O@XdK->+5)-Eo@V%slV<`7SxBSq@Xl8L#lx(p-D@X6%&6#zksiWN4I=7?nq@=Ks!))ZJ`7ew*Z0uJ%4}M)iGu_g9av z7m5Q*%X39#Dj_Q7xfR%`lc5@EU-xEB`=0rFeHbghJ?TN~&7Vl6pc^Dusjpw2 z?cAuE4PUqYQ<(CKp35h~2}MvNl-9U{Gv3xyg$dd8HO_B=xv09=F%0znKKjp0?e-=DSg^Qk99uI-%B1nbGd4l z^!I#&hm1T)8`Qb;Dr8vhrCyik0n9N!1OQIVwhM4Hhfq4(bG^6`+1Vf%mgU4~zxo^k}7Pe`s3IwMBz*(p{;;1?DostD6L$V!7KLqsGJ8OqQNDhpT^nJL;jJ!Wl(KhojSJ5-*w4G+B z+QZ3ps))-?I4n{4UY7QEV+MIL@q1=m8liNv;kmr|%4=&argi?hEGwEl5K8ByS)q=x zjGKa>x>*DjB)c`$*^IOxrKw`9YHAVYgZzqAiQZX2p7di;e+dW|MqLq)+DQzrUW3cM*OA!8%uh;_;8dNp*v^i zcAQ=@W4px}>v*_D7V0xJ%#~Q4Be%i?{y;HGDlF%=0X^p2nV11TQ*CnlCcX5zV4k^P zx=Cv#IcnK>*z3w%nuCp3%SnnpbV}juV758sJ}YTM#(biCk&_eSs+2e-bCW37+>ctO zHJzWvCAr}(K38zS&13riZaZ51AUSd_UX&}qgcN^YFM2WC!mU}%0!hC}t*872SdX>mgdQj8#+{a?CW`(A zh=IN^ysUUOC>6aY>9d{cAT|P9i+C9J#W<-ZFxc^_ot~wS&R$E;gw`mD10y-|vz)58 zD%bYuqM+3sx-Qt1rhZsZ`TTtn_;;eTy9xsS5F+joC9PYW&IaF1D4!-YUE&^0Al>9b zNa&;igTs=MH5wTD?2In1h*Wf(wzuK|+wz|?&VGf@KeW48FK-@4wat>+RkbieLVq5a z)^DBq^yY65#ER?0>=Jb~DLQ;Hx$nb9b64Ii!$9Pby8c>JdUpf_`Q40aavzPYz8Qy1 zoibvjucYHhY6y)>#Eok83@x`8d7Ux;9NRDe!J4{ylOQL2qx+m=@9Hl=v=*Ubdl+66Q?mEp%Jpy8$9{;<(%e%- zx43U7t9&VzXZG@Rp^Ug-&83DbM-B9R31|!!`r; zo`xhYoTj6nRv%rbPvO^}n!YjXKy3zZS^*oC$l(-wMth6FxzC#!X?(ok>rVm9?4P;o zChLs9uXBGk^11Kx{r)FlMycPReUq5yvkep1WXI(T{@FZZ*(APd#ZUf+17|U(aerif zE!2tKfj(n#(yP5*8(!PYfiroA2 z*xDP19?;p3bW0=|srC2!e6ow%Y`c@*DnBBzV`@rW!l-imm~#{_U*Y`VExy?2{gZ@p zmfl_-99UcCvoqBGqCzeXJMbZ(zUAoAQ;@u9J}zTjLxjHLt`zr)<+Qclnw?%(!pu=w zkbsq`2=&9?^9)t1?Z>exL|n}jMy5-K9)XTN(`{@-8g}da8|qEbBMMu!F)`^w9a(je zlgo81z35{;bUV!#;0<9=v|wJ(+if=BFst>LVDb?=L(v^E%*VO6n(d)GS=8i)&MD`^1a1EAw#>nu zyG0tL_f4kn7JBGQ>r5lx7?+1B4KASesqz`i`-sgk zGu7jJs;6{xq@K*;Uh;SFuX{o-_QuS!!=M}BX~nUj}ZwjAo(Un ze0fyzbiF}E&TCRUb67Hjq{3U@Hf^Ab?-2HzE40ef{ij1vWpPiloWkYVv@t@gh995A z8~fVcx-N#FJ@YOR96~1oU+l!w^&~^q^=PYPkBUPhX{;n@(=^RKp9Q>%ox&U3j_BI1vmwTf2^Awbl)}8A{P& zo-zGCOg4J(%jC^;SBLwERs!$4Ly?$~-gZNm;SHfutUI-bkT;Q%R_iY|_TSv~WY~Gs zlmV0geAKf)m@WN*f0QquIP4r0&^PvA( z3PH&{4FFXA=y-Y`_Xyk_r{rQcjWic(*&u<>!$>^vW}WGxZLoMAmFR zh?jpzK6@Sirsh$vg+j_MZrMW1j@u2Rt(d2KND?psAYe6-<+z~=mRa|4SE`*#=udIY zM#rStgjURpS#`zg>?1R}p9#0<yw@WNFwc^Q=+asJr!@j^_jgRcA6ncvSeNp^CZRGlJ*rVf2nauA5RaZ z+;htx82nl2zg~g;V@j)=y&U<-zKLIK0HP~ zTuk1!wgJ=EpF)PWQ`qdO_Nk^}gtP^+cSVBHz!j}yY>UV=84z>}9Nx17t=zFgEQS9{ z;{QNMVWb?ClW;~|IC*U5+%$f7>7!{^M6RX1Uc?JLRmCUDy&<3Xk&xrkib>9;@dLdO z_h;xir)VUY$2JMn!xaKaf>??OzlYOb&K5g~AH_|!Hs>TAW`rI?r^3zfUu>#BB@Y5) ze{z7YKGl-Pq38>K67g3+|?lDwh-IPVot)g!D?6PGjM;#oMn zlMGsj`#&b*y_FX!sr>4czQ7`O-m|68H=;+&t?#1yg)#EQ;b3G`blJ_dX~+5l$ta;2 zR<;Gy;^Je7Ojru{k}sE0CrQywB+i3?nlej#3qPY@A0SX0KpT*UOa8Xwoos6{8G^|jRj!V*$7U!Imw8HcG2CUHGp&Efq}_jAf;*-D ze*t`yTX>V0qK-#pvKKiDo)MqWu6f%s=&g2>6xx@f+Tvwd!Y*eeVmLHpdz%ND;;Ve^ zRH~c*e$WeI2Pce2kJ;ocSVbBu@KhDQNNUYo+czHc-pLVb2ND2j1e_Zdv%1#5E4|Y` z&XPqNHTSsY4`O?1$-JefnpS>*#CynqAB9%*ukgJVbHeql2oZocOwid7j-Lid?PkR-P z+4@d!z*gth&D-(z-=PsxkWxMJ^nR?aLsMH^aD?$H-hPlMfB*J5Y z(Ae0HW3 z#`+h~Qb-Ue{^@x=1Aa?I&10TB5&D#9N;Euy=_@F;zt%iFKwv$yFK1K*wN7T;it}{) z?2AuLQ^i!dLZkI2mlwu+R2mYbf3Q_T(c`;%@%s<_NAE}}H>CmUe^n@NIH++&jb31B zNhm!co%hU+C3Z6YRY6{xYU^8M7p~G|f1O_|�L)kp1S05+XNhGI(P4E#Q}z*Z2th zy6Q46OVUZ4y`&zYiPABNj0ubFEU!dvPnZ(Lv1rov3s<0<>#tx+dzt-N_?6l!*tJPj z<_=21Y-&NSLLP4uwsto&?B`FB9rc%szLZs;hC$Danlj31G11TL@|iU+NllGwZ8VFe ziv2xHH$+yQEaxLK66Uv%ptKDfCuy3g`JZiszs&NiPLY>%t9uP$&j5!@p9u6|cvShe zSV5F+>U^8i6$MmtA8Y0;O($rXr{V{G1u ztN)ODQ@z>^x>+!1PnhE9RUX&^F-IV}tpn%V;*ZUjU-?#IW}kPf{t~eGmvMUQT?OXi z*9Y!G^gz=&fxpLB>TIyxQg1>@fKfsOpVzCi`JN#|+B}w@nob*`bthNMI%PW?9F97V z{sOut9a_{R#MLM)A6X@B%J92t+JdeZ)q{LB1?CwW4)OsOnZBVn4+D>E{5hVaT@L^B z{26t0UKvq9Dgn~v$ZTg8vqrNxZ2tGPPtf7Bssyv_7!M^)nu3GR^=8gk=#eu638Zay zSBm}%fRG;cscV+C^%JLMsc*^6bT8@2a7}%xj4nRj%KVnOhKEI~EVbl^%u(@dtszRs zpQr7<)yB(bs!lv+J#&KVgT8s!FRA{;Rn0<=xW<{vT+0F|O$ovFNDD~`)YxA@SRAd1 zf|+#D=TsV}O$~f#e}4&?e%_-CRnHH1TVu@b z1Y6rSxG7k)CmSW};u0-2xN_Ix^-a@aMb-B5@Kl)8GZ7d~*40f4V$wP;Yvrdfv3Ej^4{;uv+25qQo9O= zd#MH98M3nH>!9FDBwQ^3z00F1yGxoVqk=}Jw81C8ia(kkx0tv6q>}qMb36SmXVXIx zoFM{>BkG%abaL~WXTjc*2WH2-Z?56Q8kpF zPWu-i1qNj;R%$oCHujnP)L{62Vc)~@MT`*1N{az=%tnL$0K9DFU71P#qpc<%r8gaN zWLuq&=C{p4TpWnVE2jt?yGf`W3+|Dx@Sv3s?Q)@JPKFdu!{Q?UC8P8pdkH?3-Ir^X z-#HNAfjB9<94Ix?3QT>pxT z1VaI9@CZ>6tczsDXa8%?|0K!&``*tt3_yjDe2voo8bxPn~4cX06h|}PeKb(KyWu=ofj5BRZ3EhW; zUC;1X8-1*<%i;0hU2`S`%5My?vzv4|S3kAJ2h^4W$Qb><8t9-EOfQsw)6O@JTYEoC zdb>s3qF)SNq?3iw9@85*h?yUQb6iqik41=v{skbt`1IK;ys~z?3(x7}b*i#}kawed zN$uPdG|XcbWFdQ#?)%c>uJTz|9~-RT;j~La539h3+%)gB!7W-dg|&Z(&Z`t->#Fim z(Je=W%!_+Wk9oPt?BZD%_?=OqbyYkeT^IC~WmC?xTV(C-q%PE@k1F1;ReWG(&|qaf zoRYY7=k=8_liZ>L>ElOPD}sf)s%HBl-C)PDm4X?CBb1}tNwFh7TavS(enQIYmU9l9 zMz@PH77h2{I9E|A>6-&l*#(>SkFI3dE33zC6ya?Ug}FtTPcgy2I`p}yR$0S>Id5tY%x_0;MczdIXh8N-Ni zulqs~+I7J@xa623>Sd|L-H&)) zA~7ju13bPd{|hj`aubhG+ONS`HtvL&J_eeTw~qb=sPk7^fXqEIQO3!yN&>L=$bgkU zxt1;HkiKr>cgAVZ!y6Zs1A8NGL`c-O%XV0p85T@95?@0}Pa1%wlYP6`@9N}_9jr(^ zAWfPKCTsP`I32OfUMu4*x~d%ux&rcJ~aEREl^UX%h JXZ`*3e*j_iS4jW> literal 0 HcmV?d00001 diff --git a/transformer/tests/test_dejpeg.py b/transformer/tests/test_dejpeg.py new file mode 100644 index 0000000..afab3d1 --- /dev/null +++ b/transformer/tests/test_dejpeg.py @@ -0,0 +1,54 @@ +import os +import unittest +import numpy as np +from PIL import Image +import StringIO + +import DeJpeg + + +class TestDataTransformer(unittest.TestCase): + def test_image_buf(self): + im_size = 0 # not resize + crop_size = 128 + tmp_name = './transformer/tests/cat.jpg' + + data = [] + with open(tmp_name) as f: + data.append(f.read()) + + data.append(data[0]) + mean = np.array([103.939, 116.779, 123.68], dtype=np.float32) + + # transform by DeJpeg + op = DeJpeg.DecodeJpeg(2, True, True, im_size, crop_size, crop_size, + mean) + labels = np.array([3, 3], dtype=np.int32) + ret = op.start(data, labels, 0) + self.assertEqual(ret, 0) + lab = np.zeros(1, dtype=np.int32) + im, lab = op.get() + im, lab = op.get() + self.assertEqual(lab, 3) + + # transform by PIL + img = Image.open(tmp_name) + im_array = np.array(img) + + h, w = im_array.shape[:2] + hoff = (h - crop_size) / 2 + woff = (w - crop_size) / 2 + pyim = im_array[hoff:hoff + crop_size, woff:woff + crop_size, :] + + pyim = pyim.astype(np.float32) + pyim = pyim.transpose((2, 0, 1)) + mean = mean[:, np.newaxis, np.newaxis] + pyim = pyim[(2, 1, 0), :, :] + pyim -= mean + pyim = pyim.flatten() + + self.assertEqual(im.all(), pyim.all()) + + +if __name__ == '__main__': + unittest.main() From 6c483b283354fc5d59d653d0bc394de070941045 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Mon, 19 Dec 2016 22:39:52 +0800 Subject: [PATCH 3/5] Add git submodule for ThreadPool and follow comments --- .gitignore | 1 + .gitmodules | 3 + .pre-commit-config.yaml | 2 + CMakeLists.txt | 3 +- third_party/.gitignore | 1 + transformer/DataTransformer.cpp | 6 +- transformer/DataTransformer.h | 2 +- transformer/Parallel.h | 20 +----- transformer/ThreadPool.h | 108 ------------------------------- transformer/tests/CMakeLists.txt | 2 +- 10 files changed, 18 insertions(+), 130 deletions(-) create mode 100644 .gitmodules create mode 100644 third_party/.gitignore delete mode 100644 transformer/ThreadPool.h diff --git a/.gitignore b/.gitignore index 567609b..74c9e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build/ +third_party/thread diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..41be4e3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/thread"] + path = third_party/thread + url = https://github.com/progschj/ThreadPool.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c458bba..fcf55b7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,7 @@ sha: c25201a00e6b0514370501050cf2a8538ac12270 hooks: - id: remove-crlf + files: (?!.*thread)^.*$ - repo: https://github.com/reyoung/mirrors-yapf.git sha: v0.13.2 hooks: @@ -15,6 +16,7 @@ - id: check-symlinks - id: detect-private-key - id: end-of-file-fixer + files: (?!.*thread)^.*$ - repo: https://github.com/PaddlePaddle/clang-format-pre-commit-hook.git sha: 28c0ea8a67a3e2dbbf4822ef44e85b63a0080a29 hooks: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d78a20..5cbe6d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,10 @@ include(flags) find_package(PythonLibs 2.7 REQUIRED) find_package(PythonInterp 2.7 REQUIRED) -find_package(Glog) +find_package(Glog REQUIRED) find_package(NumPy REQUIRED) +include_directories(${PROJ_ROOT}) include_directories(${PYTHON_INCLUDE_DIR}) include_directories(${LIBGLOG_INCLUDE_DIR}) include_directories(${PYTHON_NUMPY_INCLUDE_DIR}) diff --git a/third_party/.gitignore b/third_party/.gitignore new file mode 100644 index 0000000..8502cd4 --- /dev/null +++ b/third_party/.gitignore @@ -0,0 +1 @@ +thread diff --git a/transformer/DataTransformer.cpp b/transformer/DataTransformer.cpp index cefae57..6ac0140 100644 --- a/transformer/DataTransformer.cpp +++ b/transformer/DataTransformer.cpp @@ -56,11 +56,13 @@ void DataTransformer::transfromString(const char* src, } int DataTransformer::Rand(int min, int max) const { - std::mt19937 rng(time(0)); + std::default_random_engine eng; std::uniform_int_distribution dist(min, max); - return dist(rng); + return dist(eng); } +// TODO(qingqing): add more data argumentation operation +// and split this function. void DataTransformer::transform(cv::Mat& cvImgOri, float* target) const { const int imgChannels = cvImgOri.channels(); const int imgHeight = cvImgOri.rows; diff --git a/transformer/DataTransformer.h b/transformer/DataTransformer.h index aac517e..8102a9f 100644 --- a/transformer/DataTransformer.h +++ b/transformer/DataTransformer.h @@ -28,7 +28,7 @@ limitations under the License. */ T(T const&) = delete; \ void operator=(T const& t) = delete -typedef enum { CHANNEL_MEAN = 0, ELEMENT_MEAN = 1, NULL_MEAN = 2 } MeanType; +enum MeanType { CHANNEL_MEAN = 0, ELEMENT_MEAN = 1, NULL_MEAN = 2 }; struct DataTransformerConfig { bool isTest_; diff --git a/transformer/Parallel.h b/transformer/Parallel.h index 34414d9..9383297 100644 --- a/transformer/Parallel.h +++ b/transformer/Parallel.h @@ -20,7 +20,7 @@ limitations under the License. */ #include #include "DataTransformer.h" -#include "ThreadPool.h" +#include "third_party/thread/ThreadPool.h" namespace bn = boost::python::numpy; @@ -35,7 +35,7 @@ class Parallel { PyObject* meanValues) : threadPool_(threadNum) { int channel = isColor ? 3 : 1; - MeanType meanType; + MeanType meanType = NULL_MEAN; float* mean = NULL; if (meanValues || meanValues != Py_None) { if (!PyArray_Check(meanValues)) { @@ -44,7 +44,7 @@ class Parallel { pyTypeCheck(meanValues); int size = PyArray_SIZE(reinterpret_cast(meanValues)); mean = (float*)PyArray_DATA(reinterpret_cast(meanValues)); - meanType = (size == channel) ? CHANNEL_MEAN : NULL_MEAN; + meanType = (size == channel) ? CHANNEL_MEAN : meanType; meanType = (size == channel * cropSizeH * cropSizeW) ? ELEMENT_MEAN : meanType; } @@ -126,20 +126,6 @@ class Parallel { } } - /** - * @brief Check whether the PyObject is writable or not. - */ - void pyWritableCheck(PyObject* o) { - CHECK(PyArray_ISWRITEABLE(reinterpret_cast(o))); - } - - /** - * @brief Check whether the PyObject is c-contiguous or not. - */ - void pyContinuousCheck(PyObject* o) { - CHECK(PyArray_IS_C_CONTIGUOUS(reinterpret_cast(o))); - } - int imgPixels_; /** diff --git a/transformer/ThreadPool.h b/transformer/ThreadPool.h deleted file mode 100644 index c6e582d..0000000 --- a/transformer/ThreadPool.h +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (c) 2012 Jakob Progsch, Václav Zeman - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. */ - -#ifndef THREAD_POOL_H -#define THREAD_POOL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class ThreadPool { -public: - ThreadPool(size_t); - template - auto enqueue(F&& f, Args&&... args) - -> std::future::type>; - ~ThreadPool(); - -private: - // need to keep track of threads so we can join them - std::vector workers; - // the task queue - std::queue> tasks; - - // synchronization - std::mutex queue_mutex; - std::condition_variable condition; - bool stop; -}; - -// the constructor just launches some amount of workers -inline ThreadPool::ThreadPool(size_t threads) : stop(false) { - for (size_t i = 0; i < threads; ++i) - workers.emplace_back([this] { - for (;;) { - std::function task; - - { - std::unique_lock lock(this->queue_mutex); - this->condition.wait( - lock, [this] { return this->stop || !this->tasks.empty(); }); - if (this->stop && this->tasks.empty()) return; - task = std::move(this->tasks.front()); - this->tasks.pop(); - } - - task(); - } - }); -} - -// add new work item to the pool -template -auto ThreadPool::enqueue(F&& f, Args&&... args) - -> std::future::type> { - using return_type = typename std::result_of::type; - - auto task = std::make_shared>( - std::bind(std::forward(f), std::forward(args)...)); - - std::future res = task->get_future(); - { - std::unique_lock lock(queue_mutex); - - // don't allow enqueueing after stopping the pool - if (stop) throw std::runtime_error("enqueue on stopped ThreadPool"); - - tasks.emplace([task]() { (*task)(); }); - } - condition.notify_one(); - return res; -} - -// the destructor joins all threads -inline ThreadPool::~ThreadPool() { - { - std::unique_lock lock(queue_mutex); - stop = true; - } - condition.notify_all(); - for (std::thread& worker : workers) worker.join(); -} - -#endif diff --git a/transformer/tests/CMakeLists.txt b/transformer/tests/CMakeLists.txt index e37b151..0216b91 100644 --- a/transformer/tests/CMakeLists.txt +++ b/transformer/tests/CMakeLists.txt @@ -1,4 +1,4 @@ add_test(NAME test_dejpeg - COMMAND ${PROJ_ROOT}/.set_python_path.sh -d ${CMAKE_CURRENT_BINARY_DIR}/../. + COMMAND ${PROJ_ROOT}/.set_python_path.sh -d ${CMAKE_CURRENT_BINARY_DIR}/.. python ${PROJ_ROOT}/transformer/tests/test_dejpeg.py WORKING_DIRECTORY ${PROJ_ROOT}) From 9d61f10ea877c3ae7953602d13ad22c0d80411ad Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Mon, 19 Dec 2016 22:55:17 +0800 Subject: [PATCH 4/5] Rename --- transformer/DataTransformer.cpp | 8 ++++---- transformer/DataTransformer.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/transformer/DataTransformer.cpp b/transformer/DataTransformer.cpp index 6ac0140..39ea8e7 100644 --- a/transformer/DataTransformer.cpp +++ b/transformer/DataTransformer.cpp @@ -55,7 +55,7 @@ void DataTransformer::transfromString(const char* src, } } -int DataTransformer::Rand(int min, int max) const { +int DataTransformer::rand(int min, int max) const { std::default_random_engine eng; std::uniform_int_distribution dist(min, max); return dist(eng); @@ -67,7 +67,7 @@ void DataTransformer::transform(cv::Mat& cvImgOri, float* target) const { const int imgChannels = cvImgOri.channels(); const int imgHeight = cvImgOri.rows; const int imgWidth = cvImgOri.cols; - const bool doMirror = (!config_->isTest_) && Rand(0, 1); + const bool doMirror = (!config_->isTest_) && rand(0, 1); int hoff = 0; int woff = 0; int th = imgHeight; @@ -89,8 +89,8 @@ void DataTransformer::transform(cv::Mat& cvImgOri, float* target) const { int cropW = config_->cropWidth_; if (cropH && cropW) { if (!config_->isTest_) { - hoff = Rand(0, th - cropH); - woff = Rand(0, tw - cropW); + hoff = rand(0, th - cropH); + woff = rand(0, tw - cropW); } else { hoff = (th - cropH) / 2; woff = (tw - cropW) / 2; diff --git a/transformer/DataTransformer.h b/transformer/DataTransformer.h index 8102a9f..43ccfd6 100644 --- a/transformer/DataTransformer.h +++ b/transformer/DataTransformer.h @@ -99,7 +99,7 @@ class DataTransformer { * @return * A uniformly random integer value from ({min, min + 1, ..., max}). */ - int Rand(int min, int max) const; + int rand(int min, int max) const; }; // class DataTransformer From 016841b076aa2c26183147c0850c205a0d006ba6 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Wed, 21 Dec 2016 14:28:50 +0800 Subject: [PATCH 5/5] change uchar to uint8_t and modify rand function. --- transformer/DataTransformer.cpp | 49 +++++++++++++-------------------- transformer/DataTransformer.h | 9 +++--- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/transformer/DataTransformer.cpp b/transformer/DataTransformer.cpp index 39ea8e7..69cdcbc 100644 --- a/transformer/DataTransformer.cpp +++ b/transformer/DataTransformer.cpp @@ -20,50 +20,39 @@ limitations under the License. */ DataTransformer::DataTransformer( std::unique_ptr&& config) - : config_(std::move(config)) {} + : config_(std::move(config)), eng_(time(NULL)) {} -void DataTransformer::transfromFile(const char* imgFile, float* trg) const { +void DataTransformer::transfromFile(const char* imgFile, float* trg) { int cvFlag = config_->isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE; - try { - cv::Mat im = cv::imread(imgFile, cvFlag); - if (!im.data) { - LOG(ERROR) << "Could not decode image"; - LOG(ERROR) << im.channels() << " " << im.rows << " " << im.cols; - } - this->transform(im, trg); - } catch (cv::Exception& e) { - LOG(ERROR) << "Caught exception in cv::imdecode " << e.msg; + cv::Mat im = cv::imread(imgFile, cvFlag); + if (!im.data) { + LOG(FATAL) << "Could not read image, image shape"; } + this->transform(im, trg); } void DataTransformer::transfromString(const char* src, - int size, - float* trg) const { - try { - cv::_InputArray imbuf(src, size); - int cvFlag = - config_->isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE; - cv::Mat im = cv::imdecode(imbuf, cvFlag); - if (!im.data) { - LOG(ERROR) << "Could not decode image"; - LOG(ERROR) << im.channels() << " " << im.rows << " " << im.cols; - } - this->transform(im, trg); - } catch (cv::Exception& e) { - LOG(ERROR) << "Caught exception in cv::imdecode " << e.msg; + const int size, + float* trg) { + cv::_InputArray imbuf(src, size); + int cvFlag = + config_->isColor_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE; + cv::Mat im = cv::imdecode(imbuf, cvFlag); + if (!im.data) { + LOG(FATAL) << "Could not decode image"; } + this->transform(im, trg); } -int DataTransformer::rand(int min, int max) const { - std::default_random_engine eng; +int DataTransformer::rand(const int min, const int max) { std::uniform_int_distribution dist(min, max); - return dist(eng); + return dist(eng_); } // TODO(qingqing): add more data argumentation operation // and split this function. -void DataTransformer::transform(cv::Mat& cvImgOri, float* target) const { +void DataTransformer::transform(cv::Mat& cvImgOri, float* target) { const int imgChannels = cvImgOri.channels(); const int imgHeight = cvImgOri.rows; const int imgWidth = cvImgOri.cols; @@ -107,7 +96,7 @@ void DataTransformer::transform(cv::Mat& cvImgOri, float* target) const { float scale = config_->scale_; float* meanVal = config_->meanValues_; for (int h = 0; h < height; ++h) { - const uchar* ptr = cv_cropped_img.ptr(h); + const uint8_t* ptr = cv_cropped_img.ptr(h); int img_index = 0; for (int w = 0; w < width; ++w) { for (int c = 0; c < imgChannels; ++c) { diff --git a/transformer/DataTransformer.h b/transformer/DataTransformer.h index 43ccfd6..898abc8 100644 --- a/transformer/DataTransformer.h +++ b/transformer/DataTransformer.h @@ -59,7 +59,7 @@ class DataTransformer { * @param img The input image Mat to be transformed. * @param target target is used to save the transformed data. */ - void transform(cv::Mat& img, float* target) const; + void transform(cv::Mat& img, float* target); /** * @brief Save image Mat as file. @@ -78,7 +78,7 @@ class DataTransformer { * @param size The length of string buffer. * @param trg trg is used to save the transformed data. */ - void transfromString(const char* src, int size, float* trg) const; + void transfromString(const char* src, const int size, float* trg); /** * @brief Load image form image file, then calls transform() function. @@ -86,7 +86,7 @@ class DataTransformer { * @param src The input image file. * @param trg trg is used to save the transformed data. */ - void transfromFile(const char* imgFile, float* trg) const; + void transfromFile(const char* imgFile, float* trg); private: std::unique_ptr config_; @@ -99,7 +99,8 @@ class DataTransformer { * @return * A uniformly random integer value from ({min, min + 1, ..., max}). */ - int rand(int min, int max) const; + int rand(const int min, const int max); + std::default_random_engine eng_; }; // class DataTransformer