├── utils ├── Merge │ ├── CMakeLists.txt │ └── merge.cpp ├── Database │ ├── CMakeLists.txt │ └── database.cpp ├── PLYConverter │ ├── CMakeLists.txt │ └── ply_converter.cpp ├── offline.sh ├── merge_sample.sh ├── view_selector.sh ├── cluster.sh ├── ply_converter.sh ├── Renderer │ ├── CMakeLists.txt │ └── renderer.cpp ├── Gabor │ ├── CMakeLists.txt │ └── gabor.cpp ├── Sample │ ├── CMakeLists.txt │ └── sample.cpp ├── KMean │ ├── read_data.h │ ├── read_data.cpp │ ├── CMakeLists.txt │ ├── kernel.h │ ├── k_mean_demo.cpp │ ├── k_mean.h │ ├── kernel.cu │ ├── k_mean.cpp │ └── k_mean_main.cpp ├── ViewSelector │ ├── CMakeLists.txt │ └── selector.cpp ├── database.sh ├── sample.sh ├── online.sh ├── Contour │ ├── CMakeLists.txt │ └── canny.cpp ├── renderer.sh ├── contour_extractor.sh ├── gabor_filter.sh └── encoder.sh ├── CMakeLists.txt ├── src ├── clusters.h ├── tf_idf.h ├── clusters.cpp ├── tf_idf.cpp └── sketch.cpp └── readme.md /utils/Merge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(Merge) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | set(SOURCE_FILES merge.cpp) 7 | add_executable(merge ${SOURCE_FILES}) -------------------------------------------------------------------------------- /utils/Database/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(Database) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | set(SOURCE_FILES database.cpp) 7 | add_executable(build_database ${SOURCE_FILES}) -------------------------------------------------------------------------------- /utils/PLYConverter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(PLYConverter) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | set(SOURCE_FILES ply_converter.cpp) 7 | add_executable(ply_converter ${SOURCE_FILES}) -------------------------------------------------------------------------------- /utils/offline.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | Data="$1" 4 | 5 | bash renderer.sh ../../../data/$Data/models_ply/ ../../../data/$Data/views/ 6 6 | bash contour_extractor.sh ../../../data/$Data/views/ ../../../data/$Data/contours/ 7 | bash view_selector.sh ../../../data/$Data/views/ view.txt 8 | 9 | -------------------------------------------------------------------------------- /utils/merge_sample.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #Ex: bash merge_sample.sh ../../../data/TinySketch/gabors/ ../../../data/TinySketch/vectors.bin 282240 512 2 4 | 5 | Path="$1" 6 | Output="$2" 7 | Line="$3" 8 | Dim="$4" 9 | Skip="$5" 10 | 11 | ./Merge/Debug/merge -a $Path -o $Output -n $Line -d $Dim -s $Skip -------------------------------------------------------------------------------- /utils/view_selector.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # One folders with PNG format files and the name of the output file 4 | # Ex: bash view_selector.sh ../../../data/TinySketch/view/ view.txt 5 | 6 | Files="$1" 7 | Name="$2" 8 | for Folder in $Files*/ 9 | do 10 | ./ViewSelector/Release/selector $Folder $Folder/$Name 11 | done 12 | -------------------------------------------------------------------------------- /utils/cluster.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Use K-Means to cluster all vectors 4 | # Ex: bash cluster.sh ../../../data/Sketch/vectors.bin 1024 ../../../data/Sketch/result.dict 20 0.01 5 | 6 | Vectors="$1" 7 | Dict="$3" 8 | K="$2" 9 | Iteration="$4" 10 | V="$5" 11 | 12 | ./KMean/Release/k_mean -1 -f $Vectors -k $K -d $Dict -s 32 -i $Iteration -v $V 13 | -------------------------------------------------------------------------------- /utils/ply_converter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # One folders with OFF format files 4 | 5 | EXT='ply' 6 | Folders="$1*.off" 7 | 8 | for Src in $Folders 9 | do 10 | # Replace all 'off' in the path by 'ply' 11 | Dest="${Src//off/$EXT}"; 12 | # Run the converter 13 | ./PLYConverter/Debug/ply_converter $Src $Dest; 14 | done 15 | -------------------------------------------------------------------------------- /utils/Renderer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.2) 2 | 3 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 4 | 5 | SET(VTK_DIR "/usr/local") 6 | FIND_PACKAGE(VTK REQUIRED) 7 | INCLUDE(${VTK_USE_FILE}) 8 | 9 | PROJECT(Renderor) 10 | 11 | SET(MAIN renderer.cpp) 12 | ADD_EXECUTABLE(renderor ${MAIN}) 13 | 14 | TARGET_LINK_LIBRARIES(renderor ${VTK_LIBRARIES}) -------------------------------------------------------------------------------- /utils/Gabor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(Gabor) 3 | 4 | set(OpenCV_STATIC OFF) 5 | find_package(OpenCV REQUIRED) 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 8 | 9 | set(SOURCE_FILES gabor.cpp) 10 | include_directories(${OpenCV_INCLUDE_DIRS}) 11 | add_executable(gabor ${SOURCE_FILES}) 12 | target_link_libraries(gabor ${OpenCV_LIBS}) -------------------------------------------------------------------------------- /utils/Sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(Sample) 3 | 4 | set(OpenCV_STATIC OFF) 5 | find_package(OpenCV REQUIRED) 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 8 | 9 | set(SOURCE_FILES sample.cpp) 10 | include_directories(${OpenCV_INCLUDE_DIRS}) 11 | add_executable(sample ${SOURCE_FILES}) 12 | target_link_libraries(sample ${OpenCV_LIBS}) -------------------------------------------------------------------------------- /utils/KMean/read_data.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 17/11/15. 3 | // 4 | 5 | #ifndef READ_DATA_H_ 6 | #define READ_DATA_H_ 7 | 8 | #include 9 | 10 | void read_int(std::ifstream& stream, int* val, bool little_endian = false); 11 | void read_bytes(std::ifstream& stream, uint8_t* val, int n); 12 | void read_floats(std::ifstream& stream, float* val, int n); 13 | 14 | #endif /* READ_DATA_H_ */ 15 | -------------------------------------------------------------------------------- /utils/ViewSelector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(View_selection) 3 | 4 | set(OpenCV_STATIC OFF) 5 | find_package(OpenCV REQUIRED) 6 | 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 8 | 9 | set(SOURCE_FILES selector.cpp) 10 | include_directories(${OpenCV_INCLUDE_DIRS}) 11 | add_executable(selector ${SOURCE_FILES}) 12 | target_link_libraries(selector ${OpenCV_LIBS}) -------------------------------------------------------------------------------- /utils/database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ex: bash database.sh ../../../data/TinySketch/encode/ ../../../data/TinySketch/tf-idf ../../../data/TinySketch/label 100 4 | 5 | Input="$1" # input folder with encoded files 6 | Output_data="$2" # output tf-idf file 7 | Output_index="$3" # output label file 8 | K="$4" # number of words 9 | 10 | ./Database/Debug/./build_database -i $Input -d $Output_data -m $Output_index -k $K 11 | -------------------------------------------------------------------------------- /utils/sample.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #Ex: bash sample.sh ../Sketch/views/ ../Sketch/bin/ view.txt ../Sketch/sample/ 4 | 5 | Files="$1" 6 | Bin_Path="$2" 7 | List="$3" 8 | Result_Path="$4" 9 | 10 | for Folder in $Files*/ 11 | do 12 | Dest_Folder_Name="${Folder%/}" 13 | Dest_Folder_Name="${Dest_Folder_Name##*/}" 14 | 15 | ./Sample/Debug/./sample $Folder/$List $Bin_Path/$Dest_Folder_Name/ $Result_Path/$Dest_Folder_Name.bin 16 | done -------------------------------------------------------------------------------- /utils/online.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Online query takes one image as input 4 | 5 | Sketch="$1" 6 | 7 | Center="2048" 8 | 9 | Data="../../../data/Sketch/tf-idf_$Center" 10 | Dict="../../../data/Sketch/result_$Center.dict" 11 | Label="../../../data/Sketch/label_$Center" 12 | Model="../../../data/Sketch/models_ply/" 13 | View="../../../data/NormalSketch/views/" 14 | Sorted="../../../data/Sketch/views/" 15 | 16 | ././../Release/sketch -d $Data -w $Dict -l $Label -m $Model -f $Sketch -v $View -s $Sorted -t -------------------------------------------------------------------------------- /utils/Contour/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.2) 2 | 3 | SET(CMAKE_FIND_LIBRARY_PREFIXES "lib64;lib") 4 | SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so;.a") 5 | SET(CMAKE_SIZEOF_VOID_P "8") 6 | 7 | SET(OpenCV_STATIC OFF) 8 | FIND_PACKAGE(OpenCV REQUIRED) 9 | 10 | PROJECT(Contour) 11 | 12 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 13 | 14 | SET(SOURCE_FILES canny.cpp) 15 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 16 | ADD_EXECUTABLE(contour ${SOURCE_FILES}) 17 | TARGET_LINK_LIBRARIES(contour ${OpenCV_LIBS}) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.2) 2 | 3 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 4 | 5 | SET(CMAKE_FIND_LIBRARY_PREFIXES "lib64;lib") 6 | SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so;.a") 7 | SET(CMAKE_SIZEOF_VOID_P "8") 8 | 9 | SET(OpenCV_STATIC OFF) 10 | FIND_PACKAGE(OpenCV REQUIRED) 11 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 12 | 13 | SET(VTK_DIR "/usr/local") 14 | FIND_PACKAGE(VTK REQUIRED) 15 | INCLUDE(${VTK_USE_FILE}) 16 | 17 | PROJECT(Sketch) 18 | 19 | SET(MAIN src/sketch.cpp) 20 | ADD_EXECUTABLE(sketch ${MAIN} src/clusters.cpp src/tf_idf.cpp) 21 | 22 | TARGET_LINK_LIBRARIES(sketch ${OpenCV_LIBS} ${VTK_LIBRARIES}) -------------------------------------------------------------------------------- /utils/KMean/read_data.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 17/11/15. 3 | // 4 | 5 | #include "read_data.h" 6 | 7 | void read_int(std::ifstream& stream, int* val, bool little_endian) { 8 | // little endian 9 | if (little_endian) 10 | for (int i = sizeof(int) - 1; i >= 0; i--) 11 | stream.read(((char*)val) + i, 1); 12 | else 13 | stream.read((char*)val, sizeof(int)); 14 | } 15 | 16 | void read_bytes(std::ifstream& stream, uint8_t* val, int n) { 17 | stream.read((char*)val, sizeof(uint8_t) * n); 18 | } 19 | 20 | void read_floats(std::ifstream& stream, float* val, int n) { 21 | stream.read((char*)val, sizeof(float) * n); 22 | } 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /utils/renderer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # One folders with PLY format files and one folder to the image 4 | # Ex: bash renderer.sh ../../../data/TinySketch/models_ply/ ../../../data/TinySketch/view/ 6 5 | 6 | Files="$1*.ply" 7 | Dest_Folder="$2" 8 | 9 | Number="$3" 10 | 11 | for Src in $Files 12 | do 13 | # Get the file name (after the last '/') 14 | File="${Src##*/}" 15 | # Remove the extension 16 | Dest="${File:0:-4}"; 17 | # Create the folder 18 | rm -rf "$Dest_Folder$Dest"; 19 | mkdir "$Dest_Folder$Dest"; 20 | # Run the renderer 21 | ./Renderer/Release/renderor -g -n $Number -f $Src -t $Dest_Folder$Dest/; 22 | done -------------------------------------------------------------------------------- /utils/KMean/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.2) 2 | PROJECT(KMean) 3 | 4 | FIND_PACKAGE(CUDA REQUIRED) 5 | 6 | INCLUDE(FindCUDA) 7 | 8 | SET(OpenCV_STATIC OFF) 9 | FIND_PACKAGE(OpenCV REQUIRED) 10 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 11 | 12 | add_definitions(-D CUDA_ENABLED) 13 | 14 | CUDA_ADD_EXECUTABLE(k_mean k_mean_main.cpp k_mean.cpp kernel.cu read_data.cpp) 15 | 16 | LIST(APPEND CMAKE_CXX_FLAGS "-std=c++0x -ffast-math ") 17 | 18 | LIST(APPEND CUDA_NVCC_FLAGS --compiler-options -fno-strict-aliasing -lineinfo -use_fast_math -Xptxas -dlcm=cg) 19 | LIST(APPEND CUDA_NVCC_FLAGS -gencode arch=compute_30,code=sm_30) 20 | 21 | TARGET_LINK_LIBRARIES(k_mean ${OpenCV_LIBS} ${CUDA_CUDART_LIBRARIES} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_curand_LIBRARY} ${CUDA_cusparse_LIBRARY}) -------------------------------------------------------------------------------- /src/clusters.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 25/11/15. 3 | // 4 | 5 | #ifndef SKETCH_CLUSTERS_H 6 | #define SKETCH_CLUSTERS_H 7 | 8 | #include 9 | #include 10 | #include "opencv2/opencv.hpp" 11 | 12 | using namespace cv; 13 | 14 | /* 15 | * This class is similar to K-means, but it is a CPU version to find a nearest neighbor 16 | */ 17 | class Clusters { 18 | 19 | private: 20 | int center_count; 21 | int dim; // the dimension by default is the same as all query vectors 22 | 23 | public: 24 | float *centers; 25 | Clusters(); 26 | Clusters(float *centers, int center_count, int dim); 27 | virtual ~Clusters(); 28 | void find_center(float *vectors, int *allocation, int n) const; // get nearest neighbor for n vectors 29 | 30 | }; 31 | 32 | #endif //SKETCH_CLUSTERS_H 33 | -------------------------------------------------------------------------------- /utils/contour_extractor.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # One folders with PNG format files and one folder to the contour 4 | # Ex: bash contour_extractor.sh ../../../data/TinySketch/view/ ../../../data/TinySketch/contour/ 5 | 6 | Files="$1" 7 | Dest_Folder="$2" 8 | 9 | for Folder in $Files*/ 10 | do 11 | Dest_Folder_Name="${Folder%/}" 12 | Dest_Folder_Name="${Dest_Folder_Name##*/}" 13 | # Create the folder 14 | rm -rf "$Dest_Folder$Dest_Folder_Name/"; 15 | mkdir "$Dest_Folder$Dest_Folder_Name/"; 16 | 17 | for Src in $Folder* 18 | do 19 | #echo $Src 20 | # Get the file name (after the last '/') 21 | File="${Src##*/}" 22 | # Run the contour_extraction 23 | ./Contour/Release/contour -f $Src -t $Dest_Folder$Dest_Folder_Name/$File; 24 | done 25 | done -------------------------------------------------------------------------------- /utils/gabor_filter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # One folders with PNG format files and one folder to the contour 4 | # Ex: bash gabor_filter.sh ../../../data/Sketch/pipeline/view/ ../../../Sketch/pipeline/contour/ ../../../data/Sketch/pipeline/bin/ view.txt 36 5 | 6 | View_Foler="$1" 7 | Contour_Folder="$2" 8 | Dest_Folder="$3" 9 | Name="$4" 10 | Picture_Number="$5" 11 | 12 | for Folder in $View_Foler*/ 13 | do 14 | Dest_Name="${Folder%/}" 15 | Dest_Name="${Dest_Name##*/}" 16 | 17 | rm -rf "$Dest_Folder$Dest_Name"; 18 | mkdir "$Dest_Folder$Dest_Name"; 19 | #echo "./Gabor/Release/./gabor -i $Folder$Name -o $Dest_Folder$Dest_Name.bin -a $Contour_Folder$Dest_Name/ -m $Picture_Number" 20 | #./Gabor/Debug/./gabor -i $Folder$Name -o $Dest_Folder$Dest_Name.bin -a $Contour_Folder$Dest_Name/ -m $Picture_Number 21 | ./Gabor/Debug/./gabor -i $Folder$Name -o $Dest_Folder/$Dest_Name/ -a $Contour_Folder$Dest_Name/ -m $Picture_Number -d 1 22 | done -------------------------------------------------------------------------------- /src/tf_idf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 25/11/15. 3 | // 4 | 5 | #ifndef SKETCH_TF_IDF_H 6 | #define SKETCH_TF_IDF_H 7 | 8 | #include 9 | #include "opencv2/opencv.hpp" 10 | 11 | using namespace std; 12 | using namespace cv; 13 | 14 | /* 15 | * The class TF_IDF simulates a real database, given a new document with word frequency (TF), it looks into its 16 | * database and find the most similar document. We use the cosines method to define the similarity. 17 | */ 18 | class TF_IDF { 19 | 20 | private: 21 | int word_count; // number of words 22 | int document_count; // number of documents in the databases 23 | float *tf_idf; 24 | float *idf; // IDF value of each word 25 | float scalor_product(float *a, float *b,int n); 26 | 27 | public: 28 | TF_IDF(); 29 | TF_IDF(string database_file); 30 | 31 | virtual ~TF_IDF(); 32 | 33 | vector > find_nearest(int *tf_value); // given Tf vector, find the nearest document 34 | }; 35 | 36 | 37 | #endif //SKETCH_TF_IDF_H 38 | -------------------------------------------------------------------------------- /src/clusters.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 25/11/15. 3 | // 4 | 5 | #include "clusters.h" 6 | 7 | Clusters::Clusters() { } 8 | Clusters::Clusters(float *centers, int center_count, int dim) : 9 | centers(centers), center_count(center_count), dim(dim) { } 10 | 11 | Clusters::~Clusters() { } 12 | 13 | void Clusters::find_center(float *vectors, int *allocation, int n) const { 14 | // find neighbor for all instances 15 | for (int k = 0; k < n; k++) { 16 | int index = -1; 17 | float dist = std::numeric_limits::max(); 18 | 19 | // find the nearest neighbor 20 | for (int i = 0; i < center_count; i++) { 21 | float tmp = 0; 22 | for (int j = 0; j < dim; j++) 23 | tmp += (vectors[k * dim + j] - centers[i * dim + j]) * (vectors[k * dim + j] - centers[i * dim + j]); 24 | if (tmp < dist) { 25 | dist = tmp; 26 | index = i; 27 | } 28 | } 29 | 30 | allocation[k] = index; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /utils/encoder.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #Ex: bash encoder.sh ../../../data/Sketch/SHREC13_SBR_Model_Index.txt ../../../data/Sketch/views/ view.txt ../../../data/Sketch/contours/ ../../../data/Sketch/result_2048.dict ../../../data/Sketch/encodes/ 18 784 4 | #Ex: bash encoder.sh ../../../data/TinySketch/indexer.txt ../../../data/TinySketch/views/ view.txt ../../../data/TinySketch/contours/ ../../../data/TinySketch/result.dict ../../../data/TinySketch/encodes/ 36 784 5 | 6 | Indexer="$1" # file containing all models' name 7 | View_Path="$2" # folder to views (containing the selector result file) 8 | View_File="$3" # selector result name 9 | Contour_Path="$4" # path to contour folder 10 | Dict="$5"; # dictionary file 11 | Encode_Folder="$6" # path to output folder 12 | Cases="$7" # case number (select some images) 13 | Size="$8" # number of local features per image 14 | 15 | while read -r Line 16 | do 17 | #Model=$Line 18 | Model=${Line%%[[:space:]]} # trailing end of line 19 | #echo "./KMean/Release/k_mean -4 -f $View_Path$Model/$View_File -r $Contour_Path$Model/ -d $Dict -s 32 -o $Encode_Folder$Model.trans -c $Cases -a $Size" 20 | ./KMean/Release/k_mean -4 -f $View_Path$Model/$View_File -r $Contour_Path$Model/ -d $Dict -s 32 -o $Encode_Folder$Model.trans -c $Cases -a $Size 21 | done < "$Indexer" 22 | -------------------------------------------------------------------------------- /utils/PLYConverter/ply_converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * OFF to PLY 3D mesh converter 3 | * 4 | * The OFF and PLY are both formats for 3D mesh, they are extremely similar, 5 | * thus the conversion is merely a reformulating. The advantage of PLY is 6 | * that there is a built-in reader with VTK. 7 | * 8 | * Usage: ply_converter Path_to_OFF_file Path_to_PLY_file 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | int main(int argc, char *argv[]) { 17 | 18 | if (argc != 3) { 19 | cout << "Usage: ply_converter Path_to_PLY_file Path_to_OFF_file" << endl; 20 | return EXIT_FAILURE; 21 | } 22 | 23 | string off_path = argv[1]; 24 | string ply_path = argv[2]; 25 | 26 | ifstream off_file(off_path); 27 | ofstream ply_file(ply_path); 28 | 29 | string line; 30 | getline(off_file, line); 31 | 32 | int n, f; 33 | off_file >> n >> f; 34 | 35 | ply_file << "ply\n" 36 | << "format ascii 1.0\n" 37 | << "element vertex " << n << "\n" 38 | << "property float x\n" 39 | << "property float y\n" 40 | << "property float z\n" 41 | << "element face " << f << "\n" 42 | << "property list uchar int vertex_index\n" 43 | << "end_header\n"; 44 | 45 | getline(off_file, line); 46 | while (getline(off_file, line)) { 47 | ply_file << line << endl; 48 | } 49 | 50 | off_file.close(); 51 | ply_file.close(); 52 | 53 | return EXIT_SUCCESS; 54 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Sketch-based 3D model retrieval 2 | 3 | ## About 4 | 5 | This project is based on Mathias Eitz's paper "[Sketch-Based Shape Retrieval](http://cybertron.cg.tu-berlin.de/eitz/pdf/2012_siggraph_sbsr.pdf)". 6 | 7 | ## Idea 8 | 9 | The idea is quite simple, 3D models are complex and can not be given directly to any machine learning mecanisms. So, instead of using the whole 10 | model, we use different views of the model. Then we summarize the view with words using K-Means and run a nearest neighbor classifier on these bag-of-features. 11 | 12 | There are two parts in the project, the online query is coded in the src directory, whereas the offline preparation is in the utils folder. Some of the codes 13 | are similar. The offline part consists of multiple modules governed by shell script. 14 | 15 | ## Interesting components 16 | 17 | This repository contains some modules that can be used in other jobs. 18 | 19 | 1. K-Means with CUDA (CuBLAS, CuSPARSE, etc.) 20 | 2. TF-IDF database for document searching 21 | 3. Gabor filter 22 | 4. Contour extractor 23 | 5. PLY 3D model to 2D image with different angles 24 | 6. OFF to PLY converter 25 | 26 | ## How to use 27 | 28 | To run this code, you will need: 29 | 30 | 1. [Data](http://www.itl.nist.gov/iad/vug/sharp/contest/2013/SBR/): images and 3D models 31 | 2. VTK library 32 | 3. OpenCV 33 | 4. CUDA and Nvidia GPU 34 | 5. C++ 11 compiler 35 | 36 | Then create your own pipeline as follow: 37 | 38 | 1. Create a folder named 'pipeline' as the same level of 'src' 39 | 2. Put the model folder inside it 40 | 3. Generate PLY models and views pictures with script inside the 'utils' (You will need to compile first the program inside 'utils') -------------------------------------------------------------------------------- /utils/KMean/kernel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Kernel file, must be separated from cpp files 3 | * 4 | * Created by lyx on 17/11/15. 5 | */ 6 | 7 | #ifndef KERNEL_H 8 | #define KERNEL_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define fatalError(s) do { \ 17 | std::stringstream _where, _message; \ 18 | _where << __FILE__ << ':' << __LINE__; \ 19 | _message << std::string(s) + "\n" << __FILE__ << ':' << __LINE__; \ 20 | std::cerr << _message.str() << "\nAborting...\n"; \ 21 | cudaDeviceReset(); \ 22 | exit(1); \ 23 | } while(0) 24 | 25 | #define callCuda(status) do { \ 26 | std::stringstream _error; \ 27 | if (status != 0) { \ 28 | _error << "Cuda failure: " << status; \ 29 | fatalError(_error.str()); \ 30 | } \ 31 | } while(0) 32 | 33 | void set_value(float *d_m, int n, float value); 34 | void set_value(int *d_m, int n, int value); 35 | void set_sequence(int *d_m, int n, int start, int step); 36 | void transpose_scale(float *d_center, int n, int m, float *d_center_transpose, float *d_cluster_size); 37 | void square_minus(float *d_center, int n, int m, float *d_point, float *d_diff); 38 | void set_uniform_value(float* d_m, int n, float min, float max); 39 | void set_uniform_value(float* d_m, int n, float epsilon); 40 | void shake(float *d_m, float *d_scale ,int n); 41 | 42 | #endif //KERNEL_H 43 | -------------------------------------------------------------------------------- /src/tf_idf.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 25/11/15. 3 | // 4 | 5 | #include "tf_idf.h" 6 | 7 | TF_IDF::TF_IDF() { 8 | word_count = 0; 9 | document_count = 0; 10 | tf_idf = NULL; 11 | idf = NULL; 12 | } 13 | 14 | TF_IDF::TF_IDF(string database_file) { 15 | ifstream input(database_file); 16 | 17 | input.read((char*)&document_count, sizeof(int)); 18 | input.read((char*)&word_count, sizeof(int)); // dimension of each tf-idf vector 19 | 20 | idf = new float[word_count]; 21 | input.read((char*)idf, sizeof(float) * word_count); 22 | 23 | tf_idf = new float[document_count * word_count]; 24 | input.read((char*)tf_idf, sizeof(float) * document_count * word_count); 25 | 26 | input.close(); 27 | } 28 | 29 | float TF_IDF::scalor_product(float *a, float *b, int n) { 30 | float tmp = 0; 31 | for(int i = 0; i < n; i++) 32 | tmp += a[i] * b[i]; 33 | return tmp; 34 | } 35 | 36 | vector > TF_IDF::find_nearest(int *tf_value) { 37 | 38 | float norm = 0; 39 | float tmp = 0; 40 | 41 | for(int j = 0; j < word_count; j++){ 42 | tmp = tf_value[j] * idf[j]; 43 | norm += tmp * tmp; 44 | } 45 | norm = sqrt(norm); 46 | 47 | int max_index = -1; 48 | float max_value = -1; 49 | 50 | vector > result; 51 | 52 | for(int i = 0; i < document_count; i++){ 53 | tmp = 0; // dot product 54 | float norm_i = 0; 55 | for(int j = 0; j < word_count; j++){ 56 | tmp += tf_idf[i * word_count + j] * tf_value[j] * idf[j]; 57 | norm_i += tf_idf[i * word_count + j] * tf_idf[i * word_count + j]; 58 | } 59 | tmp = tmp / norm / sqrt(norm_i); 60 | /*if (max_index == -1 || tmp > max_value){ 61 | max_index = i; 62 | max_value = tmp; 63 | }*/ 64 | result.push_back(make_pair(tmp,i)); 65 | } 66 | return result; 67 | } 68 | 69 | TF_IDF::~TF_IDF() { 70 | if (idf) 71 | delete[] idf; 72 | if (tf_idf) 73 | delete[] tf_idf; 74 | }; -------------------------------------------------------------------------------- /utils/ViewSelector/selector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * View selector 3 | * 4 | * View selector sort the given views according to their area of shape. 5 | * 6 | * Parameter: 7 | * 8 | * input folder containing views of format png 9 | * output file (txt file) 10 | * 11 | * Output format: 12 | * [name of image with the largest shape area] [area of image] 13 | * [name of image with the second largest shape area] [area of image] 14 | * ... 15 | * 16 | * Usage: 17 | * view_selection [input_path] [output_path] 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace std; 29 | using namespace cv; 30 | 31 | string input,output; 32 | vector file_names; 33 | 34 | int areaCalculate(string filename){ 35 | Mat img = imread(filename, CV_LOAD_IMAGE_GRAYSCALE); 36 | //double mini, maxi; 37 | //minMaxLoc(img, &mini, &maxi); 38 | //img.convertTo(img,CV_8U, 255.0/(maxi - mini), -mini * 255.0/(maxi - mini)); 39 | //cout << mini <<' ' << maxi <(i,j)) != 0 ) 44 | area++; 45 | } 46 | } 47 | return area; 48 | } 49 | 50 | int main(int argc, char** argv) { 51 | DIR *dir; 52 | struct dirent *ent; 53 | int size = 0; 54 | 55 | input = argv[1]; 56 | output = argv[2]; 57 | 58 | //get all the file names in the input folder 59 | if ((dir = opendir(input.c_str())) != NULL){ 60 | while((ent = readdir(dir)) != NULL){ 61 | file_names.push_back(ent->d_name); 62 | size++; 63 | } 64 | } 65 | 66 | ofstream out(output); 67 | vector > dic; 68 | 69 | for(int i = 0; i < size; i++){ 70 | if (file_names[i][0] != 'm') 71 | continue; 72 | int k = areaCalculate((input + file_names[i]).c_str()); 73 | if (k == 0) 74 | cout << (input + file_names[i]).c_str() << endl; 75 | dic.push_back(make_pair(k, file_names[i])); 76 | } 77 | //sort according to its area 78 | sort(dic.begin(),dic.end()); 79 | 80 | for(int i = dic.size() - 1; i > 0; i--) 81 | out << dic[i].second <<' ' << dic[i].first << endl; 82 | 83 | out.close(); 84 | 85 | cout << input << " done!" << endl; 86 | return EXIT_SUCCESS; 87 | } -------------------------------------------------------------------------------- /utils/Sample/sample.cpp: -------------------------------------------------------------------------------- 1 | /* Sample 2 | * 3 | * Sample selects qualified vectors among vectors of each view. 4 | * (qualified vector is the one who has different colors) 5 | * 6 | * Parameters: 7 | * txt file with sorted view files' names 8 | * folder of views' binary files 9 | * output file with selected vectors 10 | * 11 | * Output format: 12 | * N (32 bits integer):number of vectors m (32 bits integer):size of vectors 13 | * vectors(32 bits float) 14 | * 15 | * Usage: 16 | * sample [txt file path] [folder path] [output path] 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace std; 25 | using namespace cv; 26 | const int lines = 25000; 27 | const int size = 512; 28 | 29 | string input, bin_path, output; 30 | int line_number; 31 | float *result; 32 | 33 | bool same_color(float *val){ 34 | int i = 1; 35 | float k = val[0]; 36 | while(i < size) { 37 | //cout << val[i] << ' '; 38 | if (abs(val[i] - k) > 5.0) 39 | return false; 40 | i++; 41 | } 42 | return true; 43 | } 44 | 45 | 46 | void get_vectors(string file){ 47 | ifstream in(file); 48 | 49 | int a,b; 50 | if(!in.read((char*)&a,sizeof(int))) 51 | return; 52 | if(!in.read((char*)&b, sizeof(int))) 53 | return; 54 | 55 | int k = 0; 56 | float* val = new float[size]; 57 | while(line_number < lines && (!in.eof()) && (k < lines)){ 58 | in.read((char*)val,size * sizeof(float)); 59 | k++; 60 | if (!same_color(val)){ 61 | for(int i = 0; i < size; i++) 62 | result[line_number * size + i] = val[i]; 63 | line_number++; 64 | } 65 | } 66 | delete[] val; 67 | in.close(); 68 | } 69 | 70 | 71 | int main(int argc, char** argv) { 72 | 73 | input = argv[1]; 74 | bin_path = argv[2]; 75 | output = argv[3]; 76 | 77 | ifstream in(input); 78 | int tmp; 79 | line_number = 0; 80 | string file_name; 81 | 82 | result = new float[lines * size]; 83 | 84 | while(line_number < lines && (!in.eof())){ 85 | in >> file_name >> tmp; 86 | file_name = file_name.substr(0, file_name.length() - 4); 87 | get_vectors(bin_path + file_name +".bin"); 88 | } 89 | 90 | ofstream out(output); 91 | 92 | out.write((char*)&line_number, sizeof(int)); 93 | out.write((char*)&size, sizeof(int)); 94 | out.write((char*)result, sizeof(float) * size * line_number); 95 | 96 | out.close(); 97 | cout << output << endl; 98 | 99 | delete[] result; 100 | return EXIT_SUCCESS; 101 | } -------------------------------------------------------------------------------- /utils/KMean/k_mean_demo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Demo 3 | * 4 | * This is a demo program that show how to use the class K-Mean, there are two samples: one easy and one from 5 | * MNIST classification problem. 6 | * 7 | * To compile the demo, change the CMakeLists.txt. Make sure you have downloads the MNIST data file in the working 8 | * directory. 9 | */ 10 | 11 | #include "k_mean.h" 12 | 13 | using namespace k_mean; 14 | 15 | string input, output, dictionary; 16 | float *data; 17 | int center_count = 0; 18 | int data_count = 0; 19 | int dim = 0; 20 | 21 | void test_2d() { 22 | 23 | data_count = 12; 24 | center_count = 3; 25 | dim = 2; 26 | 27 | assert(data_count >= center_count); 28 | 29 | data = new float[dim * data_count]; 30 | data[0] = 0; 31 | data[1] = 0; 32 | data[2] = 1; 33 | data[3] = 0; 34 | data[4] = 1; 35 | data[5] = 1; 36 | data[6] = 0; 37 | data[7] = 1; 38 | data[8] = 10; 39 | data[9] = 10; 40 | data[10] = 11; 41 | data[11] = 10; 42 | data[12] = 11; 43 | data[13] = 11; 44 | data[14] = 10; 45 | data[15] = 11; 46 | data[16] = 10; 47 | data[17] = 0; 48 | data[18] = 11; 49 | data[19] = 0; 50 | data[20] = 11; 51 | data[21] = 1; 52 | data[22] = 10; 53 | data[23] = 1; 54 | 55 | K_Mean model(data, data_count, dim, center_count); 56 | model.execute(10, 0.1); 57 | 58 | delete[] data; 59 | 60 | } 61 | 62 | void test_mnist() { 63 | 64 | // data file 65 | input = "t10k-images.idx3-ubyte"; 66 | center_count = 10; 67 | 68 | ifstream file(input); 69 | int tmp, row, col; 70 | read_int(file, &tmp); 71 | read_int(file, &data_count); 72 | read_int(file, &row); 73 | read_int(file, &col); 74 | dim = row * col; 75 | uint8_t *_data = new uint8_t[dim * data_count]; 76 | read_bytes(file, _data, dim * data_count); 77 | file.close(); 78 | 79 | data = new float[dim * data_count]; 80 | for (int i = 0; i < dim * data_count; i++) 81 | data[i] = float(_data[i]); 82 | 83 | // train the model 84 | K_Mean model(data, data_count, dim, center_count); 85 | model.execute(50, 0.05); 86 | 87 | float *center = new float[dim * center_count]; 88 | model.get_clusters(center); 89 | 90 | // show result 91 | for (int i = 0; i < center_count; i++) { 92 | Mat m(row, col, CV_32F, center + i * dim); 93 | Mat img; 94 | m.convertTo(img, CV_8U); 95 | imshow("Image" + to_string(i), img); 96 | } 97 | waitKey(0); 98 | 99 | delete[] center; 100 | delete[] data; 101 | 102 | } 103 | 104 | int main() { 105 | srand(time(NULL)); 106 | test_mnist(); 107 | return EXIT_SUCCESS; 108 | } -------------------------------------------------------------------------------- /utils/Merge/merge.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Merge Binary Files 3 | * 4 | * Merge will merge all the binary files in a folder into one binary file 5 | * 6 | * Parameters: 7 | * a: [Folder_path] 8 | * o: [Output_file_name] 9 | * n: [Number_of_total_lines] 10 | * d: [Dimension of each line] 11 | * s: Skip size 12 | * 13 | * Usage: 14 | * merge -a [Folder_path] -o [Output_file_name] -n [Number_of_total_lines] -d [Dimension] -s [Skip] 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std; 22 | 23 | string path, name, output; 24 | int k, dim, N, skip; 25 | float *result; 26 | 27 | bool parse_command_line(int argc, char **argv) { 28 | int i = 1; 29 | while(i < argc) { 30 | if (argv[i][0] != '-') 31 | break; 32 | switch(argv[i][1]) { 33 | case 'n': 34 | N = atoi(argv[++i]); 35 | break; 36 | case 'a': // input file 37 | path = argv[++i]; 38 | break; 39 | case 'o': // output file 40 | output = argv[++i]; 41 | break; 42 | case 'd': 43 | dim = atoi(argv[++i]); 44 | break; 45 | case 's': 46 | skip = atoi(argv[++i]); 47 | break; 48 | } 49 | i++; 50 | } 51 | if (path == "" || output == "" ) { // invalid file name 52 | return false; 53 | } 54 | return true; 55 | } 56 | 57 | void merge(string filename){ 58 | 59 | if (!ifstream(filename)) 60 | return; 61 | ifstream input(filename); 62 | int n, size; 63 | if (!input.read((char*)&n, sizeof(int))) 64 | return; 65 | if (!input.read((char*)&size, sizeof(int))) 66 | return; 67 | float tmp; 68 | //cout << n <<' ' << size; 69 | for(int i = 0; i < n * size; i++){ 70 | input.read((char*)&tmp, sizeof(float)); 71 | result[k++] = tmp; 72 | } 73 | 74 | } 75 | int main(int argc, char** argv) { 76 | 77 | if (!parse_command_line(argc, argv)) 78 | return EXIT_FAILURE; 79 | 80 | DIR *dir; 81 | struct dirent *ent; 82 | result = new float[N * dim]; 83 | k = 0; 84 | int index = 0; 85 | if ((dir = opendir(path.c_str())) != NULL){ 86 | while((ent = readdir(dir)) != NULL){ 87 | string filename = ent->d_name; 88 | index++; 89 | if (index % skip != 0) 90 | continue; 91 | //cout << path + "/" + filename << endl; 92 | merge(path + "/" + filename); 93 | } 94 | } 95 | 96 | ofstream out(output); 97 | int lines = k / dim; 98 | out.write((char*)&lines, sizeof(int)); 99 | out.write((char*)&dim, sizeof(int)); 100 | out.write((char*)result, sizeof(float) * k); 101 | out.close(); 102 | 103 | return EXIT_SUCCESS; 104 | } -------------------------------------------------------------------------------- /utils/KMean/k_mean.h: -------------------------------------------------------------------------------- 1 | /* 2 | * K-Mean algorithm 3 | * 4 | * Created by lyx on 23/11/15. 5 | */ 6 | 7 | #ifndef K_MEAN_H 8 | #define K_MEAN_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "kernel.h" 22 | #include "read_data.h" 23 | 24 | using namespace std; 25 | 26 | namespace k_mean { 27 | 28 | class K_Mean { 29 | 30 | private: 31 | 32 | float *data; // data to be clustered (vectors are stored line after line) 33 | float *d_data; // device copy of data 34 | int data_count; // number of vectors 35 | 36 | float *center; // centers of k-mean 37 | float *d_center; // device copy of centers 38 | float *d_center_transpose; // temporary matrix 39 | float *d_tmp_diff; // temporary matrix to calculate the nearest neighbor 40 | float *d_tmp_dist; // temporary array to store distance 41 | int center_count; // number of centers (k) 42 | 43 | int *allocation; // index of all vectors (one dimension array), same as d_allocation_col_csr 44 | 45 | cusparseMatDescr_t d_allocation_descr; 46 | float *d_allocation_val_csr; // allocation result of all vectors (sparse matrix of 0 or 1) 47 | int *d_allocation_row_csr; // row numbers (from 0 to data_count) 48 | int *d_allocation_col_csr; // column numbers (from 0 to center_count) 49 | float *d_allocation_val_csc; // csc is just the transpose in some way 50 | int *d_allocation_row_csc; // this one still store the pointer 51 | int *d_allocation_col_csc; 52 | 53 | float *cluster_size; // size of all clusters 54 | float *d_cluster_size; // device copy 55 | 56 | float *d_one; // all one vector (the length is large enough) 57 | 58 | int dim; // vector dimension 59 | 60 | cublasHandle_t cublas_handle; 61 | cusparseHandle_t cusparse_handle; 62 | 63 | void initialize_monoid(); // use existing points as centers 64 | 65 | void initialize_centroid(); // use random centers 66 | 67 | void find_nearest_center(); 68 | 69 | void update_center(); // compute the new barycenter of the group 70 | 71 | void shake_center(float delta); // move a little the existing center to leave local minimum 72 | 73 | void print_center(); 74 | 75 | public: 76 | 77 | K_Mean(float *data, int data_count, int dim, int center_count); // train a clustering function 78 | 79 | K_Mean(float *data, float* center, int data_count, int dim, int center_count); // classify an image with a dictionary 80 | 81 | virtual ~K_Mean(); 82 | 83 | void execute(int iteration, float delta); // train 84 | 85 | void save(string file, bool add_null = true); // save the center information into a file 86 | 87 | void translate(int *result); // get cluster number of all vectors 88 | 89 | void get_clusters(float *dest); // get center coordinates 90 | 91 | void update_data(); 92 | 93 | }; 94 | } 95 | 96 | #endif //K_MEAN_H 97 | -------------------------------------------------------------------------------- /utils/Contour/canny.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Contour extraction with Canny algorithm 3 | * 4 | * This program uses Canny module from OpenCV to extract the contour of an image. For the algorithm's detail, 5 | * please read the OpenCV documentation. 6 | * 7 | * Parameters: 8 | * f (path to the input image) 9 | * t (path to store the result) 10 | * s (parameters to tune the result) 11 | * Ex: contour -f [Path_to_input_image] -t [Path_to_contour_image] [-s max_threshold min_threshold] 12 | * 13 | * For large image, use GPU can accelerate a lot. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #ifdef CUDA_ENABLED 20 | #include 21 | #else 22 | #include 23 | #endif 24 | 25 | using namespace std; 26 | using namespace cv; 27 | 28 | #ifdef CUDA_ENABLED 29 | using namespace cv::cuda; 30 | #endif 31 | 32 | char *input,*output; 33 | int max_threshold, min_threshold; 34 | 35 | void show_help(){ 36 | printf("Contour extraction with Canny algorithm\n" 37 | "\n" 38 | "This program uses Canny module from OpenCV to extract the contour of an image. For the algorithm's detail,\n" 39 | "please read the OpenCV documentation.\n" 40 | "\n" 41 | "Parameters:\n" 42 | " f (path to the input image)\n" 43 | " t (path to store the result)\n" 44 | " s (parameters to tune the result)\n" 45 | "Ex: contour -f [Path_to_input_image] -t [Path_to_contour_image] [-s max_threshold min_threshold]\n"); 46 | } 47 | 48 | bool parse_command_line(int argc, char **argv) { 49 | int i = 1; 50 | while(i < argc) { 51 | if (argv[i][0] != '-') 52 | break; 53 | switch(argv[i][1]) { 54 | case 'h': // help 55 | show_help(); 56 | return false; 57 | case 's': // threshold flag 58 | max_threshold = atoi(argv[++i]); 59 | min_threshold = atoi(argv[++i]); 60 | break; 61 | case 'f': // input file 62 | input = argv[++i]; 63 | break; 64 | case 't': // output file 65 | output = argv[++i]; 66 | break; 67 | } 68 | i++; 69 | } 70 | if (input == NULL || output == NULL) { // invalid file name 71 | show_help(); 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | int main(int argc, char** argv) { 78 | 79 | input = NULL; 80 | output = NULL; 81 | 82 | // default parameters 83 | min_threshold = 300; 84 | max_threshold = 500; 85 | 86 | if (!parse_command_line(argc, argv)) 87 | return EXIT_FAILURE; 88 | 89 | Mat image = imread(input, IMREAD_GRAYSCALE); // use 8-bits image 90 | 91 | #ifdef CUDA_ENABLED 92 | GpuMat d_image; 93 | d_image.upload(image); 94 | GpuMat d_contour; 95 | Ptr detector = createCannyEdgeDetector(min_threshold, max_threshold); 96 | #endif 97 | 98 | Mat outline; 99 | 100 | #ifdef CUDA_ENABLED 101 | detector->detect(d_image, d_contour); 102 | d_contour.download(outline); 103 | #else 104 | Canny(image, outline, min_threshold, max_threshold); 105 | #endif 106 | 107 | imwrite(output, outline); // save the image file 108 | 109 | return EXIT_SUCCESS; 110 | } -------------------------------------------------------------------------------- /utils/KMean/kernel.cu: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 17/11/15. 3 | // 4 | 5 | #include "kernel.h" 6 | #include 7 | 8 | #define BLOCK_SIZE 256 9 | 10 | template 11 | __device__ T square(const T x) { 12 | return x * x; 13 | } 14 | 15 | template 16 | __global__ void kernel_sequence(T *d_m, int n, T start, T step) { 17 | int i = blockDim.x * blockIdx.x + threadIdx.x; 18 | if (i < n) 19 | d_m[i] = start + step * i; 20 | } 21 | 22 | template 23 | __global__ void kernal_set_value(T *d_m, int n, T value) { 24 | int i = blockDim.x * blockIdx.x + threadIdx.x; 25 | if (i < n) 26 | d_m[i] = value; 27 | } 28 | 29 | template 30 | __global__ void kernel_scale(T *d_m, float *d_scale, int n) { 31 | int i = blockDim.x * blockIdx.x + threadIdx.x; 32 | if (i < n) 33 | d_m[i] *= 1 + d_scale[i]; 34 | } 35 | 36 | template 37 | __global__ void kernel_square_minus(T *d_center, int n, int m, T *point, T *d_diff) { 38 | int id_center = blockIdx.x; // blockDim.x == dim && max(blockIdx.x) == center_count 39 | int id_pos = threadIdx.x; // max(threadIdx.x) == dim 40 | if (id_pos < n && id_center < m) 41 | d_diff[id_center * blockDim.x + id_pos] = square(d_center[id_center * blockDim.x + id_pos] - point[id_pos]); 42 | } 43 | 44 | template 45 | __global__ void kernel_transpose_scale(T *d_center, int n, int m, T *d_center_tmp, T *size) { 46 | // each column of d_center (n * m) correspond to each row of d_center_tmp * size[row] 47 | int row = threadIdx.x; 48 | int col = blockIdx.x; 49 | // m == center_count && n == dim 50 | if (row + m * col < n * m && row * n + col < n * m) { 51 | if (size[col] != 0) 52 | d_center[row + m * col] = d_center_tmp[row * n + col] / size[col]; 53 | else 54 | d_center[row + m * col] = d_center_tmp[row * n + col]; 55 | } 56 | } 57 | 58 | template 59 | __global__ void kernel_uniform_scale(T *x, int n, T min, T max) { 60 | int i = blockDim.x * blockIdx.x + threadIdx.x; 61 | if (i < n) 62 | x[i] = x[i] * (max - min) + min; 63 | } 64 | 65 | void set_value(float *d_m, int n, float value) { 66 | if (value == 0) 67 | callCuda(cudaMemset(d_m, 0, sizeof(float) * n)); 68 | else 69 | kernal_set_value<<<(n - 1 + BLOCK_SIZE) / BLOCK_SIZE, BLOCK_SIZE>>>(d_m, n, value); 70 | } 71 | 72 | void set_value(int *d_m, int n, int value) { 73 | if (value == 0) 74 | callCuda(cudaMemset(d_m, 0, sizeof(int) * n)); 75 | else 76 | kernal_set_value<<<(n - 1 + BLOCK_SIZE) / BLOCK_SIZE, BLOCK_SIZE>>>(d_m, n, value); 77 | } 78 | 79 | void set_sequence(int *d_m, int n, int start, int step) { 80 | kernel_sequence<<<(n - 1 + BLOCK_SIZE) / BLOCK_SIZE, BLOCK_SIZE>>>(d_m, n, start, step); 81 | } 82 | 83 | void transpose_scale(float *d_center, int n, int m, float *d_center_transpose, float *d_cluster_size) { 84 | kernel_transpose_scale<<>>(d_center, m, n, d_center_transpose, d_cluster_size); 85 | } 86 | 87 | void square_minus(float *d_center, int n, int m, float *d_point, float *d_diff) { 88 | kernel_square_minus<<>>(d_center, n, m, d_point, d_diff); 89 | } 90 | 91 | void set_uniform_value(float *d_m, int n, float min, float max) { 92 | int threadsPerBlock = 256; 93 | int blocksPerGrid = (n + threadsPerBlock - 1) / threadsPerBlock; 94 | curandGenerator_t generator; 95 | curandCreateGenerator(&generator, CURAND_RNG_PSEUDO_MTGP32); 96 | curandSetPseudoRandomGeneratorSeed(generator, rand()); 97 | curandGenerateUniform(generator, d_m, n); 98 | kernel_uniform_scale <<>>(d_m, n, min, max); 99 | curandDestroyGenerator(generator); 100 | } 101 | 102 | void set_uniform_value(float *d_m, int n, float epsilon) { 103 | set_uniform_value(d_m, n, 0, epsilon); 104 | } 105 | 106 | void shake(float *d_m, float *d_scale ,int n) { 107 | kernel_scale<<<(n - 1 + BLOCK_SIZE) / BLOCK_SIZE, BLOCK_SIZE>>>(d_m, d_scale, n); 108 | } -------------------------------------------------------------------------------- /utils/Database/database.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Database generator 3 | * 4 | * Database generator creates the tf-idf index table with a set of given documents. 5 | * By calculating the idf value of each word in the document set and the tf value of each word in each file, 6 | * we can get the keyword of each file. 7 | * 8 | * Parameters: 9 | * i: input folder containing translated images (one file per model) 10 | * d: database with TF-IDF value for all pair of word and image 11 | * m: txt file indicate the correspond model of each image 12 | * k: number of word 13 | * 14 | * N.B. The translated image file per model has the following format 15 | * 16 | * Image_Count (32 bits integer) Dim (32 bits integer) 17 | * Image_1 (Dim * 32 bits integer) 18 | * ... 19 | * Image_2 (Dim * 32 bits integer) 20 | * 21 | * The Dim represent the number of selected point or the number of local features of an image 22 | * 23 | * Output format: Database of TF-IDF value: 24 | * 25 | * Image_Count (32 bits integer) Word_Count (32 bits integer) 26 | * IDF_1 ... (32 bits float) 27 | * Value_Image_1_Word_1 ... 28 | * ... 29 | * Value_Image_i_Word_1 ... Value_Image_i_Word_j ... (32 bits float) 30 | * ... 31 | *Output index format: 32 | * Model_count Views_per_model 33 | * Model_index 34 | * ... 35 | * 36 | * Usage: 37 | * build_database -i [input_path] -d [output_database_path] -m [output_index_path] -k [number of word] 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | using namespace std; 49 | 50 | int center_count, document_count, image_per_model; // total number of image 51 | string input, output_data, output_index; 52 | float* idf; 53 | vector> tf; // temporary tf values 54 | float* dict; // tf-idf values 55 | vector models; 56 | 57 | //read one file 58 | void read_file(string path, string file_name){ 59 | 60 | if (file_name[0] == '.') 61 | return; 62 | 63 | set word_set; 64 | 65 | ifstream input(path + file_name); 66 | 67 | int image_count, feature_count; // number of image per model 68 | 69 | if (!input.read((char*)&image_count, sizeof(int))) 70 | return; 71 | if (!input.read((char*)&feature_count, sizeof(int))) 72 | return; 73 | 74 | image_per_model = image_count; 75 | 76 | int *feature = new int[feature_count]; 77 | 78 | for(int i = 0; i < image_count; i++){ 79 | word_set.clear(); 80 | 81 | //calculate the local tf value 82 | vector tf_value(center_count); // use vector is easier than pointer 83 | fill(tf_value.begin(), tf_value.end(), 0); // init at 0 84 | 85 | input.read((char*) feature, sizeof(int) * feature_count); 86 | for(int j = 0; j < feature_count; j++){ 87 | word_set.insert(feature[j]); 88 | tf_value[feature[j]]++; // build tf vector 89 | } 90 | 91 | tf.push_back(tf_value); 92 | 93 | //update the occurrence of each word 94 | for(auto j: word_set) 95 | idf[j]++; 96 | 97 | document_count++; 98 | assert(document_count == tf.size()); 99 | } 100 | 101 | delete[] feature; 102 | 103 | string name = file_name.substr(1,file_name.find('.')-1); 104 | models.push_back(name); 105 | 106 | input.close(); 107 | } 108 | 109 | bool parse_command_line(int argc, char **argv) { 110 | int i = 1; 111 | while(i < argc) { 112 | if (argv[i][0] != '-') 113 | break; 114 | switch(argv[i][1]) { 115 | case 'h': // help 116 | return false; 117 | case 'k': // threshold flag 118 | center_count = atoi(argv[++i]); 119 | break; 120 | case 'i': // input file 121 | input = argv[++i]; 122 | break; 123 | case 'd': // output file 124 | output_data = argv[++i]; 125 | break; 126 | case 'm': 127 | output_index = argv[++i]; 128 | break; 129 | } 130 | i++; 131 | } 132 | if (input.length() <= 0 || output_data.length() <= 0 || output_index.length() <= 0) { // invalid file name 133 | return false; 134 | } 135 | return true; 136 | } 137 | 138 | int main(int argc, char** argv) { 139 | 140 | if (!parse_command_line(argc, argv)) 141 | return EXIT_FAILURE; 142 | 143 | idf = new float[center_count]; 144 | document_count = 0; 145 | 146 | DIR *dir; 147 | struct dirent *ent; 148 | 149 | if((dir = opendir(input.c_str()))==NULL) 150 | return EXIT_FAILURE; 151 | 152 | //treat each file 153 | while((ent = readdir(dir))!=NULL) 154 | read_file(input, ent->d_name); 155 | 156 | //calculate the idf value 157 | for(int i = 0; i < center_count; i++) { 158 | idf[i] = idf[i] == 0 ? 0 : log((float) document_count / idf[i]); 159 | } 160 | 161 | dict = new float[document_count * center_count]; 162 | 163 | //calculate the tf-idf value 164 | for(int i = 0; i < document_count; i++) 165 | for(int j = 0; j < center_count; j++) 166 | dict[i * center_count + j] = tf[i][j] * idf[j]; 167 | 168 | // write the dictionary 169 | ofstream out_dict(output_data); 170 | 171 | out_dict.write((char*)&document_count, sizeof(int)); 172 | out_dict.write((char*)¢er_count, sizeof(int)); 173 | 174 | out_dict.write((char*)idf, sizeof(float) * center_count); 175 | out_dict.write((char*)dict, sizeof(float) * document_count * center_count); 176 | 177 | delete[] idf; 178 | delete[] dict; 179 | 180 | out_dict.close(); 181 | 182 | // write down the model number 183 | ofstream out_index(output_index); 184 | out_index << models.size() << ' ' << image_per_model << endl; 185 | for(int i = 0; i < models.size(); i++) 186 | out_index << models[i] << endl; 187 | out_index.close(); 188 | 189 | return EXIT_SUCCESS; 190 | } -------------------------------------------------------------------------------- /utils/Gabor/gabor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Gabor filter 3 | * 4 | * Gabor filter is one way to extract edge or other features from an image. By using multiple filters with 5 | * different orientation, one can build a bag-of-features for a given image. This program takes as parameters 6 | * the kernel size, orientation, etc.. 7 | * 8 | * Parameters: 9 | * k: number of orientations 10 | * p: number of points 11 | * n: size of the kernel 12 | * s[sigma]: standard deviation of the gaussian envelope 13 | * t[theta]: orientation of the normal to the parallel stripes of a Gabor function 14 | * l[lambda]: wavelength of the sinusoidal factor 15 | * g[gamma]: spatial aspect ratio 16 | * i: input image 17 | * o: output file 18 | * a: picture_path 19 | * m: number of pictures to read 20 | * d: draw gabor or not 21 | * 22 | * Usage: 23 | * gabor [-k k] [-p number of points] [-n n] [-s sigma] [-t theta] [-l lambda] [-b beta] -i [Path_to_input] -o [Path_to_output] -a [picture_path] [-d on/off] 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | using namespace std; 32 | using namespace cv; 33 | 34 | string input, output, picture_path; 35 | 36 | int kernel_size = 15; 37 | int k = 8; // number of directions for Gabor filter 38 | int window_size = 8; // local feature area (not size) 39 | int point_per_row = 28; 40 | int picture_number = 1; 41 | double sigma = 4; 42 | double theta = 0; 43 | double lambda = 10.0; 44 | double beta = 0.5; 45 | bool draw_gabor = false; 46 | 47 | vector filters(k); // k filters 48 | vector result; // matrix representing the image 49 | 50 | // get local feature for a specific point 51 | float* get_vector(int x, int y) { 52 | 53 | float* res = new float[k * window_size * window_size]; // size of one local feature 54 | 55 | int d = 0; 56 | for(int i = 0; i < k; i++) // iterate over all filters 57 | for(int u = 0; u < window_size; u++) 58 | for(int v = 0; v < window_size; v++) 59 | res[d++] = filters[i].at(u + x, v + y); 60 | 61 | // return a pointer, need to call delete 62 | return res; 63 | 64 | } 65 | 66 | 67 | void save(string output) { 68 | 69 | ofstream out(output); 70 | int lines = result.size(); // number of local features per image 71 | int dimension = k * window_size * window_size; 72 | 73 | out.write((char*)&lines, sizeof(int)); 74 | out.write((char*)&dimension, sizeof(int)); 75 | 76 | for(float* v: result) 77 | out.write((char*)v, sizeof(float) * dimension); 78 | 79 | out.close(); 80 | 81 | for(float* v: result) 82 | delete[] v; 83 | } 84 | 85 | void show_help() { 86 | printf("Gabor filter\n" 87 | "\n" 88 | "Gabor filter is one way to extract edge or other features from an image. By using multiple filters with\n" 89 | "different orientation, one can build a bag-of-features for a given image. This program takes as parameters\n" 90 | "the kernel size, orientation, etc..\n" 91 | "\n" 92 | "Parameters:\n" 93 | " k: number of orientations\n" 94 | " p: number of points\n" 95 | " n: size of the kernel\n" 96 | " s[sigma]: standard deviation of the gaussian envelope\n" 97 | " t[theta]: orientation of the normal to the parallel stripes of a Gabor function\n" 98 | " l[lambda]: wavelength of the sinusoidal factor\n" 99 | " g[gamma]: spatial aspect ratio\n" 100 | " i: input image\n" 101 | " o: output image folder (with '/' in the end)\n" 102 | "\n" 103 | "Usage:\n" 104 | " gabor -k [k] -n [n] -s [sigma] -t [theta] -l [lambda] -b [beta] -i [Path_to_input] -o [Path_to_output]\n"); 105 | 106 | } 107 | 108 | bool parse_command_line(int argc, char **argv) { 109 | int i = 1; 110 | while(i < argc) { 111 | if (argv[i][0] != '-') 112 | break; 113 | switch(argv[i][1]) { 114 | case 'h': // help 115 | show_help(); 116 | return false; 117 | case 'k': 118 | k = atoi(argv[++i]); 119 | break; 120 | case 'p': 121 | point_per_row = atoi(argv[++i]); 122 | break; 123 | case 'n': 124 | kernel_size = atoi(argv[++i]); 125 | break; 126 | case 's': 127 | sigma = atof(argv[++i]); 128 | break; 129 | case 't': 130 | theta = atof(argv[++i]); 131 | break; 132 | case 'l': 133 | lambda = atof(argv[++i]); 134 | break; 135 | case 'g': 136 | beta = atof(argv[++i]); 137 | break; 138 | case 'i': // input file 139 | input = argv[++i]; 140 | break; 141 | case 'o': // output file 142 | output = argv[++i]; 143 | break; 144 | case 'a': 145 | picture_path = argv[++i]; 146 | break; 147 | case 'm': 148 | picture_number = atoi(argv[++i]); 149 | break; 150 | case 'd': 151 | if (atoi(argv[++i]) == 1) 152 | draw_gabor = true; 153 | break; 154 | 155 | } 156 | i++; 157 | } 158 | if (input == "" || output == "" || picture_path == "") { // invalid file name 159 | show_help(); 160 | return false; 161 | } 162 | return true; 163 | } 164 | 165 | void show_mat(Mat &m){ 166 | for(int i = 0; i < m.rows; i++){ 167 | for(int j = 0; j < m.cols; j++) 168 | cout << m.at(i, j) << ' '; 169 | cout << endl; 170 | } 171 | } 172 | 173 | void show_middle_row(Mat &m){ 174 | for(int i = 0; i < m.cols; i++) 175 | cout << m.at(m.rows / 2, i) << ' '; 176 | cout << endl; 177 | } 178 | 179 | void convertTo(Mat & a, Mat &b){ 180 | double maxi, mini; 181 | minMaxLoc(a,&mini, &maxi); 182 | for(int i = 0; i < a.rows; i++){ 183 | for(int j = 0; j < a.cols; j++){ 184 | b.at(i,j) = (uchar)((a.at(i,j)-mini)/(maxi - mini) * 255.0); 185 | } 186 | } 187 | } 188 | 189 | int main(int argc, char** argv) { 190 | 191 | if (!parse_command_line(argc, argv)) 192 | return 0; 193 | 194 | ifstream in(input); 195 | string picture_name; 196 | 197 | int tmp; 198 | 199 | //calculate of kernels 200 | vector kernels; 201 | double step = CV_PI / k; 202 | for(int i = 0; i < k; i++) { 203 | Mat kernel = getGaborKernel(Size(kernel_size, kernel_size), sigma, 204 | theta + step * (double) i, lambda, beta, CV_PI * 0.5, CV_32F); 205 | kernels.push_back(kernel); 206 | if (draw_gabor){ 207 | Mat kernel_grey(Size(kernel_size,kernel_size),CV_8U), heatkernel; 208 | 209 | convertTo(kernel,kernel_grey); 210 | applyColorMap(kernel_grey, heatkernel, COLORMAP_HSV); 211 | string output_path = output+"kernel" + to_string(i) +".png"; 212 | //cout << output_path << endl; 213 | imwrite(output_path,heatkernel); 214 | } 215 | } 216 | 217 | for(int pictures = 0; pictures < picture_number; pictures++) { 218 | in >> picture_name >> tmp; 219 | 220 | //cout << input <<' ' << output <<' ' << picture_path << ' ' << picture_name << endl; 221 | 222 | Mat img = imread(picture_path + picture_name, CV_LOAD_IMAGE_GRAYSCALE); 223 | Mat src; 224 | img.convertTo(src, CV_32F); 225 | 226 | 227 | // build all Gabor filters 228 | for (int i = 0; i < k; i++) { 229 | 230 | filter2D(src, filters[i], -1, kernels[i], Point(-1, -1), 0, BORDER_DEFAULT); 231 | 232 | if (draw_gabor){ 233 | Mat filter_grey(Size(filters[i].cols,filters[i].rows), CV_8U), filter_heat; 234 | 235 | convertTo(filters[i],filter_grey); 236 | 237 | applyColorMap(filter_grey, filter_heat, COLORMAP_HSV); 238 | string output_path = output + "filter" + to_string(i) + ".png"; 239 | // cout << output_path << endl; 240 | imwrite(output_path, filter_heat); 241 | } 242 | } 243 | 244 | 245 | 246 | // uniformly distributed points 247 | int row_gap = (img.rows - window_size) / point_per_row; 248 | int col_gap = (img.cols - window_size) / point_per_row; 249 | 250 | /*int line_count = (img.rows - window_size)/row_gap * (img.cols - window_size) / col_gap; 251 | Mat gabor(Size(window_size * window_size * k, line_count), CV_32F); 252 | int line_index = 0; 253 | */ 254 | for (int i = 0; i < img.rows; i += row_gap) 255 | for (int j = 0; j < img.cols; j += col_gap) 256 | if (i + window_size < img.rows && j + window_size < img.cols) 257 | result.push_back(get_vector(i, j)); // append new local feature 258 | } 259 | 260 | if (!draw_gabor) 261 | save(output); 262 | cout << input << " Done!" << endl; 263 | return 0; 264 | } -------------------------------------------------------------------------------- /utils/Renderer/renderer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3D Mesh (PLY) rendering 3 | * 4 | * In the project, we will show how to draw 3D mesh with VTK library, you can 5 | * use this code to generate snapshot of your model from any point of view 6 | * you like. 7 | * 8 | * Usage 1 - single image: 9 | * Parameters: 10 | * s (single mode) 11 | * p (position of the camera in polar coordinates: r, phi[0 - 359], theta[0 - 359]) 12 | * c (color of background in RGB [0 - 255]: red, green, blue) 13 | * f (path to PLY mesh to be rendered) 14 | * t (PNG file name of generated image) 15 | * ex: Renderer -s [-p r theta phi] [-c r g b] -f Path_to_model_file -t Path_to_image_file 16 | * 17 | * Usage 2 - group rendering: 18 | * Parameters: 19 | * g (group mode) 20 | * p (distance of the camera from mass center of the model: r) 21 | * c (color of background in RGB [0 - 255]: red, green, blue) 22 | * n (square root of number of images: enter 3 to generate 9 images) 23 | * f (path to PLY model) 24 | * t (path of the folder where images will be generated, with '/' in the end) 25 | * d (number of rotations) 26 | * ex: Renderer -g [-p r] [-c r g b] [-n Number] -f Path_to_model_file -t Path_to_image_folder [-d ang] 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | #include 43 | 44 | using namespace std; 45 | 46 | typedef double* Color; // 3 double numbers 47 | enum Mode {Single, Group}; 48 | 49 | struct Cartesian { 50 | double x, y, z; 51 | 52 | Cartesian(double x, double y, double z): x(x), y(y), z(z) {} 53 | 54 | Cartesian() { 55 | x = 0; 56 | y = 0; 57 | z = 0; 58 | }; 59 | }; 60 | 61 | Cartesian from_polar(double r, double phi, double theta) { 62 | double x, y, z; 63 | x = r * cos(theta) * cos(phi); 64 | y = r * cos(theta) * sin(phi); 65 | z = r * sin(theta); 66 | return Cartesian(x, y, z); 67 | } 68 | 69 | string output, input; // model and image file name 70 | 71 | Mode mode = Single; 72 | Color background; // model background color 73 | 74 | Cartesian position; // camera position 75 | double camera_distance = 2.4; // initial distance form the mass center of the object 76 | double phi, theta; // camera angles 77 | 78 | int step_count = 1; 79 | double min_angle = 0; 80 | double max_angle = 360; 81 | int ang = 1; 82 | 83 | void show_help(){ 84 | printf("3D Mesh (PLY) rendering\n" 85 | "Usage 1 - single image:\n" 86 | "Parameters:\n" 87 | " s (single mode)\n" 88 | " p (position of the camera in polar coordinates: r, phi[0 - 359], theta[0 - 359])\n" 89 | " c (color of background in RGB [0 - 255]: red, green, blue)\n" 90 | " f (path to PLY mesh to be rendered)\n" 91 | " t (PNG file name of generated image)\n" 92 | " ex: Renderer -s [-p r phi theta] [-c r g b] -f Path_to_model_file -t Path_to_image_file\n\n" 93 | " Usage 2 - group rendering:\n" 94 | " Parameters:\n" 95 | " g (group mode)\n" 96 | " p (distance of the camera from mass center of the model: r)\n" 97 | " c (color of background in RGB [0 - 255]: red, green, blue)\n" 98 | " n (square root of number of images: enter 3 to generate 9 images)\n" 99 | " f (path to PLY model)\n" 100 | " t (path of the folder where images will be generated)\n" 101 | " ex: Renderer -g [-p r] [-c r g b] [-n Number] -f Path_to_model_file -t Path_to_image_folder);\n"); 102 | } 103 | 104 | /* 105 | * Process all arguments 106 | */ 107 | bool parse_command_line(int argc, char **argv) { 108 | 109 | int i = 1; 110 | while(i < argc) { 111 | if (argv[i][0] != '-') 112 | break; 113 | switch(argv[i][1]) { 114 | case 'h': // help 115 | show_help(); 116 | return false; 117 | case 's': // single mode flag 118 | mode = Single; 119 | break; 120 | case 'g': // group mode 121 | mode = Group; 122 | break; 123 | case 'p': // position 124 | switch (mode) { 125 | case Single: 126 | camera_distance = (double)atoi(argv[++i]); 127 | phi = (double)atoi(argv[++i]); 128 | theta = (double)atoi(argv[++i]); 129 | position = from_polar(camera_distance, phi, theta); 130 | break; 131 | case Group: 132 | camera_distance = (double)atoi(argv[++i]); 133 | position = from_polar(camera_distance, 0, 0); 134 | break; 135 | default: 136 | show_help(); 137 | return false; 138 | } 139 | break; 140 | case 'c': // color 141 | background[0] = (double)atoi(argv[++i]) / 255.0; 142 | background[1] = (double)atoi(argv[++i]) / 255.0; 143 | background[2] = (double)atoi(argv[++i]) / 255.0; 144 | break; 145 | case 'n': // square root of number of images 146 | step_count = atoi(argv[++i]); 147 | break; 148 | case 'f': // input model file 149 | input = argv[++i]; 150 | break; 151 | case 't': // output image file 152 | output = argv[++i]; 153 | break; 154 | case 'd': 155 | ang = atoi(argv[++i]); 156 | break; 157 | } 158 | i++; 159 | } 160 | if (input.length() == 0 || output.length() == 0) { // invalid file name 161 | show_help(); 162 | return false; 163 | } 164 | return true; 165 | } 166 | 167 | const int dir[8][2] = {{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}}; 168 | 169 | int main(int argc, char **argv) { 170 | 171 | background = new double[3]; 172 | background[0] = 0; 173 | background[1] = 0; 174 | background[2] = 0; 175 | 176 | position = from_polar(camera_distance, 0, 0); // initial position 177 | 178 | if (!parse_command_line(argc, argv)) 179 | return EXIT_FAILURE; 180 | 181 | // Read the model 182 | vtkSmartPointer reader = vtkSmartPointer::New(); 183 | reader->SetFileName(input.c_str()); 184 | 185 | // Visualize 186 | vtkSmartPointer mapper = vtkSmartPointer::New(); 187 | mapper->SetInputConnection(reader->GetOutputPort()); 188 | 189 | vtkSmartPointer actor = vtkSmartPointer::New(); 190 | actor->SetMapper(mapper); 191 | 192 | double* bound = mapper->GetBounds(); 193 | Cartesian center((bound[1] - bound[0]) / 2, (bound[3] - bound[2]) / 2, (bound[5] - bound[4]) / 2); 194 | 195 | // common prefix 196 | if (mode == Group) 197 | output = output + input.substr(input.find_last_of('/') + 1, input.find_last_of('.') - input.find_last_of('/') - 1); 198 | 199 | // compute delta angle of each iteration 200 | double step = (max_angle - min_angle) / step_count; 201 | 202 | // use all possible angle combinations 203 | for(int i = 0; i < step_count; i++, theta += step) { 204 | for(int j = 0; j < step_count; j++, phi += step) { 205 | for (int d = 0; d < ang; d++) { 206 | position = from_polar(camera_distance, phi, theta); 207 | 208 | vtkSmartPointer camera = vtkSmartPointer::New(); 209 | camera->SetPosition(position.x + center.x, position.y + center.y, position.z + center.z); 210 | camera->SetFocalPoint(center.x, center.y, center.z); 211 | camera->SetViewUp(0, dir[d][0], dir[d][1]); 212 | 213 | vtkSmartPointer renderer = vtkSmartPointer::New(); 214 | renderer->SetActiveCamera(camera); 215 | renderer->AddActor(actor); 216 | renderer->SetBackground(background); // Black background 217 | 218 | vtkSmartPointer renderWindow = vtkSmartPointer::New(); 219 | renderWindow->AddRenderer(renderer); 220 | renderWindow->SetSize(64, 64); // width, height 221 | renderWindow->SetOffScreenRendering(1); // Off Screen rendering 222 | 223 | renderWindow->Render(); // Renderer needs a windows to render its image 224 | 225 | vtkSmartPointer renderLarge = vtkSmartPointer::New(); 226 | renderLarge->SetInput(renderer); 227 | renderLarge->SetMagnification(1); 228 | 229 | vtkSmartPointer writer = 230 | vtkSmartPointer::New(); 231 | 232 | // use angles in the file name 233 | string suffix = ""; 234 | if (mode == Group) 235 | suffix = '_' + to_string(((int) theta) % 360) + '_' + to_string(((int) phi) % 360) + '_' + to_string(d) + ".png"; 236 | 237 | writer->SetFileName((output + suffix).c_str()); 238 | writer->SetInputConnection(renderLarge->GetOutputPort()); 239 | writer->Write(); 240 | } 241 | } 242 | } 243 | 244 | delete[] background; 245 | 246 | return EXIT_SUCCESS; 247 | } -------------------------------------------------------------------------------- /src/sketch.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Sketch-based 3D model retrieval 3 | * 4 | * This program is based on Eitz et al. paper, it has two component: a offline one and an online one. In the main 5 | * structure, we will code the online part which take a sketch as input and output a 3D model. The offline part is 6 | * coded in other project under the folder "utils/". 7 | * 8 | * Whenever you want to use this program, run the offline part first, which builds a database for online query. 9 | * 10 | * Parameters: 11 | * d: TF-IDF database of all existing views information 12 | * w: dictionary file generated by K-Means 13 | * l: label file for all existing views 14 | * m: folder to all PLY models 15 | * c: camera mode 16 | * f: file mode with input image file path 17 | * v: path to the view folder 18 | * t: show the top matching views or model 19 | * 20 | * Usage 1 (file based query): 21 | * sketch -d [database_file] -w [dictionary_file] -l [label_file] -m [model_folder] -f [input_file] -v [view_folder_path] [-t] 22 | * 23 | * Usage 2 (real-time query with camera): 24 | * sketch -d [database_file] -w [dictionary_file] -l [label_file] -m [model_folder] -c 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "opencv2/opencv.hpp" 40 | 41 | #include "tf_idf.h" 42 | #include "clusters.h" 43 | 44 | enum Mode {Camera, File, Testing}; 45 | Mode mode = Testing; 46 | 47 | string database_file, label_file, input_file, dictionary_file, view_path, view_sorted_path; 48 | 49 | string model_base = "/home/lyx/workspace/data/TinySketch/models_ply/"; 50 | 51 | vector kernels; 52 | 53 | const int show = 3; 54 | int k = 8; // number of Gabor filters 55 | int kernel_size = 15; 56 | double sigma = 4; 57 | double theta = 0; 58 | double lambda = 10.0; 59 | double beta = 0.5; 60 | int window_size = 8; // local feature area (not size) 61 | int point_per_row = 28; 62 | int center_count = 0; 63 | int dim = 0; 64 | int feature_count = point_per_row * point_per_row; // number of features per image 65 | 66 | bool show_top_mathcing = false; 67 | Mat result_mat(Size(900,900),CV_8U); 68 | 69 | void show_help(); 70 | 71 | void read_dict(Clusters &dict); 72 | 73 | int retrieve(Mat& image, Clusters& dictionary, TF_IDF& tf_idf); // return the index of model 74 | 75 | void show_model(string file); // show 3D model with VTK 76 | 77 | int to_index(int label); 78 | 79 | string to_name(int index); // build 3D model name given the index 80 | 81 | bool parse_command_line(int argc, char **argv); // Process all arguments 82 | 83 | int main(int argc, char** argv) { 84 | 85 | if (!parse_command_line(argc, argv)) 86 | return EXIT_FAILURE; 87 | 88 | int model_index = -1; 89 | 90 | ifstream input(dictionary_file); 91 | input.read((char*) ¢er_count, sizeof(int)); 92 | input.read((char*) &dim, sizeof(int)); 93 | 94 | float *centers = new float[center_count * dim]; 95 | input.read((char*) centers, sizeof(float) * center_count * dim); 96 | input.close(); 97 | 98 | Clusters dict(centers, center_count, dim); 99 | 100 | // compute TF-IDF 101 | TF_IDF tf_idf(database_file); 102 | 103 | if (mode == File) { 104 | Mat image_gray = imread(input_file, CV_LOAD_IMAGE_GRAYSCALE); 105 | Mat image_resize; 106 | resize(image_gray, image_resize, Size(300, 300), 0, 0, INTER_AREA); 107 | Mat image_scale; 108 | resize(image_gray, image_scale, Size(64, 64), 0, 0, INTER_AREA); 109 | int sum = 0; 110 | for (int i = 0; i < image_scale.rows; i++) 111 | for (int j = 0; j < image_scale.cols; j++) 112 | sum += image_scale.at(i, j); 113 | if (sum / (64 * 64) > 50) // if the background is write, need to reverse the color 114 | for (int i = 0; i < image_scale.rows; i++) 115 | for (int j = 0; j < image_scale.cols; j++) 116 | image_scale.at(i, j) = (image_scale.at(i, j) > 250) ? 0 : 255; 117 | 118 | Mat image; 119 | imshow("Sketch", image_scale); // show sketch 120 | waitKey(2); 121 | 122 | image_scale.convertTo(image, CV_32F); 123 | int label = retrieve(image, dict, tf_idf); // document index 124 | 125 | model_index = to_index(label); 126 | cout << model_index << endl; 127 | if (!show_top_mathcing) 128 | show_model(to_name(model_index)); // show model*/ 129 | else { 130 | image_resize.copyTo(result_mat(Rect(0, 0, image_resize.cols, image_resize.rows))); 131 | imshow("result", result_mat); 132 | string save_name = view_path + input_file.substr(input_file.find_last_of('/')); 133 | imwrite(save_name,result_mat); 134 | waitKey(0); 135 | } 136 | 137 | } 138 | else if (mode == Camera) { 139 | 140 | VideoCapture cap(0); // open the default camera 141 | 142 | Mat image_scale; 143 | namedWindow("Sketch", 1); 144 | int threshold = 128; 145 | createTrackbar("Threshold", "Sketch", &threshold, 255); 146 | while (true) { 147 | Mat frame; 148 | cap >> frame; // get a new frame from camera 149 | Mat tmp; 150 | resize(frame, tmp, Size(64, 64)); 151 | cvtColor(tmp, image_scale, CV_BGR2GRAY); 152 | for (int i = 0; i < image_scale.rows; i++) 153 | for (int j = 0; j < image_scale.cols; j++) 154 | image_scale.at(i, j) = (image_scale.at(i, j) > threshold) ? 0 : 255; 155 | imshow("Sketch", image_scale); 156 | 157 | Mat image; 158 | image_scale.convertTo(image, CV_32F); 159 | int label = retrieve(image, dict, tf_idf); // document index 160 | 161 | model_index = to_index(label); 162 | cout << model_index << endl; 163 | 164 | if (waitKey(100) >= 0) 165 | break; 166 | } 167 | 168 | } 169 | else { // Testing mode 170 | show_model(to_name(87)); 171 | } 172 | 173 | delete[] centers; 174 | 175 | return EXIT_SUCCESS; 176 | } 177 | void kernel(){ 178 | double step = CV_PI / k; 179 | 180 | for(int i = 0; i < k; i++) { 181 | Mat kernel = getGaborKernel(Size(kernel_size, kernel_size), sigma, 182 | theta + step * (double) i, lambda, beta, CV_PI * 0.5, CV_32F); 183 | kernels.push_back(kernel); 184 | } 185 | } 186 | 187 | void gabor_filter(Mat& img , float *data){ 188 | 189 | if (kernels.empty()) 190 | kernel(); 191 | 192 | vector filters(k); 193 | 194 | for(int i = 0; i < k; i++) 195 | filter2D(img, filters[i], -1, kernels[i], Point(-1, -1), 0, BORDER_DEFAULT); 196 | 197 | int index = 0; 198 | int row_gap = (img.rows - window_size) / point_per_row; 199 | int col_gap = (img.cols - window_size) / point_per_row; 200 | 201 | for(int i = 0; i < img.rows - window_size; i += row_gap) 202 | for(int j = 0; j < img.cols - window_size; j += col_gap) 203 | for(int dir = 0; dir < k; dir++) 204 | for(int u = 0; u < window_size; u++) 205 | for(int v = 0; v < window_size; v++) 206 | data[index++] = filters[dir].at(u + i, v + j); 207 | 208 | assert(index == feature_count * dim && dim == k * window_size * window_size); 209 | 210 | 211 | } 212 | // return the index of model 213 | 214 | //find and (show) the view correspondant to the index 215 | Mat show_picture(int index){ 216 | int model_index = to_index(index); 217 | string name = view_sorted_path+"m"+to_string(model_index)+"/view.txt"; 218 | int rest = index%36; 219 | cout << name <<' ' << rest<< endl; 220 | ifstream input(name); 221 | string img_name; 222 | int tmp; 223 | for(int i = 0; i <= rest; i++){ 224 | input >> img_name >> tmp; 225 | } 226 | cout << view_path + "m" + to_string(model_index) + "/" +img_name < > result; 250 | result = tf_idf.find_nearest(tf_value); 251 | sort(result.begin(), result.end()); 252 | 253 | delete[] tf_value; 254 | delete[] feature; 255 | delete[] gabor_data; 256 | 257 | // if show_top_matching mode then change the result_mat 258 | if (show_top_mathcing) { 259 | for (int i = result.size() - 1; i > result.size() - show * show; i--) { 260 | Mat img = show_picture(result[i].second); 261 | int j = result.size() - i; 262 | img.copyTo(result_mat(Rect(j / show * 300, (j % show) * 300, 300, 300))); 263 | } 264 | } 265 | 266 | //cout << result[result.size() -1].first << endl; 267 | return result[result.size() -1].second; 268 | //return result_mat; 269 | } 270 | 271 | // show 3D model with VTK 272 | void show_model(string file) { 273 | 274 | vtkSmartPointer reader = vtkSmartPointer::New(); 275 | reader->SetFileName(file.c_str()); 276 | 277 | // Visualize 278 | vtkSmartPointer mapper = vtkSmartPointer::New(); 279 | mapper->SetInputConnection(reader->GetOutputPort()); 280 | 281 | vtkSmartPointer actor = vtkSmartPointer::New(); 282 | actor->SetMapper(mapper); 283 | 284 | vtkSmartPointer renderer = vtkSmartPointer::New(); 285 | vtkSmartPointer renderWindow = vtkSmartPointer::New(); 286 | renderWindow->AddRenderer(renderer); 287 | vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); 288 | renderWindowInteractor->SetRenderWindow(renderWindow); 289 | 290 | renderer->AddActor(actor); 291 | renderer->SetBackground(0, 0, 0); 292 | 293 | renderWindow->Render(); 294 | renderWindowInteractor->Start(); 295 | 296 | } 297 | 298 | // build 3D model name given the index 299 | string to_name(int index) { 300 | return model_base + "m" + to_string(index) + ".ply"; 301 | } 302 | 303 | int to_index(int label){ 304 | ifstream input(label_file); 305 | int model_count, view_count; 306 | input >> model_count >> view_count; 307 | int k = 0; 308 | int tmp; 309 | input >> tmp; 310 | while(k + view_count <= label){ 311 | input >> tmp; 312 | k = k + view_count; 313 | } 314 | return tmp; 315 | } 316 | 317 | // Process all arguments 318 | bool parse_command_line(int argc, char **argv) { 319 | 320 | int i = 1; 321 | while(i < argc) { 322 | if (argv[i][0] != '-') 323 | break; 324 | switch (argv[i][1]) { 325 | case 'h': // help 326 | show_help(); 327 | return false; 328 | case 'd': // TF-IDF file 329 | database_file = argv[++i]; 330 | break; 331 | case 'w': // dictionary file 332 | dictionary_file = argv[++i]; 333 | break; 334 | case 'l': // label file 335 | label_file = argv[++i]; 336 | break; 337 | case 'm': // folder containing all PLY models 338 | model_base = argv[++i]; 339 | break; 340 | case 'f': // input sktech image 341 | mode = File; 342 | input_file = argv[++i]; 343 | break; 344 | case 'c': // camera mode 345 | mode = Camera; 346 | break; 347 | case 'p': 348 | feature_count = atoi(argv[++i]); 349 | break; 350 | case 'v': 351 | view_path = argv[++i]; 352 | break; 353 | case 't': 354 | show_top_mathcing = true; 355 | break; 356 | case 's': 357 | view_sorted_path = argv[++i]; 358 | break; 359 | } 360 | i++; 361 | } 362 | if (database_file.length() <= 0 || dictionary_file.length() <= 0) 363 | return false; 364 | return true; 365 | } 366 | 367 | void show_help() { 368 | } 369 | 370 | -------------------------------------------------------------------------------- /utils/KMean/k_mean.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by lyx on 17/11/15. 3 | // 4 | 5 | #include "k_mean.h" 6 | 7 | namespace k_mean { 8 | 9 | template 10 | void printCpuMatrix(T *m, int n, int r, int c, int precision) { 11 | cout << "Size: " << r << " * " << c << endl; 12 | for (int i = 0; i < r; i++) { 13 | for (int j = 0; j < c; j++) 14 | cout << fixed << setprecision(precision) << m[i + j * r] << '\t'; 15 | cout << endl; 16 | } 17 | cout << endl; 18 | } 19 | 20 | template 21 | void printGpuMatrix(T *d_m, int n, int r, int c, int precision) { 22 | T *m = new T[n]; 23 | cublasGetVector(n, sizeof(*m), d_m, 1, m, 1); 24 | printCpuMatrix(m, n, r, c, precision); 25 | delete[] m; 26 | } 27 | 28 | void K_Mean::initialize_monoid() { 29 | 30 | // select k different centers 31 | unordered_set selected; 32 | while (selected.size() < center_count) // different 33 | selected.insert(rand() % data_count); 34 | 35 | // copy theirs coordinates 36 | int column = 0; 37 | for (int i: selected) { 38 | //cout << i << endl; 39 | for (int j = 0; j < dim; j++) 40 | center[column * dim + j] = data[i * dim + j]; 41 | column++; 42 | } 43 | 44 | // copy to the device 45 | callCuda(cudaMemcpy(d_data, data, sizeof(float) * dim * data_count, cudaMemcpyHostToDevice)); 46 | callCuda(cudaMemcpy(d_center, center, sizeof(float) * dim * center_count, cudaMemcpyHostToDevice)); 47 | 48 | } 49 | 50 | void K_Mean::initialize_centroid() { 51 | 52 | set_uniform_value(d_center, dim * center_count, 256); 53 | callCuda(cudaMemcpy(d_data, data, sizeof(float) * dim * data_count, cudaMemcpyHostToDevice)); 54 | callCuda(cudaMemcpy(center, d_center, sizeof(float) * dim * center_count, cudaMemcpyDeviceToHost)); 55 | 56 | } 57 | 58 | void K_Mean::find_nearest_center() { 59 | 60 | // clear previous allocation status 61 | fill(cluster_size, cluster_size + center_count, 0); 62 | 63 | // find nearest neighbor for all data vectors 64 | float one = 1; 65 | float zero = 0; 66 | int index = -1; 67 | 68 | for (int i = 0; i < data_count; i++) { 69 | // compute the distance 70 | square_minus(d_center, dim, center_count, d_data + i * dim, d_tmp_diff); 71 | //printGpuMatrix(d_tmp_diff, 4, 2, 2, 0); 72 | callCuda(cublasSgemv(cublas_handle, CUBLAS_OP_T, dim, center_count, &one, d_tmp_diff, 73 | dim, d_one, 1, &zero, d_tmp_dist, 1)); 74 | 75 | // get the minimal one 76 | callCuda(cublasIsamin(cublas_handle, center_count, d_tmp_dist, 1, &index)); 77 | //printGpuMatrix(d_tmp_dist, 2, 1, 2, 0); 78 | allocation[i] = index - 1; 79 | cluster_size[allocation[i]]++; 80 | } 81 | 82 | //printCpuMatrix(cluster_size, min(100, center_count), 1, min(100, center_count), 0); 83 | //printCpuMatrix(allocation, data_count, 1, data_count, 0); 84 | 85 | } 86 | 87 | void K_Mean::update_center() { 88 | 89 | float one = 1; 90 | float zero = 0; 91 | 92 | // update the allocation information 93 | callCuda( 94 | cudaMemcpy(d_allocation_col_csr, allocation, sizeof(int) * size_t(data_count), cudaMemcpyHostToDevice)); 95 | callCuda( 96 | cudaMemcpy(d_cluster_size, cluster_size, sizeof(float) * size_t(center_count), cudaMemcpyHostToDevice)); 97 | 98 | // conversion method, use dense matrix 99 | /* 100 | float *tmp; 101 | callCuda(cudaMalloc(&tmp, sizeof(float) * center_count * data_count)); 102 | callCuda(cusparseScsr2dense(cusparse_handle, data_count, center_count, d_allocation_descr, d_allocation_val_csr, 103 | d_allocation_row_csr, d_allocation_col_csr, tmp, data_count)); 104 | callCuda(cublasSgemm(cublas_handle, CUBLAS_OP_T, CUBLAS_OP_T, center_count, dim, data_count, &one, tmp, 105 | data_count, d_data, dim, &zero, d_center_transpose, center_count)); 106 | callCuda(cudaFree(tmp)); 107 | */ 108 | 109 | // transpose the allocation matrix 110 | callCuda(cusparseScsr2csc(cusparse_handle, data_count, center_count, data_count, d_allocation_val_csr, 111 | d_allocation_row_csr, d_allocation_col_csr, d_allocation_val_csc, 112 | d_allocation_col_csc, 113 | d_allocation_row_csc, CUSPARSE_ACTION_NUMERIC, CUSPARSE_INDEX_BASE_ZERO)); 114 | 115 | // compute the new center 116 | // attention: while the second matrix is transposed, the first one should be normal 117 | callCuda(cusparseScsrmm2(cusparse_handle, CUSPARSE_OPERATION_NON_TRANSPOSE, CUSPARSE_OPERATION_TRANSPOSE, 118 | center_count, dim, data_count, data_count, &one, d_allocation_descr, 119 | d_allocation_val_csc, 120 | d_allocation_row_csc, d_allocation_col_csc, d_data, dim, &zero, d_center_transpose, 121 | center_count)); 122 | 123 | transpose_scale(d_center, dim, center_count, d_center_transpose, d_cluster_size); 124 | callCuda(cudaMemcpy(center, d_center, sizeof(float) * dim * center_count, cudaMemcpyDeviceToHost)); 125 | 126 | } 127 | 128 | void K_Mean::shake_center(float delta) { 129 | float *d_scale; 130 | callCuda(cudaMalloc(&d_scale, sizeof(float) * center_count * dim)); 131 | // use random variation 132 | set_uniform_value(d_scale, center_count * dim, -delta, delta); 133 | shake(d_center, d_scale, center_count * dim); 134 | callCuda(cudaFree(d_scale)); 135 | } 136 | 137 | void K_Mean::print_center() { 138 | printCpuMatrix(center, dim * center_count, dim, center_count, 3); 139 | } 140 | 141 | K_Mean::K_Mean(float *_data, int _data_count, int _dim, int _center_count) { 142 | 143 | data = _data; 144 | data_count = _data_count; 145 | center_count = _center_count; 146 | dim = _dim; 147 | 148 | assert(data_count >= center_count); 149 | 150 | center = new float[dim * center_count]; 151 | allocation = new int[data_count]; 152 | cluster_size = new float[center_count]; 153 | 154 | callCuda(cudaMalloc(&d_data, sizeof(float) * dim * data_count)); 155 | callCuda(cudaMalloc(&d_center, sizeof(float) * dim * center_count)); 156 | callCuda(cudaMalloc(&d_center_transpose, sizeof(float) * center_count * dim)); 157 | callCuda(cudaMalloc(&d_tmp_diff, sizeof(float) * dim * center_count)); 158 | callCuda(cudaMalloc(&d_tmp_dist, sizeof(float) * center_count)); 159 | callCuda(cudaMalloc(&d_allocation_val_csr, sizeof(float) * (data_count))); 160 | callCuda(cudaMalloc(&d_allocation_row_csr, sizeof(int) * (data_count + 1))); // CRS data_format 161 | callCuda(cudaMalloc(&d_allocation_col_csr, sizeof(int) * (data_count))); 162 | callCuda(cudaMalloc(&d_allocation_val_csc, sizeof(float) * (data_count))); 163 | callCuda(cudaMalloc(&d_allocation_col_csc, sizeof(int) * (data_count))); // CRS data_format 164 | callCuda(cudaMalloc(&d_allocation_row_csc, sizeof(int) * (center_count + 1))); 165 | callCuda(cudaMalloc(&d_cluster_size, sizeof(float) * center_count)); 166 | callCuda(cudaMalloc(&d_one, sizeof(float) * max(data_count, max(dim, center_count)))); // long enough 167 | 168 | set_value(d_one, max(data_count, max(dim, center_count)), 1.0f); 169 | 170 | set_value(d_allocation_val_csr, data_count, 1.0f); 171 | set_sequence(d_allocation_row_csr, data_count + 1, 0, 1); // one 1 per row 172 | set_value(d_allocation_col_csr, data_count, 0); 173 | 174 | callCuda(cublasCreate(&cublas_handle)); 175 | callCuda(cusparseCreate(&cusparse_handle)); 176 | 177 | callCuda(cusparseCreateMatDescr(&d_allocation_descr)); 178 | callCuda(cusparseSetMatType(d_allocation_descr, CUSPARSE_MATRIX_TYPE_GENERAL)); 179 | callCuda(cusparseSetMatIndexBase(d_allocation_descr, CUSPARSE_INDEX_BASE_ZERO)); 180 | 181 | } 182 | 183 | K_Mean::K_Mean(float *_data, float* _center, int _data_count, int _dim, int _center_count) { 184 | 185 | data = _data; 186 | data_count = _data_count; 187 | center_count = _center_count; 188 | dim = _dim; 189 | 190 | center = _center; 191 | allocation = new int[data_count]; 192 | cluster_size = new float[center_count]; 193 | 194 | callCuda(cudaMalloc(&d_data, sizeof(float) * dim * data_count)); 195 | callCuda(cudaMalloc(&d_center, sizeof(float) * dim * center_count)); 196 | callCuda(cudaMalloc(&d_center_transpose, sizeof(float) * center_count * dim)); 197 | callCuda(cudaMalloc(&d_tmp_diff, sizeof(float) * dim * center_count)); 198 | callCuda(cudaMalloc(&d_tmp_dist, sizeof(float) * center_count)); 199 | callCuda(cudaMalloc(&d_allocation_val_csr, sizeof(float) * (data_count))); 200 | callCuda(cudaMalloc(&d_allocation_row_csr, sizeof(int) * (data_count + 1))); // CRS data_format 201 | callCuda(cudaMalloc(&d_allocation_col_csr, sizeof(int) * (data_count))); 202 | callCuda(cudaMalloc(&d_allocation_val_csc, sizeof(float) * (data_count))); 203 | callCuda(cudaMalloc(&d_allocation_col_csc, sizeof(int) * (data_count))); // CRS data_format 204 | callCuda(cudaMalloc(&d_allocation_row_csc, sizeof(int) * (center_count + 1))); 205 | callCuda(cudaMalloc(&d_cluster_size, sizeof(float) * center_count)); 206 | callCuda(cudaMalloc(&d_one, sizeof(float) * max(data_count, max(dim, center_count)))); // long enough 207 | 208 | callCuda(cudaMemcpy(d_center, center, sizeof(float) * dim * center_count, cudaMemcpyHostToDevice)); 209 | callCuda(cudaMemcpy(d_data, data, sizeof(float) * dim * data_count, cudaMemcpyHostToDevice)); 210 | 211 | set_value(d_one, max(data_count, max(dim, center_count)), 1.0f); 212 | 213 | set_value(d_allocation_val_csr, data_count, 1.0f); 214 | set_sequence(d_allocation_row_csr, data_count + 1, 0, 1); // one 1 per row 215 | set_value(d_allocation_col_csr, data_count, 0); 216 | 217 | callCuda(cublasCreate(&cublas_handle)); 218 | callCuda(cusparseCreate(&cusparse_handle)); 219 | 220 | callCuda(cusparseCreateMatDescr(&d_allocation_descr)); 221 | callCuda(cusparseSetMatType(d_allocation_descr, CUSPARSE_MATRIX_TYPE_GENERAL)); 222 | callCuda(cusparseSetMatIndexBase(d_allocation_descr, CUSPARSE_INDEX_BASE_ZERO)); 223 | 224 | } 225 | 226 | K_Mean::~K_Mean() { 227 | callCuda(cusparseDestroyMatDescr(d_allocation_descr)); 228 | 229 | callCuda(cusparseDestroy(cusparse_handle)); 230 | callCuda(cublasDestroy(cublas_handle)); 231 | 232 | callCuda(cudaFree(d_data)); 233 | callCuda(cudaFree(d_center)); 234 | callCuda(cudaFree(d_center_transpose)); 235 | callCuda(cudaFree(d_tmp_diff)); 236 | callCuda(cudaFree(d_tmp_dist)); 237 | callCuda(cudaFree(d_allocation_val_csr)); 238 | callCuda(cudaFree(d_allocation_row_csr)); 239 | callCuda(cudaFree(d_allocation_col_csr)); 240 | callCuda(cudaFree(d_allocation_val_csc)); 241 | callCuda(cudaFree(d_allocation_row_csc)); 242 | callCuda(cudaFree(d_allocation_col_csc)); 243 | callCuda(cudaFree(d_cluster_size)); 244 | callCuda(cudaFree(d_one)); 245 | } 246 | 247 | void K_Mean::execute(int iteration, float delta) { 248 | 249 | initialize_monoid(); 250 | //initialize_centroid(); 251 | 252 | //print_center(); 253 | for (int i = 0; i < iteration; i++) { 254 | cout << "Iteration #" << i << endl; 255 | shake_center(delta); 256 | //callCuda(cudaMemcpy(center, d_center, sizeof(float) * dim * center_count, cudaMemcpyDeviceToHost)); 257 | //print_center(); 258 | find_nearest_center(); 259 | printCpuMatrix(cluster_size, min(100, center_count), 1, min(100, center_count), 0); 260 | update_center(); 261 | //print_center(); 262 | } 263 | 264 | //print_center(); 265 | 266 | } 267 | 268 | void K_Mean::save(string file, bool add_null) { 269 | ofstream out(file); 270 | 271 | // add a all zero center 272 | if (add_null) { 273 | center_count++; 274 | out.write((char *)¢er_count, sizeof(int)); 275 | center_count--; 276 | } 277 | else 278 | out.write((char*)¢er_count, sizeof(int)); 279 | 280 | out.write((char*)&dim, sizeof(int)); 281 | out.write((char*)center, sizeof(float) * dim * center_count); 282 | if (add_null) { 283 | float *tmp = new float[dim]; 284 | fill(tmp, tmp + dim, 0); 285 | out.write((char*)tmp, sizeof(float) * dim); 286 | delete[] tmp; 287 | } 288 | out.close(); 289 | } 290 | 291 | void K_Mean::translate(int *result) { 292 | find_nearest_center(); 293 | callCuda(cudaMemcpy(result, allocation, sizeof(int) * data_count, cudaMemcpyHostToHost)); 294 | } 295 | 296 | void K_Mean::get_clusters(float *dest) { 297 | callCuda(cudaMemcpy(dest, d_center, sizeof(float) * dim * center_count, cudaMemcpyHostToHost)); 298 | } 299 | 300 | void K_Mean::update_data() { 301 | callCuda(cudaMemcpy(d_data, data, sizeof(float) * dim * data_count, cudaMemcpyHostToDevice)); 302 | } 303 | 304 | } -------------------------------------------------------------------------------- /utils/KMean/k_mean_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * K-means clustering 3 | * 4 | * This program implements the K-Means clustering method. It takes a set of points (lines in a binary file) and 5 | * returns the group number of each point and an extra file to describe the group, namely the group center. 6 | * 7 | * Parameters: 8 | * f: local features to be clustered 9 | * k: number of cluster 10 | * d: path to the output dictionary 11 | * s: size of the value: 8 for uint8_t, 32 for float 12 | * i: number of iteration 13 | * v: value of variation 14 | * 15 | * Usage 1 (Training): 16 | * k_mean -1 -f [Path_to_file] -k Number_of_center -d [Path_to_output_file] -s [8|32] -i [Iteration] -v [Variation] 17 | * 18 | * N.B. The file format is very specific, it is a binary file with integers and floats, so please pay attention to the 19 | * big / little endian problem. You may want to generate the file by program in case of theses sorts of problems. 20 | * The first number is the number of points: N, the second is the dimension of the point: d. Then there should 21 | * be N * d float numbers after. So the binary file looks like: 22 | * 23 | * N (32 bits integer) d (32 bits integer) 24 | * P_1 (d * 32 bits float or d * 8 bits integer) 25 | * ... 26 | * P_N (d * 32 bits float or d * 8 bits intege) 27 | * 28 | * Parameters: 29 | * f: Gabor features of an image file (line + dim + data in float) 30 | * t: output binary file (vector of index (int)) 31 | * d: dictionary to be used 32 | * s: size of the value: 8 for uint8_t, 32 for float 33 | * 34 | * Usage 2 (Testing): 35 | * k_mean -2 -f [Path_to_input] -t [Path_to_output] -d [Path_to_dictionary] -s [8|32] 36 | * 37 | * Parameters: 38 | * f: file containing all Gabor features files name (cases + line per case + names, text file) 39 | * d: dictionary to be used 40 | * s: size of the value: 8 for uint8_t, 32 for float 41 | * c: case number 42 | * a: data size (number of features in an image) 43 | * o: output file (a binary file for all files in the group) 44 | * 45 | * Output: translated images: 46 | * 47 | * Line_Count (32 bits integer) Dimension (32 bits integer) 48 | * Image_1 (Dimension * 32 bits int) 49 | * ... 50 | * Image_Line_Count (Dimension * 32 bits) 51 | * 52 | * Usage 3 (Group_Testing): 53 | * k_mean -3 -f [Path_to_folder] -d [Path_to_dictionary] -s [8|32] -c [case_number] -a [data_size] -o [output_file] 54 | * 55 | * Parameters: 56 | * f: input file containing the name of all views 57 | * r: folder containing all contour image 58 | * d: path to the dictionary 59 | * s: size of the value 60 | * o: output encoded file 61 | * c: number of image to be considered 62 | * a: data size (number of features in an image) 63 | * 64 | * Input file: 65 | * Name_i Importance_i 66 | * 67 | * Usage 4 (Group_Testing_From_Contour): 68 | * k_mean -4 -f [Path_to_file] -r [Folder_to_contour] -d [Path_to_dictionary] -s [8|32] -o [output_file] -c [Cases] -a [data_size] 69 | */ 70 | 71 | #include "k_mean.h" 72 | #include 73 | #include 74 | #include 75 | 76 | using namespace k_mean; 77 | using namespace std; 78 | using namespace cv; 79 | 80 | enum Mode {Group_Testing, Testing, Training, Contour_Testing}; // Group_Testing for usage 3, Testing for usage 2, Training for usage 1 81 | enum Format {Integer, Float}; // format of initial data 82 | 83 | Mode mode = Contour_Testing; 84 | Format data_format = Integer; 85 | 86 | string input, output, dictionary, output_file, root_folder; 87 | float *data; 88 | int center_count = 0; 89 | int data_count = 0; 90 | int dim = 0; 91 | int cases = 0; 92 | 93 | int iteration = 0; 94 | float delta = 0; 95 | 96 | 97 | int kernel_size = 15; 98 | int k = 8; // number of directions for Gabor filter 99 | int window_size = 8; // local feature area (not size) 100 | int point_per_row = 28; 101 | double sigma = 4; 102 | double theta = 0; 103 | double lambda = 10.0; 104 | double beta = 0.5; 105 | 106 | 107 | 108 | void show_help() { 109 | 110 | } 111 | 112 | /* 113 | * Process all arguments 114 | */ 115 | bool parse_command_line(int argc, char **argv) { 116 | 117 | int i = 1; 118 | while(i < argc) { 119 | if (argv[i][0] != '-') 120 | break; 121 | switch(argv[i][1]) { 122 | case '1': 123 | mode = Training; 124 | break; 125 | case '2': 126 | mode = Testing; 127 | break; 128 | case '3': 129 | mode = Group_Testing; 130 | break; 131 | case '4': 132 | mode = Contour_Testing; 133 | break; 134 | case 'h': // help 135 | show_help(); 136 | return false; 137 | case 's': // data_format 138 | data_format = (argv[++i] == "8") ? Integer : Float; 139 | break; 140 | case 'k': // training mode 141 | center_count = atoi(argv[++i]); 142 | break; 143 | case 'd': // dictionary file 144 | dictionary = argv[++i]; 145 | break; 146 | case 'f': // input file 147 | input = argv[++i]; 148 | break; 149 | case 't': // output file 150 | output = argv[++i]; 151 | break; 152 | case 'v': // variation rate 153 | delta = float(atof(argv[++i])); 154 | break; 155 | case 'i': // number of iteration 156 | iteration = atoi(argv[++i]); 157 | break; 158 | case 'c': 159 | cases = atoi(argv[++i]); 160 | break; 161 | case 'a': 162 | data_count = atoi(argv[++i]); 163 | break; 164 | case 'o': 165 | output_file = argv[++i]; 166 | break; 167 | case 'r': 168 | root_folder = argv[++i]; 169 | break; 170 | } 171 | i++; 172 | } 173 | if (input.length() == 0) { // invalid file name 174 | show_help(); 175 | return false; 176 | } 177 | return true; 178 | } 179 | 180 | void training() { 181 | 182 | cout << "Training" << endl; 183 | 184 | ifstream in(input); 185 | read_int(in, &data_count); // read meta-info 186 | read_int(in, &dim); 187 | 188 | data_count = min(1<<19, data_count); 189 | 190 | // read data 191 | if (data_format == Integer) { 192 | uint8_t *_data = new uint8_t[dim * data_count]; 193 | read_bytes(in, _data, dim * data_count); 194 | 195 | data = new float[dim * data_count]; 196 | for (int i = 0; i < dim * data_count; i++) 197 | data[i] = float(_data[i]); 198 | 199 | delete[] _data; 200 | } 201 | else { 202 | data = new float[dim * data_count]; 203 | read_floats(in, data, dim * data_count); 204 | } 205 | 206 | in.close(); 207 | cout << data_count << ' ' << dim << ' ' << center_count << endl; 208 | 209 | // train the model 210 | K_Mean model(data, data_count, dim, center_count); 211 | model.execute(iteration, delta); 212 | 213 | // save the dictionary 214 | model.save(dictionary, false); 215 | 216 | delete[] data; 217 | 218 | } 219 | 220 | void group_testing() { 221 | 222 | cout << "Group Testing" << endl; 223 | 224 | DIR *dir; 225 | struct dirent *ent; 226 | 227 | // read the dictionary 228 | ifstream dict(dictionary); 229 | read_int(dict, ¢er_count); // read meta-info 230 | read_int(dict, &dim); // should be the same 231 | 232 | float *center = new float[dim * center_count]; 233 | read_floats(dict, center, dim * center_count); 234 | 235 | dict.close(); 236 | 237 | data = new float[dim * data_count]; 238 | int *allocation = new int[data_count]; 239 | 240 | // prepare the output file 241 | ofstream out(output_file); 242 | out.write((char*)&cases, sizeof(int)); 243 | out.write((char*)&data_count, sizeof(int)); 244 | 245 | K_Mean model(data, center, data_count, dim, center_count); 246 | 247 | string file; 248 | if ((dir = opendir(input.c_str()))!=NULL) { 249 | while ((ent = readdir(dir)) != NULL) { 250 | file = ent->d_name; 251 | if (file[0] != 'm') 252 | continue; 253 | ifstream f(input + file); 254 | cout << input + file << endl; 255 | 256 | read_int(f, &data_count); // read meta-info 257 | read_int(f, &dim); 258 | 259 | // read data 260 | if (data_format == Integer) { 261 | uint8_t *_data = new uint8_t[dim * data_count]; 262 | read_bytes(f, _data, dim * data_count); 263 | 264 | for (int i = 0; i < dim * data_count; i++) 265 | data[i] = float(_data[i]); 266 | 267 | delete[] _data; 268 | } 269 | else { 270 | read_floats(f, data, dim * data_count); 271 | } 272 | 273 | f.close(); 274 | 275 | // replace the data with new image 276 | model.update_data(); 277 | // translate the local features into words 278 | model.translate(allocation); 279 | 280 | out.write((char *) allocation, sizeof(int) * data_count); 281 | } 282 | } 283 | 284 | out.close(); 285 | 286 | delete[] center; 287 | delete[] data; 288 | delete[] allocation; 289 | 290 | } 291 | 292 | void contour_testing() { 293 | 294 | cout << "Contour Testing" << endl; 295 | 296 | ifstream in(input); 297 | 298 | // read the dictionary 299 | ifstream dict(dictionary); 300 | read_int(dict, ¢er_count); // read meta-info 301 | read_int(dict, &dim); // should be the same 302 | 303 | float *center = new float[dim * center_count]; 304 | read_floats(dict, center, dim * center_count); 305 | 306 | dict.close(); 307 | 308 | data = new float[dim * data_count]; 309 | int *allocation = new int[data_count]; 310 | 311 | // prepare the output file 312 | ofstream out(output_file); 313 | out.write((char*)&cases, sizeof(int)); 314 | out.write((char*)&data_count, sizeof(int)); 315 | 316 | K_Mean model(data, center, data_count, dim, center_count); 317 | 318 | string file; 319 | int tmp; 320 | 321 | // pre-calculate kernels 322 | double step = CV_PI / k; 323 | vector kernels; 324 | for(int i = 0; i < k; i++) { 325 | Mat kernel = getGaborKernel(Size(kernel_size, kernel_size), sigma, 326 | theta + step * (double) i, lambda, beta, CV_PI * 0.5, CV_32F); 327 | kernels.push_back(kernel); 328 | } 329 | 330 | for (int z = 0; z < cases; z++) { 331 | in >> file >> tmp; 332 | 333 | int d = 0; 334 | vector filters(k); 335 | // read image 336 | Mat img = imread(root_folder + file, CV_LOAD_IMAGE_GRAYSCALE); 337 | img.convertTo(img, CV_32F); 338 | 339 | //use gabor filter 340 | for(int i = 0; i < k; i++) 341 | filter2D(img, filters[i], -1, kernels[i], Point(-1, -1), 0, BORDER_DEFAULT); 342 | 343 | // compute the new value 344 | 345 | // replace the data with new image 346 | int row_gap = (img.rows - window_size) / point_per_row; 347 | int col_gap = (img.cols - window_size) / point_per_row; 348 | 349 | for(int i = 0; i < img.rows - window_size; i += row_gap) 350 | for(int j = 0; j < img.cols - window_size; j += col_gap) 351 | for(int dir = 0; dir < k; dir++) 352 | for(int u = 0; u < window_size; u++) 353 | for(int v = 0; v < window_size; v++) 354 | data[d++] = filters[dir].at(u + i, v + j); 355 | 356 | model.update_data(); 357 | // translate the local features into words 358 | model.translate(allocation); 359 | 360 | out.write((char *) allocation, sizeof(int) * data_count); 361 | 362 | } 363 | 364 | in.close(); 365 | out.close(); 366 | 367 | delete[] center; 368 | delete[] data; 369 | delete[] allocation; 370 | 371 | } 372 | 373 | void testing() { 374 | 375 | cout << "Testing" << endl; 376 | 377 | ifstream in(input); 378 | read_int(in, &data_count); // read meta-info 379 | read_int(in, &dim); 380 | 381 | cout << data_count << ' ' << dim << endl; 382 | 383 | // read data 384 | if (data_format == Integer) { 385 | uint8_t *_data = new uint8_t[dim * data_count]; 386 | read_bytes(in, _data, dim * data_count); 387 | 388 | data = new float[dim * data_count]; 389 | for (int i = 0; i < dim * data_count; i++) 390 | data[i] = float(_data[i]); 391 | 392 | delete[] _data; 393 | } 394 | else { 395 | data = new float[dim * data_count]; 396 | read_floats(in, data, dim * data_count); 397 | } 398 | 399 | in.close(); 400 | 401 | // read the dictionary 402 | ifstream dir(dictionary); 403 | read_int(dir, ¢er_count); // read meta-info 404 | read_int(dir, &dim); // should be the same 405 | 406 | float *center = new float[dim * center_count]; 407 | read_floats(dir, center, dim * center_count); 408 | 409 | dir.close(); 410 | 411 | // build the model 412 | K_Mean model(data, center, data_count, dim, center_count); 413 | 414 | // translate the local features into words 415 | int *allocation = new int[data_count]; 416 | model.translate(allocation); 417 | 418 | /* 419 | for (int i = 0; i < data_count; i++) 420 | cout << allocation[i] << ' '; 421 | cout << endl; 422 | */ 423 | 424 | ofstream out(output); 425 | out.write((char*)&dim, sizeof(int)); 426 | out.write((char*)allocation, sizeof(int) * data_count); 427 | out.close(); 428 | 429 | delete[] allocation; 430 | delete[] data; 431 | delete[] center; 432 | 433 | } 434 | 435 | int main(int argc, char **argv) { 436 | 437 | if (!parse_command_line(argc, argv)) 438 | return EXIT_FAILURE; 439 | 440 | srand(time(NULL)); 441 | 442 | switch (mode) { 443 | case Group_Testing: 444 | group_testing(); 445 | break; 446 | case Testing: 447 | testing(); 448 | break; 449 | case Training: 450 | training(); 451 | break; 452 | case Contour_Testing: 453 | contour_testing(); 454 | break; 455 | default: 456 | return EXIT_FAILURE; 457 | } 458 | 459 | return EXIT_SUCCESS; 460 | } --------------------------------------------------------------------------------