├── BoundingBox.cpp ├── BoundingBox.h ├── Color.cpp ├── Color.h ├── Edge.h ├── Face.h ├── FileUtilities.h ├── GeometricObject.cpp ├── GeometricObject.h ├── GeometryExporter.cpp ├── GeometryExporter.h ├── GeometryProcessing.cpp ├── GeometryProcessing.h ├── GeospatialBoundingBox.cpp ├── GeospatialBoundingBox.h ├── Image.cpp ├── Image.h ├── ImageProcessing.cpp ├── ImageProcessing.h ├── Monitoring.h ├── Readme.txt ├── Sorter.h ├── StructurePointcloud.cbp ├── StructurePointcloud.depend ├── StructurePointcloud.layout ├── Utilities.h ├── bilateral_filtering ├── fft_3D │ ├── convolution_3D.h │ ├── fill_3D.h │ ├── support_3D.cpp │ └── support_3D.h └── include │ ├── array.h │ ├── array_n.h │ ├── chrono.h │ ├── fast_color_bf.h │ ├── fft_3D.h │ ├── geom.h │ ├── linear_bf.h │ ├── math_tools.h │ ├── mixed_vector.h │ └── msg_stream.h ├── data └── test_file.xyz └── main.cpp /BoundingBox.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __BOUNDING_BOX_CPP__ 7 | #define __BOUNDING_BOX_CPP__ 8 | 9 | #include "BoundingBox.h" 10 | 11 | 12 | BoundingBox::BoundingBox() { 13 | 14 | min_pt = Vector3f(FLT_MAX, FLT_MAX, FLT_MAX); 15 | max_pt = Vector3f(-FLT_MAX, -FLT_MAX, -FLT_MAX); 16 | } 17 | 18 | BoundingBox::BoundingBox(Vector3f const &_min_pt, Vector3f const &_max_pt) { 19 | min_pt = _min_pt; 20 | max_pt = _max_pt; 21 | } 22 | 23 | BoundingBox::~BoundingBox() { 24 | 25 | } 26 | 27 | 28 | Vector3f BoundingBox::getMinPt() const { 29 | return min_pt; 30 | } 31 | 32 | Vector3f BoundingBox::getMaxPt() const { 33 | return max_pt; 34 | } 35 | 36 | void BoundingBox::setMinPt(Vector3f const _min_pt) { 37 | 38 | min_pt = _min_pt; 39 | return; 40 | } 41 | 42 | void BoundingBox::setMaxPt(Vector3f const _max_pt) { 43 | 44 | max_pt = _max_pt; 45 | return; 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /BoundingBox.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | 7 | #ifndef __BOUNDING_BOX_H__ 8 | #define __BOUNDING_BOX_H__ 9 | 10 | /** Bounding Box 11 | * This class defines a bounding box and related functions 12 | * 13 | */ 14 | 15 | 16 | #include 17 | #include "Eigen/Eigen" 18 | using namespace Eigen; 19 | 20 | class BoundingBox { 21 | public: 22 | ///Constructor 23 | BoundingBox(); 24 | ///Constructor 25 | BoundingBox(Vector3f const &_min_pt, Vector3f const &_max_pt); 26 | ///Destructor 27 | ~BoundingBox(); 28 | 29 | ///Get min point 30 | Vector3f getMinPt() const; 31 | 32 | ///Get max point 33 | Vector3f getMaxPt() const; 34 | 35 | ///Set min point 36 | void setMinPt(Vector3f const _min_pt); 37 | 38 | ///Set max point 39 | void setMaxPt(Vector3f const _max_pt); 40 | 41 | protected: 42 | ///The minimum point 43 | Vector3f min_pt; 44 | ///The maximum point 45 | Vector3f max_pt; 46 | }; 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Color.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | #ifndef __COLOR_CPP__ 6 | #define __COLOR_CPP__ 7 | 8 | #include "Color.h" 9 | 10 | Color::Color() { 11 | data(0) = 0.0f; 12 | data(1) = 0.0f; 13 | data(2) = 0.0f; 14 | data(3) = 1.0f; 15 | } 16 | 17 | Color::Color(Color const &_color) { 18 | data(0) = _color.r(); 19 | data(1) = _color.g(); 20 | data(2) = _color.b(); 21 | data(3) = _color.a(); 22 | } 23 | 24 | Color::Color(float _grayscale) { 25 | data(0) = _grayscale; 26 | data(1) = _grayscale; 27 | data(2) = _grayscale; 28 | data(3) = 1.0f; 29 | } 30 | 31 | Color::Color(float _r, float _g, float _b) { 32 | data(0) = _r; 33 | data(1) = _g; 34 | data(2) = _b; 35 | data(3) = 1.0f; 36 | } 37 | 38 | Color::Color(float _r, float _g, float _b, float _a) { 39 | data(0) = _r; 40 | data(1) = _g; 41 | data(2) = _b; 42 | data(3) = _a; 43 | } 44 | 45 | Color::~Color() { 46 | } 47 | 48 | float Color::r() const { 49 | return data(0); 50 | } 51 | 52 | float &Color::r() { 53 | return data(0); 54 | } 55 | 56 | float Color::g() const { 57 | return data(1); 58 | } 59 | 60 | float &Color::g() { 61 | return data(1); 62 | } 63 | 64 | float Color::b() const { 65 | return data(2); 66 | } 67 | 68 | float &Color::b() { 69 | return data(2); 70 | } 71 | 72 | float Color::a() const { 73 | return data(3); 74 | } 75 | 76 | float &Color::a() { 77 | return data(3); 78 | } 79 | 80 | float& Color::operator()(int elementNumber) { 81 | return data(elementNumber); 82 | } 83 | 84 | float Color::operator()(int elementNumber) const { 85 | return data(elementNumber); 86 | } 87 | 88 | Color operator*(Color const &c1, Color const &c2) { 89 | Color result; 90 | for (int i = 0; i < 3; i++) 91 | result(i) = c1(i) * c2(i); 92 | return result; 93 | } 94 | 95 | Color operator*(Color const &c1, float scalar) { 96 | Color result; 97 | for (int i = 0; i < 3; i++) 98 | result(i) = c1(i) * scalar; 99 | return result; 100 | } 101 | 102 | Color operator*(float scalar, Color const &c1) { 103 | Color result; 104 | for (int i = 0; i < 3; i++) 105 | result(i) = c1(i) * scalar; 106 | return result; 107 | } 108 | 109 | Color operator+(Color const &c1, Color const &c2) { 110 | Color result; 111 | for (int i = 0; i < 3; i++) 112 | result(i) = c1(i) + c2(i); 113 | return result; 114 | } 115 | 116 | Color operator-(Color const &c1, Color const &c2) { 117 | Color result; 118 | for (int i = 0; i < 3; i++) 119 | result(i) = c1(i) - c2(i); 120 | return result; 121 | } 122 | 123 | Color operator/(Color const &c1, Color const &c2) { 124 | Color result; 125 | for (int i = 0; i < 3; i++) 126 | result(i) = c1(i) / std::max(float(COLOR_EPSILON), c2(i)); 127 | return result; 128 | } 129 | 130 | Color operator/(Color const &c1, float scalar) { 131 | Color result; 132 | for (int i = 0; i < 3; i++) 133 | result(i) = c1(i) / std::max(float(COLOR_EPSILON), scalar); 134 | return result; 135 | } 136 | 137 | bool operator==(Color const &c1, Color const &c2) { 138 | for (int i = 0; i < 3; i++) 139 | if (fabs(c1(i) - c2(i)) > COLOR_EPSILON) 140 | return false; 141 | return true; 142 | } 143 | 144 | bool operator!=(Color const &c1, Color const &c2) { 145 | for (int i = 0; i < 3; i++) 146 | if (fabs(c1(i) - c2(i)) > COLOR_EPSILON) 147 | return true; 148 | return false; 149 | } 150 | 151 | Color &Color::operator+=(Color const &vec) { 152 | for (int i = 0; i < 3; i++) { 153 | data(i) += vec.data(i); 154 | } 155 | return *this; 156 | } 157 | 158 | Color &Color::operator*=(float scalar) { 159 | for (int i = 0; i < 3; i++) { 160 | data(i) *= scalar; 161 | } 162 | return *this; 163 | } 164 | 165 | Color &Color::operator/=(float scalar) { 166 | for (int i = 0; i < 3; i++) { 167 | data(i) /= scalar; 168 | } 169 | return *this; 170 | } 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /Color.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | #ifndef __COLOR_H__ 6 | #define __COLOR_H__ 7 | 8 | #include 9 | #include 10 | using namespace Eigen; 11 | 12 | #define COLOR_EPSILON 1e-03 13 | 14 | class Color { 15 | public: 16 | ///Constructors 17 | Color(); 18 | Color(Color const &_color); 19 | Color(float _grayscale); 20 | Color(float _r, float _g, float _b); 21 | Color(float _r, float _g, float _b, float _a); 22 | ///Destructor 23 | ~Color(); 24 | 25 | ///Returns the color and opacity 26 | float r() const; 27 | float &r(); 28 | float g() const; 29 | float &g(); 30 | float b() const; 31 | float &b(); 32 | float a() const; 33 | float &a(); 34 | 35 | ///Get a specific element 36 | float& operator() (int elementNumber); 37 | float operator() (int elementNumber) const; 38 | 39 | ///Mathematical functions 40 | friend Color operator* ( Color const &c1, Color const &c2); 41 | friend Color operator* ( Color const &c1, float scalar); 42 | friend Color operator* ( float scalar, Color const &c1); 43 | friend Color operator+ ( Color const &c1, Color const &c2); 44 | friend Color operator- ( Color const &c1, Color const &c2); 45 | friend Color operator/ ( Color const &c1, Color const &c2); 46 | friend Color operator/ ( Color const &c1, float scalar); 47 | friend bool operator== ( Color const &c1, Color const &c2); 48 | friend bool operator!= ( Color const &c1, Color const &c2); 49 | 50 | Color &operator+= (Color const &vec); 51 | Color &operator*= (float scalar); 52 | Color &operator/= (float scalar); 53 | 54 | ///Conversion functions 55 | friend Color vector2color3(Vector3i const &v) { 56 | return Color(float(v(0)), float(v(1)), float(v(2)), 1.0f); 57 | } 58 | 59 | friend Color vector2color3(Vector3f const &v) { 60 | return Color(v(0), v(1), v(2), 1.0f); 61 | } 62 | 63 | friend Color vector2color3(Vector3d const &v) { 64 | return Color(float(v(0)), float(v(1)), float(v(2)), 1.0f); 65 | } 66 | 67 | friend Color vector2color4(Vector4i const &v) { 68 | return Color(float(v(0)), float(v(1)), float(v(2)), float(v(3))); 69 | } 70 | 71 | friend Color vector2color4(Vector4f const &v) { 72 | return Color(float(v(0)), float(v(1)), float(v(2)), float(v(3))); 73 | } 74 | 75 | friend Color vector2color4(Vector4d const &v) { 76 | return Color(float(v(0)), float(v(1)), float(v(2)), float(v(3))); 77 | } 78 | 79 | friend Vector3f color2vector3(Color const &c) { 80 | return Vector3f(c.r(), c.g(), c.b()); 81 | } 82 | 83 | friend Vector4f color2vector4(Color const &c) { 84 | return Vector4f(c.r(), c.g(), c.b(), c.a()); 85 | } 86 | 87 | ///Print functions 88 | friend std::ostream &operator<<(std::ostream &os, Color const &c) { 89 | os << "red: " << c.r() << " green: " << c.g() << " blue: " << c.b() << " alpha: " << c.a() << std::endl; 90 | return os; 91 | } 92 | 93 | friend std::istream &operator>>(std::istream& is, Color &c) { 94 | is >> c.data[0]; 95 | is >> c.data[1]; 96 | is >> c.data[2]; 97 | is >> c.data[3]; 98 | return is; 99 | } 100 | 101 | private: 102 | Vector4f data; 103 | }; 104 | 105 | Color vector2color3(Vector3i const &v); 106 | 107 | Color vector2color3(Vector3f const &v); 108 | 109 | Color vector2color3(Vector3d const &v); 110 | 111 | Color vector2color4(Vector4i const &v); 112 | 113 | Color vector2color4(Vector4f const &v); 114 | 115 | Color vector2color4(Vector4d const &v); 116 | 117 | Vector3f color2vector3(Color const &c); 118 | 119 | Vector4f color2vector4(Color const &c); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /Edge.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __EDGE_H__ 7 | #define __EDGE_H__ 8 | 9 | 10 | /** Edge 11 | * This class defines an edge connecting 2 points 12 | * 13 | **/ 14 | 15 | 16 | class Edge { 17 | public: 18 | ///Constructor 2 19 | Edge(Edge *_edge) { 20 | vertex1 = _edge->vertex1; 21 | vertex2 = _edge->vertex2; 22 | } 23 | 24 | ///Copy constructor 25 | Edge(Edge const &_edge) { 26 | vertex1 = _edge.vertex1; 27 | vertex2 = _edge.vertex2; 28 | } 29 | 30 | ///Constructor 3 31 | Edge(int v1, int v2) { 32 | vertex1 = v1; 33 | vertex2 = v2; 34 | } 35 | 36 | ///Get values 37 | int getVertex1() { 38 | return vertex1; 39 | } 40 | 41 | int getVertex2() { 42 | return vertex2; 43 | } 44 | 45 | private: 46 | ///The two vertices 47 | int vertex1,vertex2; 48 | 49 | ///Constructor 50 | Edge() {;} 51 | 52 | }; 53 | 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /Face.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | 7 | #ifndef __FACE_H__ 8 | #define __FACE_H__ 9 | 10 | /** 11 | * Face 12 | * This class defines the properties of a polygon/face 13 | **/ 14 | 15 | #include 16 | #include "Edge.h" 17 | 18 | class Face { 19 | 20 | public: 21 | ///Constructor 22 | Face() { 23 | vertex_indices.clear(); 24 | normal_indices.clear(); 25 | tex_coord_indices.clear(); 26 | edges.clear(); 27 | } 28 | ///Copy constructor 29 | Face(Face *_face) { 30 | vertex_indices.clear(); 31 | vertex_indices.insert(vertex_indices.begin(), _face->vertex_indices.begin(), _face->vertex_indices.end()); 32 | normal_indices.clear(); 33 | normal_indices.insert(normal_indices.begin(), _face->normal_indices.begin(), _face->normal_indices.end()); 34 | tex_coord_indices.clear(); 35 | tex_coord_indices.insert(tex_coord_indices.begin(), _face->tex_coord_indices.begin(), _face->tex_coord_indices.end()); 36 | edges.clear(); 37 | edges.insert(edges.begin(), _face->edges.begin(), _face->edges.end()); 38 | 39 | } 40 | 41 | ///Destructor 42 | ~Face() { 43 | vertex_indices.clear(); 44 | normal_indices.clear(); 45 | tex_coord_indices.clear(); 46 | edges.clear(); 47 | } 48 | 49 | ///Get values 50 | int getNumberOfVertices() { 51 | return vertex_indices.size(); 52 | } 53 | int getNumberOfNormals() { 54 | return normal_indices.size(); 55 | } 56 | int getNumberOfTextureCoords() { 57 | return tex_coord_indices.size(); 58 | } 59 | 60 | ///Vertex functions 61 | void setVertices(std::vector const &vertices) { 62 | vertex_indices.clear(); 63 | vertex_indices.insert(vertex_indices.begin(), vertices.begin(), vertices.end()); 64 | return; 65 | } 66 | void setVertexAt(int index,int vertex) { 67 | if (index>=0 && index const &normals) { 75 | normal_indices.clear(); 76 | normal_indices.insert(normal_indices.begin(), normals.begin(), normals.end()); 77 | return; 78 | } 79 | void setNormalAt(int index,int normal) { 80 | if (index>=0 && index const &textureCoords) { 88 | tex_coord_indices.clear(); 89 | tex_coord_indices.insert(tex_coord_indices.begin(), textureCoords.begin(), textureCoords.end()); 90 | return; 91 | } 92 | void setTextureCoord(int index, int tex_coord) { 93 | if (index>=0 && index const &_edges) { 101 | edges.clear(); 102 | edges.insert(edges.begin(), _edges.begin(), _edges.end()); 103 | return; 104 | } 105 | void setEdge(int index, Edge *edge) { 106 | if (index>=0 && index getVertices() { 114 | return vertex_indices; 115 | } 116 | ///Get vertex at 117 | int getVertexAt(int index) { 118 | return vertex_indices[index]; 119 | } 120 | ///Get normals 121 | std::vector getNormals() { 122 | return normal_indices; 123 | } 124 | ///Get normal at 125 | int getNormalAt(int index) { 126 | return normal_indices[index]; 127 | } 128 | ///Get textureCoords 129 | std::vector getTextureCoords() { 130 | return tex_coord_indices; 131 | } 132 | ///Get textureCoord at 133 | int getTextureCoordAt(int index) { 134 | return tex_coord_indices[index]; 135 | } 136 | ///Get edges 137 | std::vector getEdges() { 138 | return edges; 139 | } 140 | ///Get edge at 141 | Edge *getEdgeAt(int index) { 142 | return edges[index]; 143 | } 144 | 145 | ///Set everything 146 | void setFace(std::vector const &vertices, 147 | std::vector const &normals, 148 | std::vector const &tex_coords, 149 | std::vector const &_edges) { 150 | 151 | vertex_indices.clear(); 152 | vertex_indices.insert(vertex_indices.begin(), vertices.begin(), vertices.end()); 153 | 154 | normal_indices.clear(); 155 | normal_indices.insert(normal_indices.begin(), normals.begin(), normals.end()); 156 | 157 | tex_coord_indices.clear(); 158 | tex_coord_indices.insert(tex_coord_indices.begin(), tex_coords.begin(), tex_coords.end()); 159 | 160 | edges.clear(); 161 | edges.insert(edges.begin(), _edges.begin(), _edges.end()); 162 | 163 | return; 164 | } 165 | 166 | void setFace(std::vector const &vertices, 167 | std::vector const &normals, 168 | std::vector const &tex_coords) { 169 | 170 | vertex_indices.clear(); 171 | vertex_indices.insert(vertex_indices.begin(), vertices.begin(), vertices.end()); 172 | 173 | normal_indices.clear(); 174 | normal_indices.insert(normal_indices.begin(), normals.begin(), normals.end()); 175 | 176 | tex_coord_indices.clear(); 177 | tex_coord_indices.insert(tex_coord_indices.begin(), tex_coords.begin(), tex_coords.end()); 178 | 179 | return; 180 | } 181 | 182 | void setFace(std::vector const &vertices, 183 | std::vector const &normals) { 184 | 185 | vertex_indices.clear(); 186 | vertex_indices.insert(vertex_indices.begin(), vertices.begin(), vertices.end()); 187 | 188 | normal_indices.clear(); 189 | normal_indices.insert(normal_indices.begin(), normals.begin(), normals.end()); 190 | 191 | return; 192 | } 193 | 194 | void setFace(std::vector const &vertices) { 195 | 196 | vertex_indices.clear(); 197 | vertex_indices.insert(vertex_indices.begin(), vertices.begin(), vertices.end()); 198 | 199 | return; 200 | } 201 | private: 202 | ///A list of vertex indices 203 | std::vector vertex_indices; 204 | ///A list of normal indices 205 | std::vector normal_indices; 206 | ///A list of tex coord indices 207 | std::vector tex_coord_indices; 208 | ///A list of edges 209 | std::vector edges; 210 | 211 | }; 212 | 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /FileUtilities.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __FILE_UTILITIES_H__ 7 | #define __FILE_UTILITIES_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | ///Enumarated type for file types 18 | typedef enum { 19 | UNSUPPORTED_FORMAT = -1, 20 | JPG, 21 | PNG, 22 | TGA, 23 | BMP, 24 | HDR, 25 | PFM, 26 | XYZ, 27 | XYZI, 28 | XYZRGB, 29 | OBJ, 30 | LAS, 31 | TIF 32 | } FILE_TYPE; 33 | 34 | 35 | ///Separates the file name from the extension 36 | static bool separate(std::string const &file_name, std::string &name, FILE_TYPE &type) { 37 | 38 | ///The position of the "." 39 | int pos = file_name.rfind('.'); 40 | 41 | ///The name of the file 42 | name = file_name.substr(0,pos-1); 43 | 44 | ///The extension of the file 45 | std::string file_extension = &file_name.c_str()[pos+1]; 46 | if (strcasecmp(file_extension.c_str(),"jpg")==0 || strcasecmp(file_extension.c_str(),"jpeg")==0) { 47 | type = JPG; 48 | return true; 49 | } 50 | if (strcasecmp(file_extension.c_str(),"png")==0) { 51 | type = PNG; 52 | return true; 53 | } 54 | if (strcasecmp(file_extension.c_str(),"tga")==0) { 55 | type = TGA; 56 | return true; 57 | } 58 | if (strcasecmp(file_extension.c_str(),"hdr")==0) { 59 | type = HDR; 60 | return true; 61 | } 62 | if (strcasecmp(file_extension.c_str(),"pfm")==0) { 63 | type = PFM; 64 | return true; 65 | } 66 | if (strcasecmp(file_extension.c_str(),"xyz")==0) { 67 | type = XYZ; 68 | return true; 69 | } 70 | if (strcasecmp(file_extension.c_str(),"xyzi")==0) { 71 | type = XYZI; 72 | return true; 73 | } 74 | if (strcasecmp(file_extension.c_str(),"xyzrgb")==0) { 75 | type = XYZRGB; 76 | return true; 77 | } 78 | if (strcasecmp(file_extension.c_str(),"obj")==0) { 79 | type = OBJ; 80 | return true; 81 | } 82 | if (strcasecmp(file_extension.c_str(),"tiff")==0) { 83 | type = TIF; 84 | return true; 85 | } 86 | type = UNSUPPORTED_FORMAT; 87 | return false; 88 | } 89 | 90 | 91 | static int getDirectoryContents(std::string const &dir, std::vector &file_names) { 92 | DIR *dp; 93 | struct dirent *dirp; 94 | 95 | if((dp = opendir(dir.c_str())) == NULL) { 96 | std::cout << "Error(" << errno << ") opening " << dir << std::endl; 97 | return errno; 98 | } 99 | 100 | while ((dirp = readdir(dp)) != NULL) { 101 | file_names.push_back(std::string(dirp->d_name)); 102 | } 103 | closedir(dp); 104 | 105 | return 0; 106 | } 107 | #endif 108 | -------------------------------------------------------------------------------- /GeometricObject.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | 7 | #ifndef __GEOMETRIC_OBJECT_CPP__ 8 | #define __GEOMETRIC_OBJECT_CPP__ 9 | 10 | 11 | #include "GeometricObject.h" 12 | #include "GeometryProcessing.h" 13 | 14 | GeometricObject::GeometricObject(std::vector const &_vertices, 15 | std::vector const &_normals, 16 | std::vector const &_tex_coords, 17 | std::vector const &_faces, 18 | std::vector const &_edges): 19 | vertices(_vertices),normals(_normals),texture_coords(_tex_coords),faces(_faces),edges(_edges) { 20 | 21 | } 22 | 23 | 24 | GeometricObject::~GeometricObject() { 25 | 26 | vertices.clear(); 27 | normals.clear(); 28 | texture_coords.clear(); 29 | 30 | for (int i=0;i GeometricObject::getVertices() const { 42 | 43 | return vertices; 44 | } 45 | 46 | Vector3f GeometricObject::getVertexAt(int index) const { 47 | 48 | if (index>=0 && index GeometricObject::getNormals() const { 58 | 59 | return normals; 60 | } 61 | 62 | Vector3f GeometricObject::getNormalAt(int index) const { 63 | 64 | if (index>=0 && index GeometricObject::getTextureCoords() const { 71 | 72 | return texture_coords; 73 | } 74 | 75 | Vector2f GeometricObject::getTextureCoordAt(int index) const { 76 | 77 | if (index>=0 && index GeometricObject::getFaces() const { 84 | 85 | return faces; 86 | } 87 | 88 | Face *GeometricObject::getFaceAt(int index) const { 89 | 90 | if (index>=0 && index GeometricObject::getEdges() const { 97 | 98 | return edges; 99 | } 100 | 101 | Edge *GeometricObject::getEdgeAt(int index) const { 102 | 103 | if (index>=0 && index 16 | #include 17 | 18 | #include "Edge.h" 19 | #include "Face.h" 20 | #include "Eigen/Eigen" 21 | using namespace Eigen; 22 | 23 | class GeometricObject { 24 | public: 25 | ///Constructors 26 | GeometricObject(std::vector const &_vertices, 27 | std::vector const &_normals, 28 | std::vector const &_tex_coords, 29 | std::vector const &_faces, 30 | std::vector const &_edges); 31 | 32 | ///Copy constructor 33 | GeometricObject(GeometricObject const &_object); 34 | ///Destructor 35 | ~GeometricObject(); 36 | 37 | ///Get methods 38 | std::vector getVertices() const ; 39 | Vector3f getVertexAt(int index) const ; 40 | 41 | std::vector getNormals() const ; 42 | Vector3f getNormalAt(int index) const ; 43 | 44 | std::vector getTextureCoords() const ; 45 | Vector2f getTextureCoordAt(int index) const ; 46 | 47 | std::vector getFaces() const ; 48 | Face *getFaceAt(int index) const ; 49 | 50 | std::vector getEdges() const ; 51 | Edge *getEdgeAt(int index) const; 52 | 53 | void translate(Vector3f _translation); 54 | 55 | private: 56 | ///A list of vertices 57 | std::vector vertices; 58 | ///A list of normals 59 | std::vector normals; 60 | ///A list of texture coordinates 61 | std::vector texture_coords; 62 | ///A list of edges 63 | std::vector edges; 64 | ///A list of faces 65 | std::vector faces; 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /GeometryExporter.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __GEOMETRY_EXPORTER_CPP__ 7 | #define __GEOMETRY_EXPORTER_CPP__ 8 | 9 | #include "GeometryExporter.h" 10 | 11 | void GeometryExporter::exportToOBJ(const char *file_name, GeometricObject *object) { 12 | 13 | FILE *file_ptr = fopen(_format("%s.obj",file_name).c_str(),"w"); 14 | if (!file_ptr) return; 15 | 16 | 17 | time_t t = time(0); // get time now 18 | struct tm *now = localtime(&t); 19 | std::string time_string = _format("%d - %d - %d", (now->tm_year + 1900) , (now->tm_mon + 1) , now->tm_mday); 20 | //go through the vertices and faces and export all 21 | fprintf(file_ptr,"###################################################\n"); 22 | fprintf(file_ptr,"# Generated by StructurePointcloud #\n"); 23 | fprintf(file_ptr,"# Copyright © Charalambos \"Charis\" Poullis #\n"); 24 | fprintf(file_ptr,"# http://www.poullis.org #\n"); 25 | fprintf(file_ptr,"###################################################\n"); 26 | fprintf(file_ptr,"# Created on %s\n",time_string.c_str()); 27 | fprintf(file_ptr,"#\n\n"); 28 | 29 | std::vector vertices = object->getVertices(); 30 | for (int i=0;i normals = object->getNormals(); 35 | bool has_normals = normals.size(); 36 | for(int i=0;i tex_coords = object->getTextureCoords(); 41 | bool has_tex_coords = tex_coords.size(); 42 | for(int i=0;i faces = object->getFaces(); 48 | for (int i=0;i vertex_indices = faces[i]->getVertices(); 50 | fprintf(file_ptr,"f "); 51 | for (int v=0;v 10 | #include "Face.h" 11 | #include "Utilities.h" 12 | #include "GeometricObject.h" 13 | 14 | class GeometryExporter { 15 | public: 16 | ///Export geometry into an OBJ file 17 | static void exportToOBJ(const char *file_name, GeometricObject *object); 18 | }; 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /GeometryProcessing.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | #ifndef __GEOMETRY_PROCESSING_CPP__ 6 | #define __GEOMETRY_PROCESSING_CPP__ 7 | 8 | #include "GeometryProcessing.h" 9 | #include "Color.h" 10 | 11 | GeometricObject *GeometryProcessing::triangulate(Image *xyz_map, Image *_normal_map) { 12 | 13 | if (!xyz_map) { 14 | return 0x00; 15 | } 16 | 17 | ///Compute the normal map from the xyz map 18 | Image *normal_map = 0x00; 19 | if (_normal_map) { 20 | normal_map = _normal_map; 21 | } 22 | else { 23 | normal_map = GeometryProcessing::computeNormalMap(xyz_map, true); 24 | } 25 | 26 | ///Measure the number of valid points and allocate memory 27 | int number_of_valid_points = 0; 28 | for (int y=0;ygetHeight();y++) { 29 | for (int x=0;xgetWidth();x++) { 30 | if (xyz_map->getPixel(x,y) == Color(0.0f,0.0f,0.0f)) continue; 31 | number_of_valid_points++; 32 | } 33 | } 34 | ///create a connected mesh of the same size as the xyz map 35 | std::vector new_vertices,normals; 36 | std::vector new_faces; 37 | int vertex_count = 0 ; 38 | Image *already_added = new Image(xyz_map->getWidth(),xyz_map->getHeight(), -1.0f,-1.0f,-1.0f,1.0f); 39 | for (int y=0;ygetHeight();y++) { 40 | for (int x=0;xgetWidth();x++) { 41 | bool stop_it = false; 42 | int offset_x=1; 43 | int offset_y=1; 44 | if (xyz_map->getPixel(x,y)==Color(0.0f,0.0f,0.0f)) continue; 45 | if (y+offset_y >= xyz_map->getHeight() || x+offset_x >= xyz_map->getWidth()) continue; 46 | while (xyz_map->getPixel(x+offset_x,y)==Color(0.0f,0.0f,0.0f)) { 47 | offset_x++; 48 | if (x+offset_x>=xyz_map->getWidth()) { 49 | stop_it = true; 50 | break; 51 | } 52 | } 53 | if (stop_it) continue; 54 | while (xyz_map->getPixel(x,y+offset_y) == Color(0.0f,0.0f,0.0f)) { 55 | offset_y++; 56 | if (y+offset_y>=xyz_map->getHeight()) { 57 | stop_it = true; 58 | break; 59 | } 60 | } 61 | if (stop_it) continue; 62 | if (y+offset_y >= xyz_map->getHeight() || x+offset_x >= xyz_map->getWidth()) continue; 63 | if (xyz_map->getPixel(x+offset_x, y+offset_y) == Color(0.0f,0.0f,0.0f)) continue; 64 | std::vector vertex_indices; 65 | int index1,index2,index3,index4; 66 | //add the points 67 | if (already_added->getPixel(x,y) == Color(-1.0f,-1.0f,-1.0f)) { 68 | Vector3f point1 = color2vector3(xyz_map->getPixel(x,y)); 69 | new_vertices.push_back(point1); 70 | Vector3f normal1 = color2vector3(normal_map->getPixel(x,y)); 71 | normals.push_back(normal1); 72 | index1 = vertex_count; 73 | already_added->setPixel(x,y,Color(float(vertex_count))); 74 | vertex_count++; 75 | } 76 | else { 77 | index1 = int(already_added->getPixel(x,y).r()); 78 | } 79 | if (already_added->getPixel(x+offset_x,y) == Color(-1.0f,-1.0f,-1.0f)) { 80 | Vector3f point2 = color2vector3(xyz_map->getPixel(x+offset_x,y)); 81 | new_vertices.push_back(point2); 82 | Vector3f normal2 = color2vector3(normal_map->getPixel(x+offset_x,y)); 83 | normals.push_back(normal2); 84 | index2 = vertex_count; 85 | already_added->setPixel(x+offset_x,y,Color(float(vertex_count))); 86 | vertex_count++; 87 | } 88 | else { 89 | index2 = int(already_added->getPixel(x+offset_x,y).r()); 90 | } 91 | if (already_added->getPixel(x,y+offset_y)==Color(-1.0f,-1.0f,-1.0f)) { 92 | Vector3f point3 = color2vector3(xyz_map->getPixel(x,y+offset_y)); 93 | new_vertices.push_back(point3); 94 | Vector3f normal3 = color2vector3(normal_map->getPixel(x,y+offset_y)); 95 | normals.push_back(normal3); 96 | index3 = vertex_count; 97 | already_added->setPixel(x,y+offset_y, Color(float(vertex_count))); 98 | vertex_count++; 99 | } 100 | else { 101 | index3 = int(already_added->getPixel(x,y+offset_y).r()); 102 | } 103 | if (already_added->getPixel(x+offset_x, y+offset_y) == Color(-1.0f,-1.0f,-1.0f)) { 104 | Vector3f point4 = color2vector3(xyz_map->getPixel(x+offset_x, y+offset_y)); 105 | new_vertices.push_back(point4); 106 | Vector3f normal4 = color2vector3(normal_map->getPixel(x+offset_x,y+offset_y)); 107 | normals.push_back(normal4); 108 | index4 = vertex_count; 109 | already_added->setPixel(x+offset_x,y+offset_y,Color(float(vertex_count))); 110 | vertex_count++; 111 | } 112 | else { 113 | index4 = int(already_added->getPixel(x+offset_x, y+offset_y).r()); 114 | } 115 | //Create 2 faces for each of the triangles form 116 | //printf("%d %d %d %d\n",index1,index2,index3,index4); 117 | vertex_indices.push_back(index1); 118 | vertex_indices.push_back(index3); 119 | vertex_indices.push_back(index2); 120 | //create a new face 121 | Face *new_face_a = new Face(); 122 | new_face_a->setVertices(vertex_indices); 123 | new_faces.push_back(new_face_a); 124 | 125 | vertex_indices.clear(); 126 | vertex_indices.push_back(index2); 127 | vertex_indices.push_back(index3); 128 | vertex_indices.push_back(index4); 129 | //create a new face 130 | Face *new_face_b = new Face(); 131 | new_face_b->setVertices(vertex_indices); 132 | new_faces.push_back(new_face_b); 133 | } 134 | } 135 | 136 | ///add a new object 137 | std::vector tex_coords; 138 | std::vector edges; 139 | 140 | GeometricObject *new_object = new GeometricObject(new_vertices,normals,tex_coords,new_faces,edges); 141 | 142 | delete already_added; 143 | if (_normal_map == 0x00) { 144 | delete normal_map; 145 | } 146 | 147 | return new_object; 148 | } 149 | 150 | Image *GeometryProcessing::computeNormalMap(Image *xyz_map, bool z_up) { 151 | Image *normal_map = new Image(xyz_map->getWidth(),xyz_map->getHeight(),0.0f,0.0f,0.0f,1.0f); 152 | for (int y=0;ygetHeight();y++) { 153 | for (int x=0;xgetWidth();x++) { 154 | if (xyz_map->getPixel(x,y) != Color(0.0f,0.0f,0.0f)) { 155 | normal_map->setPixel(x,y,vector2color3(computeLocalNormal(xyz_map,Vector2i(x,y),z_up))); 156 | } 157 | } 158 | } 159 | return normal_map; 160 | } 161 | 162 | Vector3f GeometryProcessing::computeLocalNormal(Image *xyz_map, Vector2i const &index, bool z_up) { 163 | 164 | //get an average normal for this point 165 | //check the 8 neighbours of the pixel in counter clockwise order 166 | std::vector indices; 167 | indices.push_back(Vector2i(index(0)-1,index(1)+1)); 168 | indices.push_back(Vector2i(index(0)-1,index(1))); 169 | indices.push_back(Vector2i(index(0)-1,index(1)-1)); 170 | indices.push_back(Vector2i(index(0),index(1)-1)); 171 | //index5 is the pixel in question 172 | indices.push_back(Vector2i(index(0)+1,index(1)-1)); 173 | indices.push_back(Vector2i(index(0)+1,index(1))); 174 | indices.push_back(Vector2i(index(0)+1,index(1)+1)); 175 | indices.push_back(Vector2i(index(0),index(1)+1)); 176 | 177 | std::vector good_points; 178 | for (int i=0;igetPixel(indices[i](0), indices[i](1)) == Color(0.0f,0.0f,0.0f)) continue; 181 | good_points.push_back(color2vector3(xyz_map->getPixel(indices[i](0),indices[i](1)))); 182 | } 183 | 184 | std::vector diff_vectors; 185 | for (int i=0;igetPixel(index(0),index(1)))); 187 | } 188 | 189 | Vector3f normal = Vector3f(0.0f,0.0f,0.0f); 190 | for (int i=0;i EPSILON) current_normal.normalize(); 194 | ///THIS IS ADDED FOR AERIAL LIDAR DATA ONLY. THE Z SHOULD POINT UP 195 | if (z_up && current_normal(2) < 0.0f) { 196 | current_normal = -current_normal; 197 | } 198 | normal += current_normal; 199 | } 200 | 201 | normal /= float(std::max(1,int(diff_vectors.size()))); 202 | if (normal.norm() > EPSILON) { 203 | normal.normalize(); 204 | } 205 | 206 | return normal; 207 | } 208 | 209 | #endif 210 | -------------------------------------------------------------------------------- /GeometryProcessing.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __GEOMETRY_PROCESSING_H__ 7 | #define __GEOMETRY_PROCESSING_H__ 8 | 9 | /** Geometry Processing 10 | * This class defines a set of functions to process geometry. 11 | * 12 | */ 13 | 14 | #include 15 | #include "opencv.hpp" 16 | 17 | #include "Image.h" 18 | #include "Color.h" 19 | #include 20 | using namespace Eigen; 21 | 22 | #include "Utilities.h" 23 | #include "GeometricObject.h" 24 | 25 | class GeometryProcessing { 26 | 27 | public: 28 | ///Performs triangulation using an XYZ map 29 | static GeometricObject *triangulate(Image *xyz_map, Image *_normal_map=0x00); 30 | 31 | ///Computes the normal at a point given an XYZ map 32 | static Vector3f computeLocalNormal(Image *map, Vector2i const &index, bool z_up=false); 33 | 34 | ///Computes the normal map per point given an XYZ map 35 | static Image *computeNormalMap(Image *map, bool z_up=false); 36 | }; 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /GeospatialBoundingBox.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __GEOSPATIAL_BOUNDING_BOX_CPP__ 7 | #define __GEOSPATIAL_BOUNDING_BOX_CPP__ 8 | 9 | #include "GeospatialBoundingBox.h" 10 | 11 | #define DEBUG 1 12 | 13 | int GeospatialBoundingBox::global_id = 0; 14 | 15 | GeospatialBoundingBox::GeospatialBoundingBox() { 16 | file_name = _format("geo_bbox_%d.xyz",global_id++); 17 | 18 | normal_map = 0x00; 19 | xyz_map = 0x00; 20 | height_and_normal_variation_map = 0x00; 21 | } 22 | 23 | GeospatialBoundingBox::GeospatialBoundingBox(int index, std::string *base_file_name) { 24 | 25 | normal_map = 0x00; 26 | xyz_map = 0x00; 27 | height_and_normal_variation_map = 0x00; 28 | 29 | 30 | load(index, base_file_name); 31 | } 32 | 33 | GeospatialBoundingBox::GeospatialBoundingBox(Vector3f const &_centroid, Vector3f const &_resolution, std::string *_file_name) { 34 | 35 | resolution = _resolution; 36 | 37 | ///mark the point as the centroid 38 | centroid = _centroid; 39 | 40 | ///Initialize the min height 41 | min_height = FLT_MAX; 42 | 43 | ///compute the min and max (allow +1 buffer zone) 44 | min_pt = centroid - (Vector3f(1.0f,1.0f,1.0f) + resolution/2.0f); 45 | max_pt = centroid + (Vector3f(1.0f,1.0f,1.0f) + resolution/2.0f); 46 | 47 | number_of_points = 0; 48 | 49 | if (_file_name) { 50 | file_name = (*_file_name); 51 | } 52 | else { 53 | file_name = _format("../output/geo_bbox_%d.xyz",global_id++); 54 | } 55 | output_file.open(file_name.c_str()); 56 | 57 | normal_map = 0x00; 58 | xyz_map = 0x00; 59 | height_and_normal_variation_map = 0x00; 60 | } 61 | 62 | GeospatialBoundingBox::~GeospatialBoundingBox() { 63 | if (output_file.is_open()) output_file.close(); 64 | if (xyz_map) delete xyz_map; 65 | if (normal_map) delete normal_map; 66 | if (height_and_normal_variation_map) delete height_and_normal_variation_map; 67 | } 68 | 69 | int GeospatialBoundingBox::getNumberOfPoints() const { 70 | return number_of_points; 71 | } 72 | 73 | std::vector GeospatialBoundingBox::getPoints(bool offset) const { 74 | 75 | std::string line; 76 | std::vector points; 77 | ///open the file for reading 78 | ifstream input_file(file_name.c_str()); 79 | if (!input_file.is_open()) return points; 80 | 81 | // Vector3f _centroid; 82 | // getline (input_file,line); 83 | // sscanf(line.c_str(),"centroid: %f %f %f\n",&_centroid(0),&_centroid(1),&_centroid(2)); 84 | // 85 | // Vector3f _min_pt; 86 | // getline (input_file,line); 87 | // sscanf(line.c_str(),"min_pt: %f %f %f\n",&_min_pt(0),&_min_pt(1),&_min_pt(2)); 88 | // 89 | // Vector3f _max_pt; 90 | // getline (input_file,line); 91 | // sscanf(line.c_str(),"max_pt: %f %f %f\n",&_max_pt(0),&_max_pt(1),&_max_pt(2)); 92 | // 93 | ///Read all the points (-1 because the centroid is not stored in file) 94 | for (int i=0;i point(2)) { 129 | min_height = point(2); 130 | } 131 | 132 | return; 133 | } 134 | 135 | void GeospatialBoundingBox::initialize(Image *resampled) { 136 | 137 | if (!resampled) { 138 | 139 | ///Close the pointer to the output file 140 | if (output_file.is_open()) output_file.close(); 141 | 142 | ///Get the points from the file 143 | std::vector points = getPoints(true); 144 | 145 | ///Resample the data into an XYZ map 146 | if (!resample()) { 147 | return; 148 | } 149 | exportXYZMap("A"); 150 | 151 | ///Perform a hole filling on the XYZ map 152 | Image *non_hole_filled_map = new Image(); 153 | non_hole_filled_map->copy(xyz_map); 154 | ImageProcessing::holeFill(xyz_map,6,4); 155 | exportXYZMap("B"); 156 | 157 | ///Perform point reduction by removing extra points added by the hole filling 158 | ImageProcessing::pointReduction(non_hole_filled_map, xyz_map, 10); 159 | exportXYZMap("C"); 160 | delete non_hole_filled_map; 161 | 162 | ///Apply bilateral filtering 163 | Image *zzz_map = new Image(xyz_map->getWidth(), xyz_map->getHeight()); 164 | ///Copy the XYZ map 165 | zzz_map->copy(xyz_map); 166 | ///Convert from RGB to BBB 167 | zzz_map->grayscale(2); 168 | ///Normalize the zzz_map 169 | zzz_map->normalize(); 170 | 171 | Image *filtered_image = ImageProcessing::bilateralFilteringGrayscale(zzz_map,1.0,1.0,0.025,0.025);//0.25,0.25,0.025,0.025); 172 | Color *min_max_val = xyz_map->range(); 173 | for (int y=0;ygetHeight();y++) { 174 | for (int x=0;xgetWidth();x++) { 175 | Color val = xyz_map->getPixel(x,y); 176 | ///If the point didn't have a valid point then don't add the smooth one 177 | if (val == Color(0.0f,0.0f,0.0f)) continue; 178 | float b = filtered_image->getPixel(x,y).b(); 179 | b = min_max_val[0].b() + b*(min_max_val[1].b()-min_max_val[0].b()); 180 | xyz_map->setPixel(x,y,Color(val.r(),val.g(),b)); 181 | } 182 | } 183 | delete zzz_map; 184 | delete filtered_image; 185 | exportXYZMap("D"); 186 | 187 | delete [] min_max_val; 188 | 189 | } 190 | else { 191 | xyz_map = resampled; 192 | ///Add the points to the geo box 193 | for (int y=0;ygetHeight();y++) { 194 | for (int x=0;xgetWidth();x++) { 195 | Vector3f pt = color2vector3(xyz_map->getPixel(x,y)); 196 | if (pt == Vector3f(0.0f,0.0f,0.0f)) continue; 197 | insert(pt); 198 | } 199 | } 200 | } 201 | 202 | ///Compute a normal map based on the XYZ map of the geo_box 203 | if (normal_map) delete normal_map; 204 | normal_map = GeometryProcessing::computeNormalMap(xyz_map, true); 205 | normal_map->blur(1); 206 | normal_map->perPixelNormalization(); 207 | normal_map->saveImage(_format("%s_normal_map.pfm", file_name.c_str())); 208 | 209 | ///Compute the height variation between the points 210 | computeHeightAndNormalVariation(); 211 | 212 | return; 213 | } 214 | 215 | bool GeospatialBoundingBox::maxedOut() { 216 | return (number_of_points >= MAX_CAPACITY); 217 | } 218 | 219 | Vector3f GeospatialBoundingBox::getResolution() { 220 | return resolution; 221 | } 222 | 223 | std::string GeospatialBoundingBox::getFileName() { 224 | return file_name; 225 | } 226 | 227 | void GeospatialBoundingBox::setFileName(std::string const &_file_name) { 228 | ///Close the existing file 229 | output_file.close(); 230 | ///Erase it from disk 231 | if (remove(file_name.c_str()) == -1) { 232 | std::cout << " Couldn't remove original file..." << std::endl; 233 | } 234 | 235 | ///Assign the new name 236 | file_name = _file_name; 237 | 238 | ///open the file with the new name 239 | output_file.open(file_name.c_str()); 240 | return; 241 | } 242 | 243 | bool GeospatialBoundingBox::liesInside(Vector3f const &point) { 244 | return ( (point(0) + EPSILON >= min_pt(0)) && (point(0) <= max_pt(0) + EPSILON) && 245 | (point(1) + EPSILON >= min_pt(1)) && (point(1) <= max_pt(1) + EPSILON) && 246 | (point(2) + EPSILON >= min_pt(2)) && (point(2) <= max_pt(2) + EPSILON) ); 247 | } 248 | 249 | void GeospatialBoundingBox::exportXYZMap(std::string const &post_script) { 250 | 251 | if (post_script == "") { 252 | xyz_map->saveImage(_format("%s.pfm",file_name.c_str())); 253 | std::cout << _format("XYZ map %s.pfm saved.",file_name.c_str()).c_str() << std::endl; 254 | } 255 | else { 256 | xyz_map->saveImage(_format("%s_%s.pfm",file_name.c_str(),post_script.c_str())); 257 | std::cout << _format("XYZ map %s_%s.pfm saved.",file_name.c_str(),post_script.c_str()).c_str() << std::endl; 258 | } 259 | 260 | return; 261 | } 262 | 263 | Image *GeospatialBoundingBox::getXYZMap() { 264 | return xyz_map; 265 | } 266 | 267 | Image *GeospatialBoundingBox::getNormalMap() { 268 | return normal_map; 269 | } 270 | 271 | Image *GeospatialBoundingBox::getHeightAndNormalVariationMap() { 272 | return height_and_normal_variation_map; 273 | } 274 | 275 | GeometricObject *GeospatialBoundingBox::getObject() { 276 | 277 | std::string start_time = timestamp(); 278 | GeometricObject *new_object = GeometryProcessing::triangulate(xyz_map, normal_map); 279 | std::string end_time = timestamp(); 280 | 281 | std::cout << _format("Triangulation process done. (start time: %s, end time: %s)", start_time.c_str(), end_time.c_str()) << std::endl; 282 | 283 | return new_object; 284 | 285 | } 286 | 287 | Vector3f GeospatialBoundingBox::getPoint(Vector2i const &point_index) { 288 | return color2vector3(xyz_map->getPixel(point_index(0),point_index(1))); 289 | } 290 | 291 | float GeospatialBoundingBox::getMinHeight() { 292 | return min_height; 293 | } 294 | 295 | void GeospatialBoundingBox::cleanUp() { 296 | 297 | if (xyz_map) delete xyz_map; 298 | if (normal_map) delete normal_map; 299 | if (height_and_normal_variation_map) delete height_and_normal_variation_map; 300 | xyz_map = normal_map = height_and_normal_variation_map = 0x00; 301 | return; 302 | } 303 | 304 | bool GeospatialBoundingBox::resample(double sampling_tolerance, double step) { 305 | 306 | std::cout << _format("Re-sampling process initiated.").c_str() << std::endl; 307 | 308 | ///Get all the points contained in the file 309 | std::vector points = getPoints(true); 310 | 311 | ///Make 2 copies of the points 312 | std::vector sorted_vertices_based_x,sorted_vertices_based_y; 313 | for (int i=0;i unique_sorted_x,unique_sorted_y; 330 | while(size_not_ok) { 331 | size_not_ok = false; 332 | for (unsigned int i=0;i MAX_SIZE_X) { 341 | size_not_ok = true; 342 | unique_sorted_x.clear(); 343 | sampling_tolerance += step; 344 | } 345 | } 346 | size_not_ok = true; 347 | while(size_not_ok) { 348 | size_not_ok = false; 349 | for (int j=0;j MAX_SIZE_Y) { 358 | size_not_ok = true; 359 | unique_sorted_y.clear(); 360 | sampling_tolerance += step; 361 | } 362 | } 363 | 364 | std::cout << _format("Chosen epsilon(bin size): %f. Map size will be %dx%d",sampling_tolerance,sizex,sizey).c_str() << std::endl; 365 | 366 | ///Check if the size of the map is more than the maximum size 367 | if (sizex > MAX_SIZE_X || sizey > MAX_SIZE_Y) { 368 | std::cout << _format("The size of the map exceeds the maximum conditions.") << std::endl; 369 | return false; 370 | } 371 | 372 | //It will generate maps of size K pixels 373 | int map_sizex = min(sizex,MAX_SIZE_X); 374 | int map_sizey = min(sizey,MAX_SIZE_Y); 375 | 376 | //create an XYZ map(2d grid) which will store all the points 377 | xyz_map = new Image(map_sizex,map_sizey, 0.0f,0.0f,0.0f,1.0f); 378 | Image *weights = new Image(map_sizex,map_sizey,0.0f,0.0f,0.0f,1.0f); 379 | 380 | //go through the sorted points in x and y and distribute them in the grid 381 | int hits = 0; 382 | for (int i=0;i= sizex) break; 384 | for (int j=0;j= 0 && pos < map_sizey) {/*printf("within range\n");*/;} 396 | else {/*printf(" %d =getPixel(i,pos); 398 | weights->setPixel(i,pos, existing_value + Color(1.0f)); 399 | existing_value = xyz_map->getPixel(i,pos); 400 | xyz_map->setPixel(i,pos, existing_value + Color(float(unique_sorted_x[i](0)),float(sorted_vertices_based_y[j](1)),float(sorted_vertices_based_y[j](2)))); 401 | hits++; 402 | } 403 | } 404 | } 405 | //averaging 406 | for (int y=0;ygetHeight();y++) { 407 | for (int x=0;xgetWidth();x++) { 408 | if (weights->getPixel(x,y) != Color(0.0f,0.0f,0.0f,1.0f)) { 409 | xyz_map->setPixel(x,y, xyz_map->getPixel(x,y)/weights->getPixel(x,y).r()); 410 | } 411 | } 412 | } 413 | if (hits==0) { 414 | std::cout << _format("XYZ map contains no points.").c_str() << std::endl; 415 | } 416 | 417 | std::cout << _format("Re-sampling process complete.").c_str() << std::endl; 418 | 419 | delete weights; 420 | return true; 421 | } 422 | 423 | void GeospatialBoundingBox::computeHeightAndNormalVariation() { 424 | 425 | int neighbourhood_size = 3; 426 | int neighbourhood_search_size = neighbourhood_size/2; 427 | ///Create a map the same size as the xyz map 428 | if (height_and_normal_variation_map) delete height_and_normal_variation_map; 429 | height_and_normal_variation_map = new Image(xyz_map->getWidth(), xyz_map->getHeight(),0.0f,0.0f,0.0f,0.0f); 430 | 431 | ///Go through the xyz map and for each point compute the min max values in a 3x3 window area 432 | for (int y=0;ygetHeight();y++) { 433 | for (int x=0;xgetWidth();x++) { 434 | ///Check only the valid points 435 | if ((xyz_map->getPixel(x,y) == Color(0.0f,0.0f,0.0f)) || (normal_map->getPixel(x,y) == Color(0.0f,0.0f,0.0f))) continue; 436 | ///Get the height of the point in question 437 | float current_height = xyz_map->getPixel(x,y).b(); 438 | 439 | float min_height = xyz_map->getPixel(x,y).b(); 440 | float max_height = xyz_map->getPixel(x,y).b(); 441 | 442 | float min_dot = 1.0f; 443 | float max_dot = 0.0f; 444 | 445 | ///Get the normal of the point in question 446 | Vector3f normal = color2vector3(normal_map->getPixel(x,y)); 447 | 448 | ///Search in a neighbourhood 449 | for (int i=y-neighbourhood_search_size;i<=y+neighbourhood_search_size;i++){ 450 | for (int j=x-neighbourhood_search_size;j<=x+neighbourhood_search_size;j++) { 451 | ///If it's the same continue 452 | if (i==y && j==x) continue; 453 | ///Check for out of bounds 454 | if (outOfBounds(xyz_map, j, i)) continue; 455 | ///if its a valid point 456 | if (xyz_map->getPixel(j,i) == Color(0.0f,0.0f,0.0f)) continue; 457 | 458 | ///Check if max or min for the depth 459 | if (min_height > xyz_map->getPixel(j,i)(2)) min_height = xyz_map->getPixel(j,i)(2); 460 | if (max_height < xyz_map->getPixel(j,i)(2)) max_height = xyz_map->getPixel(j,i)(2); 461 | 462 | ///Check if max or min for the dot product 463 | float dot_product = std::max(0.0f,1.0f - fabs(normal.dot(color2vector3(normal_map->getPixel(j,i))))); 464 | 465 | if (min_dot > dot_product) min_dot = dot_product; 466 | if (max_dot < dot_product) max_dot = dot_product; 467 | } 468 | } 469 | 470 | ///Compute the height variation 471 | float max_minus_min = max_height - min_height; 472 | if (max_minus_min < EPSILON) max_minus_min = 1.0f; 473 | float height_var = (current_height-min_height)/max_minus_min; 474 | ///Compute the normal variation 475 | float normal_var = max_dot - min_dot; 476 | ///Store it in the variation map 477 | height_and_normal_variation_map->setPixel(x,y,Color(height_var,normal_var,0.0f)); 478 | } 479 | } 480 | 481 | if (DEBUG) { 482 | height_and_normal_variation_map->saveImage(_format("%s_height_and_dot_variation_map_A.pfm", file_name.c_str())); 483 | } 484 | return; 485 | } 486 | 487 | void GeospatialBoundingBox::load(int index, std::string *base_file_name) { 488 | 489 | if (base_file_name) { 490 | file_name = (*base_file_name); 491 | } 492 | else { 493 | file_name = _format("geo_bbox_%d.xyz.info",index); 494 | } 495 | 496 | FILE *file_ptr = fopen(file_name.c_str(), "r"); 497 | fscanf(file_ptr,"centroid: %f %f %f\n",¢roid(0), ¢roid(1), ¢roid(2)); 498 | fscanf(file_ptr,"resolution: %f %f %f\n",&resolution(0), &resolution(1), &resolution(2)); 499 | fscanf(file_ptr,"min_pt: %f %f %f\n",&min_pt(0), &min_pt(1), &min_pt(2)); 500 | fscanf(file_ptr,"max_pt: %f %f %f\n",&max_pt(0), &max_pt(1), &max_pt(2)); 501 | fscanf(file_ptr,"points: %d\n", &number_of_points); 502 | fclose(file_ptr); 503 | return; 504 | } 505 | 506 | void GeospatialBoundingBox::exportInformationFile() { 507 | 508 | FILE *file_ptr = fopen(_format("%s.info",file_name.c_str()).c_str(), "w"); 509 | fprintf(file_ptr, "centroid: %f %f %f\n", centroid(0), centroid(1), centroid(2)); 510 | fprintf(file_ptr, "resolution: %f %f %f\n",resolution(0), resolution(1), resolution(2)); 511 | fprintf(file_ptr, "min_pt: %f %f %f\n", min_pt(0), min_pt(1), min_pt(2)); 512 | fprintf(file_ptr, "max_pt: %f %f %f\n", max_pt(0), max_pt(1), max_pt(2)); 513 | fprintf(file_ptr, "points: %d\n", number_of_points); 514 | fclose(file_ptr); 515 | return; 516 | } 517 | 518 | #endif 519 | 520 | -------------------------------------------------------------------------------- /GeospatialBoundingBox.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __GEOSPATIAL_BOUNDING_BOX_H__ 7 | #define __GEOSPATIAL_BOUNDING_BOX_H__ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | using namespace Eigen; 16 | 17 | #include "BoundingBox.h" 18 | #include "ImageProcessing.h" 19 | #include "GeometryProcessing.h" 20 | #include "Image.h" 21 | #include "Utilities.h" 22 | #include "Sorter.h" 23 | #include 24 | 25 | using namespace std; 26 | 27 | #define MAX_SIZE_X 2048 28 | #define MAX_SIZE_Y 2048 29 | 30 | #define MAX_CAPACITY MAX_SIZE_X * MAX_SIZE_Y 31 | 32 | 33 | /** Geospatial Bounding Box 34 | * This class defines a geospatial bounding box and related functions 35 | * Subclasses the BoundingBox class 36 | */ 37 | 38 | class GeospatialBoundingBox : public BoundingBox { 39 | public: 40 | ///Constructors 41 | GeospatialBoundingBox(); 42 | GeospatialBoundingBox(int index, std::string *base_file_name=0x00); 43 | GeospatialBoundingBox(Vector3f const &_centroid, Vector3f const &_resolution, std::string *_file_name=0x00); 44 | ///Destructor 45 | ~GeospatialBoundingBox(); 46 | 47 | ///Return the number of points contained in the bounding box 48 | int getNumberOfPoints() const; 49 | 50 | ///Return the points 51 | std::vector getPoints(bool offset=false) const; 52 | 53 | ///Insert a new point in the bounding box 54 | void insert(Vector3f const &point); 55 | 56 | ///Get the centroid 57 | Vector3f getCentroid() const; 58 | 59 | ///Initializes the data required for further processing i.e. xyz map, triangulation, and closing the file 60 | void initialize(Image *resampled=0x00); 61 | 62 | ///Returns true if the box has reached maximum capacity 63 | bool maxedOut(); 64 | 65 | ///Returns a 3D vector representing the resolution of the box 66 | Vector3f getResolution(); 67 | 68 | ///Returns the filename corresponding to this geospatial bounding box 69 | std::string getFileName(); 70 | 71 | ///Sets the filename 72 | void setFileName(std::string const &_file_name); 73 | 74 | ///Returns true if the point lies inside the geospatial bounding box 75 | bool liesInside(Vector3f const &point); 76 | 77 | ///Exports the XYZ map corresponding to the geobox 78 | void exportXYZMap(std::string const &post_script); 79 | 80 | ///Returns the XYZ map corresponding to the geobox 81 | Image *getXYZMap(); 82 | 83 | ///Returns the normal map corresponding to the geobox 84 | Image *getNormalMap(); 85 | 86 | ///Returns the height and normal variation map corresponding to the geobox 87 | Image *getHeightAndNormalVariationMap(); 88 | 89 | ///Return the point corresponding to the index 90 | Vector3f getPoint(Vector2i const &point_index); 91 | 92 | ///Returns the minimum height (actual value) 93 | float getMinHeight(); 94 | 95 | ///Clean up excess memory 96 | void cleanUp(); 97 | 98 | ///Resample into the right-sized XYZ in order to minimize information loss 99 | bool resample(double sampling_tolerance= 1.0, double step = 0.05); 100 | 101 | ///Loads the information from disk 102 | void load(int index, std::string *base_file_name=0x00); 103 | 104 | ///Exports an information about the geobox 105 | void exportInformationFile(); 106 | 107 | ///Returns the geometric object 108 | GeometricObject *getObject(); 109 | 110 | private: 111 | ///The number of points contained in this bounding box 112 | int number_of_points; 113 | ///The file pointer containing the points 114 | ofstream output_file; 115 | ///The filename of the bounding box's points 116 | std::string file_name; 117 | ///Resolution on x,y,z 118 | Vector3f resolution; 119 | ///The XYZ map corresponding to the bbox 120 | Image *xyz_map; 121 | ///The normal map 122 | Image *normal_map; 123 | ///The height and variation map 124 | Image *height_and_normal_variation_map; 125 | ///The keypoint of the bounding box 126 | Vector3f centroid; 127 | ///Minimum value for height 128 | float min_height; 129 | 130 | ///Global static bounding box id 131 | static int global_id; 132 | 133 | 134 | ///Computes the height and normal variation map 135 | void computeHeightAndNormalVariation(); 136 | 137 | }; 138 | 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /Image.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | #ifndef __IMAGE_CPP__ 6 | #define __IMAGE_CPP__ 7 | 8 | #include "Image.h" 9 | #include "Utilities.h" 10 | #include "GeometryProcessing.h" 11 | #include "ImageProcessing.h" 12 | #include "BoundingBox.h" 13 | 14 | #include "Monitoring.h" 15 | 16 | float Image::gamma_table[65536]; 17 | float Image::current_gamma = -1.0f; 18 | unsigned short Image::lookup_table[65536]; 19 | 20 | Image::Image() { 21 | functions_used["Image: Image"] = 1; 22 | 23 | data = 0x00; 24 | width = height = 0; 25 | type = UNSUPPORTED_FORMAT; 26 | file_name = ""; 27 | 28 | ///Initialize the gamma table 29 | initializeGammaTable(1.0f); 30 | ///Initialize the lookup table 31 | initializeLookupTable(); 32 | } 33 | 34 | Image::Image(Image const &img) { 35 | functions_used["Image: Image2"] = 1; 36 | 37 | width = img.getWidth(); 38 | height = img.getHeight(); 39 | 40 | ///Allocate and initialize memory 41 | data = new Color*[height]; 42 | for (int y=0;yred << " " << pixel->green << " " << pixel->blue << std::endl; 232 | data[y][x] = Color(gamma_table[int(pixel->red)],gamma_table[int(pixel->green)],gamma_table[int(pixel->blue)],1.0f); 233 | } 234 | } 235 | type = JPG; 236 | } 237 | catch( Magick::Exception &e) { 238 | std::cout << "Loading JPG exception: " << e.what() << std::endl; 239 | return false; 240 | } 241 | 242 | return true; 243 | } 244 | 245 | bool Image::loadTIF(std::string const &file_name) { 246 | functions_used["Image: loadTIF"] = 1; 247 | 248 | try { 249 | Magick::Image img; 250 | img.read(file_name); 251 | width = img.columns(); 252 | height = img.rows(); 253 | ///Copy over to the internal structure 254 | Magick::PixelPacket *pixel_cache = img.getPixels(0,0,width,height); 255 | data = new Color*[height]; 256 | for (int y=0;yred)],gamma_table[int(pixel->green)],gamma_table[int(pixel->blue)],1.0f); 261 | } 262 | } 263 | type = TIF; 264 | } 265 | catch( Magick::Exception &e) { 266 | std::cout << "Loading TIF exception: " << e.what() << std::endl; 267 | return false; 268 | } 269 | 270 | return true; 271 | } 272 | 273 | bool Image::loadPNG(std::string const &file_name) { 274 | functions_used["Image: loadPNG"] = 1; 275 | 276 | try { 277 | Magick::Image img; 278 | img.read(file_name); 279 | width = img.columns(); 280 | height = img.rows(); 281 | ///Copy over to the internal structure 282 | Magick::PixelPacket *pixel_cache = img.getPixels(0,0,width,height); 283 | data = new Color*[height]; 284 | for (int y=0;yred << " " << pixel->green << " " << pixel->blue << std::endl; 289 | data[y][x] = Color(gamma_table[int(pixel->red)],gamma_table[int(pixel->green)],gamma_table[int(pixel->blue)],1.0f); 290 | } 291 | } 292 | type = PNG; 293 | } 294 | catch( Magick::Exception &e) { 295 | std::cout << "Loading PNG exception: " << e.what() << std::endl; 296 | return false; 297 | } 298 | 299 | return true; 300 | } 301 | 302 | bool Image::loadTGA(std::string const &file_name) { 303 | functions_used["Image: loadTGA"] = 1; 304 | 305 | try { 306 | Magick::Image img; 307 | img.read(file_name); 308 | width = img.columns(); 309 | height = img.rows(); 310 | ///Copy over to the internal structure 311 | Magick::PixelPacket *pixel_cache = img.getPixels(0,0,width,height); 312 | data = new Color*[height]; 313 | for (int y=0;yred)],gamma_table[int(pixel->green)],gamma_table[int(pixel->blue)],1.0f); 318 | } 319 | } 320 | type = TGA; 321 | } 322 | catch( Magick::Exception &e) { 323 | std::cout << "Loading TGA exception: " << e.what() << std::endl; 324 | return false; 325 | } 326 | 327 | return true; 328 | } 329 | 330 | bool Image::loadBMP(std::string const &file_name) { 331 | functions_used["Image: loadBMP"] = 1; 332 | 333 | try { 334 | Magick::Image img; 335 | img.read(file_name); 336 | width = img.columns(); 337 | height = img.rows(); 338 | ///Copy over to the internal structure 339 | Magick::PixelPacket *pixel_cache = img.getPixels(0,0,width,height); 340 | data = new Color*[height]; 341 | for (int y=0;yred)],gamma_table[int(pixel->green)],gamma_table[int(pixel->blue)],1.0f); 346 | } 347 | } 348 | type = BMP; 349 | } 350 | catch( Magick::Exception &e) { 351 | std::cout << "Loading BMP exception: " << e.what() << std::endl; 352 | return false; 353 | } 354 | 355 | return true; 356 | } 357 | 358 | bool Image::loadPFM(std::string const &file_name) { 359 | functions_used["Image: loadPFM"] = 1; 360 | 361 | bool result = false; 362 | 363 | FILE *infile = 0x00; 364 | infile = fopen(file_name.c_str(), "rb"); 365 | if (!infile) return false; 366 | 367 | unsigned int a = fgetc(infile); 368 | int b = fgetc(infile); 369 | int junk = fgetc(infile); 370 | float *val = new float[3]; 371 | float *cval = new float[1]; 372 | 373 | if ((a != 'P') || ((b != 'F') && (b != 'f'))) 374 | goto LoadPFM_error_end; 375 | 376 | b = (b == 'F'); // 'F' = RGB, 'f' = monochrome 377 | 378 | int x, y; 379 | fscanf(infile, "%d %d%c", &x, &y, &junk); 380 | if ((x <= 0) || (y <= 0)) 381 | goto LoadPFM_error_end; 382 | 383 | float scalefactor; 384 | fscanf(infile, "%f%c", &scalefactor, &junk); 385 | 386 | width = x; 387 | height = y; 388 | data = new Color*[height]; 389 | for (int i=0;i 0.0f) // MSB 405 | { 406 | REVERSEBYTES(&(val[0]), &data[y][x].r()); 407 | REVERSEBYTES(&(val[1]), &data[y][x].g()); 408 | REVERSEBYTES(&(val[2]), &data[y][x].b()); 409 | } 410 | else // LSB 411 | { 412 | data[y][x].r() = val[0]; 413 | data[y][x].g() = val[1]; 414 | data[y][x].b() = val[2]; 415 | } 416 | // // delete [] val; 417 | } 418 | else // black and white 419 | { 420 | if (fread(cval, sizeof(float), 1, infile) != 1) goto LoadPFM_error_end; 421 | if (scalefactor > 0.0f) // MSB 422 | { 423 | REVERSEBYTES(&(cval[0]), &data[y][x].r()); 424 | } 425 | else // LSB 426 | data[y][x].r() = cval[0]; 427 | 428 | data[y][x].g() = data[y][x].r(); 429 | data[y][x].b() = data[y][x].r(); 430 | // delete [] cval; 431 | } 432 | 433 | } 434 | } 435 | result = true; 436 | 437 | LoadPFM_error_end: 438 | fclose(infile); 439 | 440 | delete [] val; 441 | delete [] cval; 442 | 443 | 444 | return result; 445 | } 446 | 447 | bool Image::saveJPG(std::string const &file_name) { 448 | functions_used["Image: saveJPG"] = 1; 449 | try { 450 | ///Copy the data from the internal structure to the Magick structure 451 | Magick::Image img(_format("%dx%d",width,height).c_str(), "black"); 452 | Magick::PixelPacket *pixel; 453 | for (int y=0;y(this),x,y)) { 609 | std::cout << _format("Indices (%d,%d) out of bounds. Image size (%d,%d)",x,y,this->width,this->height) << std::endl; 610 | return Color(0.0f,0.0f,0.0f,1.0f); 611 | } 612 | return data[y][x]; 613 | } 614 | 615 | ///The range of values is between 0-1 616 | void Image::setPixel(int x, int y, Color const &color) { 617 | functions_used["Image: setPixel"] = 1; 618 | if (outOfBounds(const_cast(this),x,y)) { 619 | std::cout << _format("Indices (%d,%d) out of bounds. Image size (%d,%d)",x,y,this->width,this->height) << std::endl; 620 | return; 621 | } 622 | data[y][x] = color; 623 | return; 624 | } 625 | 626 | void Image::perPixelNormalization(float _range) { 627 | for (int y=0;ygetWidth() || height != _img->getHeight()) return; 700 | 701 | for (int y=0;ygetPixel(x,y); 704 | setPixel(x,y,val); 705 | } 706 | } 707 | 708 | return; 709 | } 710 | 711 | void Image::add(int x, int y, Color const &val) { 712 | functions_used["Image: add2"] = 1; 713 | Color pix_val = getPixel(x,y); 714 | setPixel(x,y,pix_val+val); 715 | return; 716 | } 717 | 718 | void Image::copy(Image *_img) { 719 | functions_used["Image: copy"] = 1; 720 | if (width != _img->getWidth() || height != _img->getHeight()) { 721 | ///Erase anything previously existing 722 | for (int y=0;ygetWidth(); 728 | height = _img->getHeight(); 729 | 730 | ///Allocate and copy memory 731 | data = new Color*[height]; 732 | for (int y=0;ygetPixel(x,y)); 736 | } 737 | } 738 | } 739 | else { 740 | ///Just copy the data 741 | for (int y=0;ygetPixel(x,y)); 744 | } 745 | } 746 | } 747 | 748 | return; 749 | } 750 | 751 | void normalizeSet(std::vector &images, float range, bool ignore_value, Color value) { 752 | functions_used["Image: normalizeSet"] = 1; 753 | ///Normalizes a set of images based on the min and max of the entire set 754 | ///Find the max and min first 755 | Color max_value = Color(FLT_MIN,FLT_MIN,FLT_MIN); 756 | Color min_value = Color(FLT_MAX,FLT_MAX,FLT_MAX); 757 | for (int i=0;igetHeight();y++) { 759 | for (int x=0;xgetWidth();x++) { 760 | if (ignore_value && images[i]->getPixel(x,y) == value) continue; 761 | if (max_value.r() < images[i]->getPixel(x,y).r()) max_value.r() = images[i]->getPixel(x,y).r(); 762 | if (max_value.g() < images[i]->getPixel(x,y).g()) max_value.g() = images[i]->getPixel(x,y).g(); 763 | if (max_value.b() < images[i]->getPixel(x,y).b()) max_value.b() = images[i]->getPixel(x,y).b(); 764 | if (min_value.r() > images[i]->getPixel(x,y).r()) min_value.r() = images[i]->getPixel(x,y).r(); 765 | if (min_value.g() > images[i]->getPixel(x,y).g()) min_value.g() = images[i]->getPixel(x,y).g(); 766 | if (min_value.b() > images[i]->getPixel(x,y).b()) min_value.b() = images[i]->getPixel(x,y).b(); 767 | } 768 | } 769 | } 770 | 771 | // printf("max_value: %f %f %f\n",max_value.r,max_value.g,max_value.b); 772 | // printf("min_value: %f %f %f\n",min_value.r,min_value.g,min_value.b); 773 | //then normalize 774 | Color max_minus_min = max_value - min_value; 775 | if (max_minus_min.r() == 0.0f) max_minus_min.r() = 1.0f; 776 | if (max_minus_min.g() == 0.0f) max_minus_min.g() = 1.0f; 777 | if (max_minus_min.b() == 0.0f) max_minus_min.b() = 1.0f; 778 | for (int i=0;igetHeight();y++) { 780 | for (int x=0;xgetWidth();x++) { 781 | images[i]->setPixel(x, y, range * (images[i]->getPixel(x,y) - min_value)/max_minus_min); 782 | } 783 | } 784 | } 785 | return; 786 | } 787 | 788 | void Image::initializeGammaTable(float gamma) { 789 | functions_used["Image: initializeGammaTable"] = 1; 790 | if (current_gamma == gamma) return; 791 | 792 | for (int x = 0; x < 65536; x++) { 793 | gamma_table[x] = (float) pow((float) x / 65535.0f, gamma); 794 | } 795 | 796 | current_gamma = gamma; 797 | 798 | return; 799 | } 800 | 801 | 802 | void Image::initializeLookupTable() { 803 | functions_used["Image: initializeLookupTable"] = 1; 804 | 805 | for (int x = 0; x < 65536; x++) { 806 | float tval = (float)(::pow(short2float(x) , 1.0f / current_gamma) * 65535.0f); 807 | lookup_table[x] = (tval > 65535.0f ? (unsigned short) 65535.0f : (unsigned short) tval); 808 | } 809 | 810 | return; 811 | } 812 | 813 | Color Image::getPixelBilinear(float sx, float sy) { 814 | functions_used["Image: getPixelBilinear"] = 1; 815 | 816 | int bx = (int) sx; int by = (int) sy; // integer part 817 | int bxo = bx + 1; int byo = by + 1; 818 | sx -= bx; sy -= by; // fractional part [0,1) 819 | 820 | bx %= width; // slow, hopefully they are already positive 821 | by %= height; 822 | bxo %= width; 823 | byo %= height; 824 | 825 | float osx = 1.0f - sx; 826 | float osy = 1.0f - sy; 827 | 828 | return (data[by][bx] * osx * osy + 829 | data[by][bxo] * sx * osy + 830 | data[byo][bx] * osx * sy + 831 | data[byo][bxo] * sx * sy); 832 | } 833 | 834 | Color Image::getPixelBicubic(float sx, float sy) { 835 | functions_used["Image: getPixelBicubic"] = 1; 836 | 837 | float A = -0.75f; 838 | Color val(0.0f,0.0f,0.0f,1.0f); 839 | float totalweight = 0.0f; 840 | 841 | int bx = (int) sx; 842 | int by = (int) sy; 843 | 844 | bx %= width; 845 | by %= height; 846 | 847 | for (int y = by - 1; y < by + 3; y++) { 848 | float disty = fabs(sy - float(y)); 849 | float yweight; 850 | /* if (disty <= 1) // photoshop bicubic 851 | yweight = (( A + 2.0 ) * disty - ( A + 3.0 )) * disty * disty + 1.0; 852 | else 853 | yweight = (( A * disty - 5.0 * A ) * disty + 8.0 * A ) * disty - 4.0 * A; 854 | */ 855 | if (disty <= 1) // 'true' bicubic 856 | yweight = ( ( disty - 9.0f/5.0f ) * disty - 1.0f/5.0f ) * disty + 1.0f; 857 | else 858 | yweight = ( ( -1.0f/3.0f * (disty-1) + 4.0f/5.0f ) * (disty-1) - 7.0f/15.0f ) * (disty-1); 859 | 860 | for (int x = bx - 1; x < bx + 3; x++) { 861 | float distx = fabs(sx - x); 862 | float weight; 863 | /* if (distx <= 1) // photoshop bicubic 864 | xweight = (( A + 2.0 ) * distx - (A + 3.0)) * distx * distx +1.0; 865 | else 866 | xweight = (( A * distx - 5.0 * A ) * distx + 8.0 * A ) * distx - 4.0 * A; 867 | */ 868 | if (distx <= 1.0f) // 'true' bicubic 869 | weight = (( ( distx - 9.0f/5.0f ) * distx - 1.0f/5.0f ) * distx + 1.0f) * yweight; 870 | else 871 | weight = (( ( -1.0f/3.0f * (distx-1) + 4.0f/5.0f ) * (distx-1.0f) - 7.0f/15.0f ) * (distx-1.0f)) * yweight; 872 | 873 | val += data[y][x] * weight; 874 | totalweight += weight; 875 | } 876 | } 877 | val *= 1.0f / totalweight; 878 | return val; 879 | } 880 | 881 | bool outOfBounds(Image *img, int x, int y, int offset_x, int offset_y, int error_code) { 882 | functions_used["Image: outOfBounds"] = 1; 883 | 884 | int width = img->getWidth() - offset_x; 885 | int height = img->getHeight() - offset_y; 886 | if (x >= offset_x && x < width-offset_x && 887 | y >= offset_y && y < height-offset_y) return false; 888 | if (error_code != -1) std::cout << _format("Indices (%d,%d) are out of bounds. Image size (%d,%d)",x,y,width,height) << std::endl; 889 | return true; 890 | } 891 | 892 | void Image::halfSize() { 893 | functions_used["Image: halfSize"] = 1; 894 | ///Create a temporary image 895 | Image *temp = new Image(width/2, height/2); 896 | 897 | for (int y = 0; y < temp->getHeight(); y++) { 898 | int cy = 2 * y; 899 | for (int x = 0; x < temp->getWidth(); x++) { 900 | int cx = 2 * x; 901 | temp->setPixel(x,y, (getPixel(cx,cy) + getPixel(cx,cy+1) + getPixel(cx+1,cy) + getPixel(cx+1,cy+1))*0.25f); 902 | } 903 | } 904 | 905 | ///Copy the data over to this 906 | this->copy(temp); 907 | 908 | ///Clean up 909 | delete temp; 910 | 911 | return; 912 | } 913 | 914 | void Image::doubleSize() { 915 | functions_used["Image: doubleSize"] = 1; 916 | Image *temp = new Image(width*2, height*2); 917 | 918 | for (int y = 0; y < temp->getHeight(); y++) { 919 | float cy = (float(y) - 0.5f ) / 2.0f; 920 | for (int x = 0; x < temp->getWidth(); x++) { 921 | float cx = (float(x) - 0.5f ) / 2.0f; 922 | temp->setPixel(x,y, getPixel(_round(cx), _round(cy))); 923 | } 924 | } 925 | 926 | 927 | ///Copy the data over to this 928 | this->copy(temp); 929 | 930 | ///Clean up 931 | delete temp; 932 | 933 | return; 934 | } 935 | 936 | void Image::doubleSizeBilinear() { 937 | functions_used["Image: doubleSizeBilinear"] = 1; 938 | Image *temp = new Image(width*2, height*2); 939 | 940 | for (int y = 0; y < temp->getHeight(); y++) { 941 | float cy = (float(y) - 0.5f ) / 2.0f; 942 | for (int x = 0; x < temp->getWidth(); x++) { 943 | float cx = (float(x) - 0.5f ) / 2.0f; 944 | temp->setPixel(x,y, getPixelBilinear(cx, cy)); 945 | } 946 | } 947 | 948 | 949 | ///Copy the data over to this 950 | this->copy(temp); 951 | 952 | ///Clean up 953 | delete temp; 954 | 955 | return; 956 | } 957 | 958 | void Image::binary(Color const &threshold) { 959 | functions_used["Image: binary"] = 1; 960 | for (int y=0;y threshold.r()) { 963 | data[y][x].r() = 1.0f; 964 | } 965 | else { 966 | data[y][x].r() = 0.0f; 967 | } 968 | if (data[y][x].g() > threshold.g()) { 969 | data[y][x].g() = 1.0f; 970 | } 971 | else { 972 | data[y][x].g() = 0.0f; 973 | } 974 | if (data[y][x].b() > threshold.b()) { 975 | data[y][x].b() = 1.0f; 976 | } 977 | else { 978 | data[y][x].b() = 0.0f; 979 | } 980 | } 981 | } 982 | return; 983 | } 984 | 985 | void Image::scale(float f) { 986 | functions_used["Image: scale"] = 1; 987 | for (int y=0;y 3) { 999 | Color val = getPixel(x,y); 1000 | return 0.2989f * val.r() + 0.5870f * val.g() + 0.1140f * val.b(); 1001 | } 1002 | else { 1003 | Color val = getPixel(x,y); 1004 | return val(index); 1005 | } 1006 | } 1007 | 1008 | void Image::grayscale(int index) { 1009 | for (int y=0;y max_val(0)) max_val(0) = val(0); 1029 | if (val(1) > max_val(1)) max_val(1) = val(1); 1030 | if (val(2) > max_val(2)) max_val(2) = val(2); 1031 | 1032 | if (val(0) < min_val(0)) min_val(0) = val(0); 1033 | if (val(1) < min_val(1)) min_val(1) = val(1); 1034 | if (val(2) < min_val(2)) min_val(2) = val(2); 1035 | } 1036 | } 1037 | 1038 | Color *min_max_val = new Color[2]; 1039 | min_max_val[0] = min_val; 1040 | min_max_val[1] = max_val; 1041 | return min_max_val; 1042 | } 1043 | 1044 | void Image::blur(float variance) { 1045 | functions_used["Image: blur"] = 1; 1046 | ///Create the gaussian filter 1047 | float constant = 1.0f/(2.0f*M_PI*variance*variance); 1048 | int size = 6 * int(variance); 1049 | if (size%2==0) size++; 1050 | size = std::max(size, 3); 1051 | Image *gaussian_kernel = new Image(size, size, 0.0f, 0.0f, 0.0f, 1.0f); 1052 | for (int y=-size/2; ysetPixel(x+size/2, y+size/2, Color(val, val, val)); 1056 | } 1057 | } 1058 | 1059 | ///Apply the filter 1060 | Image *filtered_image = ImageProcessing::convolve(this, gaussian_kernel); 1061 | 1062 | 1063 | this->copy(filtered_image); 1064 | 1065 | ///Delete the kernel 1066 | delete gaussian_kernel; 1067 | 1068 | return; 1069 | } 1070 | 1071 | Image *Image::mathOp(Image *img, IMAGE_MATH_OP const &type) { 1072 | functions_used["Image: mathOp"] = 1; 1073 | ///Check if the size is the same 1074 | if (this->getWidth() != img->getWidth() || this->getHeight() != img->getHeight()) return 0x00; 1075 | 1076 | Image *result = new Image(this->getWidth(), this->getHeight()); 1077 | for (int y=0;ygetHeight();y++) { 1078 | for (int x=0;xgetWidth();x++) { 1079 | Color val; 1080 | switch(type) { 1081 | case IMAGE_MATH_OP_ADD: val = this->getPixel(x,y) + img->getPixel(x,y); 1082 | break; 1083 | case IMAGE_MATH_OP_SUBTRACT: val = this->getPixel(x,y) - img->getPixel(x,y); 1084 | break; 1085 | case IMAGE_MATH_OP_MULTIPLY: val = this->getPixel(x,y) * img->getPixel(x,y); 1086 | break; 1087 | case IMAGE_MATH_OP_DIVIDE: if (img->getPixel(x,y).r() != 0.0f) { 1088 | val.r() = this->getPixel(x,y).r() / img->getPixel(x,y).r(); 1089 | } 1090 | if (img->getPixel(x,y).g() != 0.0f) { 1091 | val.g() = this->getPixel(x,y).g() / img->getPixel(x,y).g(); 1092 | } 1093 | if (img->getPixel(x,y).b() != 0.0f) { 1094 | val.b() = this->getPixel(x,y).b() / img->getPixel(x,y).b(); 1095 | } 1096 | break; 1097 | } 1098 | result->setPixel(x,y,val); 1099 | } 1100 | } 1101 | 1102 | return result; 1103 | } 1104 | 1105 | Image *Image::negate() { 1106 | functions_used["Image: negate"] = 1; 1107 | Image *result = new Image(this->getWidth(), this->getHeight()); 1108 | for (int y=0;ygetHeight();y++) { 1109 | for (int x=0;xgetWidth();x++) { 1110 | Color val = Color(0.0f,0.0f,0.0f)-this->getPixel(x,y); 1111 | result->setPixel(x,y,val); 1112 | } 1113 | } 1114 | return result; 1115 | } 1116 | 1117 | void Image::RGBtoXYZ() { 1118 | for (int y=0;y 0.04045f){ r = pow((r + 0.055f) / 1.055f, 2.4f); } 1125 | else { r = r / 12.92f; } 1126 | if ( g > 0.04045f){ g = pow((g + 0.055f) / 1.055f, 2.4f); } 1127 | else { g = g / 12.92f; } 1128 | if (b > 0.04045){ b = pow((b + 0.055f) / 1.055f, 2.4f); } 1129 | else { b = b / 12.92; } 1130 | 1131 | r = r * 100.0f; 1132 | g = g * 100.0f; 1133 | b = b * 100.0f; 1134 | 1135 | //Observer. = 2°, Illuminant = D65 1136 | 1137 | float X = r * 0.4124f + g * 0.3576f + b * 0.1805f; 1138 | float Y = r * 0.2126f + g * 0.7152f + b * 0.0722f; 1139 | float Z = r * 0.0193f + g * 0.1192f + b * 0.9505f; 1140 | 1141 | setPixel(x,y,Color(X,Y,Z)); 1142 | } 1143 | } 1144 | 1145 | return; 1146 | } 1147 | 1148 | void Image::RGBtoLUV() { 1149 | ///convert first to XYZ 1150 | RGBtoXYZ(); 1151 | 1152 | ///CIE chromaticity coordinates: 1153 | float xn = 0.312713f; 1154 | float yn = 0.329016f; 1155 | ///CIE luminance: 1156 | float Yn = 1.0f; 1157 | for (int y=0;y 0.008856f ) { X = pow( X , 1.0f/3.0f ); } 1206 | else { X = ( 7.787f * X ) + ( 16.0f/116.0f ); } 1207 | if ( Y > 0.008856f ) { Y = pow( Y , 1.0f/3.0f ); } 1208 | else { Y = ( 7.787f * Y ) + ( 16.0f/116.0f ); } 1209 | if ( Z > 0.008856f ) { Z = pow( Z , 1.0f/3.0f ); } 1210 | else { Z = ( 7.787f * Z ) + ( 16.0f/116.0f ); } 1211 | 1212 | float l = ( 116.0f * Y ) - 16.0f; 1213 | float a = 500.0f * ( X - Y ); 1214 | float b = 200.0f * ( Y - Z ); 1215 | 1216 | setPixel(x,y,Color(l,a,b)); 1217 | } 1218 | } 1219 | return; 1220 | } 1221 | #endif 1222 | -------------------------------------------------------------------------------- /Image.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | #ifndef __IMAGE_H__ 6 | #define __IMAGE_H__ 7 | 8 | 9 | //#include 10 | //using namespace cv; 11 | 12 | #include 13 | #include 14 | #include "Color.h" 15 | #include "Magick++.h" 16 | #include "FileUtilities.h" 17 | 18 | #define REVERSE2BYTES(source, dest) \ 19 | { \ 20 | char *j = (char *) source; \ 21 | char *dj = (char *) dest; \ 22 | dj[0] = j[1]; \ 23 | dj[1] = j[0]; \ 24 | } 25 | 26 | 27 | #define REVERSEBYTES(source, dest) \ 28 | { \ 29 | char *j = (char *) source; \ 30 | char *dj = (char *) dest; \ 31 | dj[0] = j[3]; \ 32 | dj[1] = j[2]; \ 33 | dj[2] = j[1]; \ 34 | dj[3] = j[0]; \ 35 | } 36 | 37 | #define REVERSE2BYTES_INPLACE(sourcedest) \ 38 | { \ 39 | char *j = (char *) sourcedest; \ 40 | j[0] ^= j[1]; j[1] ^= j[0]; j[0] ^= j[1]; \ 41 | } 42 | 43 | #define REVERSEBYTES_INPLACE(sourcedest) \ 44 | { \ 45 | char *j = (char *) sourcedest; \ 46 | j[0] ^= j[3]; j[3] ^= j[0]; j[0] ^= j[3]; \ 47 | j[1] ^= j[2]; j[2] ^= j[1]; j[1] ^= j[2]; \ 48 | } 49 | 50 | class Image { 51 | public: 52 | ///Constructor 53 | Image(); 54 | Image(Image const &img); 55 | Image(int _width, int _height); 56 | Image(int _width, int _height, float r, float g, float b, float a); 57 | 58 | ///Destructor 59 | ~Image(); 60 | 61 | ///Returns true if the index (x,y) is outside the image boundaries; false otherwise 62 | friend bool outOfBounds(Image *img, int x, int y, int offset_x=0, int offset_y=0, int error_code=-1); 63 | 64 | ///Load image 65 | bool loadImage(std::string const &_file_name); 66 | ///Save image 67 | bool saveImage(std::string const &_file_name); 68 | 69 | ///Returns the width of the image 70 | int getWidth() const; 71 | ///Returns the height of the image 72 | int getHeight() const; 73 | 74 | ///Returns the pixel value at the specified location 75 | Color getPixel(int x, int y) const; 76 | Color getPixelBilinear(float sx, float sy); 77 | Color getPixelBicubic(float sx, float sy); 78 | ///Returns the corresponding grayscale value for an RGB pixel. If an index is given the particular channel is used for all channels. 79 | float getPixelGrayscale(int x, int y, int index=-1); 80 | 81 | 82 | ///Sets the pixel value at the specified location 83 | void setPixel(int x, int y, Color const &color); 84 | 85 | ///Blurs the image 86 | void blur(float variance); 87 | 88 | ///Returns the image file name 89 | std::string getFileName(); 90 | ///Returns the file type 91 | FILE_TYPE getFileType() const; 92 | ///Sets the file type 93 | void setFileType(FILE_TYPE _type); 94 | 95 | ///Normalizes the image 96 | void normalize(float _range=1.0f); 97 | ///Normalizes the image in a specific range 98 | void normalize(Color *_range); 99 | ///Per pixel normalization i.e. each RGB is normalized to 1 100 | void perPixelNormalization(float _range=1.0f); 101 | 102 | ///Clear the image to the given color 103 | void clear(Color const &val); 104 | void clear(float _r, float _g, float _b); 105 | 106 | ///Adds another image to the existing one provided they are the same size - per pixel addition 107 | void add(Image *_img); 108 | 109 | ///Adds to pixel the given color value 110 | void add(int x, int y, Color const &val); 111 | 112 | ///Copies an image to the existing data 113 | void copy(Image *_img); 114 | 115 | ///Scales the images by a factors of f 116 | void scale(float f); 117 | 118 | ///Halfsize the image 119 | void halfSize(); 120 | 121 | ///Doublesize the image 122 | void doubleSize(); 123 | void doubleSizeBilinear(); 124 | 125 | ///Makes the image binary 126 | void binary(Color const &threshold = Color(COLOR_EPSILON, COLOR_EPSILON, COLOR_EPSILON)); 127 | 128 | ///Normalizes over a set of images i.e. the min and max values are for the entire set 129 | friend void normalizeSet(std::vector &images, float range=1.0f, bool ignore_value=false, Color value=Color(-1.0f)); 130 | 131 | ///Converts to grayscale 132 | void grayscale(int index=-1); 133 | ///Converts to XYZ color space 134 | void RGBtoXYZ(); 135 | ///Converts to Luv color space 136 | void RGBtoLUV(); 137 | ///Converts to Lab color space 138 | void RGBtoLab(); 139 | 140 | ///Returns the range of the image 141 | Color *range(); 142 | 143 | typedef enum IMAGE_MATH_OP { 144 | IMAGE_MATH_OP_ADD, 145 | IMAGE_MATH_OP_SUBTRACT, 146 | IMAGE_MATH_OP_MULTIPLY, 147 | IMAGE_MATH_OP_DIVIDE 148 | } IMAGE_MATH_OP; 149 | 150 | ///Math operations 151 | Image *mathOp(Image *img, IMAGE_MATH_OP const &type); 152 | Image *negate(); 153 | 154 | private: 155 | ///The width of the image 156 | unsigned int width; 157 | ///The height of the image 158 | unsigned int height; 159 | ///File name 160 | std::string file_name; 161 | ///File type 162 | FILE_TYPE type; 163 | 164 | ///The image data 165 | Color **data; 166 | ///Magick image 167 | //Magick::Image data; 168 | ///The gamma table 169 | static float gamma_table[65536]; 170 | ///The lookup table. Used when converting from HDR to LDR 171 | static unsigned short lookup_table[65536]; 172 | ///The current gamma 173 | static float current_gamma; 174 | 175 | ///Initialize the gamma table 176 | void initializeGammaTable(float gamma); 177 | ///Initialize the lookup table 178 | void initializeLookupTable(); 179 | 180 | ///Image loaders 181 | bool loadJPG(std::string const &file_name); 182 | bool loadPNG(std::string const &file_name); 183 | bool loadTGA(std::string const &file_name); 184 | bool loadPFM(std::string const &file_name); 185 | bool loadTIF(std::string const &file_name); 186 | bool loadBMP(std::string const &file_name); 187 | 188 | ///Image exporters 189 | bool saveJPG(std::string const &file_name); 190 | bool savePNG(std::string const &file_name); 191 | bool saveTGA(std::string const &file_name); 192 | bool savePFM(std::string const &file_name); 193 | bool saveTIF(std::string const &file_name); 194 | bool saveBMP(std::string const &file_name); 195 | 196 | }; 197 | 198 | #endif 199 | -------------------------------------------------------------------------------- /ImageProcessing.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __IMAGE_PROCESSING_CPP__ 7 | #define __IMAGE_PROCESSING_CPP__ 8 | 9 | #include "ImageProcessing.h" 10 | #include "Image.h" 11 | #include "Color.h" 12 | 13 | ImageProcessing::ImageProcessing() { 14 | 15 | } 16 | 17 | 18 | ImageProcessing::~ImageProcessing() { 19 | 20 | } 21 | 22 | void ImageProcessing::holeFill(Image *input_image, int max_neighbourhood_size, int min_neighbourhood_size, bool dominant_neighbour) { 23 | 24 | Image *result = holeFill2(input_image, max_neighbourhood_size, min_neighbourhood_size, dominant_neighbour); 25 | input_image->copy(result); 26 | delete result; 27 | return; 28 | } 29 | 30 | Image *ImageProcessing::holeFill2(Image *input_image, int max_neighbourhood_size, int min_neighbourhood_size, bool dominant_neighbour) { 31 | 32 | 33 | Image *img = new Image(*input_image); 34 | bool have_holes = true; 35 | int neighbourhood_size = max_neighbourhood_size; 36 | while (have_holes) { 37 | have_holes = false; 38 | ///go through the image and find an empty pixel 39 | for (int y=0;ygetHeight();y++) { 40 | for (int x=0;xgetWidth();x++) { 41 | ///if the pixel is valid then no need to process it 42 | if (img->getPixel(x,y) != Color(0.0f)) continue; 43 | ///otherwise compute a value from the local neighbourhood 44 | Vector3f avg_value = Vector3f(0.0f,0.0f,0.0f); 45 | std::vector neighbour_values; 46 | std::vector neighbour_votes; 47 | int number_of_valid_neighbours = 0; 48 | for (int i=y-1;i<=y+1;i++) { 49 | for (int j=x-1;j<=x+1;j++) { 50 | ///check if its out of bounds 51 | if (outOfBounds(img,j,i)) continue; 52 | ///if its the pixel in question continue 53 | if (i==y && j==x) continue; 54 | ///if the pixel is empty don't count it 55 | if (img->getPixel(j,i)==Color(0.0f)) continue; 56 | ///if its substituting by the most dominant neighbour then keep track of the votes 57 | if (dominant_neighbour) { 58 | Vector3f neighbour_value = color2vector3(img->getPixel(j,i)); 59 | ///first check if the value is already there 60 | bool already_there = false; 61 | int pos = -1; 62 | for (int k=0;kgetPixel(j,i))); 76 | ///add a single vote 77 | neighbour_votes.push_back(1); 78 | } 79 | 80 | } 81 | number_of_valid_neighbours++; 82 | avg_value += color2vector3(img->getPixel(j,i)); 83 | } 84 | } 85 | ///if there are more than 6 neighbours then fill in the value 86 | if (number_of_valid_neighbours < neighbourhood_size) continue; 87 | if (number_of_valid_neighbours) avg_value /= float(number_of_valid_neighbours); 88 | 89 | ///if its setting the dominant neighbour then find the one with max votes 90 | if (dominant_neighbour) { 91 | int max_votes = 0; 92 | int pos = -1; 93 | for (int k=0;k max_votes) { 95 | max_votes = neighbour_votes[k]; 96 | pos = k; 97 | } 98 | } 99 | ///set the right value 100 | img->setPixel(x,y,vector2color3(neighbour_values[pos])); 101 | } 102 | else { 103 | img->setPixel(x,y,vector2color3(avg_value)); 104 | } 105 | have_holes = true; 106 | } 107 | } 108 | 109 | ///if there are no changes and its above the min threshold then do it again with a smaller neighbourhood size 110 | if ((!have_holes) && (neighbourhood_size > min_neighbourhood_size)) { 111 | neighbourhood_size-=2; 112 | have_holes = true; 113 | } 114 | } 115 | 116 | return img; 117 | } 118 | 119 | Image *ImageProcessing::bilateralFilteringGrayscale(Image *grayscale_image, double sigma_s, double sampling_s, double sigma_r, double sampling_r) { 120 | 121 | typedef Array_2D image_type; 122 | 123 | ///Convert to grayscale 124 | image_type grayscale_image_2d(grayscale_image->getWidth(), grayscale_image->getHeight()); 125 | for (int y=0;ygetHeight();y++) { 126 | for (int x=0;xgetWidth();x++) { 127 | grayscale_image_2d(x,y) = double(grayscale_image->getPixel(x,y).r()); 128 | } 129 | } 130 | 131 | ///Run the Bilateral filtering 132 | image_type filtered_image_2d(grayscale_image->getWidth(),grayscale_image->getHeight()); 133 | 134 | FFT::Support_3D::set_fftw_flags(FFTW_ESTIMATE); 135 | Image_filter::linear_BF(grayscale_image_2d, 136 | sigma_s,sigma_r, 137 | sampling_s,sampling_r, 138 | &filtered_image_2d); 139 | ///Convert back to regular image format 140 | Image *filtered_image = new Image(grayscale_image->getWidth() , grayscale_image->getHeight(), 0.0f,0.0f,0.0f,1.0f); 141 | for (int y=0;ygetHeight();y++) { 142 | for (int x=0;xgetWidth();x++) { 143 | filtered_image->setPixel(x,y,Color(float(filtered_image_2d(x,y)))); 144 | } 145 | } 146 | 147 | return filtered_image; 148 | } 149 | 150 | 151 | void ImageProcessing::pointReduction(Image *non_hole_filled_map, Image *hole_filled_map, int radius) { 152 | 153 | Image *markup_image = new Image(non_hole_filled_map->getWidth(), non_hole_filled_map->getHeight(), 0.0f, 0.0f, 0.0f, 1.0f); 154 | ///Go through all the valid points in the non-hole-filled image and mark a neighbourhood of radius around them. 155 | for (int y=0;ygetHeight();y++) { 156 | for (int x=0;xgetWidth();x++) { 157 | ///If there is no point then ignore 158 | if (non_hole_filled_map->getPixel(x,y) == Color(0.0f,0.0f,0.0f)) continue; 159 | ///Otherwise mark a neighbourhood of radius around it 160 | for (int i=-radius/2;isetPixel(x+j, y+i, Color(1.0f,1.0f,1.0f)); 164 | } 165 | } 166 | } 167 | } 168 | 169 | ///Go through the hole-filled image and keep only the points in the markup image 170 | for (int y=0;ygetHeight();y++) { 171 | for (int x=0;xgetWidth();x++) { 172 | if (markup_image->getPixel(x,y) == Color(0.0f,0.0f,0.0f)) { 173 | hole_filled_map->setPixel(x,y,Color(0.0f,0.0f,0.0f)); 174 | } 175 | } 176 | } 177 | 178 | ///Clean up 179 | delete markup_image; 180 | return; 181 | } 182 | 183 | Image *ImageProcessing::convolve(Image *input_image, Image *kernel) { 184 | 185 | ///Check if the input image is valid 186 | if (!input_image || !kernel) return 0x00; 187 | 188 | ///Get the range of the input image 189 | Color *range = new Color[2]; 190 | range = input_image->range(); 191 | //std::cout << range[0] << std::endl; 192 | //std::cout << range[1] << std::endl; 193 | 194 | ///Kernel size is 2k + 1 195 | int kernel_half_sizex = (kernel->getWidth() - 1)/2; 196 | int kernel_half_sizey = (kernel->getHeight() - 1)/2; 197 | 198 | ///Make the image into a square 199 | int max_dim = std::max(input_image->getWidth(), input_image->getHeight()); 200 | Image *squared_input_image = new Image(max_dim, max_dim, 0.0f, 0.0f, 0.0f,1.0f); 201 | for (int y=0;ygetHeight();y++) { 202 | for (int x=0;xgetWidth();x++) { 203 | squared_input_image->setPixel(x,y,input_image->getPixel(x,y)); 204 | } 205 | } 206 | 207 | Image * padded_input_image = padImage(squared_input_image, kernel_half_sizex, kernel_half_sizey); 208 | 209 | ///Perform the DFT on the image 210 | fftw_complex *dft_image_red = dft(padded_input_image, 0, false, -1, -1); 211 | fftw_complex *dft_image_green = dft(padded_input_image, 1, false, -1, -1); 212 | fftw_complex *dft_image_blue = dft(padded_input_image, 2, false, -1, -1); 213 | 214 | ///Perform the DFT on the kernel. Make sure the padding is set to 0 and it's the same size as the image 215 | fftw_complex *dft_kernel_red = dft(kernel, 0, true, padded_input_image->getWidth(), padded_input_image->getHeight()); 216 | fftw_complex *dft_kernel_green = dft(kernel, 1, true, padded_input_image->getWidth(), padded_input_image->getHeight()); 217 | fftw_complex *dft_kernel_blue = dft(kernel, 2, true, padded_input_image->getWidth(), padded_input_image->getHeight()); 218 | 219 | ///Multiply the two together 220 | for (int k=0,y=0;ygetHeight();y++) { 221 | for (int x=0;xgetWidth();x++,k++) { 222 | float red_val1 = dft_image_red[k][0] * dft_kernel_red[k][0] - dft_image_red[k][1] * dft_kernel_red[k][1]; 223 | float red_val2 = dft_image_red[k][0] * dft_kernel_red[k][1] + dft_image_red[k][1] * dft_kernel_red[k][0]; 224 | 225 | dft_image_red[k][0] = red_val1; 226 | dft_image_red[k][1] = red_val2; 227 | 228 | float green_val1 = dft_image_green[k][0] * dft_kernel_green[k][0] - dft_image_green[k][1] * dft_kernel_green[k][1]; 229 | float green_val2 = dft_image_green[k][0] * dft_kernel_green[k][1] + dft_image_green[k][1] * dft_kernel_green[k][0]; 230 | 231 | dft_image_green[k][0] = green_val1; 232 | dft_image_green[k][1] = green_val2; 233 | 234 | float blue_val1 = dft_image_blue[k][0] * dft_kernel_blue[k][0] - dft_image_blue[k][1] * dft_kernel_blue[k][1]; 235 | float blue_val2 = dft_image_blue[k][0] * dft_kernel_blue[k][1] + dft_image_blue[k][1] * dft_kernel_blue[k][0]; 236 | 237 | dft_image_blue[k][0] = blue_val1; 238 | dft_image_blue[k][1] = blue_val2; 239 | } 240 | } 241 | 242 | ///Perform the inverse DFT on the multiplication result 243 | fftw_complex *idft_image_red = idft(dft_image_red, padded_input_image->getWidth(), padded_input_image->getHeight()); 244 | fftw_complex *idft_image_green = idft(dft_image_green, padded_input_image->getWidth(), padded_input_image->getHeight()); 245 | fftw_complex *idft_image_blue = idft(dft_image_blue, padded_input_image->getWidth(), padded_input_image->getHeight()); 246 | 247 | ///Convert to Image format 248 | Image *padded_output_image = copy(idft_image_red, 0, padded_input_image->getWidth(), padded_input_image->getHeight(), 0x00); 249 | copy(idft_image_green, 1, padded_input_image->getWidth(), padded_input_image->getHeight(), padded_output_image); 250 | copy(idft_image_blue, 2, padded_input_image->getWidth(), padded_input_image->getHeight(), padded_output_image); 251 | 252 | //Unpad the image 253 | Image *squared_output_image = unpadImage(padded_output_image, kernel_half_sizex, kernel_half_sizey); 254 | Image *output_image = new Image(input_image->getWidth(), input_image->getHeight()); 255 | for (int y=0;ygetHeight();y++) { 256 | for (int x=0;xgetWidth();x++) { 257 | output_image->setPixel(x,y,squared_output_image->getPixel(x,y)); 258 | } 259 | } 260 | output_image->normalize(); 261 | output_image->normalize(range); 262 | 263 | fftw_free(dft_image_red); 264 | fftw_free(dft_image_green); 265 | fftw_free(dft_image_blue); 266 | fftw_free(dft_kernel_red); 267 | fftw_free(dft_kernel_green); 268 | fftw_free(dft_kernel_blue); 269 | fftw_free(idft_image_red); 270 | fftw_free(idft_image_green); 271 | fftw_free(idft_image_blue); 272 | delete padded_input_image; 273 | delete padded_output_image; 274 | delete squared_input_image; 275 | delete squared_output_image; 276 | 277 | delete [] range; 278 | 279 | return output_image; 280 | } 281 | 282 | fftw_complex *ImageProcessing::dft(Image *input_image, int channel, bool padding, int width, int height) { 283 | 284 | ///Check if the input image is valid 285 | if (!input_image) return 0x00; 286 | 287 | ///Input image 288 | fftw_complex *in_img = copy(input_image, channel, padding, width, height); 289 | 290 | ///Output DFT of the input image 291 | fftw_complex *out_dft_img; 292 | ///Create the plan for the DFT of the image 293 | fftw_plan dft_img_plan; 294 | if (padding) { 295 | out_dft_img = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height); 296 | dft_img_plan = fftw_plan_dft_2d(width, height, in_img, out_dft_img, FFTW_FORWARD, FFTW_ESTIMATE); 297 | } 298 | else { 299 | out_dft_img = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * input_image->getWidth() * input_image->getHeight()); 300 | dft_img_plan = fftw_plan_dft_2d(input_image->getWidth(), input_image->getHeight(), in_img, out_dft_img, FFTW_FORWARD, FFTW_ESTIMATE); 301 | } 302 | ///Execute the plan 303 | fftw_execute(dft_img_plan); 304 | 305 | 306 | fftw_destroy_plan(dft_img_plan); 307 | fftw_free(in_img); 308 | 309 | return out_dft_img; 310 | } 311 | 312 | 313 | fftw_complex *ImageProcessing::idft(fftw_complex *input_image, int width, int height, Image *existing_image) { 314 | //Check if the input image is valid 315 | if (!input_image) return 0x00; 316 | ///Allocate memory for the structures used by FFTW. 317 | int dim = width * height; 318 | 319 | ///Output inverse DFT of the input image 320 | fftw_complex *out_idft_img = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * dim); 321 | ///Create the plan for the DFT of the image 322 | fftw_plan idft_img_plan = fftw_plan_dft_2d(width, height, input_image, out_idft_img, FFTW_BACKWARD, FFTW_ESTIMATE); 323 | ///Execute the plan 324 | fftw_execute(idft_img_plan); 325 | 326 | fftw_destroy_plan(idft_img_plan); 327 | 328 | return out_idft_img; 329 | } 330 | 331 | Image *ImageProcessing::copy(fftw_complex *data, int channel, int width, int height, Image *existing_image) { 332 | 333 | if (existing_image) { 334 | for (int k=0, y=0;ygetPixel(x,y); 337 | switch(channel) { 338 | case 0: existing_image->setPixel(x,y,Color(data[k][0],val.g(), val.b())); 339 | break; 340 | case 1: existing_image->setPixel(x,y,Color(val.r(), data[k][0], val.b())); 341 | break; 342 | case 2: existing_image->setPixel(x,y,Color(val.r(), val.g(), data[k][0])); 343 | break; 344 | } 345 | } 346 | } 347 | return 0x00; 348 | } 349 | else { 350 | ///Allocate the memory 351 | Image *new_image = new Image(width,height,0.0f,0.0f,0.0f,1.0f); 352 | for (int k=0, y=0;ygetPixel(x,y); 355 | switch(channel) { 356 | case 0: new_image->setPixel(x,y,Color(data[k][0],val.g(), val.b())); 357 | break; 358 | case 1: new_image->setPixel(x,y,Color(val.r(), data[k][0], val.b())); 359 | break; 360 | case 2: new_image->setPixel(x,y,Color(val.r(), val.g(), data[k][0])); 361 | break; 362 | } 363 | } 364 | } 365 | return new_image; 366 | } 367 | return 0x00; 368 | } 369 | 370 | fftw_complex *ImageProcessing::copy(Image *data, int channel, bool padding, int width, int height) { 371 | ///Allocate the memory 372 | fftw_complex *new_data = 0x00; 373 | if (padding) { 374 | new_data = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * width * height); 375 | for (int k=0,y=0;y= data->getHeight() || x >= data->getWidth()) { 378 | new_data[k][0] = new_data[k][1] = 0.0f; 379 | } 380 | else { 381 | switch (channel) { 382 | case 0: new_data[k][0] = data->getPixel(x,y).r(); 383 | break; 384 | case 1: new_data[k][0] = data->getPixel(x,y).g(); 385 | break; 386 | case 2: new_data[k][0] = data->getPixel(x,y).b(); 387 | break; 388 | } 389 | new_data[k][1] = 0.0f; 390 | } 391 | } 392 | } 393 | } 394 | else { 395 | new_data = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * data->getWidth() * data->getHeight()); 396 | for (int k=0,y=0;ygetHeight();y++) { 397 | for (int x=0;xgetWidth();x++,k++) { 398 | switch (channel) { 399 | case 0: new_data[k][0] = data->getPixel(x,y).r(); 400 | break; 401 | case 1: new_data[k][0] = data->getPixel(x,y).g(); 402 | break; 403 | case 2: new_data[k][0] = data->getPixel(x,y).b(); 404 | break; 405 | } 406 | new_data[k][1] = 0.0f; 407 | } 408 | } 409 | } 410 | return new_data; 411 | } 412 | 413 | ///Recreate the image to sizex + kernel_half_sizex etc 414 | Image *ImageProcessing::padImage(Image *input_image, int width, int height) { 415 | Image *padded_input_image = new Image(input_image->getWidth() + width, input_image->getHeight() + height); 416 | for (int y=0;ygetHeight();y++) { 417 | for (int x=0;xgetWidth();x++) { 418 | if (x < input_image->getWidth() && y < input_image->getHeight()) { 419 | padded_input_image->setPixel(x,y, input_image->getPixel(x,y)); 420 | } 421 | } 422 | } 423 | 424 | return padded_input_image; 425 | } 426 | 427 | Image *ImageProcessing::unpadImage(Image *input_image, int width, int height) { 428 | Image *unpadded_output_image = new Image(input_image->getWidth() - width, input_image->getHeight() - height); 429 | for (int y=0;ygetHeight();y++) { 430 | for (int x=0;xgetWidth();x++) { 431 | unpadded_output_image->setPixel(x,y, input_image->getPixel(x+width,y+height)); 432 | } 433 | } 434 | return unpadded_output_image; 435 | } 436 | 437 | #endif 438 | -------------------------------------------------------------------------------- /ImageProcessing.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __IMAGE_PROCESSING_H__ 7 | #define __IMAGE_PROCESSING_H__ 8 | 9 | #include "fftw3.h" 10 | 11 | ///Bilateral filtering 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | //#define CHRONO 18 | #include "geom.h" 19 | #include "fast_color_bf.h" 20 | #include "linear_bf.h" 21 | 22 | ///Forward declarations 23 | class Image; 24 | 25 | 26 | class ImageProcessing { 27 | public: 28 | ///Hole filling 29 | static void holeFill(Image *input_image, int max_neighbourhood_size = 6, int min_neighbourhood_size = 2, bool dominant_neighbour=false); 30 | static Image *holeFill2(Image *input_image, int max_neighbourhood_size = 6, int min_neighbourhood_size = 2, bool dominant_neighbour=false); 31 | static void pointReduction(Image *non_hole_filled_map, Image *hole_filled_map, int radius); 32 | 33 | ///Convolution 34 | static Image *convolve(Image *input_image, Image *kernel); 35 | 36 | ///Discrete Fourier Transform and inverse 37 | static fftw_complex *dft(Image *input_image, int channel, bool padding = false, int width = -1, int height = -1); 38 | static fftw_complex *idft(fftw_complex *input_image, int width, int height, Image *existing_image=0x00); 39 | 40 | ///Bilateral filtering 41 | static Image *bilateralFilteringGrayscale(Image *grayscale_image, double sigma_s = 16.0, double sampling_s=0.1, double sigma_r = 0.1, double sampling_r=0.1 ); 42 | private: 43 | ///Consructor 44 | ImageProcessing(); 45 | ///Destructor 46 | ~ImageProcessing(); 47 | 48 | ///Helper function for fftw 49 | static Image *copy(fftw_complex *data, int channel, int width, int height, Image *existing_image=0x00); 50 | static fftw_complex *copy(Image *data, int channel, bool padding=false, int width = -1, int height = -1); 51 | 52 | static Image *padImage(Image *input_image, int width, int height); 53 | static Image *unpadImage(Image *input_image, int width, int height); 54 | 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /Monitoring.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __MONITORING_H__ 7 | #define __MONITORING_H__ 8 | 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | ///Keeps track of which functions are used and how many times 15 | static std::map functions_used; 16 | 17 | static void printFunctionsUsed() { 18 | std::map::iterator it = functions_used.begin(); 19 | 20 | for (;it != functions_used.end();it++) { 21 | std::cout << it->first << " => " << it->second << std::endl; 22 | } 23 | 24 | return; 25 | } 26 | 27 | #endif // __MONITORING_H__ 28 | -------------------------------------------------------------------------------- /Readme.txt: -------------------------------------------------------------------------------- 1 | The preprocessing module in IEEE PAMI 2013: A Framework for Automatic Modeling from Point Cloud Data 2 | 3 | 4 | Structures the unstructured LiDAR data captured using an airborne scanner, into memory-manageable components which can be further processed in parallel. 5 | 6 | The size of the "tiles" or "geospatial bounding boxes" can be adjusted by changing the preprocessor flags: RES_x, RES_Y, RES_Z. 7 | 8 | The copyright for the included bilateral filtering code belongs to Sylvain Paris and Frédo Durand. 9 | 10 | References: 11 | 1. IEEE PAMI 2013: A Framework for Automatic Modeling from Point Cloud Data 12 | 2. IEEE CVPR 2009: Automatic reconstruction of cities from remote sensor data 13 | 14 | More information about this work: www.poullis.org 15 | 16 | Technical details: 17 | - The project file is provided for Code::Blocks IDE. 18 | - It requires the libraries Image Magick and fftw3. 19 | - A small sample file is provided in the data folder. 20 | 21 | *IMPORTANT: To use this software, please consider citing the following in any resulting publication:* 22 | 23 | @article{poullis2013framework, 24 | title={A Framework for Automatic Modeling from Point Cloud Data}, 25 | author={Poullis, Charalambos}, 26 | journal={Pattern Analysis and Machine Intelligence, IEEE Transactions on}, 27 | volume={35}, 28 | number={11}, 29 | pages={2563--2575}, 30 | year={2013}, 31 | publisher={IEEE} 32 | } 33 | 34 | @inproceedings{poullis2009automatic, 35 | title={Automatic reconstruction of cities from remote sensor data}, 36 | author={Poullis, Charalambos and You, Suya}, 37 | booktitle={Computer Vision and Pattern Recognition, 2009. CVPR 2009. IEEE Conference on}, 38 | pages={2775--2782}, 39 | year={2009}, 40 | organization={IEEE} 41 | } 42 | -------------------------------------------------------------------------------- /Sorter.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | #ifndef __SORTER_H__ 6 | #define __SORTER_H__ 7 | 8 | #include 9 | #include "Eigen/Eigen" 10 | using namespace Eigen; 11 | #include 12 | 13 | class SorterComplexType { 14 | public: 15 | static bool quicksort(std::vector &_array, int bottom, int top, std::vector *helper_array=0x00) { 16 | // uses recursion-the process of calling itself 17 | int middle; 18 | if(bottom &_array, int bottom, int top, int element, std::vector *helper_array=0x00) { 27 | // uses recursion-the process of calling itself 28 | int middle; 29 | if(bottom &_array, int bottom, int top, std::vector *helper_array=0x00) { 42 | float x = _array[bottom]; 43 | int i = bottom - 1; 44 | int j = top + 1; 45 | float temp; 46 | Vector3f temp2; 47 | do { 48 | do { 49 | j--; 50 | } while (x <_array[j]); 51 | do { 52 | i++; 53 | } while (x >_array[i]); 54 | if (i < j) { 55 | temp = _array[i]; // switch elements at positions i and j 56 | _array[i] = _array[j]; 57 | _array[j] = temp; 58 | if (helper_array) { 59 | temp2 = (*helper_array)[i]; // switch elements at positions i and j 60 | (*helper_array)[i] = (*helper_array)[j]; 61 | (*helper_array)[j] = temp2; 62 | } 63 | } 64 | } while (i < j); 65 | return j; // returns middle index 66 | } 67 | 68 | // partitions the array and returns the middle index (subscript) 69 | static int partitionArray(std::vector &_array, int bottom, int top, int element, std::vector *helper_array=0x00) { 70 | float x = _array[bottom][element]; 71 | int i = bottom - 1; 72 | int j = top + 1; 73 | Vector3f temp; 74 | Vector3f temp2; 75 | do { 76 | do { 77 | j--; 78 | } while (x <_array[j][element]); 79 | do { 80 | i++; 81 | } while (x >_array[i][element]); 82 | if (i < j) { 83 | temp = _array[i]; // switch elements at positions i and j 84 | _array[i] = _array[j]; 85 | _array[j] = temp; 86 | if (helper_array) { 87 | temp2 = (*helper_array)[i]; // switch elements at positions i and j 88 | (*helper_array)[i] = (*helper_array)[j]; 89 | (*helper_array)[j] = temp2; 90 | } 91 | } 92 | } while (i < j); 93 | return j; // returns middle index 94 | } 95 | 96 | }; 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /StructurePointcloud.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 104 | 105 | -------------------------------------------------------------------------------- /StructurePointcloud.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /Utilities.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © Charalambos "Charis" Poullis, charalambos@poullis.org // 3 | // This work can only be used under an exclusive license of the author. // 4 | //////////////////////////////////////////////////////////////////////////////////// 5 | 6 | #ifndef __UTILITIES_H__ 7 | #define __UTILITIES_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "Eigen/Eigen" 17 | using namespace Eigen; 18 | #include 19 | #include 20 | #include 21 | #define _USE_MATH_DEFINES 22 | #include 23 | 24 | ///To convert from radians to degrees and back 25 | #define RADIANS_TO_DEGREES 57.295779513082320876798 26 | #define DEGREES_TO_RADIANS 0.0174532925199432957692 27 | 28 | #define INVALID_VALUE 666 29 | 30 | #define EPSILON 1e-06 31 | 32 | #define _MAX_PATH 256 33 | 34 | ///Helper function for _format(...) 35 | static std::string vformat(const char *fmt, va_list argPtr) { 36 | /// We draw the line at a 1MB string. 37 | const int maxSize = 1000000; 38 | 39 | /// If the string is less than 161 characters, 40 | /// allocate it on the stack because this saves 41 | /// the malloc/free time. 42 | const int bufSize = 512; 43 | char stackBuffer[bufSize]; 44 | 45 | int attemptedSize = bufSize - 1; 46 | 47 | int numChars = vsnprintf(stackBuffer, attemptedSize, fmt, argPtr); 48 | 49 | if (numChars >= 0) { 50 | /// Got it on the first try. 51 | //printf("%s\n",stackBuffer); 52 | return std::string(stackBuffer); 53 | } 54 | 55 | /// Now use the heap. 56 | char* heapBuffer = NULL; 57 | 58 | while ((numChars == -1 || numChars >= attemptedSize) && (attemptedSize < maxSize)) { 59 | /// Try a bigger size 60 | attemptedSize *= 2; 61 | heapBuffer = (char*)realloc(heapBuffer, attemptedSize + 1); 62 | numChars = vsnprintf(heapBuffer, attemptedSize, fmt, argPtr); 63 | } 64 | 65 | //printf("%s\n",heapBuffer); 66 | std::string result = std::string(heapBuffer); 67 | 68 | free(heapBuffer); 69 | 70 | return result; 71 | } 72 | 73 | ///Prints out a string given a set of parameters. Like printf but for strings. 74 | static std::string _format(const char* fmt ...) { 75 | va_list argList; 76 | va_start(argList,fmt); 77 | std::string result = vformat(fmt, argList); 78 | va_end(argList); 79 | return result; 80 | } 81 | 82 | ///Function for timestamp. Returns the time in the format YMDHMS 83 | static std::string timestamp() { 84 | ///TIMESTAMP prints the current YMDHMS date as a time stamp. 85 | #define TIME_SIZE 40 86 | 87 | static char time_buffer[TIME_SIZE]; 88 | time_t now = time ( NULL ); 89 | const struct tm *tm = localtime ( &now ); 90 | 91 | size_t len = strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm ); 92 | 93 | #undef TIME_SIZE 94 | 95 | return _format("%s",time_buffer); 96 | } 97 | 98 | ///Clamps a value between the range [a,b] 99 | inline float clamp(float x, float a, float b) 100 | { 101 | return x < a ? a : (x > b ? b : x); 102 | } 103 | 104 | ///Copies the string and returns a pointer to the copied string 105 | inline void copy_string(char *input, char *&output) { 106 | output = new char[strlen(input)+1]; 107 | for (unsigned int i=0;i> 15); 144 | } 145 | 146 | inline static unsigned short int signedfloat2short(float f) { 147 | unsigned int bits = * (unsigned int *) &f; 148 | return (bits >> 16); 149 | } 150 | 151 | inline static float short2float(unsigned short int s) { 152 | unsigned int bits = s << 15; 153 | return * (float *) &bits; 154 | } 155 | 156 | inline static float short2signedfloat(unsigned short int s) { 157 | unsigned int bits = s << 16; 158 | return * (float *) &bits; 159 | } 160 | 161 | inline int _round(float num) { 162 | if (ceil(num)-num > 0.5f) { 163 | return int(floor(num)); 164 | } 165 | else { 166 | return int(ceil(num)); 167 | } 168 | } 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /bilateral_filtering/fft_3D/convolution_3D.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charalambos/StructurePointcloud/eb02cd0aaaf75f7f1f1bf9bf4a04964cb682a3a8/bilateral_filtering/fft_3D/convolution_3D.h -------------------------------------------------------------------------------- /bilateral_filtering/fft_3D/fill_3D.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charalambos/StructurePointcloud/eb02cd0aaaf75f7f1f1bf9bf4a04964cb682a3a8/bilateral_filtering/fft_3D/fill_3D.h -------------------------------------------------------------------------------- /bilateral_filtering/fft_3D/support_3D.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charalambos/StructurePointcloud/eb02cd0aaaf75f7f1f1bf9bf4a04964cb682a3a8/bilateral_filtering/fft_3D/support_3D.cpp -------------------------------------------------------------------------------- /bilateral_filtering/fft_3D/support_3D.h: -------------------------------------------------------------------------------- 1 | /*! \file 2 | \verbatim 3 | 4 | Copyright (c) 2006, Sylvain Paris and Fr�do Durand 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the "Software"), to deal in the Software without 9 | restriction, including without limitation the rights to use, copy, 10 | modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | DEALINGS IN THE SOFTWARE. 25 | 26 | \endverbatim 27 | */ 28 | 29 | 30 | 31 | #ifndef __SUPPORT_FFT_3D__ 32 | #define __SUPPORT_FFT_3D__ 33 | 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #include "fftw3.h" 44 | 45 | #include "msg_stream.h" 46 | 47 | 48 | namespace FFT{ 49 | 50 | /* 51 | #################### 52 | # class Support_3D # 53 | #################### 54 | */ 55 | 56 | 57 | 58 | //! Type for support of FFT calculation in 3D. 59 | class Support_3D { 60 | 61 | public: 62 | 63 | typedef unsigned int size_type; 64 | 65 | typedef double real_type; 66 | typedef fftw_complex complex_type; 67 | 68 | typedef real_type* space_data_type; 69 | typedef complex_type* frequency_data_type; 70 | typedef void* buffer_type; 71 | 72 | 73 | //! Needs the \e width, \e height and \e depth in the space domain of the real 74 | //! function with which it will deal. 75 | inline Support_3D(const size_type w, 76 | const size_type h, 77 | const size_type d); 78 | 79 | inline ~Support_3D(); 80 | 81 | 82 | //@{ 83 | //! Give the size of the space domain. 84 | inline size_type x_space_size() const; 85 | inline size_type y_space_size() const; 86 | inline size_type z_space_size() const; 87 | //@} 88 | 89 | //@{ 90 | //! Give the size of the frequency domain. 91 | inline size_type x_frequency_size() const; 92 | inline size_type y_frequency_size() const; 93 | inline size_type z_frequency_size() const; 94 | //@} 95 | 96 | //! Create a buffer to store data. 97 | inline buffer_type create_buffer() const; 98 | 99 | //! Destroy the buffer. 100 | inline void destroy_buffer(buffer_type buffer) const; 101 | 102 | 103 | 104 | 105 | //! Put space data into a 3D array. 106 | template 107 | void save_space_data(Real_data_3D_array* const array) const; 108 | 109 | //! Get space data from a 3D array. 110 | template 111 | void load_space_data(const Real_data_3D_array& array); 112 | 113 | 114 | 115 | 116 | //! Put frequency data into a 3D array. 117 | template 118 | void save_frequency_data(Complex_data_3D_array* const array) const; 119 | 120 | //! Get frequency data from a 3D array. 121 | template 122 | void load_frequency_data(const Complex_data_3D_array& array); 123 | 124 | 125 | 126 | 127 | //! Put the space data into a buffer. 128 | inline void save_space_data_into_buffer(buffer_type buffer) const; 129 | 130 | //! Get the space data from a buffer. 131 | inline void load_space_data_from_buffer(buffer_type buffer); 132 | 133 | 134 | 135 | 136 | //! Put the frequency data into a buffer. 137 | inline void save_frequency_data_into_buffer(buffer_type buffer) const; 138 | 139 | //! Get the frequency data from a buffer. 140 | inline void load_frequency_data_from_buffer(buffer_type buffer); 141 | 142 | 143 | 144 | 145 | //! Compute the FFT. 146 | inline void space_to_frequency(); 147 | 148 | //! Compute the inverse FFT. 149 | inline void frequency_to_space(); 150 | 151 | 152 | 153 | //! Apply the normalization coefficient (image_size)^(-0.5) to all the values. 154 | template 155 | inline void half_normalize(Iterator begin,Iterator end) const; 156 | 157 | //! Apply the normalization coefficient (image_size)^(-1) to all the values. 158 | template 159 | inline void full_normalize(Iterator begin,Iterator end) const; 160 | 161 | 162 | //! Compute a fast, one-by-one, in-place, complex-valued multiplication 163 | //! of the frequency data. 164 | inline void multiply_frequency_data_by_buffer(buffer_type buffer, 165 | const bool scale = false); 166 | 167 | 168 | 169 | //! Convert a buffer into space data 170 | template 171 | void buffer_to_space_data(const buffer_type& buffer, 172 | Real_data_3D_array* const array) const; 173 | 174 | 175 | //! Convert a buffer into frequency data 176 | template 177 | void buffer_to_frequency_data(const buffer_type& buffer, 178 | Real_data_3D_array* const array) const; 179 | 180 | 181 | // #################### 182 | // # Static functions # 183 | // #################### 184 | 185 | 186 | //! Set the flags for FFTW. 187 | static inline void set_fftw_flags(const unsigned flags); 188 | 189 | //! Set the file name of the wisdom file. "" not to use a file. 190 | static inline void set_wisdom_file(const std::string file_name); 191 | 192 | //! Set 'autosave' mode. 193 | static inline void set_auto_save(const bool auto_save); 194 | 195 | //! Save the wisdom file; nothing fancy. 196 | static inline void save_wisdom(); 197 | 198 | private: 199 | 200 | //@{ 201 | //! FFTW structure. 202 | fftw_plan forward,backward; 203 | //@} 204 | 205 | //! Real data (in space domain). 206 | space_data_type space_data; 207 | 208 | //! Complex data (in frequency domain). 209 | frequency_data_type frequency_data; 210 | 211 | //@{ 212 | //! Size of the real data. 213 | 214 | const size_type width; 215 | const size_type height; 216 | const size_type depth; 217 | //@} 218 | 219 | //@{ 220 | //! Size of the complex data. 221 | 222 | const size_type support_w; 223 | const size_type support_h; 224 | const size_type support_d; 225 | const size_type mem_size; 226 | //@} 227 | 228 | static inline size_type index(const size_type x, 229 | const size_type y, 230 | const size_type z, 231 | const size_type w, 232 | const size_type h, 233 | const size_type d); 234 | 235 | 236 | // #################### 237 | // # Static functions # 238 | // #################### 239 | 240 | 241 | //! See set_fftw_flags() 242 | static unsigned fftw_flags; 243 | 244 | //! Flag to indicate whether a wisdom file has been set or not 245 | static bool wisdom_file_set; 246 | 247 | //! See set_wisdom_file() 248 | static std::string wisdom_file; 249 | 250 | //! Number of created support. 251 | static size_type support_number; 252 | 253 | //! If true, the wisdom file is saved each time the last support is deleted. 254 | //! This induces useless disk access if several supports are created then deleted. 255 | static bool auto_save_wisdom; 256 | 257 | //! Indicates if the wisdom has been loaded. Avoid reading it several times. 258 | static bool wisdom_loaded; 259 | 260 | 261 | //! Must be called in a support constructor. 262 | static inline void new_support(); 263 | 264 | //! Must be called in a support destructor. 265 | static inline void delete_support(); 266 | 267 | }; 268 | 269 | 270 | 271 | /* 272 | ########################################################### 273 | ########################################################### 274 | ########################################################### 275 | ############## ############## 276 | ############## I M P L E M E N T A T I O N ############## 277 | ############## ############## 278 | ########################################################### 279 | ########################################################### 280 | ########################################################### 281 | */ 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | void Support_3D::set_fftw_flags(const unsigned flags){ 291 | fftw_flags = flags; 292 | } 293 | 294 | void Support_3D::set_wisdom_file(const std::string file_name){ 295 | wisdom_file = file_name; 296 | wisdom_file_set = true; 297 | } 298 | 299 | void Support_3D::set_auto_save(const bool auto_save){ 300 | auto_save_wisdom = auto_save; 301 | } 302 | 303 | 304 | void Support_3D::save_wisdom(){ 305 | FILE* f = fopen(wisdom_file.c_str(),"w"); 306 | 307 | if (f != NULL){ // System says "ok". 308 | fftw_export_wisdom_to_file(f); 309 | fflush(f); 310 | fclose(f); 311 | } 312 | else{ // System says "error". 313 | Message::warning<<"error while opening (write mode) FFTW wisdom file \"" 314 | < 1)||(wisdom_file.size() == 0)){ 333 | return; 334 | } 335 | 336 | 337 | // If it is the first support, try to load the wisdom data. 338 | 339 | wisdom_loaded = true; 340 | 341 | 342 | // Opening the wisdom file. 343 | FILE* f = fopen(wisdom_file.c_str(),"r"); 344 | 345 | if (f != NULL){ // System says "ok" 346 | if(fftw_import_wisdom_from_file(f) == 0){ // But FFTW says "error". 347 | Message::warning<<"error while reading FFTW wisdom file \"" 348 | < 0)||(wisdom_file.size() == 0)||(!auto_save_wisdom)){ 367 | return; 368 | } 369 | 370 | // If it is the last support, try to save the wisdom data. 371 | 372 | save_wisdom(); 373 | } 374 | 375 | 376 | 377 | // ###################### 378 | // # Non-static section # 379 | // ###################### 380 | 381 | 382 | 383 | /*! 384 | If \e wisdom_file is specified, uses the optimized process. This may 385 | take time at the first run but it goes faster during the next ones. 386 | Informations are read from and written into \e wisdom_file. 387 | 388 | TODO: if the file does not exist, it segfaults. Use the \e touch shell 389 | command to workaround. 390 | */ 391 | Support_3D::Support_3D(const size_type w, 392 | const size_type h, 393 | const size_type d): 394 | width(w), 395 | height(h), 396 | depth(d), 397 | 398 | support_w(w), 399 | support_h(h), 400 | support_d(d/2+1), 401 | mem_size(w*h*(d/2+1)*sizeof(complex_type)) { 402 | 403 | new_support(); 404 | 405 | // Pointer to the space data. 406 | space_data = reinterpret_cast(fftw_malloc(mem_size)); 407 | 408 | // Same pointer to perform "in place" computation. 409 | frequency_data = reinterpret_cast(space_data); 410 | 411 | 412 | // We make the best measurement possible because we use the wisdom system. 413 | forward = fftw_plan_dft_r2c_3d(width,height,depth, 414 | space_data,frequency_data, 415 | fftw_flags); 416 | 417 | backward = fftw_plan_dft_c2r_3d(width,height,depth, 418 | frequency_data,space_data, 419 | fftw_flags); 420 | } 421 | 422 | 423 | 424 | 425 | 426 | 427 | Support_3D::~Support_3D(){ 428 | 429 | delete_support(); 430 | 431 | // Free memory. 432 | fftw_destroy_plan(forward); 433 | fftw_destroy_plan(backward); 434 | 435 | fftw_free(space_data); 436 | } 437 | 438 | 439 | 440 | 441 | Support_3D::size_type Support_3D::x_space_size() const{ 442 | return width; 443 | } 444 | 445 | 446 | 447 | Support_3D::size_type Support_3D::y_space_size() const{ 448 | return height; 449 | } 450 | 451 | 452 | Support_3D::size_type Support_3D::z_space_size() const{ 453 | return depth; 454 | } 455 | 456 | 457 | Support_3D::size_type Support_3D::x_frequency_size() const{ 458 | return support_w; 459 | } 460 | 461 | 462 | Support_3D::size_type Support_3D::y_frequency_size() const{ 463 | return support_h; 464 | } 465 | 466 | 467 | Support_3D::size_type Support_3D::z_frequency_size() const{ 468 | return support_d; 469 | } 470 | 471 | 472 | 473 | 474 | Support_3D::size_type Support_3D::index(const size_type x, 475 | const size_type y, 476 | const size_type z, 477 | const size_type w, 478 | const size_type h, 479 | const size_type d){ 480 | return z + y*d + x*h*d; 481 | } 482 | 483 | 484 | 485 | 486 | Support_3D::buffer_type Support_3D::create_buffer() const{ 487 | 488 | return fftw_malloc(mem_size); 489 | } 490 | 491 | void Support_3D::destroy_buffer(buffer_type buffer) const{ 492 | 493 | fftw_free(buffer); 494 | } 495 | 496 | 497 | 498 | 499 | /*! 500 | \e Real_data_3D_array must provide the following functions: resize(), 501 | and a (x,y,z) access operator. 502 | */ 503 | template 504 | void Support_3D::save_space_data(Real_data_3D_array* const array) const{ 505 | 506 | array->resize(width,height,depth); 507 | 508 | for(size_type x=0;x 525 | void Support_3D::load_space_data(const Real_data_3D_array& array){ 526 | 527 | if ((array.x_size()!=width)||(array.y_size()!=height)||(array.z_size()!=depth)){ 528 | Message::error<<"Support_3D::load_space_data : size does not match " 529 | < structure. 553 | */ 554 | template 555 | void Support_3D::save_frequency_data(Complex_data_3D_array* const array) const{ 556 | 557 | typedef typename Complex_data_3D_array::value_type cplx_type; 558 | 559 | array->resize(support_w,support_h,support_d); 560 | 561 | for(size_type x=0;x structure. 577 | */ 578 | template 579 | void Support_3D::load_frequency_data(const Complex_data_3D_array& array){ 580 | 581 | if ((array.x_size()!=support_w)||(array.y_size()!=support_h)||(array.z_size()!=support_d)){ 582 | Message::error<<"Support_3D::load_frequency_data : size does not match " 583 | < 636 | void Support_3D::half_normalize(Iterator begin,Iterator end) const{ 637 | 638 | const real_type scale = 1.0 / std::sqrt(static_cast(width*height*depth)); 639 | 640 | for(Iterator i=begin;i!=end;i++){ 641 | *i *= scale; 642 | } 643 | } 644 | 645 | 646 | 647 | template 648 | void Support_3D::full_normalize(Iterator begin,Iterator end) const{ 649 | 650 | const real_type scale = 1.0 / static_cast(width*height*depth); 651 | 652 | for(Iterator i=begin;i!=end;i++){ 653 | *i *= scale; 654 | } 655 | } 656 | 657 | 658 | void Support_3D::multiply_frequency_data_by_buffer(buffer_type buffer, 659 | const bool scale){ 660 | 661 | const size_type size = support_w*support_h*support_d; 662 | const frequency_data_type freq_buffer = 663 | reinterpret_cast(buffer); 664 | 665 | const real_type s = scale ? 1.0/(width*height*depth) : 1.0; 666 | 667 | for(size_type i=0;i 685 | void Support_3D::buffer_to_space_data(const buffer_type& buffer, 686 | Real_data_3D_array* const array) const{ 687 | 688 | space_data_type space_buffer = reinterpret_cast(buffer); 689 | 690 | array->resize(width,height); 691 | 692 | for(size_type x=0;x 708 | void Support_3D::buffer_to_frequency_data(const buffer_type& buffer, 709 | Complex_data_3D_array* const array) const{ 710 | 711 | typedef typename Complex_data_3D_array::value_type cplx_type; 712 | 713 | frequency_data_type frequency_buffer = reinterpret_cast(buffer); 714 | 715 | array->resize(support_w,support_h,support_d); 716 | 717 | for(size_type x=0;x 36 | # include "chrono.h" 37 | #endif 38 | 39 | #include "array.h" 40 | #include "array_n.h" 41 | #include "math_tools.h" 42 | 43 | namespace Image_filter{ 44 | 45 | template 46 | void fast_color_BF(const Data_array& input, 47 | const Base_array& base, 48 | const Real space_sigma, 49 | const Real range_sigma, 50 | Weight_array* const weight, 51 | Data_array* const result); 52 | 53 | template 54 | void fast_color_BF(const Data_array& input, 55 | const Real space_sigma, 56 | const Real range_sigma, 57 | Data_array* const result); 58 | 59 | 60 | 61 | /* 62 | 63 | ############################################# 64 | ############################################# 65 | ############################################# 66 | ###### ###### 67 | ###### I M P L E M E N T A T I O N ###### 68 | ###### ###### 69 | ############################################# 70 | ############################################# 71 | ############################################# 72 | 73 | */ 74 | 75 | 76 | 77 | 78 | template 79 | void fast_color_BF(const Data_array& input, 80 | const Real space_sigma, 81 | const Real range_sigma, 82 | Data_array* const result){ 83 | 84 | Array_2D weight; 85 | fast_color_BF(input,input, 86 | space_sigma,range_sigma, 87 | &weight,result); 88 | } 89 | 90 | 91 | template 92 | void fast_color_BF(const Data_array& input, 93 | const Base_array& base, 94 | const Real space_sigma, 95 | const Real range_sigma, 96 | Weight_array* const weight, 97 | Data_array* const result){ 98 | 99 | using namespace std; 100 | typedef typename Base_array::value_type base_type; 101 | typedef double real_type; 102 | typedef typename Data_array::value_type data_type; 103 | typedef Mixed_vector mixed_type; 104 | typedef unsigned int size_type; 105 | 106 | const size_type dimension = 5; 107 | 108 | typedef Base_array base_array_2D_type; 109 | typedef Array_ND mixed_array_ND_type; 110 | typedef typename mixed_array_ND_type::key_type key_type; 111 | 112 | const size_type width = input.x_size(); 113 | const size_type height = input.y_size(); 114 | 115 | const size_type padding_s = 2; 116 | const size_type padding_r = 2; 117 | 118 | key_type padding; 119 | padding[0] = padding_s; 120 | padding[1] = padding_s; 121 | padding[2] = padding_r; 122 | padding[3] = padding_r; 123 | padding[4] = padding_r; 124 | 125 | base_type base_min; 126 | key_type size,index = key_type(); 127 | 128 | for(size_type c = 0;c < 3;++c){ 129 | 130 | real_type m = base[index][c]; 131 | real_type M = m; 132 | 133 | for(typename base_array_2D_type::const_iterator 134 | b = base.begin(), 135 | b_end = base.end(); 136 | b != b_end;++b){ 137 | 138 | const real_type r = (*b)[c]; 139 | m = min(m,r); 140 | M = max(M,r); 141 | } 142 | 143 | base_min[c] = m; 144 | size[2 + c] = static_cast((M - m) / range_sigma) 145 | + 1 + 2 * padding[2 + c]; 146 | } 147 | size[0] = static_cast((width - 1) / space_sigma) + 1 + 2*padding[0]; 148 | size[1] = static_cast((height - 1) / space_sigma) + 1 + 2*padding[1]; 149 | 150 | #ifdef CHRONO 151 | Chrono chrono("filter"); 152 | chrono.start(); 153 | 154 | Chrono chrono_down("downsampling"); 155 | chrono_down.start(); 156 | #endif 157 | 158 | mixed_array_ND_type data(size,mixed_type()); 159 | 160 | for(size_type x = 0,x_end = width;x < x_end;++x){ 161 | 162 | index[0] = static_cast(1.0*x / space_sigma + 0.5) + padding[0]; 163 | 164 | for(size_type y = 0,y_end = height;y < y_end;++y){ 165 | 166 | index[1] = static_cast(1.0*y / space_sigma + 0.5) + padding[1]; 167 | 168 | for(size_type c = 0;c < 3;++c){ 169 | index[2 + c] = 170 | static_cast((base(x,y)[c] - base_min[c]) / range_sigma + 0.5) 171 | + padding[2 + c]; 172 | } 173 | 174 | mixed_type& d = data[index]; 175 | d.first += input(x,y); 176 | d.second += 1.0; 177 | } // END OF for y 178 | } // END OF for x 179 | 180 | #ifdef CHRONO 181 | chrono_down.stop(); 182 | cout<<" "<resize(width,height); 236 | 237 | vector pos(dimension); 238 | 239 | 240 | weight->resize(width,height); 241 | 242 | for(size_type x = 0,x_end = width;x < x_end;++x){ 243 | 244 | pos[0] = static_cast(x) / space_sigma + padding[0]; 245 | 246 | for(size_type y = 0,y_end = height;y < y_end;++y){ 247 | 248 | pos[1] = static_cast(y) / space_sigma + padding[1]; 249 | 250 | for(size_type c = 0;c < 3;++c){ 251 | pos[2 + c] = (base(x,y)[c] - base_min[c]) / range_sigma + padding[2 + c]; 252 | } 253 | const mixed_type D = Math_tools::Nlinear_interpolation(data,pos); 254 | 255 | (*weight)(x,y) = D.second; 256 | (*result)(x,y) = D.first / D.second; 257 | } // END OF for y 258 | } // END OF for x 259 | 260 | #ifdef CHRONO 261 | chrono_nonlinearities.stop(); 262 | cout<<" "< 35 | 36 | #include "array.h" 37 | #include "fft_3D.h" 38 | #include "math_tools.h" 39 | 40 | #ifdef CHRONO 41 | #include 42 | #include "chrono.h" 43 | #endif 44 | 45 | namespace Image_filter{ 46 | 47 | typedef double real_type; 48 | typedef unsigned int size_type; 49 | 50 | template 51 | void linear_BF(const Array& input, 52 | const Array& base, 53 | const real_type space_sigma, 54 | const real_type range_sigma, 55 | const real_type space_sampling, 56 | const real_type range_sampling, 57 | const bool early_division, 58 | Array* const result); 59 | 60 | template 61 | inline void linear_BF(const Array& input, 62 | const real_type space_sigma, 63 | const real_type range_sigma, 64 | Array* const result); 65 | 66 | 67 | 68 | 69 | /* 70 | 71 | ############################################# 72 | ############################################# 73 | ############################################# 74 | ###### ###### 75 | ###### I M P L E M E N T A T I O N ###### 76 | ###### ###### 77 | ############################################# 78 | ############################################# 79 | ############################################# 80 | 81 | */ 82 | 83 | 84 | template 85 | void linear_BF(const Array& input, 86 | const real_type space_sigma, 87 | const real_type range_sigma, 88 | Array* const result){ 89 | 90 | linear_BF(input, 91 | space_sigma,range_sigma, 92 | space_sigma,range_sigma, 93 | result); 94 | } 95 | 96 | 97 | 98 | template 99 | void linear_BF(const Array& input, 100 | const real_type space_sigma, 101 | const real_type range_sigma, 102 | const real_type space_sampling, 103 | const real_type range_sampling, 104 | Array* const result){ 105 | 106 | using namespace std; 107 | 108 | typedef Array real_array_2D_type; 109 | typedef Array_3D real_array_3D_type; 110 | 111 | const size_type width = input.x_size(); 112 | const size_type height = input.y_size(); 113 | 114 | const real_type sigma_r = range_sigma / range_sampling; 115 | const real_type sigma_s = space_sigma / space_sampling; 116 | 117 | const size_type padding_xy = static_cast(2.0 * sigma_s) + 1; 118 | const size_type padding_z = static_cast(2.0 * sigma_r) + 1; 119 | 120 | const real_type input_min = *min_element(input.begin(),input.end()); 121 | const real_type input_max = *max_element(input.begin(),input.end()); 122 | const real_type input_delta = input_max - input_min; 123 | 124 | const size_type small_width = static_cast((width - 1) / space_sampling) + 1 + 2*padding_xy; 125 | const size_type small_height = static_cast((height - 1) / space_sampling) + 1 + 2*padding_xy; 126 | const size_type small_depth = static_cast(input_delta / range_sampling) + 1 + 2*padding_z; 127 | 128 | FFT::Support_3D fft_support(small_width,small_height,small_depth); 129 | 130 | #ifdef CHRONO 131 | Chrono chrono("filter"); 132 | chrono.start(); 133 | 134 | Chrono chrono_down("downsampling"); 135 | chrono_down.start(); 136 | #endif 137 | 138 | real_array_3D_type w(small_width,small_height,small_depth,0.0); 139 | real_array_3D_type iw(small_width,small_height,small_depth,0.0); 140 | 141 | for(size_type x=0,x_end=width;x(1.0*x / space_sampling + 0.5) + padding_xy; 145 | const size_type small_y = static_cast(1.0*y / space_sampling + 0.5) + padding_xy; 146 | const size_type small_z = static_cast((input(x,y)-input_min) / range_sampling + 0.5) + padding_z; 147 | 148 | w(small_x,small_y,small_z) += 1.0; 149 | iw(small_x,small_y,small_z) += input(x,y); 150 | } // END OF for y 151 | } // END OF for x 152 | 153 | real_array_3D_type kernel(small_width,small_height,small_depth); 154 | 155 | const size_type half_width = small_width/2; 156 | const size_type half_height = small_height/2; 157 | const size_type half_depth = small_depth/2; 158 | 159 | for(size_type x=0,x_end=small_width;x(x) - ((x>half_width) ? small_width : 0.0); 162 | 163 | for(size_type y=0,y_end=small_height;y(y) - ((y>half_height) ? small_height : 0.0); 166 | 167 | for(size_type z=0,z_end=small_depth;z(z) - ((z>half_depth) ? small_depth : 0.0); 170 | 171 | const real_type rr = (X*X + Y*Y) / (sigma_s*sigma_s) + Z*Z / (sigma_r*sigma_r); 172 | 173 | kernel(x,y,z) = exp(-rr*0.5); 174 | 175 | } // END OF for z 176 | } // END OF for y 177 | } // END OF for x 178 | 179 | #ifdef CHRONO 180 | chrono_down.stop(); 181 | 182 | Chrono chrono_convolution("convolution"); 183 | chrono_convolution.start(); 184 | #endif 185 | 186 | FFT::convolve_3D(iw,kernel,&iw,fft_support); 187 | FFT::convolve_3D(w,kernel,&w,fft_support); 188 | 189 | #ifdef CHRONO 190 | chrono_convolution.stop(); 191 | 192 | Chrono chrono_nonlinearities("nonlinearities"); 193 | chrono_nonlinearities.start(); 194 | #endif 195 | 196 | result->resize(width,height); 197 | 198 | for(size_type x=0,x_end=width;x(x)/space_sampling + padding_xy, 205 | static_cast(y)/space_sampling + padding_xy, 206 | z/range_sampling + padding_z); 207 | 208 | const real_type W = Math_tools::trilinear_interpolation(w, 209 | static_cast(x)/space_sampling + padding_xy, 210 | static_cast(y)/space_sampling + padding_xy, 211 | z/range_sampling + padding_z); 212 | 213 | (*result)(x,y) = IW / W; 214 | 215 | } // END OF for y 216 | } // END OF for x 217 | 218 | #ifdef CHRONO 219 | chrono_nonlinearities.stop(); 220 | chrono.stop(); 221 | 222 | cout< geo_bboxes; 29 | ///The resolution of the bounding boxes 30 | float res_x, res_y, res_z; 31 | 32 | ///Measures the total bytes in a file 33 | void byteLength(const char *file_name); 34 | ///Removes a geospatial bounding box i.e. in case it will be subdivided later 35 | bool removeGeospatialBoundingBox(int index); 36 | ///Creates all the geospatial bounding boxes 37 | void createGeospatialBoundingBoxes(); 38 | ///Subdivides a geospatial bounding box into 8 smaller 39 | void subdivideGeospatialBoundingBox(int index, int level); 40 | ///Inserts a point in the data 41 | void insert(Vector3f const &point, int level=0); 42 | ///Exports the structure information file which contains spatial information about the dataset 43 | void exportStructureInformationFile(); 44 | ///Sorts the points in a file 45 | void sort(const char *file_name); 46 | ///Computes a bounding box for the data in the file 47 | void computeBoundingBox(const char *file_name); 48 | ///Structures the data into a number of geospatial bounding boxes 49 | void structure(const char *file_name); 50 | ///Creates a single geospatial bounding box 51 | int createGeospatialBoundingBox(Vector3f const ¢roid, Vector3f const &resolution, std::string *_file_name); 52 | 53 | void byteLength(const char *file_name) { 54 | 55 | std::cout << "Computing size of data in bytes..." << std::endl; 56 | 57 | ///Create the input file stream 58 | ifstream is; 59 | ///Open the file for reading in text mode 60 | is.open(file_name); 61 | ///Make sure the file was opened correctly 62 | if (!is.is_open()) return; 63 | 64 | ///Compute the length of the file 65 | ///First position the file pointer at the end 66 | is.seekg(0, ios::end); 67 | ///Then get the byte number 68 | byte_length = is.tellg(); 69 | 70 | ///Close the file 71 | is.close(); 72 | 73 | ///Print out the length 74 | std::cout << _format("Byte length: %ld",byte_length) << std::endl; 75 | std::cout << "...done." << std::endl; 76 | 77 | return; 78 | } 79 | 80 | int createGeospatialBoundingBox(Vector3f const ¢roid, Vector3f const &resolution, std::string *_file_name=0x00) { 81 | 82 | ///Create a geospatial bounding box for this bbox 83 | GeospatialBoundingBox *geo_bbox = new GeospatialBoundingBox(centroid,resolution,_file_name); 84 | ///add it to the list 85 | geo_bboxes.push_back(geo_bbox); 86 | return geo_bboxes.size()-1; 87 | } 88 | 89 | bool removeGeospatialBoundingBox(int index) { 90 | 91 | ///check for boundary conditions 92 | if (index<0 || index>= geo_bboxes.size()) return false; 93 | ///remove the geospatial box 94 | std::vector::iterator geo_itor = geo_bboxes.begin(); 95 | geo_itor += index; 96 | std::string file_name = geo_bboxes[index]->getFileName(); 97 | delete geo_bboxes[index]; 98 | geo_bboxes.erase(geo_itor); 99 | ///remove the geobox from the file system 100 | remove(file_name.c_str()); 101 | return true; 102 | } 103 | 104 | void createGeospatialBoundingBoxes() { 105 | 106 | Vector3f min_pt = bbox->getMinPt(); 107 | Vector3f max_pt = bbox->getMaxPt(); 108 | 109 | ///Compute the number of geospatial bounding boxes for each dimension 110 | int sizex = max_pt(0) - min_pt(0); 111 | int sizey = max_pt(1) - min_pt(1); 112 | int sizez = max_pt(2) - min_pt(2); 113 | 114 | int numx = max(1.0f,ceil(float(sizex)/float(res_x))); 115 | int numy = max(1.0f,ceil(float(sizey)/float(res_y))); 116 | int numz = max(1.0f,ceil(float(sizez)/float(res_z))); 117 | 118 | 119 | std::cout << _format("Initial geospatial bounding boxes resolution: %.02fx%.02fx%.02f.",res_x,res_y,res_z) << std::endl; 120 | std::cout << _format("Initial number of geospatial bounding boxes: %dx%dx%d.",numx,numy,numz) << std::endl; 121 | 122 | Vector3f resolution = Vector3f(res_x,res_y,res_z); 123 | 124 | ///Add the centroid as the first point to each geospatial bounding box. 125 | float z=min_pt(2) + float(res_z)/2.0f; 126 | for (unsigned int i=0;igetCentroid(); 151 | 152 | ///get the resolution of the parent geobox 153 | Vector3f resolution = geo_bboxes[index]->getResolution(); 154 | 155 | ///get the min and max points of the geo box 156 | Vector3f geo_min_pt = geo_bboxes[index]->getMinPt(); 157 | Vector3f geo_max_pt = geo_bboxes[index]->getMaxPt(); 158 | 159 | ///get the name of the parent geobox 160 | std::string parent_file_name = geo_bboxes[index]->getFileName(); 161 | ///get only the number of the file and and not the extention 162 | char geo_box_parent_id[256]; 163 | sscanf(parent_file_name.c_str(), "../output/geo_bbox_%[^xyz].xyz",&geo_box_parent_id); 164 | ///get all the points in the parent geo box 165 | std::vector points = geo_bboxes[index]->getPoints(); 166 | ///remove the parent geobox 167 | if (!removeGeospatialBoundingBox(index)) { 168 | std::cout << "Error removing parent geospatial bounding box" << std::endl; 169 | } 170 | 171 | ///Create 8 octands, 2 on each axis 172 | ///Add the centroid as the first point to each geospatial bounding box. 173 | ///The resolution of the sub geo boxes will be half the parent's resolution 174 | ///Provide buffer zone 175 | Vector3f half_resolution = Vector3f(0.5f,0.5f,0.5f) + resolution/2.0f; 176 | //std::cout << geo_min_pt << " " << geo_max_pt << "||" << "from: " << resolution << " to: " << half_resolution << std::endl; 177 | int new_ids = 0; 178 | std::string new_file_name; 179 | float z=geo_min_pt(2) + half_resolution(2)/2.0f; 180 | for (int i=0;i<2;i++) { 181 | float y=geo_min_pt(1) + half_resolution(1)/2.0f; 182 | for (int j=0;j<2;j++) { 183 | float x=geo_min_pt(0) + half_resolution(0)/2.0f; 184 | for (int k=0;k<2;k++) { 185 | ///Form the filename for the first geobox 186 | new_file_name = _format("../output/geo_bbox_%s%d.xyz",geo_box_parent_id,new_ids); 187 | new_ids++; 188 | ///Create a geospatial bounding box for this bbox 189 | int new_index = createGeospatialBoundingBox(Vector3f(x,y,z),half_resolution,&new_file_name); 190 | //std::cout << Vector3f(x,y,z) << std::endl; 191 | x += float(half_resolution(0)); 192 | } 193 | y += half_resolution(1); 194 | } 195 | z += half_resolution(2); 196 | } 197 | 198 | ///sort the points which were part of the parent geo box 199 | for (int i=0;iliesInside(point)) { 215 | ///save the index of the geospatial bounding box 216 | index = i; 217 | break; 218 | } 219 | } 220 | ///if there is a valid index 221 | if (index!=-1) { 222 | ///the point is inside the cube so add it 223 | ///add the point to the corresponding geospatial bounding box 224 | geo_bboxes[index]->insert(point); 225 | ///check whether the geospatial bounding box has reached it's maximum capacity 226 | if (geo_bboxes[index]->maxedOut()) { 227 | ///subdivide into 8 228 | subdivideGeospatialBoundingBox(index,level); 229 | } 230 | } 231 | else { 232 | std::cout << "level: " << level << " index: " << index << " point: " << point << " min: " << bbox->getMinPt() << " max: " << bbox->getMaxPt() << std::endl; 233 | std::cout << geo_bboxes.size() << std::endl; 234 | for (unsigned int i=0;igetMinPt() << " " << geo_bboxes[i]->getMaxPt() << std::endl; 236 | } 237 | std::cout <<"Probably this problem is caused by the last line of the XYZ file which is read as 0,0,0 and the centroid is subtracted from it. Removing the line will fix the problem." << std::endl; 238 | exit(-1); 239 | } 240 | 241 | return; 242 | } 243 | 244 | 245 | void exportStructureInformationFile() { 246 | 247 | FILE *file_ptr = fopen("structure_file.info","w"); 248 | fprintf(file_ptr, "%d\n",geo_bboxes.size()); 249 | fprintf(file_ptr, "centroid offset: %f %f %f\n", centroid_repos_offset(0), centroid_repos_offset(1), centroid_repos_offset(2)); 250 | fprintf(file_ptr, "res: %f %f %f\n", res_x, res_y, res_z); 251 | fprintf(file_ptr, "points: %d\n", number_of_points); 252 | fprintf(file_ptr, "filename: %s\n", file_name.c_str()); 253 | fclose(file_ptr); 254 | return; 255 | } 256 | 257 | void sort(const char *file_name) { 258 | 259 | ///Read the data once and distribute each point to the right cell in the kd-tree. At the same 260 | ///time create geospatial bounding boxes which store the points in each cell to the disk. 261 | ///Destroy the kd-tree at the end. 262 | 263 | std::cout << "Sorting points..." << std::endl; 264 | 265 | ///Create the input file stream 266 | ifstream is; 267 | ///Open the file for reading in text mode 268 | is.open(file_name); 269 | ///Make sure the file was opened correctly 270 | if (!is.is_open()) return; 271 | 272 | ///Read all points and insert them to the kd-tree 273 | std::string line; 274 | Vector3f current_pt; 275 | while (!is.eof()) { 276 | ///Get the current line 277 | getline (is, line); 278 | if (!line.size()) continue; 279 | ///Process the string 280 | sscanf(line.c_str(), "%f %f %f\n", ¤t_pt(0),¤t_pt(1),¤t_pt(2)); 281 | ///Flip the Y - only applies for the CHP dataset 282 | //current_pt(1) = -current_pt(1); 283 | ///Offset the point 284 | current_pt -= centroid_repos_offset; 285 | ///insert the point to the right place 286 | insert(current_pt); 287 | } 288 | 289 | ///Close the file 290 | is.close(); 291 | 292 | ///Go through the geospatial bounding boxes and remove any of them without any points in them 293 | int variable_size = geo_bboxes.size(); 294 | for (int i=0;igetNumberOfPoints() == 0) { 296 | removeGeospatialBoundingBox(i); 297 | i--; 298 | variable_size--; 299 | } 300 | } 301 | std::cout << "...done." << std::endl; 302 | 303 | return; 304 | } 305 | 306 | void computeBoundingBox(const char *file_name) { 307 | 308 | ///Read the data once and compute the Bounding Box (min and max points) 309 | ///Using the min and max points reposition the dataset around the origine ( subtract by the centroid) 310 | 311 | std::cout << "Recentering..." << std::endl; 312 | 313 | ///Create the input file stream 314 | ifstream is; 315 | ///Open the file for reading in text mode 316 | is.open(file_name); 317 | ///Make sure the file was opened correctly 318 | if (!is.is_open()) return; 319 | 320 | ///Min/Max points for BBox 321 | Vector3f min_pt = Vector3f(FLT_MAX, FLT_MAX, FLT_MAX); 322 | Vector3f max_pt = Vector3f(-FLT_MAX, -FLT_MAX, -FLT_MAX); 323 | 324 | ///Read all points and compute the min/max points for the BBox 325 | std::string line; 326 | float x, y, z; 327 | number_of_points = 0; 328 | while (!is.eof()) { 329 | ///Get the current line 330 | getline (is, line); 331 | if (!line.size()) continue; 332 | sscanf(line.c_str(),"%f %f %f\n",&x,&y,&z); 333 | 334 | ///Flip the Y. This is only needed for the CHP dataset 335 | //y = -y; 336 | 337 | if (min_pt(0) > x) min_pt(0) = x; 338 | if (max_pt(0) < x) max_pt(0) = x; 339 | 340 | if (min_pt(1) > y) min_pt(1) = y; 341 | if (max_pt(1) < y) max_pt(1) = y; 342 | 343 | if (min_pt(2) > z) min_pt(2) = z; 344 | if (max_pt(2) < z) max_pt(2) = z; 345 | 346 | number_of_points++; 347 | } 348 | 349 | ///Print the BBox min/max 350 | std::cout << _format("Min pt: %f %f %f", min_pt(0), min_pt(1), min_pt(2)).c_str() << std::endl; 351 | std::cout << _format("Max pt: %f %f %f", max_pt(0), max_pt(1), max_pt(2)).c_str() << std::endl; 352 | 353 | ///Compute centroid 354 | centroid_repos_offset = (min_pt + max_pt)/2.0f; 355 | ///Print the centroid 356 | std::cout << _format("Centroid repositioning offset: %f %f %f", centroid_repos_offset(0),centroid_repos_offset(1),centroid_repos_offset(2)) << std::endl; 357 | 358 | ///Recenter the min and max points. 359 | min_pt -= centroid_repos_offset; 360 | max_pt -= centroid_repos_offset; 361 | 362 | ///Create the bounding box of the entire dataset 363 | if (bbox) delete bbox; 364 | bbox = new BoundingBox(min_pt, max_pt); 365 | 366 | 367 | ///Print the recentered BBox min/max 368 | std::cout << _format("Recentered Min pt: %f %f %f", min_pt(0), min_pt(1), min_pt(2)) << std::endl; 369 | std::cout << _format("Recentered Max pt: %f %f %f", max_pt(0), max_pt(1), max_pt(2)) << std::endl; 370 | 371 | ///Print the total number of points in the dataset 372 | std:: cout << _format("Total number of points in dataset: %d", number_of_points) << std::endl; 373 | 374 | ///Close the file 375 | is.close(); 376 | 377 | std::cout << "...done." << std::endl; 378 | 379 | return; 380 | } 381 | 382 | 383 | 384 | void structure(const char *file_name) { 385 | 386 | std::cout << _format("Starting structuring %s.", timestamp().c_str()).c_str() << std::endl; 387 | 388 | ///Compute the bounding box of the data 389 | computeBoundingBox(file_name); 390 | 391 | ///Create the geo spatial boxes 392 | createGeospatialBoundingBoxes(); 393 | 394 | ///Sort the data to the appropriate geospatial bounding box 395 | sort(file_name); 396 | 397 | std::cout << _format("Final number of geospatial bounding boxes: %d",geo_bboxes.size()) << std::endl; 398 | 399 | ///Export the information file 400 | exportStructureInformationFile(); 401 | 402 | std::cout << _format("Finished structuring %s.", timestamp().c_str()).c_str() << std::endl; 403 | 404 | return; 405 | } 406 | 407 | 408 | int main(int argc, char *argv[]) { 409 | 410 | res_x = float(DEFAULT_RES); 411 | res_y = float(DEFAULT_RES); 412 | res_z = float(DEFAULT_RES); 413 | 414 | centroid_repos_offset = Vector3f(0.0f,0.0f,0.0f); 415 | bbox = 0x00; 416 | 417 | byte_length = 0; 418 | 419 | number_of_points = 0; 420 | 421 | if (argc != 2) { 422 | std::cout << "Number of arguments should be 2. Quiting..." << std::endl; 423 | exit(-1); 424 | //file_name = std::string("pointcloud.xyz"); 425 | } 426 | else { 427 | file_name = std::string(argv[1]); 428 | } 429 | 430 | ///Compute the byte length of the file 431 | byteLength(file_name.c_str()); 432 | 433 | ///Structure the data 434 | structure(file_name.c_str()); 435 | 436 | ///Create a geometry exporter object 437 | GeometryExporter *ge = new GeometryExporter(); 438 | 439 | ///Export the information files for each geobox 440 | for (int i=0;iexportInformationFile(); 442 | 443 | if (geo_bboxes[i]->getNumberOfPoints()>0) { 444 | ///Initialize the data required for further processing 445 | geo_bboxes[i]->initialize(); 446 | 447 | ///Export the entire mesh object 448 | 449 | ///Get the triangulated object corresponding to this geospatial bounding box 450 | GeometricObject *object = geo_bboxes[i]->getObject(); 451 | object->translate(Vector3f(geo_bboxes[i]->getCentroid())); 452 | ge->exportToOBJ(geo_bboxes[i]->getFileName().c_str(),object); 453 | std::cout << _format("Exported geospatial bounding box: %s", _format("%s.obj",geo_bboxes[i]->getFileName().c_str()).c_str()) << std::endl; 454 | delete object; 455 | } 456 | } 457 | 458 | delete ge; 459 | 460 | 461 | return 0; 462 | } 463 | 464 | 465 | 466 | --------------------------------------------------------------------------------