├── CMakeLists.txt ├── CMakeLists_deepsort-tensorrt_win10.txt ├── LICENSE ├── README.md ├── demo.cpp ├── exportOnnx.py ├── include ├── datatype.h ├── deepsort.h ├── deepsortenginegenerator.h ├── featuretensor.h ├── hungarianoper.h ├── kalmanfilter.h ├── linear_assignment.h ├── logging.h ├── matrix.h ├── model.hpp ├── munkres.h ├── nn_matching.h ├── track.h └── tracker.h ├── onnx2engine.cpp ├── resources └── track.txt └── src ├── deepsort.cpp ├── deepsortenginegenerator.cpp ├── featuretensor.cpp ├── hungarianoper.cpp ├── kalmanfilter.cpp ├── linear_assignment.cpp ├── munkres.cpp ├── nn_matching.cpp ├── track.cpp └── tracker.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(deepsort) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | set(CMAKE_BUILD_TYPE Release) 6 | 7 | find_package(CUDA REQUIRED) 8 | find_package(OpenCV REQUIRED) 9 | 10 | 11 | include_directories( 12 | ${CUDA_INCLUDE_DIRS} 13 | ${OpenCV_INCLUDE_DIRS} 14 | ${PROJECT_SOURCE_DIR}/include 15 | ) 16 | link_directories( 17 | /usr/local/cuda/lib64 18 | ) 19 | aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC_DIR) 20 | 21 | # ===== deepsort ===== 22 | add_library(deepsort SHARED ${SRC_DIR}) 23 | target_link_libraries(deepsort 24 | ${CUDA_LIBS} ${OpenCV_LIBS} 25 | cudart nvinfer nvonnxparser 26 | ) 27 | 28 | # ===== onnx2engine ===== 29 | add_executable(onnx2engine ${PROJECT_SOURCE_DIR}/onnx2engine.cpp) 30 | target_link_libraries(onnx2engine 31 | ${CUDA_LIBS} 32 | cudart nvinfer nvonnxparser deepsort 33 | ) 34 | 35 | # ===== demo ===== 36 | add_executable(demo ${PROJECT_SOURCE_DIR}/demo.cpp) 37 | target_link_libraries(demo 38 | ${CUDA_LIBS} ${OpenCV_LIBS} 39 | cudart nvinfer nvonnxparser deepsort 40 | ) 41 | 42 | 43 | -------------------------------------------------------------------------------- /CMakeLists_deepsort-tensorrt_win10.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(deepsort) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | set(CMAKE_BUILD_TYPE Release) 6 | set(CUDA_BIN_PATH C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.1) #1.修改为自己的cuda路径 7 | set(TRT_DIR "D:\\lbq\\TensorRT-7.2.3.4") #2.修改为自己的tensorRT路径 8 | set(TRT_INCLUDE_DIRS ${TRT_DIR}\\include) #3.修改为自己的tensorRT头文件路径 9 | set(TRT_LIB_DIRS ${TRT_DIR}\\lib) #4.修改为自己的tensorRT库文件路径 10 | set(Eigen3_PATH D:\\lbq\\eigen) #5.修改为自己的Eigen路径 11 | option(CUDA_USE_STATIC_CUDA_RUNTIME OFF) 12 | find_package(OpenCV REQUIRED) 13 | find_package(CUDA REQUIRED) 14 | 15 | 16 | enable_language(CUDA) 17 | include_directories( 18 | ${CUDA_INCLUDE_DIRS} 19 | ${OpenCV_INCLUDE_DIRS} 20 | ${TRT_INCLUDE_DIRS} 21 | ${Eigen3_PATH} 22 | ${PROJECT_SOURCE_DIR}/include 23 | ) 24 | 25 | # tensorRT 26 | link_directories(${TRT_LIB_DIRS}) 27 | link_directories(${OpenCV_LIB_DIRS}) 28 | 29 | aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC_DIR) 30 | 31 | # ===== deepsort ===== 32 | add_library(deepsort STATIC ${SRC_DIR}) 33 | target_link_libraries(deepsort 34 | ${CUDA_LIBRARIES} ${OpenCV_LIBS} 35 | cudart nvinfer nvonnxparser 36 | ) 37 | 38 | # ===== onnx2engine ===== 39 | add_executable(onnx2engine ${PROJECT_SOURCE_DIR}/onnx2engine.cpp) 40 | target_link_libraries(onnx2engine 41 | ${CUDA_LIBRARIES} 42 | cudart nvinfer nvonnxparser deepsort 43 | ) 44 | 45 | # ===== demo ===== 46 | add_executable(demo ${PROJECT_SOURCE_DIR}/demo.cpp) 47 | target_link_libraries(demo 48 | ${CUDA_LIBRARIES} ${OpenCV_LIBS} 49 | cudart nvinfer nvonnxparser deepsort 50 | ) 51 | 52 | 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeepSort_TensorRT 2 | Use tensorrt to accelerate deepsort tracking. 3 | 使用tensorrt来加速deepsort的特征提取。 4 | ## Install 5 | [REQUIRE] TensorRT 7

6 | [REQUIRE] OpenCV

7 | 下载本库和deepsort的pytorch库 8 | ``` 9 | git clone git@github.com:ZQPei/deep_sort_pytorch.git 10 | git clone https://github.com/RichardoMrMu/deepsort-tensorrt.git 11 | ``` 12 | 配置好pytorch工程后,将exportOnnx.py复制到pytorch工程的项目目录下运行,将会生成deepsort.onnx。

13 | 把deepsort.onnx移到本工程的resources目录下。然后回到工程的根目录

14 | ``` 15 | mkdir build 16 | cd build 17 | cmake .. 18 | make 19 | ./onnx2engine ../resources/deepsort.onnx ../resources/deepsort.engine 20 | ``` 21 | 然后就可以运行demo测试了

22 | ``` 23 | ./demo ../resources/deepsort.engine ../resources/track.txt 24 | ``` 25 | 然后就可以把自己TensorRT的目标检测工程进行对接。 26 | ``` 27 | git clone https://github.com/wang-xinyu/tensorrtx.git 28 | ``` 29 | 注:视频文件太大,所以只把检测结果保存成txt进行测试。 30 | -------------------------------------------------------------------------------- /demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "deepsort.h" 6 | #include "logging.h" 7 | #include 8 | 9 | using std::vector; 10 | 11 | static Logger gLogger; 12 | 13 | void showDetection(cv::Mat& img, std::vector& boxes) { 14 | cv::Mat temp = img.clone(); 15 | for (auto box : boxes) { 16 | cv::Point lt(box.x1, box.y1); 17 | cv::Point br(box.x2, box.y2); 18 | cv::rectangle(temp, lt, br, cv::Scalar(255, 0, 0), 1); 19 | std::string lbl = cv::format("ID:%d_C:%d_CONF:%.2f", (int)box.trackID, (int)box.classID, box.confidence); 20 | cv::putText(temp, lbl, lt, cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0,255,0)); 21 | } 22 | cv::imshow("img", temp); 23 | cv::waitKey(1); 24 | } 25 | 26 | class Tester { 27 | public: 28 | Tester(string modelPath) { 29 | allDetections.clear(); 30 | out.clear(); 31 | DS = new DeepSort(modelPath, 128, 256, 0, &gLogger); 32 | } 33 | ~Tester() { 34 | } 35 | 36 | public: 37 | 38 | void split(const std::string& s, vector& token, char delim=' ') { 39 | token.clear(); 40 | auto string_find_first_not = [s, delim](size_t pos = 0) -> size_t { 41 | for (size_t i = pos; i < s.size(); ++i) 42 | if (s[i] != delim) return i; 43 | return string::npos; 44 | }; 45 | size_t lastPos = string_find_first_not(0); 46 | size_t pos = s.find(delim, lastPos); 47 | while (lastPos != string::npos) { 48 | token.emplace_back(s.substr(lastPos, pos-lastPos)); 49 | lastPos = string_find_first_not(pos); 50 | pos = s.find(delim, lastPos); 51 | } 52 | } 53 | 54 | void loadDetections(std::string txtPath) { 55 | //fstream f(filePath, ios::in); 56 | this->txtPath = txtPath; 57 | ifstream inFile; 58 | inFile.open(txtPath, ios::binary); 59 | std::string temp; 60 | vector token; 61 | while (getline(inFile, temp)) { 62 | // std::cout << temp << std::endl; 63 | split(temp, token, ' '); 64 | int frame = atoi(token[0].c_str()); 65 | int c = atoi(token[1].c_str()); 66 | int x = atoi(token[2].c_str()); 67 | int y = atoi(token[3].c_str()); 68 | int w = atoi(token[4].c_str()); 69 | int h = atoi(token[5].c_str()); 70 | float con= atof(token[6].c_str()); 71 | while (allDetections.size() <= frame) { 72 | vector t; 73 | allDetections.push_back(t); 74 | } 75 | DetectBox dd(x-w/2, y-h/2, x+w/2, y+h/2, con, c); 76 | allDetections[frame].push_back(dd); 77 | } 78 | allDetections.pop_back(); 79 | } 80 | 81 | void run() { 82 | cv::namedWindow("DeepSortTest"); 83 | int i = 1; 84 | cv::Mat whiteBoard(1080, 1920, CV_8UC3, cv::Scalar::all(0)); 85 | for (vector d : allDetections) { 86 | cv::Mat img_rgb; 87 | cv::cvtColor(whiteBoard, img_rgb, cv::COLOR_BGR2RGB); 88 | 89 | DS->sort(img_rgb, d); 90 | 91 | showDetection(whiteBoard, d); 92 | } 93 | cv::destroyAllWindows(); 94 | } 95 | 96 | private: 97 | vector> allDetections; 98 | vector out; 99 | std::string txtPath; 100 | DeepSort* DS; 101 | }; 102 | 103 | int main(int argc, char** argv) { 104 | if (argc < 3) { 105 | std::cout << "./demo [input model path] [input txt path]" << std::endl; 106 | return -1; 107 | } 108 | Tester* test = new Tester(argv[1]); 109 | test->loadDetections(argv[2]); 110 | test->run(); 111 | } 112 | -------------------------------------------------------------------------------- /exportOnnx.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import cv2 4 | import time 5 | import argparse 6 | import torch 7 | import numpy as np 8 | 9 | from deep_sort import build_tracker 10 | from utils.draw import draw_boxes 11 | from utils.parser import get_config 12 | 13 | from tqdm import tqdm 14 | 15 | if __name__ == '__main__': 16 | parser = argparse.ArgumentParser() 17 | parser.add_argument("--config_deepsort", type=str, default="./configs/deep_sort.yaml", help='Configure tracker') 18 | parser.add_argument("--cpu", dest="use_cuda", action="store_false", default=True, help='Run in CPU') 19 | args = parser.parse_args() 20 | 21 | cfg = get_config() 22 | cfg.merge_from_file(args.config_deepsort) 23 | use_cuda = args.use_cuda and torch.cuda.is_available() 24 | torch.set_grad_enabled(False) 25 | model = build_tracker(cfg, use_cuda=False) 26 | 27 | model.reid = True 28 | model.extractor.net.eval() 29 | 30 | device = 'cuda' 31 | output_onnx = 'deepsort.onnx' 32 | # ------------------------ export ----------------------------- 33 | print("==> Exporting model to ONNX format at '{}'".format(output_onnx)) 34 | input_names = ['input'] 35 | output_names = ['output'] 36 | 37 | input_tensor = torch.randn(1, 3, 128, 64, device=device) 38 | 39 | torch.onnx.export(model.extractor.net.cuda(), input_tensor, output_onnx, export_params=True, verbose=False, 40 | input_names=input_names, output_names=output_names, opset_version=10, 41 | do_constant_folding=True, 42 | dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}) 43 | -------------------------------------------------------------------------------- /include/datatype.h: -------------------------------------------------------------------------------- 1 | #ifndef DATATYPE_H 2 | #define DATATYPE_H 3 | 4 | typedef struct DetectBox { 5 | DetectBox(float x1=0, float y1=0, float x2=0, float y2=0, 6 | float confidence=0, float classID=-1, float trackID=-1) { 7 | this->x1 = x1; 8 | this->y1 = y1; 9 | this->x2 = x2; 10 | this->y2 = y2; 11 | this->confidence = confidence; 12 | this->classID = classID; 13 | this->trackID = trackID; 14 | } 15 | float x1, y1, x2, y2; 16 | float confidence; 17 | float classID; 18 | float trackID; 19 | } DetectBox; 20 | 21 | #endif // DATATYPE_H 22 | 23 | #ifndef DEEPSORTDATATYPE_H 24 | #define DEEPSORTDATATYPE_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | typedef struct CLSCONF { 31 | CLSCONF() { 32 | this->cls = -1; 33 | this->conf = -1; 34 | } 35 | CLSCONF(int cls, float conf) { 36 | this->cls = cls; 37 | this->conf = conf; 38 | } 39 | int cls; 40 | float conf; 41 | } CLSCONF; 42 | 43 | typedef Eigen::Matrix DETECTBOX; 44 | typedef Eigen::Matrix DETECTBOXSS; 45 | typedef Eigen::Matrix FEATURE; 46 | typedef Eigen::Matrix FEATURESS; 47 | //typedef std::vector FEATURESS; 48 | 49 | //Kalmanfilter 50 | //typedef Eigen::Matrix KAL_FILTER; 51 | typedef Eigen::Matrix KAL_MEAN; 52 | typedef Eigen::Matrix KAL_COVA; 53 | typedef Eigen::Matrix KAL_HMEAN; 54 | typedef Eigen::Matrix KAL_HCOVA; 55 | using KAL_DATA = std::pair; 56 | using KAL_HDATA = std::pair; 57 | 58 | //main 59 | using RESULT_DATA = std::pair; 60 | 61 | //tracker: 62 | using TRACKER_DATA = std::pair; 63 | using MATCH_DATA = std::pair; 64 | typedef struct t{ 65 | std::vector matches; 66 | std::vector unmatched_tracks; 67 | std::vector unmatched_detections; 68 | }TRACHER_MATCHD; 69 | 70 | //linear_assignment: 71 | typedef Eigen::Matrix DYNAMICM; 72 | 73 | #endif //DEEPSORTDATATYPE_H -------------------------------------------------------------------------------- /include/deepsort.h: -------------------------------------------------------------------------------- 1 | #ifndef DEEPSORT_H 2 | #define DEEPSORT_H 3 | 4 | #include 5 | #include 6 | #include "featuretensor.h" 7 | #include "tracker.h" 8 | #include "datatype.h" 9 | #include 10 | 11 | using std::vector; 12 | using nvinfer1::ILogger; 13 | 14 | class DeepSort { 15 | public: 16 | DeepSort(std::string modelPath, int batchSize, int featureDim, int gpuID, ILogger* gLogger); 17 | ~DeepSort(); 18 | 19 | public: 20 | void sort(cv::Mat& frame, vector& dets); 21 | 22 | private: 23 | void sort(cv::Mat& frame, DETECTIONS& detections); 24 | void sort(cv::Mat& frame, DETECTIONSV2& detectionsv2); 25 | void sort(vector& dets); 26 | void sort(DETECTIONS& detections); 27 | void init(); 28 | 29 | private: 30 | std::string enginePath; 31 | int batchSize; 32 | int featureDim; 33 | cv::Size imgShape; 34 | float confThres; 35 | float nmsThres; 36 | int maxBudget; 37 | float maxCosineDist; 38 | 39 | private: 40 | vector result; 41 | vector> results; 42 | tracker* objTracker; 43 | FeatureTensor* featureExtractor; 44 | ILogger* gLogger; 45 | int gpuID; 46 | }; 47 | 48 | #endif //deepsort.h 49 | -------------------------------------------------------------------------------- /include/deepsortenginegenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef DEEPSORT_ENGINE_GENERATOR_H 2 | #define DEEPSORT_ENGINE_GENERATOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace nvinfer1; 9 | 10 | const int IMG_HEIGHT = 128; 11 | const int IMG_WIDTH = 64; 12 | const int MAX_BATCH_SIZE = 128; 13 | const std::string INPUT_NAME("input"); 14 | 15 | class DeepSortEngineGenerator { 16 | public: 17 | DeepSortEngineGenerator(ILogger* gLogger); 18 | ~DeepSortEngineGenerator(); 19 | 20 | public: 21 | void setFP16(bool state); 22 | void createEngine(std::string onnxPath, std::string enginePath); 23 | 24 | private: 25 | std::string modelPath, engingPath; 26 | ILogger* gLogger; 27 | bool useFP16; 28 | }; 29 | 30 | #endif -------------------------------------------------------------------------------- /include/featuretensor.h: -------------------------------------------------------------------------------- 1 | #ifndef FEATURETENSOR_H 2 | #define FEATURETENSOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "model.hpp" 10 | #include "datatype.h" 11 | #include "cuda_runtime_api.h" 12 | 13 | using std::vector; 14 | using nvinfer1::ILogger; 15 | 16 | class FeatureTensor { 17 | public: 18 | FeatureTensor(const int maxBatchSize, const cv::Size imgShape, const int featureDim, int gpuID, ILogger* gLogger); 19 | ~FeatureTensor(); 20 | 21 | public: 22 | bool getRectsFeature(const cv::Mat& img, DETECTIONS& det); 23 | bool getRectsFeature(DETECTIONS& det); 24 | void loadEngine(std::string enginePath); 25 | void loadOnnx(std::string onnxPath); 26 | int getResult(float*& buffer); 27 | void doInference(vector& imgMats); 28 | 29 | private: 30 | void initResource(); 31 | void doInference(float* inputBuffer, float* outputBuffer); 32 | void mat2stream(vector& imgMats, float* stream); 33 | void stream2det(float* stream, DETECTIONS& det); 34 | 35 | private: 36 | nvinfer1::IRuntime* runtime; 37 | nvinfer1::ICudaEngine* engine; 38 | nvinfer1::IExecutionContext* context; 39 | const int maxBatchSize; 40 | const cv::Size imgShape; 41 | const int featureDim; 42 | 43 | private: 44 | int curBatchSize; 45 | const int inputStreamSize, outputStreamSize; 46 | bool initFlag; 47 | float* const inputBuffer; 48 | float* const outputBuffer; 49 | int inputIndex, outputIndex; 50 | void* buffers[2]; 51 | cudaStream_t cudaStream; 52 | // BGR format 53 | float means[3], std[3]; 54 | const std::string inputName, outputName; 55 | ILogger* gLogger; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/hungarianoper.h: -------------------------------------------------------------------------------- 1 | #ifndef HUNGARIANOPER_H 2 | #define HUNGARIANOPER_H 3 | 4 | #include "munkres.h" 5 | #include "datatype.h" 6 | 7 | 8 | class HungarianOper { 9 | public: 10 | static Eigen::Matrix Solve(const DYNAMICM &cost_matrix); 11 | }; 12 | 13 | #endif // HUNGARIANOPER_H 14 | -------------------------------------------------------------------------------- /include/kalmanfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef KALMANFILTER_H 2 | #define KALMANFILTER_H 3 | 4 | #include "datatype.h" 5 | 6 | class KalmanFilter { 7 | public: 8 | static const double chi2inv95[10]; 9 | KalmanFilter(); 10 | KAL_DATA initiate(const DETECTBOX& measurement); 11 | void predict(KAL_MEAN& mean, KAL_COVA& covariance); 12 | KAL_HDATA project(const KAL_MEAN& mean, const KAL_COVA& covariance); 13 | KAL_DATA update(const KAL_MEAN& mean, 14 | const KAL_COVA& covariance, 15 | const DETECTBOX& measurement); 16 | 17 | Eigen::Matrix gating_distance( 18 | const KAL_MEAN& mean, 19 | const KAL_COVA& covariance, 20 | const std::vector& measurements, 21 | bool only_position = false); 22 | 23 | private: 24 | Eigen::Matrix _motion_mat; 25 | Eigen::Matrix _update_mat; 26 | float _std_weight_position; 27 | float _std_weight_velocity; 28 | }; 29 | 30 | #endif // KALMANFILTER_H 31 | -------------------------------------------------------------------------------- /include/linear_assignment.h: -------------------------------------------------------------------------------- 1 | #ifndef LINEAR_ASSIGNMENT_H 2 | #define LINEAR_ASSIGNMENT_H 3 | 4 | #include "datatype.h" 5 | #include "tracker.h" 6 | 7 | #define INFTY_COST 1e5 8 | class tracker; 9 | //for matching; 10 | class linear_assignment 11 | { 12 | linear_assignment(); 13 | linear_assignment(const linear_assignment& ); 14 | linear_assignment& operator=(const linear_assignment&); 15 | static linear_assignment* instance; 16 | 17 | public: 18 | static linear_assignment* getInstance(); 19 | TRACHER_MATCHD matching_cascade(tracker* distance_metric, 20 | tracker::GATED_METRIC_FUNC distance_metric_func, 21 | float max_distance, 22 | int cascade_depth, 23 | std::vector& tracks, 24 | const DETECTIONS& detections, 25 | std::vector &track_indices, 26 | std::vector detection_indices = std::vector()); 27 | TRACHER_MATCHD min_cost_matching( 28 | tracker* distance_metric, 29 | tracker::GATED_METRIC_FUNC distance_metric_func, 30 | float max_distance, 31 | std::vector& tracks, 32 | const DETECTIONS& detections, 33 | std::vector& track_indices, 34 | std::vector& detection_indices); 35 | DYNAMICM gate_cost_matrix( 36 | KalmanFilter* kf, 37 | DYNAMICM& cost_matrix, 38 | std::vector& tracks, 39 | const DETECTIONS& detections, 40 | const std::vector& track_indices, 41 | const std::vector& detection_indices, 42 | float gated_cost = INFTY_COST, 43 | bool only_position = false); 44 | }; 45 | 46 | #endif // LINEAR_ASSIGNMENT_H 47 | -------------------------------------------------------------------------------- /include/logging.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef TENSORRT_LOGGING_H 18 | #define TENSORRT_LOGGING_H 19 | 20 | #include "NvInferRuntimeCommon.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using Severity = nvinfer1::ILogger::Severity; 30 | 31 | class LogStreamConsumerBuffer : public std::stringbuf 32 | { 33 | public: 34 | LogStreamConsumerBuffer(std::ostream& stream, const std::string& prefix, bool shouldLog) 35 | : mOutput(stream) 36 | , mPrefix(prefix) 37 | , mShouldLog(shouldLog) 38 | { 39 | } 40 | 41 | LogStreamConsumerBuffer(LogStreamConsumerBuffer&& other) 42 | : mOutput(other.mOutput) 43 | { 44 | } 45 | 46 | ~LogStreamConsumerBuffer() 47 | { 48 | // std::streambuf::pbase() gives a pointer to the beginning of the buffered part of the output sequence 49 | // std::streambuf::pptr() gives a pointer to the current position of the output sequence 50 | // if the pointer to the beginning is not equal to the pointer to the current position, 51 | // call putOutput() to log the output to the stream 52 | if (pbase() != pptr()) 53 | { 54 | putOutput(); 55 | } 56 | } 57 | 58 | // synchronizes the stream buffer and returns 0 on success 59 | // synchronizing the stream buffer consists of inserting the buffer contents into the stream, 60 | // resetting the buffer and flushing the stream 61 | virtual int sync() 62 | { 63 | putOutput(); 64 | return 0; 65 | } 66 | 67 | void putOutput() 68 | { 69 | if (mShouldLog) 70 | { 71 | // prepend timestamp 72 | std::time_t timestamp = std::time(nullptr); 73 | tm* tm_local = std::localtime(×tamp); 74 | std::cout << "["; 75 | std::cout << std::setw(2) << std::setfill('0') << 1 + tm_local->tm_mon << "/"; 76 | std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_mday << "/"; 77 | std::cout << std::setw(4) << std::setfill('0') << 1900 + tm_local->tm_year << "-"; 78 | std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_hour << ":"; 79 | std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_min << ":"; 80 | std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_sec << "] "; 81 | // std::stringbuf::str() gets the string contents of the buffer 82 | // insert the buffer contents pre-appended by the appropriate prefix into the stream 83 | mOutput << mPrefix << str(); 84 | // set the buffer to empty 85 | str(""); 86 | // flush the stream 87 | mOutput.flush(); 88 | } 89 | } 90 | 91 | void setShouldLog(bool shouldLog) 92 | { 93 | mShouldLog = shouldLog; 94 | } 95 | 96 | private: 97 | std::ostream& mOutput; 98 | std::string mPrefix; 99 | bool mShouldLog; 100 | }; 101 | 102 | //! 103 | //! \class LogStreamConsumerBase 104 | //! \brief Convenience object used to initialize LogStreamConsumerBuffer before std::ostream in LogStreamConsumer 105 | //! 106 | class LogStreamConsumerBase 107 | { 108 | public: 109 | LogStreamConsumerBase(std::ostream& stream, const std::string& prefix, bool shouldLog) 110 | : mBuffer(stream, prefix, shouldLog) 111 | { 112 | } 113 | 114 | protected: 115 | LogStreamConsumerBuffer mBuffer; 116 | }; 117 | 118 | //! 119 | //! \class LogStreamConsumer 120 | //! \brief Convenience object used to facilitate use of C++ stream syntax when logging messages. 121 | //! Order of base classes is LogStreamConsumerBase and then std::ostream. 122 | //! This is because the LogStreamConsumerBase class is used to initialize the LogStreamConsumerBuffer member field 123 | //! in LogStreamConsumer and then the address of the buffer is passed to std::ostream. 124 | //! This is necessary to prevent the address of an uninitialized buffer from being passed to std::ostream. 125 | //! Please do not change the order of the parent classes. 126 | //! 127 | class LogStreamConsumer : protected LogStreamConsumerBase, public std::ostream 128 | { 129 | public: 130 | //! \brief Creates a LogStreamConsumer which logs messages with level severity. 131 | //! Reportable severity determines if the messages are severe enough to be logged. 132 | LogStreamConsumer(Severity reportableSeverity, Severity severity) 133 | : LogStreamConsumerBase(severityOstream(severity), severityPrefix(severity), severity <= reportableSeverity) 134 | , std::ostream(&mBuffer) // links the stream buffer with the stream 135 | , mShouldLog(severity <= reportableSeverity) 136 | , mSeverity(severity) 137 | { 138 | } 139 | 140 | LogStreamConsumer(LogStreamConsumer&& other) 141 | : LogStreamConsumerBase(severityOstream(other.mSeverity), severityPrefix(other.mSeverity), other.mShouldLog) 142 | , std::ostream(&mBuffer) // links the stream buffer with the stream 143 | , mShouldLog(other.mShouldLog) 144 | , mSeverity(other.mSeverity) 145 | { 146 | } 147 | 148 | void setReportableSeverity(Severity reportableSeverity) 149 | { 150 | mShouldLog = mSeverity <= reportableSeverity; 151 | mBuffer.setShouldLog(mShouldLog); 152 | } 153 | 154 | private: 155 | static std::ostream& severityOstream(Severity severity) 156 | { 157 | return severity >= Severity::kINFO ? std::cout : std::cerr; 158 | } 159 | 160 | static std::string severityPrefix(Severity severity) 161 | { 162 | switch (severity) 163 | { 164 | case Severity::kINTERNAL_ERROR: return "[F] "; 165 | case Severity::kERROR: return "[E] "; 166 | case Severity::kWARNING: return "[W] "; 167 | case Severity::kINFO: return "[I] "; 168 | case Severity::kVERBOSE: return "[V] "; 169 | default: assert(0); return ""; 170 | } 171 | } 172 | 173 | bool mShouldLog; 174 | Severity mSeverity; 175 | }; 176 | 177 | //! \class Logger 178 | //! 179 | //! \brief Class which manages logging of TensorRT tools and samples 180 | //! 181 | //! \details This class provides a common interface for TensorRT tools and samples to log information to the console, 182 | //! and supports logging two types of messages: 183 | //! 184 | //! - Debugging messages with an associated severity (info, warning, error, or internal error/fatal) 185 | //! - Test pass/fail messages 186 | //! 187 | //! The advantage of having all samples use this class for logging as opposed to emitting directly to stdout/stderr is 188 | //! that the logic for controlling the verbosity and formatting of sample output is centralized in one location. 189 | //! 190 | //! In the future, this class could be extended to support dumping test results to a file in some standard format 191 | //! (for example, JUnit XML), and providing additional metadata (e.g. timing the duration of a test run). 192 | //! 193 | //! TODO: For backwards compatibility with existing samples, this class inherits directly from the nvinfer1::ILogger 194 | //! interface, which is problematic since there isn't a clean separation between messages coming from the TensorRT 195 | //! library and messages coming from the sample. 196 | //! 197 | //! In the future (once all samples are updated to use Logger::getTRTLogger() to access the ILogger) we can refactor the 198 | //! class to eliminate the inheritance and instead make the nvinfer1::ILogger implementation a member of the Logger 199 | //! object. 200 | 201 | class Logger : public nvinfer1::ILogger 202 | { 203 | public: 204 | Logger(Severity severity = Severity::kWARNING) 205 | : mReportableSeverity(severity) 206 | { 207 | } 208 | 209 | //! 210 | //! \enum TestResult 211 | //! \brief Represents the state of a given test 212 | //! 213 | enum class TestResult 214 | { 215 | kRUNNING, //!< The test is running 216 | kPASSED, //!< The test passed 217 | kFAILED, //!< The test failed 218 | kWAIVED //!< The test was waived 219 | }; 220 | 221 | //! 222 | //! \brief Forward-compatible method for retrieving the nvinfer::ILogger associated with this Logger 223 | //! \return The nvinfer1::ILogger associated with this Logger 224 | //! 225 | //! TODO Once all samples are updated to use this method to register the logger with TensorRT, 226 | //! we can eliminate the inheritance of Logger from ILogger 227 | //! 228 | nvinfer1::ILogger& getTRTLogger() 229 | { 230 | return *this; 231 | } 232 | 233 | //! 234 | //! \brief Implementation of the nvinfer1::ILogger::log() virtual method 235 | //! 236 | //! Note samples should not be calling this function directly; it will eventually go away once we eliminate the 237 | //! inheritance from nvinfer1::ILogger 238 | //! 239 | void log(Severity severity, const char* msg) override 240 | { 241 | LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl; 242 | } 243 | 244 | //! 245 | //! \brief Method for controlling the verbosity of logging output 246 | //! 247 | //! \param severity The logger will only emit messages that have severity of this level or higher. 248 | //! 249 | void setReportableSeverity(Severity severity) 250 | { 251 | mReportableSeverity = severity; 252 | } 253 | 254 | //! 255 | //! \brief Opaque handle that holds logging information for a particular test 256 | //! 257 | //! This object is an opaque handle to information used by the Logger to print test results. 258 | //! The sample must call Logger::defineTest() in order to obtain a TestAtom that can be used 259 | //! with Logger::reportTest{Start,End}(). 260 | //! 261 | class TestAtom 262 | { 263 | public: 264 | TestAtom(TestAtom&&) = default; 265 | 266 | private: 267 | friend class Logger; 268 | 269 | TestAtom(bool started, const std::string& name, const std::string& cmdline) 270 | : mStarted(started) 271 | , mName(name) 272 | , mCmdline(cmdline) 273 | { 274 | } 275 | 276 | bool mStarted; 277 | std::string mName; 278 | std::string mCmdline; 279 | }; 280 | 281 | //! 282 | //! \brief Define a test for logging 283 | //! 284 | //! \param[in] name The name of the test. This should be a string starting with 285 | //! "TensorRT" and containing dot-separated strings containing 286 | //! the characters [A-Za-z0-9_]. 287 | //! For example, "TensorRT.sample_googlenet" 288 | //! \param[in] cmdline The command line used to reproduce the test 289 | // 290 | //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). 291 | //! 292 | static TestAtom defineTest(const std::string& name, const std::string& cmdline) 293 | { 294 | return TestAtom(false, name, cmdline); 295 | } 296 | 297 | //! 298 | //! \brief A convenience overloaded version of defineTest() that accepts an array of command-line arguments 299 | //! as input 300 | //! 301 | //! \param[in] name The name of the test 302 | //! \param[in] argc The number of command-line arguments 303 | //! \param[in] argv The array of command-line arguments (given as C strings) 304 | //! 305 | //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). 306 | static TestAtom defineTest(const std::string& name, int argc, char const* const* argv) 307 | { 308 | auto cmdline = genCmdlineString(argc, argv); 309 | return defineTest(name, cmdline); 310 | } 311 | 312 | //! 313 | //! \brief Report that a test has started. 314 | //! 315 | //! \pre reportTestStart() has not been called yet for the given testAtom 316 | //! 317 | //! \param[in] testAtom The handle to the test that has started 318 | //! 319 | static void reportTestStart(TestAtom& testAtom) 320 | { 321 | reportTestResult(testAtom, TestResult::kRUNNING); 322 | assert(!testAtom.mStarted); 323 | testAtom.mStarted = true; 324 | } 325 | 326 | //! 327 | //! \brief Report that a test has ended. 328 | //! 329 | //! \pre reportTestStart() has been called for the given testAtom 330 | //! 331 | //! \param[in] testAtom The handle to the test that has ended 332 | //! \param[in] result The result of the test. Should be one of TestResult::kPASSED, 333 | //! TestResult::kFAILED, TestResult::kWAIVED 334 | //! 335 | static void reportTestEnd(const TestAtom& testAtom, TestResult result) 336 | { 337 | assert(result != TestResult::kRUNNING); 338 | assert(testAtom.mStarted); 339 | reportTestResult(testAtom, result); 340 | } 341 | 342 | static int reportPass(const TestAtom& testAtom) 343 | { 344 | reportTestEnd(testAtom, TestResult::kPASSED); 345 | return EXIT_SUCCESS; 346 | } 347 | 348 | static int reportFail(const TestAtom& testAtom) 349 | { 350 | reportTestEnd(testAtom, TestResult::kFAILED); 351 | return EXIT_FAILURE; 352 | } 353 | 354 | static int reportWaive(const TestAtom& testAtom) 355 | { 356 | reportTestEnd(testAtom, TestResult::kWAIVED); 357 | return EXIT_SUCCESS; 358 | } 359 | 360 | static int reportTest(const TestAtom& testAtom, bool pass) 361 | { 362 | return pass ? reportPass(testAtom) : reportFail(testAtom); 363 | } 364 | 365 | Severity getReportableSeverity() const 366 | { 367 | return mReportableSeverity; 368 | } 369 | 370 | private: 371 | //! 372 | //! \brief returns an appropriate string for prefixing a log message with the given severity 373 | //! 374 | static const char* severityPrefix(Severity severity) 375 | { 376 | switch (severity) 377 | { 378 | case Severity::kINTERNAL_ERROR: return "[F] "; 379 | case Severity::kERROR: return "[E] "; 380 | case Severity::kWARNING: return "[W] "; 381 | case Severity::kINFO: return "[I] "; 382 | case Severity::kVERBOSE: return "[V] "; 383 | default: assert(0); return ""; 384 | } 385 | } 386 | 387 | //! 388 | //! \brief returns an appropriate string for prefixing a test result message with the given result 389 | //! 390 | static const char* testResultString(TestResult result) 391 | { 392 | switch (result) 393 | { 394 | case TestResult::kRUNNING: return "RUNNING"; 395 | case TestResult::kPASSED: return "PASSED"; 396 | case TestResult::kFAILED: return "FAILED"; 397 | case TestResult::kWAIVED: return "WAIVED"; 398 | default: assert(0); return ""; 399 | } 400 | } 401 | 402 | //! 403 | //! \brief returns an appropriate output stream (cout or cerr) to use with the given severity 404 | //! 405 | static std::ostream& severityOstream(Severity severity) 406 | { 407 | return severity >= Severity::kINFO ? std::cout : std::cerr; 408 | } 409 | 410 | //! 411 | //! \brief method that implements logging test results 412 | //! 413 | static void reportTestResult(const TestAtom& testAtom, TestResult result) 414 | { 415 | severityOstream(Severity::kINFO) << "&&&& " << testResultString(result) << " " << testAtom.mName << " # " 416 | << testAtom.mCmdline << std::endl; 417 | } 418 | 419 | //! 420 | //! \brief generate a command line string from the given (argc, argv) values 421 | //! 422 | static std::string genCmdlineString(int argc, char const* const* argv) 423 | { 424 | std::stringstream ss; 425 | for (int i = 0; i < argc; i++) 426 | { 427 | if (i > 0) 428 | ss << " "; 429 | ss << argv[i]; 430 | } 431 | return ss.str(); 432 | } 433 | 434 | Severity mReportableSeverity; 435 | }; 436 | 437 | namespace 438 | { 439 | 440 | //! 441 | //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kVERBOSE 442 | //! 443 | //! Example usage: 444 | //! 445 | //! LOG_VERBOSE(logger) << "hello world" << std::endl; 446 | //! 447 | inline LogStreamConsumer LOG_VERBOSE(const Logger& logger) 448 | { 449 | return LogStreamConsumer(logger.getReportableSeverity(), Severity::kVERBOSE); 450 | } 451 | 452 | //! 453 | //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINFO 454 | //! 455 | //! Example usage: 456 | //! 457 | //! LOG_INFO(logger) << "hello world" << std::endl; 458 | //! 459 | inline LogStreamConsumer LOG_INFO(const Logger& logger) 460 | { 461 | return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINFO); 462 | } 463 | 464 | //! 465 | //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kWARNING 466 | //! 467 | //! Example usage: 468 | //! 469 | //! LOG_WARN(logger) << "hello world" << std::endl; 470 | //! 471 | inline LogStreamConsumer LOG_WARN(const Logger& logger) 472 | { 473 | return LogStreamConsumer(logger.getReportableSeverity(), Severity::kWARNING); 474 | } 475 | 476 | //! 477 | //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kERROR 478 | //! 479 | //! Example usage: 480 | //! 481 | //! LOG_ERROR(logger) << "hello world" << std::endl; 482 | //! 483 | inline LogStreamConsumer LOG_ERROR(const Logger& logger) 484 | { 485 | return LogStreamConsumer(logger.getReportableSeverity(), Severity::kERROR); 486 | } 487 | 488 | //! 489 | //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINTERNAL_ERROR 490 | // ("fatal" severity) 491 | //! 492 | //! Example usage: 493 | //! 494 | //! LOG_FATAL(logger) << "hello world" << std::endl; 495 | //! 496 | inline LogStreamConsumer LOG_FATAL(const Logger& logger) 497 | { 498 | return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINTERNAL_ERROR); 499 | } 500 | 501 | } // anonymous namespace 502 | 503 | #endif // TENSORRT_LOGGING_H 504 | -------------------------------------------------------------------------------- /include/matrix.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 John Weaver 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | */ 18 | 19 | #ifndef _MATRIX_H_ 20 | #define _MATRIX_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define XYZMIN(x, y) (x)<(y)?(x):(y) 30 | #define XYZMAX(x, y) (x)>(y)?(x):(y) 31 | 32 | template 33 | class Matrix { 34 | public: 35 | Matrix(){ 36 | m_rows = 0; 37 | m_columns = 0; 38 | m_matrix = nullptr; 39 | } 40 | Matrix(const size_t rows, const size_t columns) { 41 | m_matrix = nullptr; 42 | resize(rows, columns); 43 | } 44 | Matrix(const std::initializer_list> init) { 45 | m_matrix = nullptr; 46 | m_rows = init.size(); 47 | if ( m_rows == 0 ) { 48 | m_columns = 0; 49 | } else { 50 | m_columns = init.begin()->size(); 51 | if ( m_columns > 0 ) { 52 | resize(m_rows, m_columns); 53 | } 54 | } 55 | 56 | size_t i = 0, j; 57 | for ( auto row = init.begin() ; row != init.end() ; ++row, ++i ) { 58 | assert ( row->size() == m_columns && "All rows must have the same number of columns." ); 59 | j = 0; 60 | for ( auto value = row->begin() ; value != row->end() ; ++value, ++j ) { 61 | m_matrix[i][j] = *value; 62 | } 63 | } 64 | } 65 | Matrix(const Matrix &other) { 66 | if ( other.m_matrix != nullptr ) { 67 | // copy arrays 68 | m_matrix = nullptr; 69 | resize(other.m_rows, other.m_columns); 70 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 71 | for ( size_t j = 0 ; j < m_columns ; j++ ) { 72 | m_matrix[i][j] = other.m_matrix[i][j]; 73 | } 74 | } 75 | } else { 76 | m_matrix = nullptr; 77 | m_rows = 0; 78 | m_columns = 0; 79 | } 80 | } 81 | Matrix & operator= (const Matrix &other){ 82 | if ( other.m_matrix != nullptr ) { 83 | // copy arrays 84 | resize(other.m_rows, other.m_columns); 85 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 86 | for ( size_t j = 0 ; j < m_columns ; j++ ) { 87 | m_matrix[i][j] = other.m_matrix[i][j]; 88 | } 89 | } 90 | } else { 91 | // free arrays 92 | for ( size_t i = 0 ; i < m_columns ; i++ ) { 93 | delete [] m_matrix[i]; 94 | } 95 | 96 | delete [] m_matrix; 97 | 98 | m_matrix = nullptr; 99 | m_rows = 0; 100 | m_columns = 0; 101 | } 102 | 103 | return *this; 104 | } 105 | ~Matrix(){ 106 | if ( m_matrix != nullptr ) { 107 | // free arrays 108 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 109 | delete [] m_matrix[i]; 110 | } 111 | 112 | delete [] m_matrix; 113 | } 114 | m_matrix = nullptr; 115 | } 116 | // all operations modify the matrix in-place. 117 | void resize(const size_t rows, const size_t columns, const T default_value = 0) { 118 | assert ( rows > 0 && columns > 0 && "Columns and rows must exist." ); 119 | 120 | if ( m_matrix == nullptr ) { 121 | // alloc arrays 122 | m_matrix = new T*[rows]; // rows 123 | for ( size_t i = 0 ; i < rows ; i++ ) { 124 | m_matrix[i] = new T[columns]; // columns 125 | } 126 | 127 | m_rows = rows; 128 | m_columns = columns; 129 | clear(); 130 | } else { 131 | // save array pointer 132 | T **new_matrix; 133 | // alloc new arrays 134 | new_matrix = new T*[rows]; // rows 135 | for ( size_t i = 0 ; i < rows ; i++ ) { 136 | new_matrix[i] = new T[columns]; // columns 137 | for ( size_t j = 0 ; j < columns ; j++ ) { 138 | new_matrix[i][j] = default_value; 139 | } 140 | } 141 | 142 | // copy data from saved pointer to new arrays 143 | size_t minrows = XYZMIN(rows, m_rows); 144 | size_t mincols = XYZMIN(columns, m_columns); 145 | for ( size_t x = 0 ; x < minrows ; x++ ) { 146 | for ( size_t y = 0 ; y < mincols ; y++ ) { 147 | new_matrix[x][y] = m_matrix[x][y]; 148 | } 149 | } 150 | 151 | // delete old arrays 152 | if ( m_matrix != nullptr ) { 153 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 154 | delete [] m_matrix[i]; 155 | } 156 | 157 | delete [] m_matrix; 158 | } 159 | 160 | m_matrix = new_matrix; 161 | } 162 | 163 | m_rows = rows; 164 | m_columns = columns; 165 | } 166 | void clear() { 167 | assert( m_matrix != nullptr ); 168 | 169 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 170 | for ( size_t j = 0 ; j < m_columns ; j++ ) { 171 | m_matrix[i][j] = 0; 172 | } 173 | } 174 | } 175 | T& operator () (const size_t x, const size_t y) { 176 | assert ( x < m_rows ); 177 | assert ( y < m_columns ); 178 | assert ( m_matrix != nullptr ); 179 | return m_matrix[x][y]; 180 | } 181 | 182 | const T& operator () (const size_t x, const size_t y) const { 183 | assert ( x < m_rows ); 184 | assert ( y < m_columns ); 185 | assert ( m_matrix != nullptr ); 186 | return m_matrix[x][y]; 187 | } 188 | const T mmin() const { 189 | assert( m_matrix != nullptr ); 190 | assert ( m_rows > 0 ); 191 | assert ( m_columns > 0 ); 192 | T min = m_matrix[0][0]; 193 | 194 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 195 | for ( size_t j = 0 ; j < m_columns ; j++ ) { 196 | min = std::min(min, m_matrix[i][j]); 197 | } 198 | } 199 | 200 | return min; 201 | } 202 | 203 | const T mmax() const { 204 | assert( m_matrix != nullptr ); 205 | assert ( m_rows > 0 ); 206 | assert ( m_columns > 0 ); 207 | T max = m_matrix[0][0]; 208 | 209 | for ( size_t i = 0 ; i < m_rows ; i++ ) { 210 | for ( size_t j = 0 ; j < m_columns ; j++ ) { 211 | max = std::max(max, m_matrix[i][j]); 212 | } 213 | } 214 | 215 | return max; 216 | } 217 | inline size_t minsize() { return ((m_rows < m_columns) ? m_rows : m_columns); } 218 | inline size_t columns() const { return m_columns;} 219 | inline size_t rows() const { return m_rows;} 220 | 221 | friend std::ostream& operator<<(std::ostream& os, const Matrix &matrix) 222 | { 223 | os << "Matrix:" << std::endl; 224 | for (size_t row = 0 ; row < matrix.rows() ; row++ ) 225 | { 226 | for (size_t col = 0 ; col < matrix.columns() ; col++ ) 227 | { 228 | os.width(8); 229 | os << matrix(row, col) << ","; 230 | } 231 | os << std::endl; 232 | } 233 | return os; 234 | } 235 | 236 | private: 237 | T **m_matrix; 238 | size_t m_rows; 239 | size_t m_columns; 240 | }; 241 | 242 | //#ifndef USE_EXPORT_KEYWORD 243 | //#include "matrix.cpp" 244 | ////#define export /*export*/ 245 | //#endif 246 | 247 | #endif /* !defined(_MATRIX_H_) */ 248 | 249 | -------------------------------------------------------------------------------- /include/model.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_HPP 2 | #define MODEL_HPP 3 | 4 | #include 5 | #include "datatype.h" 6 | 7 | 8 | // * Each rect's data structure. 9 | // * tlwh: topleft point & (w,h) 10 | // * confidence: detection confidence. 11 | // * feature: the rect's 256d feature. 12 | // */ 13 | 14 | const float kRatio=0.5; 15 | enum DETECTBOX_IDX {IDX_X = 0, IDX_Y, IDX_W, IDX_H }; 16 | 17 | class DETECTION_ROW { 18 | public: 19 | DETECTBOX tlwh; 20 | float confidence; 21 | FEATURE feature; 22 | DETECTBOX to_xyah() const { 23 | //(centerx, centery, ration, h) 24 | DETECTBOX ret = tlwh; 25 | ret(0, IDX_X) += (ret(0, IDX_W)*kRatio); 26 | ret(0, IDX_Y) += (ret(0, IDX_H)*kRatio); 27 | ret(0, IDX_W) /= ret(0, IDX_H); 28 | return ret; 29 | } 30 | DETECTBOX to_tlbr() const { 31 | //(x,y,xx,yy) 32 | DETECTBOX ret = tlwh; 33 | ret(0, IDX_X) += ret(0, IDX_W); 34 | ret(0, IDX_Y) += ret(0, IDX_H); 35 | return ret; 36 | } 37 | }; 38 | 39 | typedef std::vector DETECTIONS; 40 | typedef std::pair, DETECTIONS> DETECTIONSV2; 41 | 42 | #endif // MODEL_HPP 43 | -------------------------------------------------------------------------------- /include/munkres.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 John Weaver 3 | * Copyright (c) 2015 Miroslav Krajicek 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | */ 19 | 20 | #if !defined(_MUNKRES_H_) 21 | #define _MUNKRES_H_ 22 | 23 | #include "matrix.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | template class Munkres 33 | { 34 | static constexpr int NORMAL = 0; 35 | static constexpr int STAR = 1; 36 | static constexpr int PRIME = 2; 37 | public: 38 | 39 | /* 40 | * 41 | * Linear assignment problem solution 42 | * [modifies matrix in-place.] 43 | * matrix(row,col): row major format assumed. 44 | * 45 | * Assignments are remaining 0 values 46 | * (extra 0 values are replaced with -1) 47 | * 48 | */ 49 | void solve(Matrix &m) { 50 | const size_t rows = m.rows(), 51 | columns = m.columns(), 52 | size = XYZMAX(rows, columns); 53 | 54 | #ifdef DEBUG 55 | std::cout << "Munkres input: " << m << std::endl; 56 | #endif 57 | 58 | // Copy input matrix 59 | this->matrix = m; 60 | 61 | if ( rows != columns ) { 62 | // If the input matrix isn't square, make it square 63 | // and fill the empty values with the largest value present 64 | // in the matrix. 65 | matrix.resize(size, size, matrix.mmax()); 66 | } 67 | 68 | 69 | // STAR == 1 == starred, PRIME == 2 == primed 70 | mask_matrix.resize(size, size); 71 | 72 | row_mask = new bool[size]; 73 | col_mask = new bool[size]; 74 | for ( size_t i = 0 ; i < size ; i++ ) { 75 | row_mask[i] = false; 76 | } 77 | 78 | for ( size_t i = 0 ; i < size ; i++ ) { 79 | col_mask[i] = false; 80 | } 81 | 82 | // Prepare the matrix values... 83 | 84 | // If there were any infinities, replace them with a value greater 85 | // than the maximum value in the matrix. 86 | replace_infinites(matrix); 87 | 88 | minimize_along_direction(matrix, rows >= columns); 89 | minimize_along_direction(matrix, rows < columns); 90 | 91 | // Follow the steps 92 | int step = 1; 93 | while ( step ) { 94 | switch ( step ) { 95 | case 1: 96 | step = step1(); 97 | // step is always 2 98 | break; 99 | case 2: 100 | step = step2(); 101 | // step is always either 0 or 3 102 | break; 103 | case 3: 104 | step = step3(); 105 | // step in [3, 4, 5] 106 | break; 107 | case 4: 108 | step = step4(); 109 | // step is always 2 110 | break; 111 | case 5: 112 | step = step5(); 113 | // step is always 3 114 | break; 115 | } 116 | } 117 | 118 | // Store results 119 | for ( size_t row = 0 ; row < size ; row++ ) { 120 | for ( size_t col = 0 ; col < size ; col++ ) { 121 | if ( mask_matrix(row, col) == STAR ) { 122 | matrix(row, col) = 0; 123 | } else { 124 | matrix(row, col) = -1; 125 | } 126 | } 127 | } 128 | 129 | #ifdef DEBUG 130 | std::cout << "Munkres output: " << matrix << std::endl; 131 | #endif 132 | // Remove the excess rows or columns that we added to fit the 133 | // input to a square matrix. 134 | matrix.resize(rows, columns); 135 | 136 | m = matrix; 137 | 138 | delete [] row_mask; 139 | delete [] col_mask; 140 | } 141 | 142 | static void replace_infinites(Matrix &matrix) { 143 | const size_t rows = matrix.rows(), 144 | columns = matrix.columns(); 145 | //assert( rows > 0 && columns > 0 ); 146 | double max = matrix(0, 0); 147 | constexpr auto infinity = std::numeric_limits::infinity(); 148 | 149 | // Find the greatest value in the matrix that isn't infinity. 150 | for ( size_t row = 0 ; row < rows ; row++ ) { 151 | for ( size_t col = 0 ; col < columns ; col++ ) { 152 | if ( matrix(row, col) != infinity ) { 153 | if ( max == infinity ) { 154 | max = matrix(row, col); 155 | } else { 156 | max = XYZMAX(max, matrix(row, col)); 157 | } 158 | } 159 | } 160 | } 161 | 162 | // a value higher than the maximum value present in the matrix. 163 | if ( max == infinity ) { 164 | // This case only occurs when all values are infinite. 165 | max = 0; 166 | } else { 167 | max++; 168 | } 169 | 170 | for ( size_t row = 0 ; row < rows ; row++ ) { 171 | for ( size_t col = 0 ; col < columns ; col++ ) { 172 | if ( matrix(row, col) == infinity ) { 173 | matrix(row, col) = max; 174 | } 175 | } 176 | } 177 | 178 | } 179 | static void minimize_along_direction(Matrix &matrix, const bool over_columns) { 180 | const size_t outer_size = over_columns ? matrix.columns() : matrix.rows(), 181 | inner_size = over_columns ? matrix.rows() : matrix.columns(); 182 | 183 | // Look for a minimum value to subtract from all values along 184 | // the "outer" direction. 185 | for ( size_t i = 0 ; i < outer_size ; i++ ) { 186 | double min = over_columns ? matrix(0, i) : matrix(i, 0); 187 | 188 | // As long as the current minimum is greater than zero, 189 | // keep looking for the minimum. 190 | // Start at one because we already have the 0th value in min. 191 | for ( size_t j = 1 ; j < inner_size && min > 0 ; j++ ) { 192 | min = XYZMIN( 193 | min, 194 | over_columns ? matrix(j, i) : matrix(i, j)); 195 | } 196 | 197 | if ( min > 0 ) { 198 | for ( size_t j = 0 ; j < inner_size ; j++ ) { 199 | if ( over_columns ) { 200 | matrix(j, i) -= min; 201 | } else { 202 | matrix(i, j) -= min; 203 | } 204 | } 205 | } 206 | } 207 | } 208 | 209 | private: 210 | 211 | inline bool find_uncovered_in_matrix(const double item, size_t &row, size_t &col) const { 212 | const size_t rows = matrix.rows(), 213 | columns = matrix.columns(); 214 | 215 | for ( row = 0 ; row < rows ; row++ ) { 216 | if ( !row_mask[row] ) { 217 | for ( col = 0 ; col < columns ; col++ ) { 218 | if ( !col_mask[col] ) { 219 | if ( matrix(row,col) == item ) { 220 | return true; 221 | } 222 | } 223 | } 224 | } 225 | } 226 | 227 | return false; 228 | } 229 | 230 | bool pair_in_list(const std::pair &needle, const std::list > &haystack) { 231 | for ( std::list >::const_iterator i = haystack.begin() ; i != haystack.end() ; i++ ) { 232 | if ( needle == *i ) { 233 | return true; 234 | } 235 | } 236 | 237 | return false; 238 | } 239 | 240 | int step1() { 241 | const size_t rows = matrix.rows(), 242 | columns = matrix.columns(); 243 | 244 | for ( size_t row = 0 ; row < rows ; row++ ) { 245 | for ( size_t col = 0 ; col < columns ; col++ ) { 246 | if ( 0 == matrix(row, col) ) { 247 | for ( size_t nrow = 0 ; nrow < row ; nrow++ ) 248 | if ( STAR == mask_matrix(nrow,col) ) 249 | goto next_column; 250 | 251 | mask_matrix(row,col) = STAR; 252 | goto next_row; 253 | } 254 | next_column:; 255 | } 256 | next_row:; 257 | } 258 | 259 | return 2; 260 | } 261 | 262 | int step2() { 263 | const size_t rows = matrix.rows(), 264 | columns = matrix.columns(); 265 | size_t covercount = 0; 266 | 267 | for ( size_t row = 0 ; row < rows ; row++ ) 268 | for ( size_t col = 0 ; col < columns ; col++ ) 269 | if ( STAR == mask_matrix(row, col) ) { 270 | col_mask[col] = true; 271 | covercount++; 272 | } 273 | 274 | if ( covercount >= matrix.minsize() ) { 275 | #ifdef DEBUG 276 | std::cout << "Final cover count: " << covercount << std::endl; 277 | #endif 278 | return 0; 279 | } 280 | 281 | #ifdef DEBUG 282 | std::cout << "Munkres matrix has " << covercount << " of " << matrix.minsize() << " Columns covered:" << std::endl; 283 | std::cout << matrix << std::endl; 284 | #endif 285 | 286 | 287 | return 3; 288 | } 289 | 290 | int step3() { 291 | /* 292 | Main Zero Search 293 | 294 | 1. Find an uncovered Z in the distance matrix and prime it. If no such zero exists, go to Step 5 295 | 2. If No Z* exists in the row of the Z', go to Step 4. 296 | 3. If a Z* exists, cover this row and uncover the column of the Z*. Return to Step 3.1 to find a new Z 297 | */ 298 | if ( find_uncovered_in_matrix(0, saverow, savecol) ) { 299 | mask_matrix(saverow,savecol) = PRIME; // prime it. 300 | } else { 301 | return 5; 302 | } 303 | 304 | for ( size_t ncol = 0 ; ncol < matrix.columns() ; ncol++ ) { 305 | if ( mask_matrix(saverow,ncol) == STAR ) { 306 | row_mask[saverow] = true; //cover this row and 307 | col_mask[ncol] = false; // uncover the column containing the starred zero 308 | return 3; // repeat 309 | } 310 | } 311 | 312 | return 4; // no starred zero in the row containing this primed zero 313 | } 314 | 315 | int step4() { 316 | const size_t rows = matrix.rows(), 317 | columns = matrix.columns(); 318 | 319 | // seq contains pairs of row/column values where we have found 320 | // either a star or a prime that is part of the ``alternating sequence``. 321 | std::list > seq; 322 | // use saverow, savecol from step 3. 323 | std::pair z0(saverow, savecol); 324 | seq.insert(seq.end(), z0); 325 | 326 | // We have to find these two pairs: 327 | std::pair z1(-1, -1); 328 | std::pair z2n(-1, -1); 329 | 330 | size_t row, col = savecol; 331 | /* 332 | Increment Set of Starred Zeros 333 | 334 | 1. Construct the ``alternating sequence'' of primed and starred zeros: 335 | 336 | Z0 : Unpaired Z' from Step 4.2 337 | Z1 : The Z* in the column of Z0 338 | Z[2N] : The Z' in the row of Z[2N-1], if such a zero exists 339 | Z[2N+1] : The Z* in the column of Z[2N] 340 | 341 | The sequence eventually terminates with an unpaired Z' = Z[2N] for some N. 342 | */ 343 | bool madepair; 344 | do { 345 | madepair = false; 346 | for ( row = 0 ; row < rows ; row++ ) { 347 | if ( mask_matrix(row,col) == STAR ) { 348 | z1.first = row; 349 | z1.second = col; 350 | if ( pair_in_list(z1, seq) ) { 351 | continue; 352 | } 353 | 354 | madepair = true; 355 | seq.insert(seq.end(), z1); 356 | break; 357 | } 358 | } 359 | 360 | if ( !madepair ) 361 | break; 362 | 363 | madepair = false; 364 | 365 | for ( col = 0 ; col < columns ; col++ ) { 366 | if ( mask_matrix(row, col) == PRIME ) { 367 | z2n.first = row; 368 | z2n.second = col; 369 | if ( pair_in_list(z2n, seq) ) { 370 | continue; 371 | } 372 | madepair = true; 373 | seq.insert(seq.end(), z2n); 374 | break; 375 | } 376 | } 377 | } while ( madepair ); 378 | 379 | for ( std::list >::iterator i = seq.begin() ; 380 | i != seq.end() ; 381 | i++ ) { 382 | // 2. Unstar each starred zero of the sequence. 383 | if ( mask_matrix(i->first,i->second) == STAR ) 384 | mask_matrix(i->first,i->second) = NORMAL; 385 | 386 | // 3. Star each primed zero of the sequence, 387 | // thus increasing the number of starred zeros by one. 388 | if ( mask_matrix(i->first,i->second) == PRIME ) 389 | mask_matrix(i->first,i->second) = STAR; 390 | } 391 | 392 | // 4. Erase all primes, uncover all columns and rows, 393 | for ( size_t row = 0 ; row < mask_matrix.rows() ; row++ ) { 394 | for ( size_t col = 0 ; col < mask_matrix.columns() ; col++ ) { 395 | if ( mask_matrix(row,col) == PRIME ) { 396 | mask_matrix(row,col) = NORMAL; 397 | } 398 | } 399 | } 400 | 401 | for ( size_t i = 0 ; i < rows ; i++ ) { 402 | row_mask[i] = false; 403 | } 404 | 405 | for ( size_t i = 0 ; i < columns ; i++ ) { 406 | col_mask[i] = false; 407 | } 408 | 409 | // and return to Step 2. 410 | return 2; 411 | } 412 | 413 | int step5() { 414 | const size_t rows = matrix.rows(), 415 | columns = matrix.columns(); 416 | /* 417 | New Zero Manufactures 418 | 419 | 1. Let h be the smallest uncovered entry in the (modified) distance matrix. 420 | 2. Add h to all covered rows. 421 | 3. Subtract h from all uncovered columns 422 | 4. Return to Step 3, without altering stars, primes, or covers. 423 | */ 424 | double h = 100000;//xyzoylz std::numeric_limits::max(); 425 | for ( size_t row = 0 ; row < rows ; row++ ) { 426 | if ( !row_mask[row] ) { 427 | for ( size_t col = 0 ; col < columns ; col++ ) { 428 | if ( !col_mask[col] ) { 429 | if ( h > matrix(row, col) && matrix(row, col) != 0 ) { 430 | h = matrix(row, col); 431 | } 432 | } 433 | } 434 | } 435 | } 436 | 437 | for ( size_t row = 0 ; row < rows ; row++ ) { 438 | if ( row_mask[row] ) { 439 | for ( size_t col = 0 ; col < columns ; col++ ) { 440 | matrix(row, col) += h; 441 | } 442 | } 443 | } 444 | 445 | for ( size_t col = 0 ; col < columns ; col++ ) { 446 | if ( !col_mask[col] ) { 447 | for ( size_t row = 0 ; row < rows ; row++ ) { 448 | matrix(row, col) -= h; 449 | } 450 | } 451 | } 452 | 453 | return 3; 454 | } 455 | 456 | Matrix mask_matrix; 457 | Matrix matrix; 458 | bool *row_mask; 459 | bool *col_mask; 460 | size_t saverow = 0, savecol = 0; 461 | }; 462 | 463 | 464 | #endif /* !defined(_MUNKRES_H_) */ 465 | -------------------------------------------------------------------------------- /include/nn_matching.h: -------------------------------------------------------------------------------- 1 | #ifndef NN_MATCHING_H 2 | #define NN_MATCHING_H 3 | 4 | #include "datatype.h" 5 | 6 | #include 7 | 8 | //A tool to calculate distance; 9 | class NearNeighborDisMetric{ 10 | public: 11 | enum METRIC_TYPE{euclidean=1, cosine}; 12 | NearNeighborDisMetric(METRIC_TYPE metric, 13 | float matching_threshold, 14 | int budget); 15 | DYNAMICM distance(const FEATURESS& features, const std::vector &targets); 16 | // void partial_fit(FEATURESS& features, std::vector targets, std::vector active_targets); 17 | void partial_fit(std::vector& tid_feats, std::vector& active_targets); 18 | float mating_threshold; 19 | 20 | private: 21 | typedef Eigen::VectorXf (NearNeighborDisMetric::*PTRFUN)(const FEATURESS&, const FEATURESS&); 22 | Eigen::VectorXf _nncosine_distance(const FEATURESS& x, const FEATURESS& y); 23 | Eigen::VectorXf _nneuclidean_distance(const FEATURESS& x, const FEATURESS& y); 24 | 25 | Eigen::MatrixXf _pdist(const FEATURESS& x, const FEATURESS& y); 26 | Eigen::MatrixXf _cosine_distance(const FEATURESS & a, const FEATURESS& b, bool data_is_normalized = false); 27 | private: 28 | PTRFUN _metric; 29 | int budget; 30 | std::map samples; 31 | }; 32 | 33 | #endif // NN_MATCHING_H 34 | -------------------------------------------------------------------------------- /include/track.h: -------------------------------------------------------------------------------- 1 | #ifndef TRACK_H 2 | #define TRACK_H 3 | 4 | 5 | #include "kalmanfilter.h" 6 | #include "datatype.h" 7 | #include "model.hpp" 8 | 9 | class Track 10 | { 11 | /*""" 12 | A single target track with state space `(x, y, a, h)` and associated 13 | velocities, where `(x, y)` is the center of the bounding box, `a` is the 14 | aspect ratio and `h` is the height. 15 | 16 | Parameters 17 | ---------- 18 | mean : ndarray 19 | Mean vector of the initial state distribution. 20 | covariance : ndarray 21 | Covariance matrix of the initial state distribution. 22 | track_id : int 23 | A unique track identifier. 24 | n_init : int 25 | Number of consecutive detections before the track is confirmed. The 26 | track state is set to `Deleted` if a miss occurs within the first 27 | `n_init` frames. 28 | max_age : int 29 | The maximum number of consecutive misses before the track state is 30 | set to `Deleted`. 31 | feature : Optional[ndarray] 32 | Feature vector of the detection this track originates from. If not None, 33 | this feature is added to the `features` cache. 34 | 35 | Attributes 36 | ---------- 37 | mean : ndarray 38 | Mean vector of the initial state distribution. 39 | covariance : ndarray 40 | Covariance matrix of the initial state distribution. 41 | track_id : int 42 | A unique track identifier. 43 | hits : int 44 | Total number of measurement updates. 45 | age : int 46 | Total number of frames since first occurance. 47 | time_since_update : int 48 | Total number of frames since last measurement update. 49 | state : TrackState 50 | The current track state. 51 | features : List[ndarray] 52 | A cache of features. On each measurement update, the associated feature 53 | vector is added to this list. 54 | 55 | """*/ 56 | enum TrackState {Tentative = 1, Confirmed, Deleted}; 57 | 58 | public: 59 | Track(KAL_MEAN& mean, KAL_COVA& covariance, int track_id, 60 | int n_init, int max_age, const FEATURE& feature); 61 | Track(KAL_MEAN& mean, KAL_COVA& covariance, int track_id, 62 | int n_init, int max_age, const FEATURE& feature, int cls, float conf); 63 | void predit(KalmanFilter* kf); 64 | void update(KalmanFilter* const kf, const DETECTION_ROW &detection); 65 | void update(KalmanFilter* const kf, const DETECTION_ROW & detection, CLSCONF pair_det); 66 | void mark_missed(); 67 | bool is_confirmed(); 68 | bool is_deleted(); 69 | bool is_tentative(); 70 | DETECTBOX to_tlwh(); 71 | int time_since_update; 72 | int track_id; 73 | FEATURESS features; 74 | KAL_MEAN mean; 75 | KAL_COVA covariance; 76 | 77 | int hits; 78 | int age; 79 | int _n_init; 80 | int _max_age; 81 | TrackState state; 82 | 83 | int cls; 84 | float conf; 85 | private: 86 | void featuresAppendOne(const FEATURE& f); 87 | }; 88 | 89 | #endif // TRACK_H 90 | -------------------------------------------------------------------------------- /include/tracker.h: -------------------------------------------------------------------------------- 1 | #ifndef TRACKER_H 2 | #define TRACKER_H 3 | 4 | 5 | #include 6 | 7 | #include "kalmanfilter.h" 8 | #include "track.h" 9 | #include "model.hpp" 10 | 11 | using namespace std; 12 | 13 | class NearNeighborDisMetric; 14 | 15 | class tracker 16 | { 17 | public: 18 | NearNeighborDisMetric* metric; 19 | float max_iou_distance; 20 | int max_age; 21 | int n_init; 22 | 23 | KalmanFilter* kf; 24 | 25 | int _next_idx; 26 | public: 27 | std::vector tracks; 28 | tracker(/*NearNeighborDisMetric* metric,*/ 29 | float max_cosine_distance, int nn_budget, 30 | float max_iou_distance = 0.7, 31 | int max_age = 70, int n_init=3); 32 | void predict(); 33 | void update(const DETECTIONS& detections); 34 | void update(const DETECTIONSV2& detectionsv2); 35 | typedef DYNAMICM (tracker::* GATED_METRIC_FUNC)( 36 | std::vector& tracks, 37 | const DETECTIONS& dets, 38 | const std::vector& track_indices, 39 | const std::vector& detection_indices); 40 | private: 41 | void _match(const DETECTIONS& detections, TRACHER_MATCHD& res); 42 | void _initiate_track(const DETECTION_ROW& detection); 43 | void _initiate_track(const DETECTION_ROW& detection, CLSCONF clsConf); 44 | public: 45 | DYNAMICM gated_matric( 46 | std::vector& tracks, 47 | const DETECTIONS& dets, 48 | const std::vector& track_indices, 49 | const std::vector& detection_indices); 50 | DYNAMICM iou_cost( 51 | std::vector& tracks, 52 | const DETECTIONS& dets, 53 | const std::vector& track_indices, 54 | const std::vector& detection_indices); 55 | Eigen::VectorXf iou(DETECTBOX& bbox, 56 | DETECTBOXSS &candidates); 57 | }; 58 | 59 | #endif // TRACKER_H 60 | -------------------------------------------------------------------------------- /onnx2engine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "deepsortenginegenerator.h" 4 | #include "cuda_runtime_api.h" 5 | #include "logging.h" 6 | 7 | using namespace nvinfer1; 8 | 9 | static Logger gLogger; 10 | 11 | int main(int argc, char** argv) { 12 | cudaSetDevice(0); 13 | if (argc < 3) { 14 | std::cout << "./onnx2engine [input .onnx path] [output .engine path]" << std::endl; 15 | return -1; 16 | } 17 | std::string onnxPath = argv[1]; 18 | std::string enginePath = argv[2]; 19 | DeepSortEngineGenerator* engG = new DeepSortEngineGenerator(&gLogger); 20 | engG->setFP16(true); 21 | engG->createEngine(onnxPath, enginePath); 22 | std::cout << "==============" << std::endl; 23 | std::cout << "| SUCCESS! |" << std::endl; 24 | std::cout << "==============" << std::endl; 25 | return 0; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/deepsort.cpp: -------------------------------------------------------------------------------- 1 | #include "deepsort.h" 2 | 3 | DeepSort::DeepSort(std::string modelPath, int batchSize, int featureDim, int gpuID, ILogger* gLogger) { 4 | this->gpuID = gpuID; 5 | this->enginePath = modelPath; 6 | this->batchSize = batchSize; 7 | this->featureDim = featureDim; 8 | this->imgShape = cv::Size(64, 128); 9 | this->maxBudget = 100; 10 | this->maxCosineDist = 0.2; 11 | this->gLogger = gLogger; 12 | init(); 13 | } 14 | 15 | void DeepSort::init() { 16 | objTracker = new tracker(maxCosineDist, maxBudget); 17 | featureExtractor = new FeatureTensor(batchSize, imgShape, featureDim, gpuID, gLogger); 18 | int ret = enginePath.find(".onnx"); 19 | if (ret != -1) 20 | featureExtractor->loadOnnx(enginePath); 21 | else 22 | featureExtractor->loadEngine(enginePath); 23 | } 24 | 25 | DeepSort::~DeepSort() { 26 | delete objTracker; 27 | } 28 | 29 | void DeepSort::sort(cv::Mat& frame, vector& dets) { 30 | // preprocess Mat -> DETECTION 31 | DETECTIONS detections; 32 | vector clsConf; 33 | 34 | for (DetectBox i : dets) { 35 | DETECTBOX box(i.x1, i.y1, i.x2-i.x1, i.y2-i.y1); 36 | DETECTION_ROW d; 37 | d.tlwh = box; 38 | d.confidence = i.confidence; 39 | detections.push_back(d); 40 | clsConf.push_back(CLSCONF((int)i.classID, i.confidence)); 41 | } 42 | result.clear(); 43 | results.clear(); 44 | if (detections.size() > 0) { 45 | DETECTIONSV2 detectionsv2 = make_pair(clsConf, detections); 46 | sort(frame, detectionsv2); 47 | } 48 | // postprocess DETECTION -> Mat 49 | dets.clear(); 50 | for (auto r : result) { 51 | DETECTBOX i = r.second; 52 | DetectBox b(i(0), i(1), i(2)+i(0), i(3)+i(1), 1.); 53 | b.trackID = (float)r.first; 54 | dets.push_back(b); 55 | } 56 | for (int i = 0; i < results.size(); ++i) { 57 | CLSCONF c = results[i].first; 58 | dets[i].classID = c.cls; 59 | dets[i].confidence = c.conf; 60 | } 61 | } 62 | 63 | 64 | void DeepSort::sort(cv::Mat& frame, DETECTIONS& detections) { 65 | bool flag = featureExtractor->getRectsFeature(frame, detections); 66 | if (flag) { 67 | objTracker->predict(); 68 | objTracker->update(detections); 69 | //result.clear(); 70 | for (Track& track : objTracker->tracks) { 71 | if (!track.is_confirmed() || track.time_since_update > 1) 72 | continue; 73 | result.push_back(make_pair(track.track_id, track.to_tlwh())); 74 | } 75 | } 76 | } 77 | 78 | void DeepSort::sort(cv::Mat& frame, DETECTIONSV2& detectionsv2) { 79 | std::vector& clsConf = detectionsv2.first; 80 | DETECTIONS& detections = detectionsv2.second; 81 | bool flag = featureExtractor->getRectsFeature(frame, detections); 82 | if (flag) { 83 | objTracker->predict(); 84 | objTracker->update(detectionsv2); 85 | result.clear(); 86 | results.clear(); 87 | for (Track& track : objTracker->tracks) { 88 | if (!track.is_confirmed() || track.time_since_update > 1) 89 | continue; 90 | result.push_back(make_pair(track.track_id, track.to_tlwh())); 91 | results.push_back(make_pair(CLSCONF(track.cls, track.conf) ,track.to_tlwh())); 92 | } 93 | } 94 | } 95 | 96 | void DeepSort::sort(vector& dets) { 97 | DETECTIONS detections; 98 | for (DetectBox i : dets) { 99 | DETECTBOX box(i.x1, i.y1, i.x2-i.x1, i.y2-i.y1); 100 | DETECTION_ROW d; 101 | d.tlwh = box; 102 | d.confidence = i.confidence; 103 | detections.push_back(d); 104 | } 105 | if (detections.size() > 0) 106 | sort(detections); 107 | dets.clear(); 108 | for (auto r : result) { 109 | DETECTBOX i = r.second; 110 | DetectBox b(i(0), i(1), i(2), i(3), 1.); 111 | b.trackID = r.first; 112 | dets.push_back(b); 113 | } 114 | } 115 | 116 | void DeepSort::sort(DETECTIONS& detections) { 117 | bool flag = featureExtractor->getRectsFeature(detections); 118 | if (flag) { 119 | objTracker->predict(); 120 | objTracker->update(detections); 121 | result.clear(); 122 | for (Track& track : objTracker->tracks) { 123 | if (!track.is_confirmed() || track.time_since_update > 1) 124 | continue; 125 | result.push_back(make_pair(track.track_id, track.to_tlwh())); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/deepsortenginegenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "deepsortenginegenerator.h" 2 | #include "assert.h" 3 | #include 4 | #include 5 | 6 | DeepSortEngineGenerator::DeepSortEngineGenerator(ILogger* gLogger) { 7 | this->gLogger = gLogger; 8 | } 9 | 10 | DeepSortEngineGenerator::~DeepSortEngineGenerator() { 11 | 12 | } 13 | 14 | void DeepSortEngineGenerator::setFP16(bool state) { 15 | this->useFP16 = state; 16 | } 17 | 18 | void DeepSortEngineGenerator::createEngine(std::string onnxPath, std::string enginePath) { 19 | // Load onnx model 20 | auto builder = createInferBuilder(*gLogger); 21 | assert(builder != nullptr); 22 | const auto explicitBatch = 1U << static_cast(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); 23 | auto network = builder->createNetworkV2(explicitBatch); 24 | assert(network != nullptr); 25 | auto config = builder->createBuilderConfig(); 26 | assert(config != nullptr); 27 | 28 | auto profile = builder->createOptimizationProfile(); 29 | Dims dims = Dims4{1, 3, IMG_HEIGHT, IMG_WIDTH}; 30 | profile->setDimensions(INPUT_NAME.c_str(), 31 | OptProfileSelector::kMIN, Dims4{1, dims.d[1], dims.d[2], dims.d[3]}); 32 | profile->setDimensions(INPUT_NAME.c_str(), 33 | OptProfileSelector::kOPT, Dims4{MAX_BATCH_SIZE, dims.d[1], dims.d[2], dims.d[3]}); 34 | profile->setDimensions(INPUT_NAME.c_str(), 35 | OptProfileSelector::kMAX, Dims4{MAX_BATCH_SIZE, dims.d[1], dims.d[2], dims.d[3]}); 36 | config->addOptimizationProfile(profile); 37 | 38 | nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, *gLogger); 39 | assert(parser != nullptr); 40 | auto parsed = parser->parseFromFile(onnxPath.c_str(), static_cast(ILogger::Severity::kWARNING)); 41 | assert(parsed); 42 | if (useFP16) config->setFlag(BuilderFlag::kFP16); 43 | config->setMaxWorkspaceSize(1 << 20); 44 | ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config); 45 | 46 | // Serialize model and save engine 47 | IHostMemory* modelStream = engine->serialize(); 48 | std::string serializeStr; 49 | std::ofstream serializeOutputStream; 50 | serializeStr.resize(modelStream->size()); 51 | memcpy((void*)serializeStr.data(), modelStream->data(), modelStream->size()); 52 | serializeOutputStream.open(enginePath, std::ios::binary); 53 | serializeOutputStream << serializeStr; 54 | serializeOutputStream.close(); 55 | } 56 | -------------------------------------------------------------------------------- /src/featuretensor.cpp: -------------------------------------------------------------------------------- 1 | #include "featuretensor.h" 2 | #include 3 | 4 | using namespace nvinfer1; 5 | 6 | #define INPUTSTREAM_SIZE (maxBatchSize*3*imgShape.area()) 7 | #define OUTPUTSTREAM_SIZE (maxBatchSize*featureDim) 8 | 9 | FeatureTensor::FeatureTensor(const int maxBatchSize, const cv::Size imgShape, const int featureDim, int gpuID, ILogger* gLogger) 10 | : maxBatchSize(maxBatchSize), imgShape(imgShape), featureDim(featureDim), 11 | inputStreamSize(INPUTSTREAM_SIZE), outputStreamSize(OUTPUTSTREAM_SIZE), 12 | inputBuffer(new float[inputStreamSize]), outputBuffer(new float[outputStreamSize]), 13 | inputName("input"), outputName("output") { 14 | cudaSetDevice(gpuID); 15 | this->gLogger = gLogger; 16 | runtime = nullptr; 17 | engine = nullptr; 18 | context = nullptr; 19 | 20 | means[0] = 0.485, means[1] = 0.456, means[2] = 0.406; 21 | std[0] = 0.229, std[1] = 0.224, std[2] = 0.225; 22 | 23 | initFlag = false; 24 | } 25 | 26 | FeatureTensor::~FeatureTensor() { 27 | delete [] inputBuffer; 28 | delete [] outputBuffer; 29 | if (initFlag) { 30 | // cudaStreamSynchronize(cudaStream); 31 | cudaStreamDestroy(cudaStream); 32 | cudaFree(buffers[inputIndex]); 33 | cudaFree(buffers[outputIndex]); 34 | } 35 | } 36 | 37 | bool FeatureTensor::getRectsFeature(const cv::Mat& img, DETECTIONS& det) { 38 | std::vector mats; 39 | for (auto& dbox : det) { 40 | cv::Rect rect = cv::Rect(int(dbox.tlwh(0)), int(dbox.tlwh(1)), 41 | int(dbox.tlwh(2)), int(dbox.tlwh(3))); 42 | rect.x -= (rect.height * 0.5 - rect.width) * 0.5; 43 | rect.width = rect.height * 0.5; 44 | rect.x = (rect.x >= 0 ? rect.x : 0); 45 | rect.y = (rect.y >= 0 ? rect.y : 0); 46 | rect.width = (rect.x + rect.width <= img.cols ? rect.width : (img.cols - rect.x)); 47 | rect.height = (rect.y + rect.height <= img.rows ? rect.height : (img.rows - rect.y)); 48 | cv::Mat tempMat = img(rect).clone(); 49 | cv::resize(tempMat, tempMat, imgShape); 50 | mats.push_back(tempMat); 51 | } 52 | doInference(mats); 53 | // decode output to det 54 | stream2det(outputBuffer, det); 55 | return true; 56 | } 57 | 58 | bool FeatureTensor::getRectsFeature(DETECTIONS& det) { 59 | return true; 60 | } 61 | 62 | void FeatureTensor::loadEngine(std::string enginePath) { 63 | // Deserialize model 64 | runtime = createInferRuntime(*gLogger); 65 | assert(runtime != nullptr); 66 | std::ifstream engineStream(enginePath, std::ios::binary); 67 | std::string engineCache(""); 68 | while (engineStream.peek() != EOF) { 69 | std::stringstream buffer; 70 | buffer << engineStream.rdbuf(); 71 | engineCache.append(buffer.str()); 72 | } 73 | engineStream.close(); 74 | engine = runtime->deserializeCudaEngine(engineCache.data(), engineCache.size(), nullptr); 75 | assert(engine != nullptr); 76 | context = engine->createExecutionContext(); 77 | assert(context != nullptr); 78 | initResource(); 79 | } 80 | 81 | void FeatureTensor::loadOnnx(std::string onnxPath) { 82 | auto builder = createInferBuilder(*gLogger); 83 | assert(builder != nullptr); 84 | const auto explicitBatch = 1U << static_cast(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); 85 | auto network = builder->createNetworkV2(explicitBatch); 86 | assert(network != nullptr); 87 | auto config = builder->createBuilderConfig(); 88 | assert(config != nullptr); 89 | 90 | auto profile = builder->createOptimizationProfile(); 91 | Dims dims = Dims4{1, 3, imgShape.height, imgShape.width}; 92 | profile->setDimensions(inputName.c_str(), 93 | OptProfileSelector::kMIN, Dims4{1, dims.d[1], dims.d[2], dims.d[3]}); 94 | profile->setDimensions(inputName.c_str(), 95 | OptProfileSelector::kOPT, Dims4{maxBatchSize, dims.d[1], dims.d[2], dims.d[3]}); 96 | profile->setDimensions(inputName.c_str(), 97 | OptProfileSelector::kMAX, Dims4{maxBatchSize, dims.d[1], dims.d[2], dims.d[3]}); 98 | config->addOptimizationProfile(profile); 99 | 100 | nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, *gLogger); 101 | assert(parser != nullptr); 102 | auto parsed = parser->parseFromFile(onnxPath.c_str(), static_cast(ILogger::Severity::kWARNING)); 103 | assert(parsed); 104 | config->setMaxWorkspaceSize(1 << 20); 105 | engine = builder->buildEngineWithConfig(*network, *config); 106 | assert(engine != nullptr); 107 | context = engine->createExecutionContext(); 108 | assert(context != nullptr); 109 | initResource(); 110 | } 111 | 112 | int FeatureTensor::getResult(float*& buffer) { 113 | if (buffer != nullptr) 114 | delete buffer; 115 | int curStreamSize = curBatchSize*featureDim; 116 | buffer = new float[curStreamSize]; 117 | for (int i = 0; i < curStreamSize; ++i) { 118 | buffer[i] = outputBuffer[i]; 119 | } 120 | return curStreamSize; 121 | } 122 | 123 | void FeatureTensor::doInference(vector& imgMats) { 124 | mat2stream(imgMats, inputBuffer); 125 | doInference(inputBuffer, outputBuffer); 126 | } 127 | 128 | void FeatureTensor::initResource() { 129 | inputIndex = engine->getBindingIndex(inputName.c_str()); 130 | outputIndex = engine->getBindingIndex(outputName.c_str()); 131 | // Create CUDA stream 132 | cudaStreamCreate(&cudaStream); 133 | buffers[inputIndex] = inputBuffer; 134 | buffers[outputIndex] = outputBuffer; 135 | 136 | // Malloc CUDA memory 137 | cudaMalloc(&buffers[inputIndex], inputStreamSize * sizeof(float)); 138 | cudaMalloc(&buffers[outputIndex], outputStreamSize * sizeof(float)); 139 | 140 | initFlag = true; 141 | } 142 | 143 | void FeatureTensor::doInference(float* inputBuffer, float* outputBuffer) { 144 | cudaMemcpyAsync(buffers[inputIndex], inputBuffer, inputStreamSize * sizeof(float), cudaMemcpyHostToDevice, cudaStream); 145 | Dims4 inputDims{curBatchSize, 3, imgShape.height, imgShape.width}; 146 | context->setBindingDimensions(0, inputDims); 147 | 148 | context->enqueueV2(buffers, cudaStream, nullptr); 149 | cudaMemcpyAsync(outputBuffer, buffers[outputIndex], outputStreamSize * sizeof(float), cudaMemcpyDeviceToHost, cudaStream); 150 | // cudaStreamSynchronize(cudaStream); 151 | } 152 | 153 | void FeatureTensor::mat2stream(vector& imgMats, float* stream) { 154 | int imgArea = imgShape.area(); 155 | curBatchSize = imgMats.size(); 156 | if (curBatchSize > maxBatchSize) { 157 | std::cout << "[WARNING]::Batch size overflow, input will be truncated!" << std::endl; 158 | curBatchSize = maxBatchSize; 159 | } 160 | for (int batch = 0; batch < curBatchSize; ++batch) { 161 | cv::Mat tempMat = imgMats[batch]; 162 | int i = 0; 163 | for (int row = 0; row < imgShape.height; ++row) { 164 | uchar* uc_pixel = tempMat.data + row * tempMat.step; 165 | for (int col = 0; col < imgShape.width; ++col) { 166 | stream[batch * 3 * imgArea + i] = ((float)uc_pixel[0] / 255.0 - means[0]) / std[0]; 167 | stream[batch * 3 * imgArea + i + imgArea] = ((float)uc_pixel[1] / 255.0 - means[1]) / std[1]; 168 | stream[batch * 3 * imgArea + i + 2 * imgArea] = ((float)uc_pixel[2] / 255.0 - means[2]) / std[2]; 169 | uc_pixel += 3; 170 | ++i; 171 | } 172 | } 173 | } 174 | } 175 | 176 | void FeatureTensor::stream2det(float* stream, DETECTIONS& det) { 177 | int i = 0; 178 | for (DETECTION_ROW& dbox : det) { 179 | for (int j = 0; j < featureDim; ++j) 180 | dbox.feature[j] = stream[i * featureDim + j]; 181 | // dbox.feature[j] = (float)1.0; 182 | ++i; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/hungarianoper.cpp: -------------------------------------------------------------------------------- 1 | #include "hungarianoper.h" 2 | 3 | Eigen::Matrix HungarianOper::Solve(const DYNAMICM &cost_matrix) { 4 | int rows = cost_matrix.rows(); 5 | int cols = cost_matrix.cols(); 6 | Matrix matrix(rows, cols); 7 | for (int row = 0; row < rows; row++) { 8 | for (int col = 0; col < cols; col++) { 9 | matrix(row, col) = cost_matrix(row, col); 10 | } 11 | } 12 | //Munkres get matrix; 13 | Munkres m; 14 | m.solve(matrix); 15 | 16 | // 17 | std::vector> pairs; 18 | for (int row = 0; row < rows; row++) { 19 | for (int col = 0; col < cols; col++) { 20 | int tmp = (int)matrix(row, col); 21 | if (tmp == 0) pairs.push_back(std::make_pair(row, col)); 22 | } 23 | } 24 | // 25 | int count = pairs.size(); 26 | Eigen::Matrix re(count, 2); 27 | for (int i = 0; i < count; i++) { 28 | re(i, 0) = pairs[i].first; 29 | re(i, 1) = pairs[i].second; 30 | } 31 | return re; 32 | }//end Solve; 33 | -------------------------------------------------------------------------------- /src/kalmanfilter.cpp: -------------------------------------------------------------------------------- 1 | #include "kalmanfilter.h" 2 | #include 3 | #include 4 | 5 | const double KalmanFilter::chi2inv95[10] = { 6 | 0, 7 | 3.8415, 8 | 5.9915, 9 | 7.8147, 10 | 9.4877, 11 | 11.070, 12 | 12.592, 13 | 14.067, 14 | 15.507, 15 | 16.919 16 | }; 17 | KalmanFilter::KalmanFilter() { 18 | int ndim = 4; 19 | double dt = 1.; 20 | 21 | _motion_mat = Eigen::MatrixXf::Identity(8, 8); 22 | for(int i = 0; i < ndim; i++) { 23 | _motion_mat(i, ndim+i) = dt; 24 | } 25 | _update_mat = Eigen::MatrixXf::Identity(4, 8); 26 | 27 | this->_std_weight_position = 1. / 20; 28 | this->_std_weight_velocity = 1. / 160; 29 | } 30 | 31 | KAL_DATA KalmanFilter::initiate(const DETECTBOX& measurement) { 32 | DETECTBOX mean_pos = measurement; 33 | DETECTBOX mean_vel; 34 | for(int i = 0; i < 4; i++) mean_vel(i) = 0; 35 | 36 | KAL_MEAN mean; 37 | for(int i = 0; i < 8; i++){ 38 | if(i < 4) mean(i) = mean_pos(i); 39 | else mean(i) = mean_vel(i - 4); 40 | } 41 | 42 | KAL_MEAN std; 43 | std(0) = 2 * _std_weight_position * measurement[3]; 44 | std(1) = 2 * _std_weight_position * measurement[3]; 45 | std(2) = 1e-2; 46 | std(3) = 2 * _std_weight_position * measurement[3]; 47 | std(4) = 10 * _std_weight_velocity * measurement[3]; 48 | std(5) = 10 * _std_weight_velocity * measurement[3]; 49 | std(6) = 1e-5; 50 | std(7) = 10 * _std_weight_velocity * measurement[3]; 51 | 52 | KAL_MEAN tmp = std.array().square(); 53 | KAL_COVA var = tmp.asDiagonal(); 54 | return std::make_pair(mean, var); 55 | } 56 | 57 | void KalmanFilter::predict(KAL_MEAN &mean, KAL_COVA &covariance) { 58 | //revise the data; 59 | DETECTBOX std_pos; 60 | std_pos << _std_weight_position * mean(3), 61 | _std_weight_position * mean(3), 62 | 1e-2, 63 | _std_weight_position * mean(3); 64 | DETECTBOX std_vel; 65 | std_vel << _std_weight_velocity * mean(3), 66 | _std_weight_velocity * mean(3), 67 | 1e-5, 68 | _std_weight_velocity * mean(3); 69 | KAL_MEAN tmp; 70 | tmp.block<1,4>(0,0) = std_pos; 71 | tmp.block<1,4>(0,4) = std_vel; 72 | tmp = tmp.array().square(); 73 | KAL_COVA motion_cov = tmp.asDiagonal(); 74 | KAL_MEAN mean1 = this->_motion_mat * mean.transpose(); 75 | KAL_COVA covariance1 = this->_motion_mat * covariance *(_motion_mat.transpose()); 76 | covariance1 += motion_cov; 77 | 78 | mean = mean1; 79 | covariance = covariance1; 80 | } 81 | 82 | KAL_HDATA KalmanFilter::project(const KAL_MEAN &mean, const KAL_COVA &covariance) { 83 | DETECTBOX std; 84 | std << _std_weight_position * mean(3), _std_weight_position * mean(3), 85 | 1e-1, _std_weight_position * mean(3); 86 | KAL_HMEAN mean1 = _update_mat * mean.transpose(); 87 | KAL_HCOVA covariance1 = _update_mat * covariance * (_update_mat.transpose()); 88 | Eigen::Matrix diag = std.asDiagonal(); 89 | diag = diag.array().square().matrix(); 90 | covariance1 += diag; 91 | // covariance1.diagonal() << diag; 92 | return std::make_pair(mean1, covariance1); 93 | } 94 | 95 | KAL_DATA 96 | KalmanFilter::update( 97 | const KAL_MEAN &mean, 98 | const KAL_COVA &covariance, 99 | const DETECTBOX &measurement) { 100 | KAL_HDATA pa = project(mean, covariance); 101 | KAL_HMEAN projected_mean = pa.first; 102 | KAL_HCOVA projected_cov = pa.second; 103 | 104 | //chol_factor, lower = 105 | //scipy.linalg.cho_factor(projected_cov, lower=True, check_finite=False) 106 | //kalmain_gain = 107 | //scipy.linalg.cho_solve((cho_factor, lower), 108 | //np.dot(covariance, self._upadte_mat.T).T, 109 | //check_finite=False).T 110 | Eigen::Matrix B = (covariance * (_update_mat.transpose())).transpose(); 111 | Eigen::Matrix kalman_gain = (projected_cov.llt().solve(B)).transpose(); // eg.8x4 112 | Eigen::Matrix innovation = measurement - projected_mean; //eg.1x4 113 | auto tmp = innovation*(kalman_gain.transpose()); 114 | KAL_MEAN new_mean = (mean.array() + tmp.array()).matrix(); 115 | KAL_COVA new_covariance = covariance - kalman_gain*projected_cov*(kalman_gain.transpose()); 116 | return std::make_pair(new_mean, new_covariance); 117 | } 118 | 119 | Eigen::Matrix 120 | KalmanFilter::gating_distance( 121 | const KAL_MEAN &mean, 122 | const KAL_COVA &covariance, 123 | const std::vector &measurements, 124 | bool only_position) { 125 | KAL_HDATA pa = this->project(mean, covariance); 126 | if(only_position) { 127 | printf("not implement!"); 128 | exit(0); 129 | } 130 | KAL_HMEAN mean1 = pa.first; 131 | KAL_HCOVA covariance1 = pa.second; 132 | 133 | // Eigen::Matrix d(size, 4); 134 | DETECTBOXSS d(measurements.size(), 4); 135 | int pos = 0; 136 | for(DETECTBOX box:measurements) { 137 | d.row(pos++) = box - mean1; 138 | } 139 | Eigen::Matrix factor = covariance1.llt().matrixL(); 140 | Eigen::Matrix z = factor.triangularView().solve(d).transpose(); 141 | auto zz = ((z.array())*(z.array())).matrix(); 142 | auto square_maha = zz.colwise().sum(); 143 | return square_maha; 144 | } 145 | 146 | -------------------------------------------------------------------------------- /src/linear_assignment.cpp: -------------------------------------------------------------------------------- 1 | #include "linear_assignment.h" 2 | #include "hungarianoper.h" 3 | #include 4 | 5 | linear_assignment *linear_assignment::instance = NULL; 6 | linear_assignment::linear_assignment() 7 | { 8 | } 9 | 10 | linear_assignment *linear_assignment::getInstance() 11 | { 12 | if(instance == NULL) instance = new linear_assignment(); 13 | return instance; 14 | } 15 | 16 | TRACHER_MATCHD 17 | linear_assignment::matching_cascade( 18 | tracker *distance_metric, 19 | tracker::GATED_METRIC_FUNC distance_metric_func, 20 | float max_distance, 21 | int cascade_depth, 22 | std::vector &tracks, 23 | const DETECTIONS &detections, 24 | std::vector& track_indices, 25 | std::vector detection_indices) 26 | { 27 | TRACHER_MATCHD res; 28 | // std::cout << "distance_metric" << distance_metric << std::endl; 29 | // std::cout << "max_distance" << max_distance << std::endl; 30 | // std::cout << "cascade_depth" << cascade_depth << std::endl; 31 | // std::cout << "tracks [" << std::endl; 32 | // for (auto i : tracks) 33 | // std::cout << i.hits << ", "; 34 | // std::cout << "]" << endl; 35 | // std::cout << "detections [" << std::endl; 36 | // for (auto i : detections) 37 | // std::cout << i.confidence << ", "; 38 | // std::cout << "]" << endl; 39 | // std::cout << "track_indices [" << std::endl; 40 | // for (auto i : track_indices) 41 | // std::cout << i << ", "; 42 | // std::cout << "]" << endl; 43 | // std::cout << "detection_indices [" << std::endl; 44 | // for (auto i : detection_indices) 45 | // std::cout << i << ", "; 46 | // std::cout << "]" << endl; 47 | // !!!python diff: track_indices will never be None. 48 | // if(track_indices.empty() == true) { 49 | // for(size_t i = 0; i < tracks.size(); i++) { 50 | // track_indices.push_back(i); 51 | // } 52 | // } 53 | 54 | //!!!python diff: detection_indices will always be None. 55 | for(size_t i = 0; i < detections.size(); i++) { 56 | detection_indices.push_back(int(i)); 57 | } 58 | 59 | std::vector unmatched_detections; 60 | unmatched_detections.assign(detection_indices.begin(), detection_indices.end()); 61 | res.matches.clear(); 62 | std::vector track_indices_l; 63 | 64 | std::map matches_trackid; 65 | for(int level = 0; level < cascade_depth; level++) { 66 | if(unmatched_detections.size() == 0) break; //No detections left; 67 | 68 | track_indices_l.clear(); 69 | for(int k:track_indices) { 70 | if(tracks[k].time_since_update == 1+level) 71 | track_indices_l.push_back(k); 72 | } 73 | if(track_indices_l.size() == 0) continue; //Nothing to match at this level. 74 | 75 | TRACHER_MATCHD tmp = min_cost_matching( 76 | distance_metric, distance_metric_func, 77 | max_distance, tracks, detections, track_indices_l, 78 | unmatched_detections); 79 | unmatched_detections.assign(tmp.unmatched_detections.begin(), tmp.unmatched_detections.end()); 80 | for(size_t i = 0; i < tmp.matches.size(); i++) { 81 | MATCH_DATA pa = tmp.matches[i]; 82 | res.matches.push_back(pa); 83 | matches_trackid.insert(pa); 84 | } 85 | } 86 | res.unmatched_detections.assign(unmatched_detections.begin(), unmatched_detections.end()); 87 | for(size_t i = 0; i < track_indices.size(); i++) { 88 | int tid = track_indices[i]; 89 | if(matches_trackid.find(tid) == matches_trackid.end()) 90 | res.unmatched_tracks.push_back(tid); 91 | } 92 | return res; 93 | } 94 | 95 | TRACHER_MATCHD 96 | linear_assignment::min_cost_matching(tracker *distance_metric, 97 | tracker::GATED_METRIC_FUNC distance_metric_func, 98 | float max_distance, 99 | std::vector &tracks, 100 | const DETECTIONS &detections, 101 | std::vector &track_indices, 102 | std::vector &detection_indices) 103 | { 104 | TRACHER_MATCHD res; 105 | //!!!python diff: track_indices && detection_indices will never be None. 106 | // if(track_indices.empty() == true) { 107 | // for(size_t i = 0; i < tracks.size(); i++) { 108 | // track_indices.push_back(i); 109 | // } 110 | // } 111 | // if(detection_indices.empty() == true) { 112 | // for(size_t i = 0; i < detections.size(); i++) { 113 | // detection_indices.push_back(int(i)); 114 | // } 115 | // } 116 | if((detection_indices.size() == 0) || (track_indices.size() == 0)) { 117 | res.matches.clear(); 118 | res.unmatched_tracks.assign(track_indices.begin(), track_indices.end()); 119 | res.unmatched_detections.assign(detection_indices.begin(), detection_indices.end()); 120 | return res; 121 | } 122 | DYNAMICM cost_matrix = (distance_metric->*(distance_metric_func))( 123 | tracks, detections, track_indices, detection_indices); 124 | for(int i = 0; i < cost_matrix.rows(); i++) { 125 | for(int j = 0; j < cost_matrix.cols(); j++) { 126 | float tmp = cost_matrix(i,j); 127 | if(tmp > max_distance) cost_matrix(i,j) = max_distance + 1e-5; 128 | } 129 | } 130 | Eigen::Matrix indices = HungarianOper::Solve(cost_matrix); 131 | res.matches.clear(); 132 | res.unmatched_tracks.clear(); 133 | res.unmatched_detections.clear(); 134 | for(size_t col = 0; col < detection_indices.size(); col++) { 135 | bool flag = false; 136 | for(int i = 0; i < indices.rows(); i++) 137 | if(indices(i, 1) == col) { flag = true; break;} 138 | if(flag == false)res.unmatched_detections.push_back(detection_indices[col]); 139 | } 140 | for(size_t row = 0; row < track_indices.size(); row++) { 141 | bool flag = false; 142 | for(int i = 0; i < indices.rows(); i++) 143 | if(indices(i, 0) == row) { flag = true; break; } 144 | if(flag == false) res.unmatched_tracks.push_back(track_indices[row]); 145 | } 146 | for(int i = 0; i < indices.rows(); i++) { 147 | int row = indices(i, 0); 148 | int col = indices(i, 1); 149 | 150 | int track_idx = track_indices[row]; 151 | int detection_idx = detection_indices[col]; 152 | if(cost_matrix(row, col) > max_distance) { 153 | res.unmatched_tracks.push_back(track_idx); 154 | res.unmatched_detections.push_back(detection_idx); 155 | } else res.matches.push_back(std::make_pair(track_idx, detection_idx)); 156 | } 157 | return res; 158 | } 159 | 160 | DYNAMICM 161 | linear_assignment::gate_cost_matrix( 162 | KalmanFilter *kf, 163 | DYNAMICM &cost_matrix, 164 | std::vector &tracks, 165 | const DETECTIONS &detections, 166 | const std::vector &track_indices, 167 | const std::vector &detection_indices, 168 | float gated_cost, bool only_position) 169 | { 170 | // std::cout << "input cost matric" << cost_matrix << std::endl; 171 | int gating_dim = (only_position == true?2:4); 172 | double gating_threshold = KalmanFilter::chi2inv95[gating_dim]; 173 | std::vector measurements; 174 | for(int i:detection_indices) { 175 | DETECTION_ROW t = detections[i]; 176 | measurements.push_back(t.to_xyah()); 177 | } 178 | for(size_t i = 0; i < track_indices.size(); i++) { 179 | Track& track = tracks[track_indices[i]]; 180 | Eigen::Matrix gating_distance = kf->gating_distance( 181 | track.mean, track.covariance, measurements, only_position); 182 | for (int j = 0; j < gating_distance.cols(); j++) { 183 | if (gating_distance(0, j) > gating_threshold) cost_matrix(i, j) = gated_cost; 184 | } 185 | } 186 | // std::cout << "out cost matrix" << cost_matrix << std::endl; 187 | return cost_matrix; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /src/munkres.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 John Weaver 3 | * Copyright (c) 2015 Miroslav Krajicek 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | */ 19 | 20 | #include "munkres.h" 21 | 22 | template class Munkres; 23 | template class Munkres; 24 | template class Munkres; 25 | 26 | -------------------------------------------------------------------------------- /src/nn_matching.cpp: -------------------------------------------------------------------------------- 1 | #include "nn_matching.h" 2 | #include 3 | 4 | using namespace Eigen; 5 | 6 | NearNeighborDisMetric::NearNeighborDisMetric( 7 | NearNeighborDisMetric::METRIC_TYPE metric, 8 | float matching_threshold, int budget) 9 | { 10 | if (metric == euclidean) { 11 | _metric = 12 | &NearNeighborDisMetric::_nneuclidean_distance; 13 | } else if (metric == cosine) { 14 | _metric = 15 | &NearNeighborDisMetric::_nncosine_distance; 16 | } 17 | 18 | this->mating_threshold = matching_threshold; 19 | this->budget = budget; 20 | this->samples.clear(); 21 | } 22 | 23 | DYNAMICM 24 | NearNeighborDisMetric::distance( 25 | const FEATURESS & features, 26 | const std::vector < int >&targets) 27 | { 28 | DYNAMICM cost_matrix = Eigen::MatrixXf::Zero(targets.size(), features.rows()); 29 | int idx = 0; 30 | for (int target:targets) { 31 | cost_matrix.row(idx) = (this->*_metric) (this->samples[target], features); 32 | idx++; 33 | } 34 | return cost_matrix; 35 | } 36 | 37 | void 38 | NearNeighborDisMetric::partial_fit( 39 | std::vector &tid_feats, 40 | std::vector < int >&active_targets) 41 | { 42 | /*python code: 43 | * let feature(target_id) append to samples; 44 | * && delete not comfirmed target_id from samples. 45 | * update samples; 46 | */ 47 | for (TRACKER_DATA & data:tid_feats) { 48 | int track_id = data.first; 49 | FEATURESS newFeatOne = data.second; 50 | 51 | if (samples.find(track_id) != samples.end()) { //append 52 | int oldSize = samples[track_id].rows(); 53 | int addSize = newFeatOne.rows(); 54 | int newSize = oldSize + addSize; 55 | 56 | if (newSize <= this->budget) { 57 | FEATURESS newSampleFeatures(newSize, 256); 58 | newSampleFeatures.block(0, 0, oldSize, 256) = samples[track_id]; 59 | newSampleFeatures.block(oldSize, 0, addSize, 256) = newFeatOne; 60 | samples[track_id] = newSampleFeatures; 61 | } else { 62 | if (oldSize < this->budget) { //original space is not enough; 63 | FEATURESS newSampleFeatures(this->budget, 256); 64 | if (addSize >= this->budget) { 65 | newSampleFeatures = newFeatOne.block(0, 0, this->budget, 256); 66 | } else { 67 | newSampleFeatures.block(0, 0, this->budget - addSize, 256) = 68 | samples[track_id].block(addSize - 1, 0, this->budget - addSize, 256).eval(); 69 | newSampleFeatures.block(this->budget - addSize, 0, addSize,256) = newFeatOne; 70 | } 71 | samples[track_id] = newSampleFeatures; 72 | } else { //original space is ok; 73 | if (addSize >= this->budget) { 74 | samples[track_id] = newFeatOne.block(0, 0, this->budget, 256); 75 | } else { 76 | samples[track_id].block(0, 0, this->budget - addSize, 256) = 77 | samples[track_id].block(addSize - 1, 0, this->budget - addSize, 256).eval(); 78 | samples[track_id].block(this->budget - addSize, 0, addSize, 256) = newFeatOne; 79 | } 80 | } 81 | } 82 | } else { //not exit, create new one; 83 | samples[track_id] = newFeatOne; 84 | } 85 | } //add features; 86 | 87 | //erase the samples which not in active_targets; 88 | for (std::map < int, FEATURESS >::iterator i = samples.begin(); i != samples.end();) { 89 | bool flag = false; 90 | for (int j:active_targets) if (j == i->first) { flag = true; break; } 91 | if (flag == false)samples.erase(i++); 92 | else i++; 93 | } 94 | } 95 | 96 | Eigen::VectorXf 97 | NearNeighborDisMetric::_nncosine_distance( 98 | const FEATURESS & x, const FEATURESS & y) 99 | { 100 | MatrixXf distances = _cosine_distance(x, y); 101 | VectorXf res = distances.colwise().minCoeff().transpose(); 102 | return res; 103 | } 104 | 105 | Eigen::VectorXf 106 | NearNeighborDisMetric::_nneuclidean_distance( 107 | const FEATURESS & x, const FEATURESS & y) 108 | { 109 | MatrixXf distances = _pdist(x, y); 110 | VectorXf res = distances.colwise().maxCoeff().transpose(); 111 | res = res.array().max(VectorXf::Zero(res.rows()).array()); 112 | return res; 113 | } 114 | 115 | Eigen::MatrixXf 116 | NearNeighborDisMetric::_pdist(const FEATURESS & x, const FEATURESS & y) 117 | { 118 | int len1 = x.rows(), len2 = y.rows(); 119 | if (len1 == 0 || len2 == 0) { 120 | return Eigen::MatrixXf::Zero(len1, len2); 121 | } 122 | MatrixXf res = -2.0 * x * y.transpose(); 123 | res = res.colwise() + x.rowwise().squaredNorm(); 124 | res = res.rowwise() + y.rowwise().squaredNorm().transpose(); 125 | res = res.array().max(MatrixXf::Zero(res.rows(), res.cols()).array()); 126 | return res; 127 | } 128 | 129 | Eigen::MatrixXf 130 | NearNeighborDisMetric::_cosine_distance( 131 | const FEATURESS & a, const FEATURESS & b, bool data_is_normalized) 132 | { 133 | FEATURESS aa = a; 134 | FEATURESS bb = b; 135 | if (!data_is_normalized) { 136 | //undo: 137 | for (int i = 0; i < a.rows(); ++i) { 138 | aa.row(i) = a.row(i) / sqrt(a.row(i).squaredNorm()); 139 | } 140 | for (int i = 0; i < b.rows(); ++i) { 141 | bb.row(i) = b.row(i) / sqrt(b.row(i).squaredNorm()); 142 | } 143 | } 144 | MatrixXf res = 1. - (aa * bb.transpose()).array(); 145 | return res; 146 | } 147 | -------------------------------------------------------------------------------- /src/track.cpp: -------------------------------------------------------------------------------- 1 | #include "track.h" 2 | #include 3 | 4 | Track::Track(KAL_MEAN & mean, KAL_COVA & covariance, int track_id, int n_init, int max_age, const FEATURE & feature) 5 | { 6 | this->mean = mean; 7 | this->covariance = covariance; 8 | this->track_id = track_id; 9 | this->hits = 1; 10 | this->age = 1; 11 | this->time_since_update = 0; 12 | this->state = TrackState::Tentative; 13 | features = FEATURESS(1, 256); 14 | features.row(0) = feature; //features.rows() must = 0; 15 | 16 | this->_n_init = n_init; 17 | this->_max_age = max_age; 18 | } 19 | 20 | Track::Track(KAL_MEAN & mean, KAL_COVA & covariance, int track_id, int n_init, int max_age, const FEATURE & feature, int cls, float conf) 21 | { 22 | this->mean = mean; 23 | this->covariance = covariance; 24 | this->track_id = track_id; 25 | this->hits = 1; 26 | this->age = 1; 27 | this->time_since_update = 0; 28 | this->state = TrackState::Tentative; 29 | features = FEATURESS(1, 256); 30 | features.row(0) = feature; //features.rows() must = 0; 31 | 32 | this->_n_init = n_init; 33 | this->_max_age = max_age; 34 | 35 | this->cls = cls; 36 | this->conf = conf; 37 | } 38 | 39 | void Track::predit(KalmanFilter * kf) 40 | { 41 | /*Propagate the state distribution to the current time step using a 42 | Kalman filter prediction step. 43 | 44 | Parameters 45 | ---------- 46 | kf : kalman_filter.KalmanFilterd 47 | The Kalman filter. 48 | */ 49 | 50 | kf->predict(this->mean, this->covariance); 51 | 52 | 53 | this->age += 1; 54 | this->time_since_update += 1; 55 | } 56 | 57 | void Track::update(KalmanFilter * const kf, const DETECTION_ROW & detection) 58 | { 59 | KAL_DATA pa = kf->update(this->mean, this->covariance, detection.to_xyah()); 60 | this->mean = pa.first; 61 | this->covariance = pa.second; 62 | 63 | featuresAppendOne(detection.feature); 64 | // this->features.row(features.rows()) = detection.feature; 65 | this->hits += 1; 66 | this->time_since_update = 0; 67 | if (this->state == TrackState::Tentative && this->hits >= this->_n_init) { 68 | this->state = TrackState::Confirmed; 69 | } 70 | } 71 | 72 | void Track::update(KalmanFilter * const kf, const DETECTION_ROW & detection, CLSCONF pair_det) 73 | { 74 | KAL_DATA pa = kf->update(this->mean, this->covariance, detection.to_xyah()); 75 | this->mean = pa.first; 76 | this->covariance = pa.second; 77 | 78 | featuresAppendOne(detection.feature); 79 | // this->features.row(features.rows()) = detection.feature; 80 | this->hits += 1; 81 | this->time_since_update = 0; 82 | if (this->state == TrackState::Tentative && this->hits >= this->_n_init) { 83 | this->state = TrackState::Confirmed; 84 | } 85 | this->cls = pair_det.cls; 86 | this->conf = pair_det.conf; 87 | } 88 | 89 | void Track::mark_missed() 90 | { 91 | if (this->state == TrackState::Tentative) { 92 | this->state = TrackState::Deleted; 93 | } else if (this->time_since_update > this->_max_age) { 94 | this->state = TrackState::Deleted; 95 | } 96 | } 97 | 98 | bool Track::is_confirmed() 99 | { 100 | return this->state == TrackState::Confirmed; 101 | } 102 | 103 | bool Track::is_deleted() 104 | { 105 | return this->state == TrackState::Deleted; 106 | } 107 | 108 | bool Track::is_tentative() 109 | { 110 | return this->state == TrackState::Tentative; 111 | } 112 | 113 | DETECTBOX Track::to_tlwh() 114 | { 115 | DETECTBOX ret = mean.leftCols(4); 116 | ret(2) *= ret(3); 117 | ret.leftCols(2) -= (ret.rightCols(2) / 2); 118 | return ret; 119 | } 120 | 121 | void Track::featuresAppendOne(const FEATURE & f) 122 | { 123 | int size = this->features.rows(); 124 | FEATURESS newfeatures = FEATURESS(size + 1, 256); 125 | newfeatures.block(0, 0, size, 256) = this->features; 126 | newfeatures.row(size) = f; 127 | features = newfeatures; 128 | } 129 | -------------------------------------------------------------------------------- /src/tracker.cpp: -------------------------------------------------------------------------------- 1 | #include "tracker.h" 2 | #include "nn_matching.h" 3 | #include "linear_assignment.h" 4 | using namespace std; 5 | 6 | #define MY_inner_DEBUG 7 | #ifdef MY_inner_DEBUG 8 | #include 9 | #include 10 | #endif 11 | using namespace std; 12 | tracker::tracker( /*NearNeighborDisMetric *metric, */ 13 | float max_cosine_distance, int nn_budget, 14 | float max_iou_distance, int max_age, int n_init) 15 | { 16 | this->metric = new NearNeighborDisMetric( 17 | NearNeighborDisMetric::METRIC_TYPE::cosine, 18 | max_cosine_distance, nn_budget); 19 | this->max_iou_distance = max_iou_distance; 20 | this->max_age = max_age; 21 | this->n_init = n_init; 22 | 23 | this->kf = new KalmanFilter(); 24 | this->tracks.clear(); 25 | this->_next_idx = 1; 26 | } 27 | 28 | void tracker::predict() 29 | { 30 | for (Track & track:tracks) { 31 | track.predit(kf); 32 | } 33 | } 34 | 35 | void tracker::update(const DETECTIONS & detections) 36 | { 37 | TRACHER_MATCHD res; 38 | _match(detections, res); 39 | 40 | vector < MATCH_DATA > &matches = res.matches; 41 | for (MATCH_DATA & data:matches) { 42 | int track_idx = data.first; 43 | int detection_idx = data.second; 44 | tracks[track_idx].update(this->kf, detections[detection_idx]); 45 | } 46 | vector < int >&unmatched_tracks = res.unmatched_tracks; 47 | for (int &track_idx:unmatched_tracks) { 48 | this->tracks[track_idx].mark_missed(); 49 | } 50 | vector < int >&unmatched_detections = res.unmatched_detections; 51 | for (int &detection_idx:unmatched_detections) { 52 | this->_initiate_track(detections[detection_idx]); 53 | } 54 | vector < Track >::iterator it; 55 | for (it = tracks.begin(); it != tracks.end();) { 56 | if ((*it).is_deleted()) it = tracks.erase(it); 57 | else ++it; 58 | } 59 | vector < int >active_targets; 60 | vector < TRACKER_DATA > tid_features; 61 | for (Track & track:tracks) { 62 | if (track.is_confirmed() == false) continue; 63 | active_targets.push_back(track.track_id); 64 | tid_features.push_back(std::make_pair(track. track_id, track.features)); 65 | FEATURESS t = FEATURESS(0, 256); 66 | track.features = t; 67 | } 68 | this->metric->partial_fit(tid_features, active_targets); 69 | } 70 | 71 | void tracker::update(const DETECTIONSV2 & detectionsv2) 72 | { 73 | const vector& clsConf = detectionsv2.first; 74 | const DETECTIONS& detections = detectionsv2.second; 75 | TRACHER_MATCHD res; 76 | _match(detections, res); 77 | 78 | vector < MATCH_DATA > &matches = res.matches; 79 | for (MATCH_DATA & data:matches) { 80 | int track_idx = data.first; 81 | int detection_idx = data.second; 82 | tracks[track_idx].update(this->kf, detections[detection_idx], clsConf[detection_idx]); 83 | } 84 | vector < int >&unmatched_tracks = res.unmatched_tracks; 85 | for (int &track_idx:unmatched_tracks) { 86 | this->tracks[track_idx].mark_missed(); 87 | } 88 | vector < int >&unmatched_detections = res.unmatched_detections; 89 | for (int &detection_idx:unmatched_detections) { 90 | this->_initiate_track(detections[detection_idx], clsConf[detection_idx]); 91 | } 92 | vector < Track >::iterator it; 93 | for (it = tracks.begin(); it != tracks.end();) { 94 | if ((*it).is_deleted()) it = tracks.erase(it); 95 | else ++it; 96 | } 97 | vector < int >active_targets; 98 | vector < TRACKER_DATA > tid_features; 99 | for (Track & track:tracks) { 100 | if (track.is_confirmed() == false) continue; 101 | active_targets.push_back(track.track_id); 102 | tid_features.push_back(std::make_pair(track. track_id, track.features)); 103 | FEATURESS t = FEATURESS(0, 256); 104 | track.features = t; 105 | } 106 | this->metric->partial_fit(tid_features, active_targets); 107 | } 108 | 109 | void tracker::_match(const DETECTIONS & detections, TRACHER_MATCHD & res) 110 | { 111 | vector < int >confirmed_tracks; 112 | vector < int >unconfirmed_tracks; 113 | int idx = 0; 114 | for (Track & t:tracks) { 115 | if (t.is_confirmed()) confirmed_tracks.push_back(idx); 116 | else unconfirmed_tracks.push_back(idx); 117 | idx++; 118 | } 119 | 120 | TRACHER_MATCHD matcha = linear_assignment::getInstance()-> matching_cascade( 121 | this, &tracker::gated_matric, 122 | this->metric->mating_threshold, 123 | this->max_age, 124 | this->tracks, 125 | detections, 126 | confirmed_tracks); 127 | vector < int >iou_track_candidates; 128 | iou_track_candidates.assign(unconfirmed_tracks.begin(), unconfirmed_tracks.end()); 129 | vector < int >::iterator it; 130 | for (it = matcha.unmatched_tracks.begin(); it != matcha.unmatched_tracks.end();) { 131 | int idx = *it; 132 | if (tracks[idx].time_since_update == 1) { //push into unconfirmed 133 | iou_track_candidates.push_back(idx); 134 | it = matcha.unmatched_tracks.erase(it); 135 | continue; 136 | } 137 | ++it; 138 | } 139 | TRACHER_MATCHD matchb = linear_assignment::getInstance()->min_cost_matching( 140 | this, &tracker::iou_cost, 141 | this->max_iou_distance, 142 | this->tracks, 143 | detections, 144 | iou_track_candidates, 145 | matcha.unmatched_detections); 146 | //get result: 147 | res.matches.assign(matcha.matches.begin(), matcha.matches.end()); 148 | res.matches.insert(res.matches.end(), matchb.matches.begin(), matchb.matches.end()); 149 | //unmatched_tracks; 150 | res.unmatched_tracks.assign( 151 | matcha.unmatched_tracks.begin(), 152 | matcha.unmatched_tracks.end()); 153 | res.unmatched_tracks.insert( 154 | res.unmatched_tracks.end(), 155 | matchb.unmatched_tracks.begin(), 156 | matchb.unmatched_tracks.end()); 157 | res.unmatched_detections.assign( 158 | matchb.unmatched_detections.begin(), 159 | matchb.unmatched_detections.end()); 160 | } 161 | 162 | void tracker::_initiate_track(const DETECTION_ROW & detection) 163 | { 164 | KAL_DATA data = kf->initiate(detection.to_xyah()); 165 | KAL_MEAN mean = data.first; 166 | KAL_COVA covariance = data.second; 167 | 168 | this->tracks.push_back(Track(mean, covariance, this->_next_idx, this->n_init, 169 | this->max_age, detection.feature)); 170 | _next_idx += 1; 171 | } 172 | void tracker::_initiate_track(const DETECTION_ROW& detection, CLSCONF clsConf) 173 | { 174 | KAL_DATA data = kf->initiate(detection.to_xyah()); 175 | KAL_MEAN mean = data.first; 176 | KAL_COVA covariance = data.second; 177 | 178 | this->tracks.push_back(Track(mean, covariance, this->_next_idx, this->n_init, 179 | this->max_age, detection.feature, clsConf.cls, clsConf.conf)); 180 | _next_idx += 1; 181 | } 182 | 183 | DYNAMICM tracker::gated_matric( 184 | std::vector < Track > &tracks, 185 | const DETECTIONS & dets, 186 | const std::vector < int >&track_indices, 187 | const std::vector < int >&detection_indices) 188 | { 189 | FEATURESS features(detection_indices.size(), 256); 190 | int pos = 0; 191 | for (int i:detection_indices) { 192 | features.row(pos++) = dets[i].feature; 193 | } 194 | vector < int >targets; 195 | for (int i:track_indices) { 196 | targets.push_back(tracks[i].track_id); 197 | } 198 | DYNAMICM cost_matrix = this->metric->distance(features, targets); 199 | DYNAMICM res = linear_assignment::getInstance()->gate_cost_matrix( 200 | this->kf, cost_matrix, tracks, dets, track_indices, 201 | detection_indices); 202 | return res; 203 | } 204 | 205 | DYNAMICM 206 | tracker::iou_cost( 207 | std::vector < Track > &tracks, 208 | const DETECTIONS & dets, 209 | const std::vector < int >&track_indices, 210 | const std::vector < int >&detection_indices) 211 | { 212 | //!!!python diff: track_indices && detection_indices will never be None. 213 | // if(track_indices.empty() == true) { 214 | // for(size_t i = 0; i < tracks.size(); i++) { 215 | // track_indices.push_back(i); 216 | // } 217 | // } 218 | // if(detection_indices.empty() == true) { 219 | // for(size_t i = 0; i < dets.size(); i++) { 220 | // detection_indices.push_back(i); 221 | // } 222 | // } 223 | int rows = track_indices.size(); 224 | int cols = detection_indices.size(); 225 | DYNAMICM cost_matrix = Eigen::MatrixXf::Zero(rows, cols); 226 | for (int i = 0; i < rows; i++) { 227 | int track_idx = track_indices[i]; 228 | if (tracks[track_idx].time_since_update > 1) { 229 | cost_matrix.row(i) = Eigen::RowVectorXf::Constant(cols, INFTY_COST); 230 | continue; 231 | } 232 | DETECTBOX bbox = tracks[track_idx].to_tlwh(); 233 | int csize = detection_indices.size(); 234 | DETECTBOXSS candidates(csize, 4); 235 | for (int k = 0; k < csize; k++) candidates.row(k) = dets[detection_indices[k]].tlwh; 236 | Eigen::RowVectorXf rowV = (1. - iou(bbox, candidates).array()).matrix().transpose(); 237 | cost_matrix.row(i) = rowV; 238 | } 239 | return cost_matrix; 240 | } 241 | 242 | Eigen::VectorXf 243 | tracker::iou(DETECTBOX & bbox, DETECTBOXSS & candidates) 244 | { 245 | float bbox_tl_1 = bbox[0]; 246 | float bbox_tl_2 = bbox[1]; 247 | float bbox_br_1 = bbox[0] + bbox[2]; 248 | float bbox_br_2 = bbox[1] + bbox[3]; 249 | float area_bbox = bbox[2] * bbox[3]; 250 | 251 | Eigen::Matrix < float, -1, 2 > candidates_tl; 252 | Eigen::Matrix < float, -1, 2 > candidates_br; 253 | candidates_tl = candidates.leftCols(2); 254 | candidates_br = candidates.rightCols(2) + candidates_tl; 255 | 256 | int size = int (candidates.rows()); 257 | // Eigen::VectorXf area_intersection(size); 258 | // Eigen::VectorXf area_candidates(size); 259 | Eigen::VectorXf res(size); 260 | for (int i = 0; i < size; i++) { 261 | float tl_1 = std::max(bbox_tl_1, candidates_tl(i, 0)); 262 | float tl_2 = std::max(bbox_tl_2, candidates_tl(i, 1)); 263 | float br_1 = std::min(bbox_br_1, candidates_br(i, 0)); 264 | float br_2 = std::min(bbox_br_2, candidates_br(i, 1)); 265 | 266 | float w = br_1 - tl_1; w = (w < 0 ? 0 : w); 267 | float h = br_2 - tl_2; h = (h < 0 ? 0 : h); 268 | float area_intersection = w * h; 269 | float area_candidates = candidates(i, 2) * candidates(i, 3); 270 | res[i] = area_intersection / (area_bbox + area_candidates - area_intersection); 271 | } 272 | return res; 273 | } 274 | --------------------------------------------------------------------------------