├── DLL └── NMSClipper.dll ├── LICENCE ├── README.md ├── clipper.cpp ├── clipper.hpp └── main.cpp /DLL/NMSClipper.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdroguski/FastNMS/f23f4ecde3873a77415cf0715347ebc9dc9f9139/DLL/NMSClipper.dll -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastNMS 2 | Fast and simple implementation of Non-Maximum Suppression algorithm for bounding boxes selection within the object detection domain, after the NN predictions are made. 3 | 4 | It was intented to be fast and simple. For clipping polygons, in this case 4-gons clipping, the Clipper library was used and the rest is just vanilla C++ with few STL dependencies and dynamically allocated arrays instead of list or vectors for speed. 5 | 6 | In my tiny personal benchmark there was a ~600 speed up in time, converting the original version from Python to C++. The execution time was measured using regular chrono library for x64 Release build (with compiler options set for maximum optimization for speed). 7 | 8 | I added also a `DLL` version of this mini project to be portable or 'runnable' from python - look below. 9 | 10 | ## How run it in C++: 11 | 12 | Just run `main.cpp` with `nms_boxes.txt` input defined. Input with lines withe the style: `x1, y1, x2, y2, x3, y3, x4, y4, char_proba` 13 | 14 | ## How to use it with Python: 15 | 16 | Header for this DLL: 17 | ```cpp 18 | extern "C" NMSCLIPPER_API int nms( 19 | const float* boxes, const int& n, const int& m, 20 | float* new_boxes, int* pick, 21 | const double& overlapThresh, const double& neighbourThresh, 22 | const float& minScore, const int& num_neig 23 | ); 24 | 25 | extern "C" NMSCLIPPER_API int nmsWithCharCls( 26 | const float* char_boxes, const int& n, const int& m, 27 | float* new_char_boxes, int* pick, 28 | const float* char_scores, const int& nc, const int& mc, 29 | float* new_char_scores, 30 | const double& overlapThresh, const double& neighbourThresh, 31 | const float& minScore, const int& num_neig 32 | ); 33 | ``` 34 | 35 | It's intended to be used in Python 3 with ctypes. Full Python example for standard NMS: 36 | 37 | ```python 38 | # Imports 39 | import numpy as np 40 | from ctypes import c_float, c_int, c_double, POINTER, byref 41 | 42 | # Loading DLL 43 | cpp_dll = ctypes.cdll.LoadLibrary(path_to_lib) 44 | cpp_nms = c_dll.nms 45 | 46 | boxes = "some_numpy_array_with_bounding_boxes".astype(np.float32) 47 | # 0 based shape is: n (instances) x 8 (x1, y1, x2, y2, x3, y3, x4, y4, nn_score) 48 | n, m = boxes.shape 49 | new_boxes = np.zeros_like(boxes).astype(np.float32) 50 | pick = (np.zeros(n) - 1).astype(np.int) 51 | 52 | c_float_p = POINTER(c_float) 53 | c_int_p = POINTER(c_int) 54 | 55 | # Conversion to C types 56 | boxes_p = boxes.astype(np.float32).ctypes.data_as(c_float_p) # create dynamic array with floats from boxes 57 | n_c = c_int(n) 58 | m_c = c_int(m) 59 | new_boxes_p = new_boxes.ctypes.data_as(c_float_p) 60 | pick_p = pick.ctypes.data_as(c_int_p) 61 | overlapThresh_c = c_double(0.15) # these are arbitraly 62 | neighbourThresh_c = c_double(0.5) 63 | minScore_c = c_float(0) 64 | num_neig_c = c_int(1) 65 | 66 | res = cpp_nms_ptr( 67 | boxes_p, byref(n_c), byref(m_c), 68 | new_boxes_p, pick_p, 69 | byref(overlapThresh_c), byref(neighbourThresh_c), byref(minScore_c), byref(num_neig_c) 70 | ) 71 | 72 | if res != 0: 73 | raise RuntimeError('Unexpected error') 74 | 75 | # Conversion from C types 76 | new_boxes = np.array(new_boxes_p[:n*m]).reshape(n,m) # convert dynamic array to np.ndarray 77 | pick = pick_p[:n] # convert dynamic array to list 78 | pick = [p for p in pick if p != -1] # filter out unnecessary picks 79 | ``` 80 | -------------------------------------------------------------------------------- /clipper.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdroguski/FastNMS/f23f4ecde3873a77415cf0715347ebc9dc9f9139/clipper.cpp -------------------------------------------------------------------------------- /clipper.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * * 3 | * Author : Angus Johnson * 4 | * Version : 6.4.2 * 5 | * Date : 27 February 2017 * 6 | * Website : http://www.angusj.com * 7 | * Copyright : Angus Johnson 2010-2017 * 8 | * * 9 | * License: * 10 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 11 | * http://www.boost.org/LICENSE_1_0.txt * 12 | * * 13 | * Attributions: * 14 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 15 | * "A generic solution to polygon clipping" * 16 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 17 | * http://portal.acm.org/citation.cfm?id=129906 * 18 | * * 19 | * Computer graphics and geometric modeling: implementation and algorithms * 20 | * By Max K. Agoston * 21 | * Springer; 1 edition (January 4, 2005) * 22 | * http://books.google.com/books?q=vatti+clipping+agoston * 23 | * * 24 | * See also: * 25 | * "Polygon Offsetting by Computing Winding Numbers" * 26 | * Paper no. DETC2005-85513 pp. 565-575 * 27 | * ASME 2005 International Design Engineering Technical Conferences * 28 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 29 | * September 24-28, 2005 , Long Beach, California, USA * 30 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 31 | * * 32 | *******************************************************************************/ 33 | 34 | #ifndef clipper_hpp 35 | #define clipper_hpp 36 | 37 | #define CLIPPER_VERSION "6.4.2" 38 | 39 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 40 | //improve performance but coordinate values are limited to the range +/- 46340 41 | //#define use_int32 42 | 43 | //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. 44 | //#define use_xyz 45 | 46 | //use_lines: Enables line clipping. Adds a very minor cost to performance. 47 | #define use_lines 48 | 49 | //use_deprecated: Enables temporary support for the obsolete functions 50 | //#define use_deprecated 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | namespace ClipperLib { 63 | 64 | enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 65 | enum PolyType { ptSubject, ptClip }; 66 | //By far the most widely used winding rules for polygon filling are 67 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 68 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 69 | //see http://glprogramming.com/red/chapter11.html 70 | enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 71 | 72 | #ifdef use_int32 73 | typedef int cInt; 74 | static cInt const loRange = 0x7FFF; 75 | static cInt const hiRange = 0x7FFF; 76 | #else 77 | typedef signed long long cInt; 78 | static cInt const loRange = 0x3FFFFFFF; 79 | static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; 80 | typedef signed long long long64; //used by Int128 class 81 | typedef unsigned long long ulong64; 82 | 83 | #endif 84 | 85 | struct IntPoint { 86 | cInt X; 87 | cInt Y; 88 | #ifdef use_xyz 89 | cInt Z; 90 | IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 91 | #else 92 | IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 93 | #endif 94 | 95 | friend inline bool operator== (const IntPoint& a, const IntPoint& b) 96 | { 97 | return a.X == b.X && a.Y == b.Y; 98 | } 99 | friend inline bool operator!= (const IntPoint& a, const IntPoint& b) 100 | { 101 | return a.X != b.X || a.Y != b.Y; 102 | } 103 | }; 104 | //------------------------------------------------------------------------------ 105 | 106 | typedef std::vector< IntPoint > Path; 107 | typedef std::vector< Path > Paths; 108 | 109 | inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} 110 | inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} 111 | 112 | std::ostream& operator <<(std::ostream &s, const IntPoint &p); 113 | std::ostream& operator <<(std::ostream &s, const Path &p); 114 | std::ostream& operator <<(std::ostream &s, const Paths &p); 115 | 116 | struct DoublePoint 117 | { 118 | double X; 119 | double Y; 120 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 121 | DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} 122 | }; 123 | //------------------------------------------------------------------------------ 124 | 125 | #ifdef use_xyz 126 | typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); 127 | #endif 128 | 129 | enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; 130 | enum JoinType {jtSquare, jtRound, jtMiter}; 131 | enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; 132 | 133 | class PolyNode; 134 | typedef std::vector< PolyNode* > PolyNodes; 135 | 136 | class PolyNode 137 | { 138 | public: 139 | PolyNode(); 140 | virtual ~PolyNode(){}; 141 | Path Contour; 142 | PolyNodes Childs; 143 | PolyNode* Parent; 144 | PolyNode* GetNext() const; 145 | bool IsHole() const; 146 | bool IsOpen() const; 147 | int ChildCount() const; 148 | private: 149 | //PolyNode& operator =(PolyNode& other); 150 | unsigned Index; //node index in Parent.Childs 151 | bool m_IsOpen; 152 | JoinType m_jointype; 153 | EndType m_endtype; 154 | PolyNode* GetNextSiblingUp() const; 155 | void AddChild(PolyNode& child); 156 | friend class Clipper; //to access Index 157 | friend class ClipperOffset; 158 | }; 159 | 160 | class PolyTree: public PolyNode 161 | { 162 | public: 163 | ~PolyTree(){ Clear(); }; 164 | PolyNode* GetFirst() const; 165 | void Clear(); 166 | int Total() const; 167 | private: 168 | //PolyTree& operator =(PolyTree& other); 169 | PolyNodes AllNodes; 170 | friend class Clipper; //to access AllNodes 171 | }; 172 | 173 | bool Orientation(const Path &poly); 174 | double Area(const Path &poly); 175 | int PointInPolygon(const IntPoint &pt, const Path &path); 176 | 177 | void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 178 | void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 179 | void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); 180 | 181 | void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); 182 | void CleanPolygon(Path& poly, double distance = 1.415); 183 | void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); 184 | void CleanPolygons(Paths& polys, double distance = 1.415); 185 | 186 | void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); 187 | void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); 188 | void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); 189 | 190 | void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); 191 | void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); 192 | void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); 193 | 194 | void ReversePath(Path& p); 195 | void ReversePaths(Paths& p); 196 | 197 | struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; 198 | 199 | //enums that are used internally ... 200 | enum EdgeSide { esLeft = 1, esRight = 2}; 201 | 202 | //forward declarations (for stuff used internally) ... 203 | struct TEdge; 204 | struct IntersectNode; 205 | struct LocalMinimum; 206 | struct OutPt; 207 | struct OutRec; 208 | struct Join; 209 | 210 | typedef std::vector < OutRec* > PolyOutList; 211 | typedef std::vector < TEdge* > EdgeList; 212 | typedef std::vector < Join* > JoinList; 213 | typedef std::vector < IntersectNode* > IntersectList; 214 | 215 | //------------------------------------------------------------------------------ 216 | 217 | //ClipperBase is the ancestor to the Clipper class. It should not be 218 | //instantiated directly. This class simply abstracts the conversion of sets of 219 | //polygon coordinates into edge objects that are stored in a LocalMinima list. 220 | class ClipperBase 221 | { 222 | public: 223 | ClipperBase(); 224 | virtual ~ClipperBase(); 225 | virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); 226 | bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); 227 | virtual void Clear(); 228 | IntRect GetBounds(); 229 | bool PreserveCollinear() {return m_PreserveCollinear;}; 230 | void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; 231 | protected: 232 | void DisposeLocalMinimaList(); 233 | TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); 234 | virtual void Reset(); 235 | TEdge* ProcessBound(TEdge* E, bool IsClockwise); 236 | void InsertScanbeam(const cInt Y); 237 | bool PopScanbeam(cInt &Y); 238 | bool LocalMinimaPending(); 239 | bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); 240 | OutRec* CreateOutRec(); 241 | void DisposeAllOutRecs(); 242 | void DisposeOutRec(PolyOutList::size_type index); 243 | void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); 244 | void DeleteFromAEL(TEdge *e); 245 | void UpdateEdgeIntoAEL(TEdge *&e); 246 | 247 | typedef std::vector MinimaList; 248 | MinimaList::iterator m_CurrentLM; 249 | MinimaList m_MinimaList; 250 | 251 | bool m_UseFullRange; 252 | EdgeList m_edges; 253 | bool m_PreserveCollinear; 254 | bool m_HasOpenPaths; 255 | PolyOutList m_PolyOuts; 256 | TEdge *m_ActiveEdges; 257 | 258 | typedef std::priority_queue ScanbeamList; 259 | ScanbeamList m_Scanbeam; 260 | }; 261 | //------------------------------------------------------------------------------ 262 | 263 | class Clipper : public virtual ClipperBase 264 | { 265 | public: 266 | Clipper(int initOptions = 0); 267 | bool Execute(ClipType clipType, 268 | Paths &solution, 269 | PolyFillType fillType = pftEvenOdd); 270 | bool Execute(ClipType clipType, 271 | Paths &solution, 272 | PolyFillType subjFillType, 273 | PolyFillType clipFillType); 274 | bool Execute(ClipType clipType, 275 | PolyTree &polytree, 276 | PolyFillType fillType = pftEvenOdd); 277 | bool Execute(ClipType clipType, 278 | PolyTree &polytree, 279 | PolyFillType subjFillType, 280 | PolyFillType clipFillType); 281 | bool ReverseSolution() { return m_ReverseOutput; }; 282 | void ReverseSolution(bool value) {m_ReverseOutput = value;}; 283 | bool StrictlySimple() {return m_StrictSimple;}; 284 | void StrictlySimple(bool value) {m_StrictSimple = value;}; 285 | //set the callback function for z value filling on intersections (otherwise Z is 0) 286 | #ifdef use_xyz 287 | void ZFillFunction(ZFillCallback zFillFunc); 288 | #endif 289 | protected: 290 | virtual bool ExecuteInternal(); 291 | private: 292 | JoinList m_Joins; 293 | JoinList m_GhostJoins; 294 | IntersectList m_IntersectList; 295 | ClipType m_ClipType; 296 | typedef std::list MaximaList; 297 | MaximaList m_Maxima; 298 | TEdge *m_SortedEdges; 299 | bool m_ExecuteLocked; 300 | PolyFillType m_ClipFillType; 301 | PolyFillType m_SubjFillType; 302 | bool m_ReverseOutput; 303 | bool m_UsingPolyTree; 304 | bool m_StrictSimple; 305 | #ifdef use_xyz 306 | ZFillCallback m_ZFill; //custom callback 307 | #endif 308 | void SetWindingCount(TEdge& edge); 309 | bool IsEvenOddFillType(const TEdge& edge) const; 310 | bool IsEvenOddAltFillType(const TEdge& edge) const; 311 | void InsertLocalMinimaIntoAEL(const cInt botY); 312 | void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); 313 | void AddEdgeToSEL(TEdge *edge); 314 | bool PopEdgeFromSEL(TEdge *&edge); 315 | void CopyAELToSEL(); 316 | void DeleteFromSEL(TEdge *e); 317 | void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); 318 | bool IsContributing(const TEdge& edge) const; 319 | bool IsTopHorz(const cInt XPos); 320 | void DoMaxima(TEdge *e); 321 | void ProcessHorizontals(); 322 | void ProcessHorizontal(TEdge *horzEdge); 323 | void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 324 | OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 325 | OutRec* GetOutRec(int idx); 326 | void AppendPolygon(TEdge *e1, TEdge *e2); 327 | void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); 328 | OutPt* AddOutPt(TEdge *e, const IntPoint &pt); 329 | OutPt* GetLastOutPt(TEdge *e); 330 | bool ProcessIntersections(const cInt topY); 331 | void BuildIntersectList(const cInt topY); 332 | void ProcessIntersectList(); 333 | void ProcessEdgesAtTopOfScanbeam(const cInt topY); 334 | void BuildResult(Paths& polys); 335 | void BuildResult2(PolyTree& polytree); 336 | void SetHoleState(TEdge *e, OutRec *outrec); 337 | void DisposeIntersectNodes(); 338 | bool FixupIntersectionOrder(); 339 | void FixupOutPolygon(OutRec &outrec); 340 | void FixupOutPolyline(OutRec &outrec); 341 | bool IsHole(TEdge *e); 342 | bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); 343 | void FixHoleLinkage(OutRec &outrec); 344 | void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); 345 | void ClearJoins(); 346 | void ClearGhostJoins(); 347 | void AddGhostJoin(OutPt *op, const IntPoint offPt); 348 | bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); 349 | void JoinCommonEdges(); 350 | void DoSimplePolygons(); 351 | void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); 352 | void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); 353 | void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); 354 | #ifdef use_xyz 355 | void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); 356 | #endif 357 | }; 358 | //------------------------------------------------------------------------------ 359 | 360 | class ClipperOffset 361 | { 362 | public: 363 | ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); 364 | ~ClipperOffset(); 365 | void AddPath(const Path& path, JoinType joinType, EndType endType); 366 | void AddPaths(const Paths& paths, JoinType joinType, EndType endType); 367 | void Execute(Paths& solution, double delta); 368 | void Execute(PolyTree& solution, double delta); 369 | void Clear(); 370 | double MiterLimit; 371 | double ArcTolerance; 372 | private: 373 | Paths m_destPolys; 374 | Path m_srcPoly; 375 | Path m_destPoly; 376 | std::vector m_normals; 377 | double m_delta, m_sinA, m_sin, m_cos; 378 | double m_miterLim, m_StepsPerRad; 379 | IntPoint m_lowest; 380 | PolyNode m_polyNodes; 381 | 382 | void FixOrientations(); 383 | void DoOffset(double delta); 384 | void OffsetPoint(int j, int& k, JoinType jointype); 385 | void DoSquare(int j, int k); 386 | void DoMiter(int j, int k, double r); 387 | void DoRound(int j, int k); 388 | }; 389 | //------------------------------------------------------------------------------ 390 | 391 | class clipperException : public std::exception 392 | { 393 | public: 394 | clipperException(const char* description): m_descr(description) {} 395 | virtual ~clipperException() throw() {} 396 | virtual const char* what() const throw() {return m_descr.c_str();} 397 | private: 398 | std::string m_descr; 399 | }; 400 | //------------------------------------------------------------------------------ 401 | 402 | } //ClipperLib namespace 403 | 404 | #endif //clipper_hpp 405 | 406 | 407 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "clipper.hpp" 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #include 12 | 13 | using namespace ClipperLib; 14 | 15 | template 16 | using vector = std::vector; 17 | 18 | bool getFileContent(std::string fileName, vector& vecOfStrs) 19 | { 20 | // Open the File 21 | std::ifstream in(fileName.c_str()); 22 | // Check if object is valid 23 | if (!in) 24 | { 25 | std::cerr << "Cannot open the File : " << fileName << std::endl; 26 | return false; 27 | } 28 | std::string str; 29 | // Read the next line from File untill it reaches the end. 30 | while (std::getline(in, str)) 31 | { 32 | // Line contains string of length > 0 then save it in vector 33 | if (str.size() > 0) 34 | vecOfStrs.push_back(str); 35 | } 36 | //Close The File 37 | in.close(); 38 | return true; 39 | } 40 | 41 | vector split(const std::string& s, char delim) { 42 | std::stringstream ss(s); 43 | std::string item; 44 | vector elems; 45 | while (std::getline(ss, item, delim)) 46 | elems.push_back(std::move(item)); 47 | 48 | return elems; 49 | } 50 | 51 | double area(const float* polygon, const int& size_x, const int& size_y) 52 | { 53 | float area = 0.0; 54 | int n = size_x * size_y; 55 | int prev_idx; 56 | 57 | float curr_pt; 58 | float next_pt; 59 | float prev_pt; 60 | 61 | for (int i = 1; i < size_x + 1; i++) 62 | { 63 | curr_pt = polygon[(i * size_y) % n + 0]; 64 | next_pt = polygon[((i + 1) * size_y) % n + 1]; 65 | prev_idx = ((size_x + (i - 1) % n) * size_y)% n; 66 | prev_pt = polygon[prev_idx + 1]; 67 | 68 | area += curr_pt * (next_pt - prev_pt); 69 | } 70 | 71 | return (double)abs(area / 2); 72 | } 73 | 74 | std::pair, float*> nms( 75 | const vector>& input_boxes, 76 | const double& overlapThresh, const double& neighbourThresh, 77 | const float& minScore, const int& num_neig 78 | ) 79 | { 80 | std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 81 | const int n = input_boxes.size(); 82 | const int m = input_boxes[0].size(); // 8, [x1, y1, x2, y2, x3, y3, x4, y4, score] 83 | 84 | int arr_size = n * m; 85 | float* boxes = new float[arr_size]; 86 | for (int i = 0; i < n; i++) 87 | for (int j = 0; j < m; j++) 88 | boxes[i * m + j] = input_boxes[i][j]; 89 | 90 | float* new_boxes = new float[arr_size]; 91 | vector pick; 92 | bool* suppressed = new bool[n] {false}; 93 | 94 | double* areas = new double[n]; 95 | float ref_polygon[4 * 2]; 96 | for (int i = 0; i < n; i++) // calculate area of each quadrilateral 97 | { 98 | for (int j = 0; j < 8; j++) 99 | ref_polygon[j] = boxes[i * m + j]; 100 | 101 | areas[i] = area(ref_polygon, 4, 2); 102 | } 103 | 104 | Path* polygons = new Path[n]; 105 | Path ref_path(4); 106 | cInt* ref_scaled_polygon = new cInt[4 * 2]; 107 | for (int i = 0; i < n; i++) // scale each quadrilateral to integer values 108 | { 109 | for (int j = 0; j < 8; j++) 110 | ref_scaled_polygon[j] = (cInt)((double)(boxes[i * m + j] * std::pow(2.0, 31))); 111 | for (int j = 0; j < 4; j++) 112 | { 113 | ref_path[j].X = ref_scaled_polygon[j*2+0]; 114 | ref_path[j].Y = ref_scaled_polygon[j*2+1]; 115 | } 116 | polygons[i] = ref_path; 117 | } 118 | 119 | arr_size = n * 2; 120 | double* centers = new double[arr_size]; 121 | double* sides = new double[arr_size]; 122 | cInt ref_coords_x[4]; 123 | cInt ref_coords_y[4]; 124 | double max_x, min_x, max_y, min_y; 125 | for (int i = 0; i < n; i++) // calculate centers and bboxes of quadrilaterals 126 | { 127 | ref_path = polygons[i]; 128 | centers[i * 2 + 0] = (ref_path[0].X + ref_path[1].X + ref_path[2].X + ref_path[3].X) / 4.0; 129 | centers[i * 2 + 1] = (ref_path[0].Y + ref_path[1].Y + ref_path[2].Y + ref_path[3].Y) / 4.0; 130 | for (int j = 0; j < 4; j++) 131 | { 132 | ref_coords_x[j] = ref_path[j].X; 133 | ref_coords_y[j] = ref_path[j].Y; 134 | } 135 | max_x = *std::max_element(ref_coords_x, ref_coords_x + 4); 136 | min_x = *std::min_element(ref_coords_x, ref_coords_x + 4); 137 | max_y = *std::max_element(ref_coords_y, ref_coords_y + 4); 138 | min_y = *std::min_element(ref_coords_y, ref_coords_y + 4); 139 | 140 | sides[i * 2 + 0] = max_x - min_x; 141 | sides[i * 2 + 1] = max_y - min_y; 142 | } 143 | 144 | float* order_floats = new float[n]; 145 | int* order_ints = new int[n]; 146 | for(int i =0; i(end - begin).count()) / 1000000.0 << std::endl; 156 | begin = std::chrono::steady_clock::now(); 157 | Clipper c; 158 | Paths solutions; 159 | arr_size = n * 8; 160 | float* box_to_agg = new float[arr_size]; 161 | int* neighbours = new int[n]; 162 | int neigh_cnt = 0; 163 | for (int _i = 0; _i < n; _i++) 164 | { 165 | if (_i % 1000 == 0) 166 | std::cout << "nms iter " << _i << std::endl; 167 | int i = order_ints[_i]; 168 | if (not suppressed[i]) 169 | { 170 | pick.push_back(i); 171 | for (int _j = _i + 1; _j < n; _j++) { 172 | int j = order_ints[_j]; 173 | bool var_x = ((sides[i * 2 + 0] + sides[j * 2 + 0]) / 2 - abs(centers[i * 2 + 0] - centers[j * 2 + 0])) > 0; 174 | bool var_y = ((sides[i * 2 + 1] + sides[j * 2 + 1]) / 2 - abs(centers[i * 2 + 1] - centers[j * 2 + 1])) > 0; 175 | if (var_x and var_y and (not suppressed[i])) 176 | { 177 | c.AddPath(polygons[i], ptClip, true); 178 | c.AddPath(polygons[j], ptSubject, true); 179 | c.Execute(ctIntersection, solutions, pftNonZero, pftNonZero); 180 | double inter; 181 | if (solutions.size() > 0) 182 | { 183 | inter = Area(solutions[0]) / std::pow(4.0, 31); 184 | c.Clear(); 185 | } 186 | else 187 | inter = 0.0; 188 | double union_ = (areas[i] + areas[j]) - inter; 189 | double iou = (union_ > 0.0) ? inter / union_ : 0.0; 190 | if (union_ > 0.0 and iou > overlapThresh) 191 | suppressed[j] = true; 192 | if (iou > neighbourThresh) 193 | { 194 | neighbours[neigh_cnt] = j; 195 | neigh_cnt += 1; 196 | } 197 | } 198 | } 199 | if (neigh_cnt >= num_neig) 200 | { 201 | neighbours[neigh_cnt] = i; 202 | neigh_cnt += 1; 203 | double temp_scores_sum = 0.0; 204 | for (int idx = 0; idx < neigh_cnt; idx++) // mul words NN scores by temp score 205 | { 206 | double temp_score = boxes[neighbours[idx] * m + 8] - minScore; 207 | temp_scores_sum += temp_score; 208 | for (int jdx = 0; jdx < 8; jdx++) 209 | box_to_agg[idx * 8 + jdx] = boxes[neighbours[idx] * m + jdx] * temp_score; 210 | } 211 | for (int idx = 0; idx < 8; idx++) // mul coords by temp score 212 | { 213 | double col_sum = 0.0; 214 | for (int jdx = 0; jdx < neigh_cnt; jdx++) 215 | col_sum += box_to_agg[jdx * 8 + idx]; 216 | col_sum /= temp_scores_sum; 217 | new_boxes[i * m + idx] = col_sum; 218 | } 219 | new_boxes[i * m + 8] = boxes[i * m + 8]; 220 | } 221 | else 222 | { 223 | for (int idx = 0; idx < neigh_cnt; idx++) 224 | suppressed[neighbours[idx]] = false; 225 | pick.pop_back(); 226 | } 227 | neigh_cnt = 0; 228 | } 229 | } 230 | std::pair, float*> result = std::make_pair(pick, new_boxes); 231 | 232 | delete[] boxes; 233 | delete[] suppressed; 234 | delete[] areas; 235 | delete[] polygons; 236 | delete[] ref_scaled_polygon; 237 | delete[] centers; 238 | delete[] sides; 239 | delete[] order_floats; 240 | delete[] order_ints; 241 | delete[] box_to_agg; 242 | 243 | end = std::chrono::steady_clock::now(); 244 | std::cout << "Time loop nms: " << (std::chrono::duration_cast(end - begin).count()) / 1000000.0 << std::endl; 245 | 246 | 247 | return result; 248 | } 249 | 250 | void testNms(std::string boxes_f_name="nms_boxes.txt") 251 | { 252 | std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 253 | vector vecOfStr; 254 | bool result = getFileContent(boxes_f_name, vecOfStr); 255 | vector> bboxes_test(vecOfStr.size(), vector(9)); 256 | if (result) 257 | { 258 | for (int i = 0; i < bboxes_test.size(); i++) 259 | { 260 | vector splitted = split(vecOfStr[i], ','); 261 | for (int j = 0; j < splitted.size(); j++) 262 | bboxes_test[i][j] = ::atof(splitted[j].c_str()); 263 | } 264 | } 265 | const int n = bboxes_test.size(); 266 | const int m = bboxes_test[0].size(); 267 | 268 | std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 269 | std::cout << "Time preparing data: " << (std::chrono::duration_cast(end - begin).count()) / 1000000.0 << std::endl; 270 | 271 | begin = std::chrono::steady_clock::now(); 272 | std::pair, float*> res = nms(bboxes_test, 0.15, 0.5, 0.0, 1); 273 | end = std::chrono::steady_clock::now(); 274 | std::cout << "Time nms: " << (std::chrono::duration_cast(end - begin).count()) / 1000000.0 << std::endl; 275 | 276 | std::cout << "Printing 5 data pts" << std::endl; 277 | std::cout << "Picks" << std::endl; 278 | for (int i = 0; i < 5; i++) 279 | std::cout << res.first[i] << ' '; 280 | std::cout << std::endl; 281 | std::cout << "Boxes" << std::endl; 282 | for (int i = 0; i < 5; i++) 283 | { 284 | int idx = res.first[i]; 285 | std::cout << "i: " << i; 286 | for (int j = 0; j < m; j++) 287 | std::cout << ' ' << std::setprecision(5) << res.second[idx * m + j] << ' '; 288 | std::cout << std::endl; 289 | } 290 | delete[] res.second; 291 | } 292 | 293 | int main(int argc, char* argv[]) 294 | { 295 | std::cout << "Starting nms" << std::endl; 296 | testNms(); 297 | std::cout << std::endl; 298 | } --------------------------------------------------------------------------------